@workflow/next 4.0.1-beta.6 → 4.0.1-beta.61

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.
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serializeMessage = serializeMessage;
4
+ exports.parseMessage = parseMessage;
5
+ exports.createSocketServer = createSocketServer;
6
+ const node_crypto_1 = require("node:crypto");
7
+ const promises_1 = require("node:fs/promises");
8
+ const node_net_1 = require("node:net");
9
+ const node_path_1 = require("node:path");
10
+ /**
11
+ * Magic preamble that must prefix all messages to authenticate them as workflow messages.
12
+ * This prevents accidental processing of messages from port scanners or other local processes.
13
+ */
14
+ const MESSAGE_PREAMBLE = 'WF:';
15
+ /**
16
+ * Generate a random authentication token for this server session.
17
+ * Clients must include this token in all messages.
18
+ */
19
+ function generateAuthToken() {
20
+ return (0, node_crypto_1.randomBytes)(16).toString('hex');
21
+ }
22
+ function getDefaultSocketInfoFilePath() {
23
+ return (0, node_path_1.join)(process.cwd(), '.next', 'cache', 'workflow-socket.json');
24
+ }
25
+ /**
26
+ * Serialize a message with authentication preamble
27
+ */
28
+ function serializeMessage(message, authToken) {
29
+ return `${MESSAGE_PREAMBLE}${authToken}:${JSON.stringify(message)}\n`;
30
+ }
31
+ /**
32
+ * Parse and authenticate a message from the socket
33
+ * Returns the parsed message if valid, null otherwise
34
+ */
35
+ function parseMessage(line, authToken) {
36
+ const trimmed = line.trim();
37
+ if (!trimmed) {
38
+ return null;
39
+ }
40
+ // Check for preamble
41
+ if (!trimmed.startsWith(MESSAGE_PREAMBLE)) {
42
+ console.warn('Received message without valid preamble, ignoring');
43
+ return null;
44
+ }
45
+ // Extract auth token and payload
46
+ const withoutPreamble = trimmed.slice(MESSAGE_PREAMBLE.length);
47
+ const colonIndex = withoutPreamble.indexOf(':');
48
+ if (colonIndex === -1) {
49
+ console.warn('Received message without auth token separator, ignoring');
50
+ return null;
51
+ }
52
+ const messageToken = withoutPreamble.slice(0, colonIndex);
53
+ const payload = withoutPreamble.slice(colonIndex + 1);
54
+ // Verify auth token
55
+ if (messageToken !== authToken) {
56
+ console.warn('Received message with invalid auth token, ignoring');
57
+ return null;
58
+ }
59
+ // Parse JSON payload
60
+ try {
61
+ return JSON.parse(payload);
62
+ }
63
+ catch (error) {
64
+ console.error('Failed to parse socket message JSON:', error);
65
+ return null;
66
+ }
67
+ }
68
+ /**
69
+ * Create a TCP socket server for loader<->builder communication.
70
+ * Returns a SocketIO interface for broadcasting messages and the auth token.
71
+ *
72
+ * SECURITY: Server listens on 127.0.0.1 (localhost only) and uses
73
+ * message authentication to prevent processing of unauthorized messages.
74
+ */
75
+ async function createSocketServer(config) {
76
+ const authToken = generateAuthToken();
77
+ const clients = new Set();
78
+ let buildTriggered = false;
79
+ const server = (0, node_net_1.createServer)((socket) => {
80
+ socket.setNoDelay(true);
81
+ clients.add(socket);
82
+ // Send build-complete if build already finished (production mode)
83
+ if (buildTriggered && !config.isDevServer) {
84
+ socket.write(serializeMessage({ type: 'build-complete' }, authToken));
85
+ }
86
+ let buffer = '';
87
+ socket.on('data', (data) => {
88
+ buffer += data.toString();
89
+ // Process complete messages (newline-delimited)
90
+ let newlineIndex = buffer.indexOf('\n');
91
+ while (newlineIndex !== -1) {
92
+ const line = buffer.slice(0, newlineIndex);
93
+ buffer = buffer.slice(newlineIndex + 1);
94
+ newlineIndex = buffer.indexOf('\n');
95
+ const message = parseMessage(line, authToken);
96
+ if (!message) {
97
+ continue;
98
+ }
99
+ if (message.type === 'file-discovered') {
100
+ config.onFileDiscovered(message.filePath, message.hasWorkflow, message.hasStep, message.hasSerde);
101
+ }
102
+ else if (message.type === 'trigger-build') {
103
+ config.onTriggerBuild();
104
+ }
105
+ }
106
+ });
107
+ socket.on('end', () => {
108
+ clients.delete(socket);
109
+ });
110
+ socket.on('error', (err) => {
111
+ console.error('Socket error:', err);
112
+ clients.delete(socket);
113
+ });
114
+ });
115
+ // Listen on random available port (localhost only)
116
+ await new Promise((resolve, reject) => {
117
+ server.once('error', reject);
118
+ server.listen(0, '127.0.0.1', () => {
119
+ const address = server.address();
120
+ if (address && typeof address === 'object') {
121
+ const socketInfoFilePath = config.socketInfoFilePath || getDefaultSocketInfoFilePath();
122
+ void (async () => {
123
+ try {
124
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(socketInfoFilePath), { recursive: true });
125
+ await (0, promises_1.writeFile)(socketInfoFilePath, JSON.stringify({
126
+ port: address.port,
127
+ authToken,
128
+ }, null, 2));
129
+ process.env.WORKFLOW_SOCKET_INFO_PATH = socketInfoFilePath;
130
+ process.env.WORKFLOW_SOCKET_PORT = String(address.port);
131
+ process.env.WORKFLOW_SOCKET_AUTH = authToken;
132
+ resolve();
133
+ }
134
+ catch (error) {
135
+ reject(error);
136
+ }
137
+ })();
138
+ return;
139
+ }
140
+ reject(new Error('Failed to obtain workflow socket server address'));
141
+ });
142
+ });
143
+ return {
144
+ emit: (_event) => {
145
+ buildTriggered = true;
146
+ const message = serializeMessage({ type: 'build-complete' }, authToken);
147
+ for (const client of clients) {
148
+ client.write(message);
149
+ }
150
+ },
151
+ getAuthToken: () => authToken,
152
+ };
153
+ }
154
+ //# sourceMappingURL=socket-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket-server.js","sourceRoot":"","sources":["../src/socket-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export declare const DEFERRED_STEP_COPY_DIR_NAME = "__workflow_step_files__";
2
+ export declare const DEFERRED_STEP_SOURCE_METADATA_PREFIX = "WORKFLOW_STEP_SOURCE_B64:";
3
+ export interface DeferredStepSourceMetadata {
4
+ relativeFilename: string;
5
+ absolutePath: string;
6
+ }
7
+ export declare function isDeferredStepCopyFilePath(filePath: string): boolean;
8
+ export declare function createDeferredStepSourceMetadataComment(metadata: DeferredStepSourceMetadata): string;
9
+ export declare function createDeferredStepCopyInlineSourceMapComment({ sourcePath, sourceContent, generatedContent, }: {
10
+ sourcePath: string;
11
+ sourceContent: string;
12
+ generatedContent?: string;
13
+ }): string;
14
+ export declare function parseInlineSourceMapComment(source: string): {
15
+ sourceWithoutMapComment: string;
16
+ sourceMap: string | null;
17
+ };
18
+ export declare function parseDeferredStepSourceMetadata(source: string): DeferredStepSourceMetadata | null;
19
+ //# sourceMappingURL=step-copy-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-copy-utils.d.ts","sourceRoot":"","sources":["../src/step-copy-utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,2BAA2B,4BAA4B,CAAC;AACrE,eAAO,MAAM,oCAAoC,8BAA8B,CAAC;AAIhF,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB;AAUD,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAKpE;AAED,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,0BAA0B,GACnC,MAAM,CAKR;AAiBD,wBAAgB,4CAA4C,CAAC,EAC3D,UAAU,EACV,aAAa,EACb,gBAAgB,GACjB,EAAE;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,MAAM,CAiBT;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG;IAC3D,uBAAuB,EAAE,MAAM,CAAC;IAChC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAuBA;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,MAAM,GACb,0BAA0B,GAAG,IAAI,CA8BnC"}
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFERRED_STEP_SOURCE_METADATA_PREFIX = exports.DEFERRED_STEP_COPY_DIR_NAME = void 0;
4
+ exports.isDeferredStepCopyFilePath = isDeferredStepCopyFilePath;
5
+ exports.createDeferredStepSourceMetadataComment = createDeferredStepSourceMetadataComment;
6
+ exports.createDeferredStepCopyInlineSourceMapComment = createDeferredStepCopyInlineSourceMapComment;
7
+ exports.parseInlineSourceMapComment = parseInlineSourceMapComment;
8
+ exports.parseDeferredStepSourceMetadata = parseDeferredStepSourceMetadata;
9
+ exports.DEFERRED_STEP_COPY_DIR_NAME = '__workflow_step_files__';
10
+ exports.DEFERRED_STEP_SOURCE_METADATA_PREFIX = 'WORKFLOW_STEP_SOURCE_B64:';
11
+ const INLINE_SOURCE_MAP_PATTERN = /(?:^|\r?\n)\s*\/\/[#@]\s*sourceMappingURL=data:application\/json(?:;charset=[^;,]+)?;base64,([A-Za-z0-9+/=]+)\s*$/;
12
+ function escapeRegExp(input) {
13
+ return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
14
+ }
15
+ function normalizePath(filePath) {
16
+ return filePath.replace(/\\/g, '/');
17
+ }
18
+ function isDeferredStepCopyFilePath(filePath) {
19
+ const normalizedPath = normalizePath(filePath);
20
+ return normalizedPath.includes(`/.well-known/workflow/v1/step/${exports.DEFERRED_STEP_COPY_DIR_NAME}/`);
21
+ }
22
+ function createDeferredStepSourceMetadataComment(metadata) {
23
+ const encoded = Buffer.from(JSON.stringify(metadata), 'utf-8').toString('base64');
24
+ return `// ${exports.DEFERRED_STEP_SOURCE_METADATA_PREFIX}${encoded}`;
25
+ }
26
+ function getLineCount(source) {
27
+ if (source.length === 0) {
28
+ return 1;
29
+ }
30
+ const lineBreakMatches = source.match(/\r\n|\r|\n/g);
31
+ return (lineBreakMatches?.length ?? 0) + 1;
32
+ }
33
+ function createIdentityLineMappings(lineCount) {
34
+ if (lineCount <= 1) {
35
+ return 'AAAA';
36
+ }
37
+ return `AAAA${';AACA'.repeat(lineCount - 1)}`;
38
+ }
39
+ function createDeferredStepCopyInlineSourceMapComment({ sourcePath, sourceContent, generatedContent, }) {
40
+ const normalizedSourcePath = normalizePath(sourcePath);
41
+ const sourceMap = {
42
+ version: 3,
43
+ file: normalizedSourcePath.split('/').pop() ?? normalizedSourcePath,
44
+ sources: [normalizedSourcePath],
45
+ sourcesContent: [sourceContent],
46
+ names: [],
47
+ mappings: createIdentityLineMappings(getLineCount(generatedContent ?? sourceContent)),
48
+ };
49
+ const encodedSourceMap = Buffer.from(JSON.stringify(sourceMap), 'utf-8').toString('base64');
50
+ return `//# sourceMappingURL=data:application/json;base64,${encodedSourceMap}`;
51
+ }
52
+ function parseInlineSourceMapComment(source) {
53
+ const mapMatch = source.match(INLINE_SOURCE_MAP_PATTERN);
54
+ if (!mapMatch?.[1]) {
55
+ return {
56
+ sourceWithoutMapComment: source,
57
+ sourceMap: null,
58
+ };
59
+ }
60
+ const sourceWithoutMapComment = source.replace(INLINE_SOURCE_MAP_PATTERN, '');
61
+ try {
62
+ const decodedMap = Buffer.from(mapMatch[1], 'base64').toString('utf-8');
63
+ JSON.parse(decodedMap);
64
+ return {
65
+ sourceWithoutMapComment,
66
+ sourceMap: decodedMap,
67
+ };
68
+ }
69
+ catch {
70
+ return {
71
+ sourceWithoutMapComment,
72
+ sourceMap: null,
73
+ };
74
+ }
75
+ }
76
+ function parseDeferredStepSourceMetadata(source) {
77
+ const pattern = new RegExp(`^\\s*//\\s*${escapeRegExp(exports.DEFERRED_STEP_SOURCE_METADATA_PREFIX)}([A-Za-z0-9+/=]+)\\s*$`, 'm');
78
+ const match = source.match(pattern);
79
+ const encoded = match?.[1];
80
+ if (!encoded) {
81
+ return null;
82
+ }
83
+ try {
84
+ const decoded = Buffer.from(encoded, 'base64').toString('utf-8');
85
+ const parsed = JSON.parse(decoded);
86
+ if (typeof parsed.relativeFilename !== 'string' ||
87
+ parsed.relativeFilename.length === 0 ||
88
+ typeof parsed.absolutePath !== 'string' ||
89
+ parsed.absolutePath.length === 0) {
90
+ return null;
91
+ }
92
+ return {
93
+ relativeFilename: parsed.relativeFilename,
94
+ absolutePath: normalizePath(parsed.absolutePath),
95
+ };
96
+ }
97
+ catch {
98
+ return null;
99
+ }
100
+ }
101
+ //# sourceMappingURL=step-copy-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-copy-utils.js","sourceRoot":"","sources":["../src/step-copy-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ ---
2
+ title: "workflow/next"
3
+ description: Next.js integration for automatic bundling and runtime configuration.
4
+ type: overview
5
+ summary: Explore the Next.js integration for automatic workflow bundling and runtime support.
6
+ related:
7
+ - /docs/getting-started/next
8
+ ---
9
+
10
+ Next.js integration for Workflow DevKit that automatically configures bundling and runtime support.
11
+
12
+ ## Functions
13
+
14
+ <Cards>
15
+ <Card title="withWorkflow" href="/docs/api-reference/workflow-next/with-workflow">
16
+ Configures webpack/turbopack loaders to transform workflow code (`"use step"`/`"use workflow"` directives)
17
+ </Card>
18
+ </Cards>
@@ -0,0 +1,56 @@
1
+ ---
2
+ title: withWorkflow
3
+ description: Configure webpack/turbopack to transform workflow directives in Next.js.
4
+ type: reference
5
+ summary: Wrap your Next.js config with withWorkflow to enable workflow directive transformation.
6
+ prerequisites:
7
+ - /docs/getting-started/next
8
+ ---
9
+
10
+ Configures webpack/turbopack loaders to transform workflow code (`"use step"`/`"use workflow"` directives)
11
+
12
+ ## Usage
13
+
14
+ To enable `"use step"` and `"use workflow"` directives while developing locally or deploying to production, wrap your `nextConfig` with `withWorkflow`.
15
+
16
+ ```typescript title="next.config.ts" lineNumbers
17
+ import { withWorkflow } from "workflow/next"; // [!code highlight]
18
+ import type { NextConfig } from "next";
19
+
20
+ const nextConfig: NextConfig = {
21
+ // … rest of your Next.js config
22
+ };
23
+
24
+ // not required but allows configuring workflow options
25
+ const workflowConfig = {}
26
+
27
+ export default withWorkflow(nextConfig, workflowConfig); // [!code highlight]
28
+ ```
29
+
30
+ If you are exporting a function in your `next.config` you will need to ensure you call the function returned from `withWorkflow`.
31
+
32
+ ```typescript title="next.config.ts" lineNumbers
33
+ import { NextConfig } from "next";
34
+ import { withWorkflow } from "workflow/next";
35
+ import createNextIntlPlugin from "next-intl/plugin";
36
+
37
+ const withNextIntl = createNextIntlPlugin();
38
+
39
+ export default async function config(
40
+ phase: string,
41
+ ctx: {
42
+ defaultConfig: NextConfig
43
+ }
44
+ ): Promise<NextConfig> {
45
+ let nextConfig: NextConfig | typeof config = {};
46
+
47
+ for (const configModifier of [withNextIntl, withWorkflow]) {
48
+ nextConfig = configModifier(nextConfig);
49
+
50
+ if (typeof nextConfig === "function") {
51
+ nextConfig = await nextConfig(phase, ctx);
52
+ }
53
+ }
54
+ return nextConfig;
55
+ }
56
+ ```
package/docs/next.mdx ADDED
@@ -0,0 +1,288 @@
1
+ ---
2
+ title: Next.js
3
+ description: This guide will walk through setting up your first workflow in a Next.js app. Along the way, you'll learn more about the concepts that are fundamental to using the development kit in your own projects.
4
+ type: guide
5
+ summary: Set up Workflow DevKit in a Next.js app.
6
+ prerequisites:
7
+ - /docs/getting-started
8
+ related:
9
+ - /docs/api-reference/workflow-next
10
+ - /docs/deploying/world/vercel-world
11
+ ---
12
+
13
+ <Steps>
14
+
15
+ <Step>
16
+ ## Create Your Next.js Project
17
+
18
+ Start by creating a new Next.js project. This command will create a new directory named `my-workflow-app` and set up a Next.js project inside it.
19
+
20
+ ```bash
21
+ npm create next-app@latest my-workflow-app
22
+ ```
23
+
24
+ Enter the newly created directory:
25
+
26
+ ```bash
27
+ cd my-workflow-app
28
+ ```
29
+
30
+ ### Install `workflow`
31
+
32
+ ```package-install
33
+ npm i workflow
34
+ ```
35
+
36
+ ### Configure Next.js
37
+
38
+ Wrap your `next.config.ts` with `withWorkflow()`. This enables usage of the `"use workflow"` and `"use step"` directives.
39
+
40
+ ```typescript title="next.config.ts" lineNumbers
41
+ import { withWorkflow } from "workflow/next"; // [!code highlight]
42
+ import type { NextConfig } from "next";
43
+
44
+ const nextConfig: NextConfig = {
45
+ // … rest of your Next.js config
46
+ };
47
+
48
+ export default withWorkflow(nextConfig); // [!code highlight]
49
+ ```
50
+
51
+ <Accordion type="single" collapsible>
52
+ <AccordionItem value="typescript-intellisense" className="[&_h3]:my-0">
53
+ <AccordionTrigger className="text-sm">
54
+ ### Setup IntelliSense for TypeScript (Optional)
55
+ </AccordionTrigger>
56
+ <AccordionContent className="[&_p]:my-2">
57
+
58
+ To enable helpful hints in your IDE, setup the workflow plugin in `tsconfig.json`:
59
+
60
+ ```json title="tsconfig.json" lineNumbers
61
+ {
62
+ "compilerOptions": {
63
+ // ... rest of your TypeScript config
64
+ "plugins": [
65
+ {
66
+ "name": "workflow" // [!code highlight]
67
+ }
68
+ ]
69
+ }
70
+ }
71
+ ```
72
+
73
+ </AccordionContent>
74
+ </AccordionItem>
75
+ </Accordion>
76
+
77
+ <Accordion type="single" collapsible>
78
+ <AccordionItem value="typescript-intellisense" className="[&_h3]:my-0">
79
+ <AccordionTrigger className="text-sm">
80
+ ### Configure Proxy Handler (if applicable)
81
+ </AccordionTrigger>
82
+ <AccordionContent className="[&_p]:my-2">
83
+
84
+ If your Next.js app has a [proxy handler](https://nextjs.org/docs/app/api-reference/file-conventions/proxy)
85
+ (formerly known as "middleware"), you'll need to update the matcher pattern to exclude Workflow's
86
+ internal paths to prevent the proxy handler from running on them.
87
+
88
+ Add `.well-known/workflow/*` to your middleware's exclusion list:
89
+
90
+ ```typescript title="proxy.ts" lineNumbers
91
+ import { NextResponse } from "next/server";
92
+ import type { NextRequest } from "next/server";
93
+
94
+ export function proxy(request: NextRequest) {
95
+ // Your middleware logic
96
+ return NextResponse.next();
97
+ }
98
+
99
+ export const config = {
100
+ matcher: [
101
+ // ... your existing matchers
102
+ {
103
+ source: "/((?!_next/static|_next/image|favicon.ico|.well-known/workflow/).*)", // [!code highlight]
104
+ },
105
+ ],
106
+ };
107
+ ```
108
+
109
+ This ensures that internal Workflow paths are not intercepted by your middleware, which could interfere with workflow execution and resumption.
110
+ </AccordionContent>
111
+ </AccordionItem>
112
+ </Accordion>
113
+
114
+ </Step>
115
+
116
+ <Step>
117
+
118
+ ## Create Your First Workflow
119
+
120
+ Create a new file for our first workflow:
121
+
122
+ ```typescript title="workflows/user-signup.ts" lineNumbers
123
+ import { sleep } from "workflow";
124
+
125
+ export async function handleUserSignup(email: string) {
126
+ "use workflow"; // [!code highlight]
127
+
128
+ const user = await createUser(email);
129
+ await sendWelcomeEmail(user);
130
+
131
+ await sleep("5s"); // Pause for 5s - doesn't consume any resources
132
+ await sendOnboardingEmail(user);
133
+
134
+ console.log("Workflow is complete! Run 'npx workflow web' to inspect your run")
135
+
136
+ return { userId: user.id, status: "onboarded" };
137
+ }
138
+
139
+ ```
140
+
141
+ We'll fill in those functions next, but let's take a look at this code:
142
+
143
+ * We define a **workflow** function with the directive `"use workflow"`. Think of the workflow function as the _orchestrator_ of individual **steps**.
144
+ * The Workflow DevKit's `sleep` function allows us to suspend execution of the workflow without using up any resources. A sleep can be a few seconds, hours, days, or even months long.
145
+
146
+ ## Create Your Workflow Steps
147
+
148
+ Let's now define those missing functions.
149
+
150
+ ```typescript title="workflows/user-signup.ts" lineNumbers
151
+ import { FatalError } from "workflow"
152
+
153
+ // Our workflow function defined earlier
154
+
155
+ async function createUser(email: string) {
156
+ "use step"; // [!code highlight]
157
+
158
+ console.log(`Creating user with email: ${email}`);
159
+
160
+ // Full Node.js access - database calls, APIs, etc.
161
+ return { id: crypto.randomUUID(), email };
162
+ }
163
+
164
+ async function sendWelcomeEmail(user: { id: string; email: string; }) {
165
+ "use step"; // [!code highlight]
166
+
167
+ console.log(`Sending welcome email to user: ${user.id}`);
168
+
169
+ if (Math.random() < 0.3) {
170
+ // By default, steps will be retried for unhandled errors
171
+ throw new Error("Retryable!");
172
+ }
173
+ }
174
+
175
+ async function sendOnboardingEmail(user: { id: string; email: string}) {
176
+ "use step"; // [!code highlight]
177
+
178
+ if (!user.email.includes("@")) {
179
+ // To skip retrying, throw a FatalError instead
180
+ throw new FatalError("Invalid Email");
181
+ }
182
+
183
+ console.log(`Sending onboarding email to user: ${user.id}`);
184
+ }
185
+ ```
186
+
187
+ Taking a look at this code:
188
+
189
+ * Business logic lives inside **steps**. When a step is invoked inside a **workflow**, it gets enqueued to run on a separate request while the workflow is suspended, just like `sleep`.
190
+ * If a step throws an error, like in `sendWelcomeEmail`, the step will automatically be retried until it succeeds (or hits the step's max retry count).
191
+ * Steps can throw a `FatalError` if an error is intentional and should not be retried.
192
+
193
+ <Callout>
194
+ We'll dive deeper into workflows, steps, and other ways to suspend or handle events in [Foundations](/docs/foundations).
195
+ </Callout>
196
+
197
+ </Step>
198
+
199
+ <Step>
200
+
201
+ ## Create Your Route Handler
202
+
203
+ To invoke your new workflow, we'll need to add your workflow to a `POST` API Route Handler, `app/api/signup/route.ts`, with the following code:
204
+
205
+ ```typescript title="app/api/signup/route.ts"
206
+ import { start } from "workflow/api";
207
+ import { handleUserSignup } from "@/workflows/user-signup";
208
+ import { NextResponse } from "next/server";
209
+
210
+ export async function POST(request: Request) {
211
+ const { email } = await request.json();
212
+
213
+ // Executes asynchronously and doesn't block your app
214
+ await start(handleUserSignup, [email]);
215
+
216
+ return NextResponse.json({
217
+ message: "User signup workflow started",
218
+ });
219
+ }
220
+ ```
221
+
222
+ This Route Handler creates a `POST` request endpoint at `/api/signup` that will trigger your workflow.
223
+
224
+ <Callout>
225
+ Workflows can be triggered from API routes, Server Actions, or any server-side code.
226
+ </Callout>
227
+
228
+ </Step>
229
+
230
+ </Steps>
231
+
232
+ ## Run in development
233
+
234
+ To start your development server, run the following command in your terminal in the Next.js root directory:
235
+
236
+ ```bash
237
+ npm run dev
238
+ ```
239
+
240
+ Once your development server is running, you can trigger your workflow by running this command in the terminal:
241
+
242
+ ```bash
243
+ curl -X POST --json '{"email":"hello@example.com"}' http://localhost:3000/api/signup
244
+ ```
245
+
246
+ Check the Next.js development server logs to see your workflow execute, as well as the steps that are being processed.
247
+
248
+ Additionally, you can use the [Workflow DevKit CLI or Web UI](/docs/observability) to inspect your workflow runs and steps in detail.
249
+
250
+ ```bash
251
+ # Open the observability Web UI
252
+ npx workflow web
253
+ # or if you prefer a terminal interface, use the CLI inspect command
254
+ npx workflow inspect runs
255
+ ```
256
+
257
+ ![Workflow DevKit Web UI](/o11y-ui.png)
258
+
259
+ ## Deploying to production
260
+
261
+ Workflow DevKit apps currently work best when deployed to [Vercel](https://vercel.com/home) and need no special configuration.
262
+
263
+ <FluidComputeCallout />
264
+
265
+ Check the [Deploying](/docs/deploying) section to learn how your workflows can be deployed elsewhere.
266
+
267
+ ## Troubleshooting
268
+
269
+ ### Next.js 16.1+ compatibility
270
+
271
+ If you see this error when upgrading to Next.js 16.1 or later:
272
+
273
+ ```
274
+ Build error occurred
275
+ Error: Cannot find module 'next/dist/lib/server-external-packages.json'
276
+ ```
277
+
278
+ Upgrade to `workflow@4.0.1-beta.26` or later:
279
+
280
+ ```package-install
281
+ workflow@latest
282
+ ```
283
+
284
+ ## Next Steps
285
+
286
+ * Learn more about the [Foundations](/docs/foundations).
287
+ * Check [Errors](/docs/errors) if you encounter issues.
288
+ * Explore the [API Reference](/docs/api-reference).