@stepflowjs/adapter-nextjs 0.0.1
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/index.d.ts +83 -0
- package/dist/index.js +280 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Stepflow } from '@stepflowjs/core';
|
|
2
|
+
export { Execution, NotifyResult, TriggerResult } from '@stepflowjs/core';
|
|
3
|
+
import { AuthContext, AuthConfig, EndpointOption, BaseAdapterOptions } from '@stepflowjs/adapter-shared';
|
|
4
|
+
export { AuthConfig, AuthContext, AuthHandler, AuthResult, EndpointConfig, EndpointOption, EndpointPreset, RouteName, allOf, anyOf, createApiKeyAuth, createBearerAuth, isRouteEnabled, resolveEndpoints } from '@stepflowjs/adapter-shared';
|
|
5
|
+
|
|
6
|
+
/** Next.js-specific request type for auth handlers */
|
|
7
|
+
type NextRequest = {
|
|
8
|
+
header(name: string): string | undefined;
|
|
9
|
+
headers: Headers;
|
|
10
|
+
url: string;
|
|
11
|
+
method: string;
|
|
12
|
+
query(name: string): string | undefined;
|
|
13
|
+
};
|
|
14
|
+
/** Next.js-specific auth context */
|
|
15
|
+
type NextAuthContext = AuthContext<NextRequest, Request>;
|
|
16
|
+
/** Next.js-specific auth config */
|
|
17
|
+
type NextAuthConfig = AuthConfig<NextRequest, Request>;
|
|
18
|
+
interface HandlerOptions {
|
|
19
|
+
/** Custom health check function. Defaults to stepflow.healthCheck(). */
|
|
20
|
+
healthCheck?: () => Promise<boolean>;
|
|
21
|
+
/** Endpoint configuration - preset or fine-grained */
|
|
22
|
+
endpoints?: EndpointOption;
|
|
23
|
+
/** Authorization configuration */
|
|
24
|
+
auth?: NextAuthConfig;
|
|
25
|
+
/** Callback when authorization fails */
|
|
26
|
+
onAuthFailure?: BaseAdapterOptions<NextRequest, Request>["onAuthFailure"];
|
|
27
|
+
}
|
|
28
|
+
interface TriggerRequestBody<TPayload = unknown> {
|
|
29
|
+
payload: TPayload;
|
|
30
|
+
metadata?: Record<string, unknown>;
|
|
31
|
+
runId?: string;
|
|
32
|
+
delay?: number;
|
|
33
|
+
idempotencyKey?: string;
|
|
34
|
+
}
|
|
35
|
+
interface NotifyRequestBody {
|
|
36
|
+
data: unknown;
|
|
37
|
+
}
|
|
38
|
+
interface ErrorResponseBody {
|
|
39
|
+
error: string;
|
|
40
|
+
message?: string;
|
|
41
|
+
}
|
|
42
|
+
/** Next.js App Router route context with params */
|
|
43
|
+
interface RouteContext<TParams = Record<string, string>> {
|
|
44
|
+
params: Promise<TParams>;
|
|
45
|
+
}
|
|
46
|
+
type RouteHandler<TParams = Record<string, string>> = (request: Request, context: RouteContext<TParams>) => Promise<Response>;
|
|
47
|
+
interface StepflowHandlers {
|
|
48
|
+
/** Handler for POST /api/stepflow/trigger/[workflowId] */
|
|
49
|
+
triggerHandler: RouteHandler<{
|
|
50
|
+
workflowId: string;
|
|
51
|
+
}>;
|
|
52
|
+
/** Handler for GET /api/stepflow/runs/[runId] */
|
|
53
|
+
runHandler: RouteHandler<{
|
|
54
|
+
runId: string;
|
|
55
|
+
}>;
|
|
56
|
+
/** Handler for GET /api/stepflow/runs/[runId]/stream */
|
|
57
|
+
streamHandler: RouteHandler<{
|
|
58
|
+
runId: string;
|
|
59
|
+
}>;
|
|
60
|
+
/** Handler for POST /api/stepflow/notify/[eventId] */
|
|
61
|
+
notifyHandler: RouteHandler<{
|
|
62
|
+
eventId: string;
|
|
63
|
+
}>;
|
|
64
|
+
/** Handler for GET /api/stepflow/health */
|
|
65
|
+
healthHandler: RouteHandler;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Create Next.js App Router route handlers for Stepflow
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* // app/api/stepflow/trigger/[workflowId]/route.ts
|
|
74
|
+
* import { stepflow } from '@/lib/stepflow';
|
|
75
|
+
* import { createStepflowHandlers } from '@stepflowjs/adapter-nextjs';
|
|
76
|
+
*
|
|
77
|
+
* const { triggerHandler } = createStepflowHandlers(stepflow);
|
|
78
|
+
* export const POST = triggerHandler;
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare function createStepflowHandlers(stepflow: Stepflow, options?: HandlerOptions): StepflowHandlers;
|
|
82
|
+
|
|
83
|
+
export { type ErrorResponseBody, type HandlerOptions, type NextAuthConfig, type NextAuthContext, type NextRequest, type NotifyRequestBody, type RouteContext, type RouteHandler, type StepflowHandlers, type TriggerRequestBody, createStepflowHandlers };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
isRouteEnabled,
|
|
4
|
+
runAuth,
|
|
5
|
+
getAuthFailureResponse,
|
|
6
|
+
createErrorBody
|
|
7
|
+
} from "@stepflowjs/adapter-shared";
|
|
8
|
+
import { NextResponse } from "next/server";
|
|
9
|
+
import {
|
|
10
|
+
createApiKeyAuth,
|
|
11
|
+
createBearerAuth,
|
|
12
|
+
anyOf,
|
|
13
|
+
allOf,
|
|
14
|
+
isRouteEnabled as isRouteEnabled2,
|
|
15
|
+
resolveEndpoints
|
|
16
|
+
} from "@stepflowjs/adapter-shared";
|
|
17
|
+
function getAccessToken(request) {
|
|
18
|
+
const url = new URL(request.url);
|
|
19
|
+
const queryToken = url.searchParams.get("token");
|
|
20
|
+
if (queryToken) return queryToken;
|
|
21
|
+
const authHeader = request.headers.get("authorization");
|
|
22
|
+
if (!authHeader) return void 0;
|
|
23
|
+
const match = authHeader.match(/^Bearer\s+(.+)$/i);
|
|
24
|
+
return match?.[1];
|
|
25
|
+
}
|
|
26
|
+
function jsonResponse(data, status = 200) {
|
|
27
|
+
return new Response(JSON.stringify(data), {
|
|
28
|
+
status,
|
|
29
|
+
headers: { "Content-Type": "application/json" }
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function jsonError(status, error, message) {
|
|
33
|
+
const body = message ? { error, message } : { error };
|
|
34
|
+
return new Response(JSON.stringify(body), {
|
|
35
|
+
status,
|
|
36
|
+
headers: { "Content-Type": "application/json" }
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function jsonAuthError(status, code, message) {
|
|
40
|
+
const body = createErrorBody(code, message);
|
|
41
|
+
return NextResponse.json(body, { status });
|
|
42
|
+
}
|
|
43
|
+
async function parseJsonBody(request) {
|
|
44
|
+
try {
|
|
45
|
+
return await request.json();
|
|
46
|
+
} catch {
|
|
47
|
+
throw new Error("Invalid JSON body");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function createNextRequest(request) {
|
|
51
|
+
const url = new URL(request.url);
|
|
52
|
+
return {
|
|
53
|
+
header: (name) => request.headers.get(name) ?? void 0,
|
|
54
|
+
headers: request.headers,
|
|
55
|
+
url: request.url,
|
|
56
|
+
method: request.method,
|
|
57
|
+
query: (name) => url.searchParams.get(name) ?? void 0
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function checkAuth(request, route, options, params = {}) {
|
|
61
|
+
const ctx = {
|
|
62
|
+
route,
|
|
63
|
+
request: createNextRequest(request),
|
|
64
|
+
extra: request,
|
|
65
|
+
...params
|
|
66
|
+
};
|
|
67
|
+
const result = await runAuth(ctx, options.auth);
|
|
68
|
+
if (!result.ok) {
|
|
69
|
+
const { status, code, message } = getAuthFailureResponse(result);
|
|
70
|
+
await options.onAuthFailure?.(ctx, result);
|
|
71
|
+
return jsonAuthError(status, code, message);
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
function createSSEStream() {
|
|
76
|
+
let controller;
|
|
77
|
+
const encoder = new TextEncoder();
|
|
78
|
+
const stream = new ReadableStream({
|
|
79
|
+
start(ctrl) {
|
|
80
|
+
controller = ctrl;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return { stream, controller, encoder };
|
|
84
|
+
}
|
|
85
|
+
function writeSSE(controller, encoder, event, data) {
|
|
86
|
+
const message = `event: ${event}
|
|
87
|
+
data: ${JSON.stringify(data)}
|
|
88
|
+
|
|
89
|
+
`;
|
|
90
|
+
controller.enqueue(encoder.encode(message));
|
|
91
|
+
}
|
|
92
|
+
function createStepflowHandlers(stepflow, options = {}) {
|
|
93
|
+
const triggerHandler = async (request, context) => {
|
|
94
|
+
if (!isRouteEnabled("trigger", options.endpoints)) {
|
|
95
|
+
return jsonError(404, "Not found", "Endpoint disabled");
|
|
96
|
+
}
|
|
97
|
+
const { workflowId } = await context.params;
|
|
98
|
+
const authError = await checkAuth(request, "trigger", options, {
|
|
99
|
+
workflowId
|
|
100
|
+
});
|
|
101
|
+
if (authError) return authError;
|
|
102
|
+
try {
|
|
103
|
+
const body = await parseJsonBody(request);
|
|
104
|
+
const result = await stepflow.trigger(workflowId, body.payload, {
|
|
105
|
+
runId: body.runId,
|
|
106
|
+
metadata: body.metadata,
|
|
107
|
+
delay: body.delay,
|
|
108
|
+
idempotencyKey: body.idempotencyKey
|
|
109
|
+
});
|
|
110
|
+
const response = result;
|
|
111
|
+
return jsonResponse(response);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
const message = error.message;
|
|
114
|
+
if (message.includes("Workflow") && message.includes("not found")) {
|
|
115
|
+
return jsonError(404, "Not found", message);
|
|
116
|
+
}
|
|
117
|
+
if (message === "Invalid JSON body") {
|
|
118
|
+
return jsonError(400, "Bad request", message);
|
|
119
|
+
}
|
|
120
|
+
return jsonError(500, "Internal server error", message);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const runHandler = async (request, context) => {
|
|
124
|
+
if (!isRouteEnabled("runs", options.endpoints)) {
|
|
125
|
+
return jsonError(404, "Not found", "Endpoint disabled");
|
|
126
|
+
}
|
|
127
|
+
const { runId } = await context.params;
|
|
128
|
+
const authError = await checkAuth(request, "runs", options, { runId });
|
|
129
|
+
if (authError) return authError;
|
|
130
|
+
try {
|
|
131
|
+
const accessToken = getAccessToken(request);
|
|
132
|
+
const run = await stepflow.getRun(runId, { accessToken });
|
|
133
|
+
if (!run) {
|
|
134
|
+
return jsonError(404, "Not found", `Run "${runId}" not found`);
|
|
135
|
+
}
|
|
136
|
+
const response = run;
|
|
137
|
+
return jsonResponse(response);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const message = error.message;
|
|
140
|
+
if (message === "Invalid access token") {
|
|
141
|
+
return jsonError(401, "Unauthorized", message);
|
|
142
|
+
}
|
|
143
|
+
return jsonError(500, "Internal server error", message);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const streamHandler = async (request, context) => {
|
|
147
|
+
if (!isRouteEnabled("runsStream", options.endpoints)) {
|
|
148
|
+
return jsonError(404, "Not found", "Endpoint disabled");
|
|
149
|
+
}
|
|
150
|
+
const { runId } = await context.params;
|
|
151
|
+
const authError = await checkAuth(request, "runsStream", options, {
|
|
152
|
+
runId
|
|
153
|
+
});
|
|
154
|
+
if (authError) return authError;
|
|
155
|
+
const accessToken = getAccessToken(request);
|
|
156
|
+
if (accessToken) {
|
|
157
|
+
try {
|
|
158
|
+
await stepflow.getRun(runId, { accessToken });
|
|
159
|
+
} catch (error) {
|
|
160
|
+
const message = error.message;
|
|
161
|
+
if (message === "Invalid access token") {
|
|
162
|
+
return jsonError(401, "Unauthorized", message);
|
|
163
|
+
}
|
|
164
|
+
return jsonError(500, "Internal server error", message);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const url = new URL(request.url);
|
|
168
|
+
const throttleMsParam = url.searchParams.get("throttle");
|
|
169
|
+
const throttleMs = throttleMsParam ? Number(throttleMsParam) : void 0;
|
|
170
|
+
const effectiveThrottleMs = throttleMs !== void 0 && Number.isFinite(throttleMs) && throttleMs >= 0 ? throttleMs : void 0;
|
|
171
|
+
const { stream, controller, encoder } = createSSEStream();
|
|
172
|
+
let lastSentAt = 0;
|
|
173
|
+
const send = (event, data) => {
|
|
174
|
+
if (effectiveThrottleMs !== void 0) {
|
|
175
|
+
const now = Date.now();
|
|
176
|
+
if (now - lastSentAt < effectiveThrottleMs) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
lastSentAt = now;
|
|
180
|
+
}
|
|
181
|
+
writeSSE(controller, encoder, event, data);
|
|
182
|
+
};
|
|
183
|
+
void (async () => {
|
|
184
|
+
try {
|
|
185
|
+
let run = await stepflow.getRun(runId);
|
|
186
|
+
while (!run) {
|
|
187
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
188
|
+
run = await stepflow.getRun(runId);
|
|
189
|
+
}
|
|
190
|
+
const executionId = run.id;
|
|
191
|
+
send("update", run);
|
|
192
|
+
if (run.status === "completed") {
|
|
193
|
+
send("execution:complete", { result: run.result });
|
|
194
|
+
}
|
|
195
|
+
if (run.status === "failed") {
|
|
196
|
+
send("execution:failed", { error: run.error });
|
|
197
|
+
}
|
|
198
|
+
const unsubscribe = stepflow.subscribeToExecution(executionId, () => {
|
|
199
|
+
void (async () => {
|
|
200
|
+
const execution = await stepflow.getExecution(executionId);
|
|
201
|
+
if (execution) {
|
|
202
|
+
send("update", execution);
|
|
203
|
+
if (execution.status === "completed") {
|
|
204
|
+
send("execution:complete", { result: execution.result });
|
|
205
|
+
}
|
|
206
|
+
if (execution.status === "failed") {
|
|
207
|
+
send("execution:failed", { error: execution.error });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
})();
|
|
211
|
+
});
|
|
212
|
+
await new Promise((resolve) => setTimeout(resolve, 60 * 60 * 1e3));
|
|
213
|
+
unsubscribe();
|
|
214
|
+
} catch {
|
|
215
|
+
} finally {
|
|
216
|
+
try {
|
|
217
|
+
controller.close();
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
})();
|
|
222
|
+
return new Response(stream, {
|
|
223
|
+
headers: {
|
|
224
|
+
"Content-Type": "text/event-stream",
|
|
225
|
+
"Cache-Control": "no-cache",
|
|
226
|
+
Connection: "keep-alive"
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
const notifyHandler = async (request, context) => {
|
|
231
|
+
if (!isRouteEnabled("notify", options.endpoints)) {
|
|
232
|
+
return jsonError(404, "Not found", "Endpoint disabled");
|
|
233
|
+
}
|
|
234
|
+
const { eventId } = await context.params;
|
|
235
|
+
const authError = await checkAuth(request, "notify", options, { eventId });
|
|
236
|
+
if (authError) return authError;
|
|
237
|
+
try {
|
|
238
|
+
const body = await parseJsonBody(request);
|
|
239
|
+
const result = await stepflow.notify(eventId, body.data);
|
|
240
|
+
const response = result;
|
|
241
|
+
return jsonResponse(response);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
const message = error.message;
|
|
244
|
+
if (message === "Invalid JSON body") {
|
|
245
|
+
return jsonError(400, "Bad request", message);
|
|
246
|
+
}
|
|
247
|
+
return jsonError(500, "Internal server error", message);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
const healthHandler = async (request) => {
|
|
251
|
+
if (!isRouteEnabled("health", options.endpoints)) {
|
|
252
|
+
return jsonError(404, "Not found", "Endpoint disabled");
|
|
253
|
+
}
|
|
254
|
+
const authError = await checkAuth(request, "health", options);
|
|
255
|
+
if (authError) return authError;
|
|
256
|
+
try {
|
|
257
|
+
const ok = await (options.healthCheck?.() ?? stepflow.healthCheck());
|
|
258
|
+
return jsonResponse({ ok });
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return jsonError(500, "Internal server error", error.message);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
return {
|
|
264
|
+
triggerHandler,
|
|
265
|
+
runHandler,
|
|
266
|
+
streamHandler,
|
|
267
|
+
notifyHandler,
|
|
268
|
+
healthHandler
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
export {
|
|
272
|
+
allOf,
|
|
273
|
+
anyOf,
|
|
274
|
+
createApiKeyAuth,
|
|
275
|
+
createBearerAuth,
|
|
276
|
+
createStepflowHandlers,
|
|
277
|
+
isRouteEnabled2 as isRouteEnabled,
|
|
278
|
+
resolveEndpoints
|
|
279
|
+
};
|
|
280
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Execution, TriggerResult, NotifyResult } from \"@stepflowjs/core\";\nimport type { Stepflow } from \"@stepflowjs/core\";\nimport {\n type AuthConfig,\n type AuthContext,\n type BaseAdapterOptions,\n type EndpointOption,\n type RouteName,\n isRouteEnabled,\n runAuth,\n getAuthFailureResponse,\n createErrorBody,\n} from \"@stepflowjs/adapter-shared\";\nimport { NextResponse } from \"next/server\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Next.js-specific request type for auth handlers */\nexport type NextRequest = {\n header(name: string): string | undefined;\n headers: Headers;\n url: string;\n method: string;\n query(name: string): string | undefined;\n};\n\n/** Next.js-specific auth context */\nexport type NextAuthContext = AuthContext<NextRequest, Request>;\n\n/** Next.js-specific auth config */\nexport type NextAuthConfig = AuthConfig<NextRequest, Request>;\n\nexport interface HandlerOptions {\n /** Custom health check function. Defaults to stepflow.healthCheck(). */\n healthCheck?: () => Promise<boolean>;\n /** Endpoint configuration - preset or fine-grained */\n endpoints?: EndpointOption;\n /** Authorization configuration */\n auth?: NextAuthConfig;\n /** Callback when authorization fails */\n onAuthFailure?: BaseAdapterOptions<NextRequest, Request>[\"onAuthFailure\"];\n}\n\nexport interface TriggerRequestBody<TPayload = unknown> {\n payload: TPayload;\n metadata?: Record<string, unknown>;\n runId?: string;\n delay?: number;\n idempotencyKey?: string;\n}\n\nexport interface NotifyRequestBody {\n data: unknown;\n}\n\nexport interface ErrorResponseBody {\n error: string;\n message?: string;\n}\n\n/** Next.js App Router route context with params */\nexport interface RouteContext<TParams = Record<string, string>> {\n params: Promise<TParams>;\n}\n\nexport type RouteHandler<TParams = Record<string, string>> = (\n request: Request,\n context: RouteContext<TParams>,\n) => Promise<Response>;\n\nexport interface StepflowHandlers {\n /** Handler for POST /api/stepflow/trigger/[workflowId] */\n triggerHandler: RouteHandler<{ workflowId: string }>;\n /** Handler for GET /api/stepflow/runs/[runId] */\n runHandler: RouteHandler<{ runId: string }>;\n /** Handler for GET /api/stepflow/runs/[runId]/stream */\n streamHandler: RouteHandler<{ runId: string }>;\n /** Handler for POST /api/stepflow/notify/[eventId] */\n notifyHandler: RouteHandler<{ eventId: string }>;\n /** Handler for GET /api/stepflow/health */\n healthHandler: RouteHandler;\n}\n\n// ============================================================================\n// Re-exports from shared\n// ============================================================================\n\nexport {\n type AuthContext,\n type AuthResult,\n type AuthHandler,\n type AuthConfig,\n type EndpointConfig,\n type EndpointOption,\n type EndpointPreset,\n type RouteName,\n createApiKeyAuth,\n createBearerAuth,\n anyOf,\n allOf,\n isRouteEnabled,\n resolveEndpoints,\n} from \"@stepflowjs/adapter-shared\";\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getAccessToken(request: Request): string | undefined {\n const url = new URL(request.url);\n const queryToken = url.searchParams.get(\"token\");\n if (queryToken) return queryToken;\n\n const authHeader = request.headers.get(\"authorization\");\n if (!authHeader) return undefined;\n\n const match = authHeader.match(/^Bearer\\s+(.+)$/i);\n return match?.[1];\n}\n\nfunction jsonResponse<T>(data: T, status = 200): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nfunction jsonError(status: number, error: string, message?: string): Response {\n const body: ErrorResponseBody = message ? { error, message } : { error };\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nfunction jsonAuthError(\n status: number,\n code: string,\n message: string,\n): Response {\n const body = createErrorBody(code, message);\n return NextResponse.json(body, { status });\n}\n\nasync function parseJsonBody<T>(request: Request): Promise<T> {\n try {\n return (await request.json()) as T;\n } catch {\n throw new Error(\"Invalid JSON body\");\n }\n}\n\n/**\n * Creates a NextRequest object from Request for auth handlers\n */\nfunction createNextRequest(request: Request): NextRequest {\n const url = new URL(request.url);\n return {\n header: (name: string) => request.headers.get(name) ?? undefined,\n headers: request.headers,\n url: request.url,\n method: request.method,\n query: (name: string) => url.searchParams.get(name) ?? undefined,\n };\n}\n\n/**\n * Runs authorization check and returns error response if denied\n */\nasync function checkAuth(\n request: Request,\n route: RouteName,\n options: HandlerOptions,\n params: { workflowId?: string; eventId?: string; runId?: string } = {},\n): Promise<Response | null> {\n const ctx: NextAuthContext = {\n route,\n request: createNextRequest(request),\n extra: request,\n ...params,\n };\n\n const result = await runAuth(ctx, options.auth);\n\n if (!result.ok) {\n const { status, code, message } = getAuthFailureResponse(result);\n await options.onAuthFailure?.(ctx, result);\n return jsonAuthError(status, code, message);\n }\n\n return null;\n}\n\n// ============================================================================\n// SSE Helpers\n// ============================================================================\n\nfunction createSSEStream(): {\n stream: ReadableStream<Uint8Array>;\n controller: ReadableStreamDefaultController<Uint8Array>;\n encoder: TextEncoder;\n} {\n let controller: ReadableStreamDefaultController<Uint8Array>;\n const encoder = new TextEncoder();\n\n const stream = new ReadableStream<Uint8Array>({\n start(ctrl) {\n controller = ctrl;\n },\n });\n\n return { stream, controller: controller!, encoder };\n}\n\nfunction writeSSE(\n controller: ReadableStreamDefaultController<Uint8Array>,\n encoder: TextEncoder,\n event: string,\n data: unknown,\n): void {\n const message = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`;\n controller.enqueue(encoder.encode(message));\n}\n\n// ============================================================================\n// Handler Factory\n// ============================================================================\n\n/**\n * Create Next.js App Router route handlers for Stepflow\n *\n * @example\n * ```typescript\n * // app/api/stepflow/trigger/[workflowId]/route.ts\n * import { stepflow } from '@/lib/stepflow';\n * import { createStepflowHandlers } from '@stepflowjs/adapter-nextjs';\n *\n * const { triggerHandler } = createStepflowHandlers(stepflow);\n * export const POST = triggerHandler;\n * ```\n */\nexport function createStepflowHandlers(\n stepflow: Stepflow,\n options: HandlerOptions = {},\n): StepflowHandlers {\n // -------------------------------------------------------------------------\n // Trigger Handler - POST /api/stepflow/trigger/[workflowId]\n // -------------------------------------------------------------------------\n const triggerHandler: RouteHandler<{ workflowId: string }> = async (\n request,\n context,\n ) => {\n if (!isRouteEnabled(\"trigger\", options.endpoints)) {\n return jsonError(404, \"Not found\", \"Endpoint disabled\");\n }\n\n const { workflowId } = await context.params;\n\n const authError = await checkAuth(request, \"trigger\", options, {\n workflowId,\n });\n if (authError) return authError;\n\n try {\n const body = await parseJsonBody<TriggerRequestBody>(request);\n const result = await stepflow.trigger(workflowId, body.payload, {\n runId: body.runId,\n metadata: body.metadata,\n delay: body.delay,\n idempotencyKey: body.idempotencyKey,\n });\n\n const response: TriggerResult = result;\n return jsonResponse(response);\n } catch (error) {\n const message = (error as Error).message;\n if (message.includes(\"Workflow\") && message.includes(\"not found\")) {\n return jsonError(404, \"Not found\", message);\n }\n if (message === \"Invalid JSON body\") {\n return jsonError(400, \"Bad request\", message);\n }\n return jsonError(500, \"Internal server error\", message);\n }\n };\n\n // -------------------------------------------------------------------------\n // Run Handler - GET /api/stepflow/runs/[runId]\n // -------------------------------------------------------------------------\n const runHandler: RouteHandler<{ runId: string }> = async (\n request,\n context,\n ) => {\n if (!isRouteEnabled(\"runs\", options.endpoints)) {\n return jsonError(404, \"Not found\", \"Endpoint disabled\");\n }\n\n const { runId } = await context.params;\n\n const authError = await checkAuth(request, \"runs\", options, { runId });\n if (authError) return authError;\n\n try {\n const accessToken = getAccessToken(request);\n const run = await stepflow.getRun(runId, { accessToken });\n\n if (!run) {\n return jsonError(404, \"Not found\", `Run \"${runId}\" not found`);\n }\n\n const response: Execution = run;\n return jsonResponse(response);\n } catch (error) {\n const message = (error as Error).message;\n if (message === \"Invalid access token\") {\n return jsonError(401, \"Unauthorized\", message);\n }\n return jsonError(500, \"Internal server error\", message);\n }\n };\n\n // -------------------------------------------------------------------------\n // Stream Handler - GET /api/stepflow/runs/[runId]/stream\n // -------------------------------------------------------------------------\n const streamHandler: RouteHandler<{ runId: string }> = async (\n request,\n context,\n ) => {\n if (!isRouteEnabled(\"runsStream\", options.endpoints)) {\n return jsonError(404, \"Not found\", \"Endpoint disabled\");\n }\n\n const { runId } = await context.params;\n\n const authError = await checkAuth(request, \"runsStream\", options, {\n runId,\n });\n if (authError) return authError;\n\n // Validate access token if provided\n const accessToken = getAccessToken(request);\n if (accessToken) {\n try {\n await stepflow.getRun(runId, { accessToken });\n } catch (error) {\n const message = (error as Error).message;\n if (message === \"Invalid access token\") {\n return jsonError(401, \"Unauthorized\", message);\n }\n return jsonError(500, \"Internal server error\", message);\n }\n }\n\n // Parse throttle parameter\n const url = new URL(request.url);\n const throttleMsParam = url.searchParams.get(\"throttle\");\n const throttleMs = throttleMsParam ? Number(throttleMsParam) : undefined;\n const effectiveThrottleMs =\n throttleMs !== undefined && Number.isFinite(throttleMs) && throttleMs >= 0\n ? throttleMs\n : undefined;\n\n const { stream, controller, encoder } = createSSEStream();\n let lastSentAt = 0;\n\n const send = (event: string, data: unknown) => {\n if (effectiveThrottleMs !== undefined) {\n const now = Date.now();\n if (now - lastSentAt < effectiveThrottleMs) {\n return;\n }\n lastSentAt = now;\n }\n writeSSE(controller, encoder, event, data);\n };\n\n // Start async processing\n void (async () => {\n try {\n // Get initial run state\n let run = await stepflow.getRun(runId);\n\n // Poll until run exists\n while (!run) {\n await new Promise((resolve) => setTimeout(resolve, 250));\n run = await stepflow.getRun(runId);\n }\n\n const executionId = run.id;\n send(\"update\", run);\n\n if (run.status === \"completed\") {\n send(\"execution:complete\", { result: run.result });\n }\n if (run.status === \"failed\") {\n send(\"execution:failed\", { error: run.error });\n }\n\n // Subscribe to updates\n const unsubscribe = stepflow.subscribeToExecution(executionId, () => {\n void (async () => {\n const execution = await stepflow.getExecution(executionId);\n if (execution) {\n send(\"update\", execution);\n\n if (execution.status === \"completed\") {\n send(\"execution:complete\", { result: execution.result });\n }\n if (execution.status === \"failed\") {\n send(\"execution:failed\", { error: execution.error });\n }\n }\n })();\n });\n\n // Keep connection open (Next.js will close on client disconnect)\n await new Promise((resolve) => setTimeout(resolve, 60 * 60 * 1000));\n unsubscribe();\n } catch {\n // Stream closed\n } finally {\n try {\n controller.close();\n } catch {\n // Already closed\n }\n }\n })();\n\n return new Response(stream, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n };\n\n // -------------------------------------------------------------------------\n // Notify Handler - POST /api/stepflow/notify/[eventId]\n // -------------------------------------------------------------------------\n const notifyHandler: RouteHandler<{ eventId: string }> = async (\n request,\n context,\n ) => {\n if (!isRouteEnabled(\"notify\", options.endpoints)) {\n return jsonError(404, \"Not found\", \"Endpoint disabled\");\n }\n\n const { eventId } = await context.params;\n\n const authError = await checkAuth(request, \"notify\", options, { eventId });\n if (authError) return authError;\n\n try {\n const body = await parseJsonBody<NotifyRequestBody>(request);\n const result = await stepflow.notify(eventId, body.data);\n const response: NotifyResult = result;\n return jsonResponse(response);\n } catch (error) {\n const message = (error as Error).message;\n if (message === \"Invalid JSON body\") {\n return jsonError(400, \"Bad request\", message);\n }\n return jsonError(500, \"Internal server error\", message);\n }\n };\n\n // -------------------------------------------------------------------------\n // Health Handler - GET /api/stepflow/health\n // -------------------------------------------------------------------------\n const healthHandler: RouteHandler = async (request) => {\n if (!isRouteEnabled(\"health\", options.endpoints)) {\n return jsonError(404, \"Not found\", \"Endpoint disabled\");\n }\n\n const authError = await checkAuth(request, \"health\", options);\n if (authError) return authError;\n\n try {\n const ok = await (options.healthCheck?.() ?? stepflow.healthCheck());\n return jsonResponse({ ok });\n } catch (error) {\n return jsonError(500, \"Internal server error\", (error as Error).message);\n }\n };\n\n return {\n triggerHandler,\n runHandler,\n streamHandler,\n notifyHandler,\n healthHandler,\n };\n}\n\n// Re-export types\nexport type { Execution, TriggerResult, NotifyResult } from \"@stepflowjs/core\";\n"],"mappings":"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","names":["isRouteEnabled"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stepflowjs/adapter-nextjs",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Next.js App Router adapter for Stepflow workflows",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@stepflowjs/adapter-shared": "0.0.1",
|
|
20
|
+
"@stepflowjs/core": "0.0.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"next": "^15.3.3",
|
|
24
|
+
"react": "^19.1.0",
|
|
25
|
+
"react-dom": "^19.1.0",
|
|
26
|
+
"tsup": "^8.5.1",
|
|
27
|
+
"vitest": "^4.0.17"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"next": "^14.0.0 || ^15.0.0",
|
|
31
|
+
"typescript": "^5.0.0"
|
|
32
|
+
},
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"author": "Stepflow Contributors",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://stepflow-production.up.railway.app",
|
|
38
|
+
"directory": "packages/adapters/nextjs"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://stepflow-production.up.railway.app",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://stepflow-production.up.railway.app"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"stepflow",
|
|
46
|
+
"adapter",
|
|
47
|
+
"nextjs",
|
|
48
|
+
"next.js",
|
|
49
|
+
"framework",
|
|
50
|
+
"workflow",
|
|
51
|
+
"orchestration"
|
|
52
|
+
],
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup",
|
|
58
|
+
"dev": "tsup --watch",
|
|
59
|
+
"typecheck": "tsc --noEmit",
|
|
60
|
+
"test": "vitest",
|
|
61
|
+
"clean": "rm -rf dist"
|
|
62
|
+
}
|
|
63
|
+
}
|