next-workflow-builder 0.3.0

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,2672 @@
1
+ import {
2
+ ARRAY_INDEX_PATTERN,
3
+ analyzeNodeUsage,
4
+ buildEdgeMap,
5
+ conditionAction,
6
+ databaseQueryAction,
7
+ escapeForTemplateLiteral,
8
+ findTriggerNodes,
9
+ generateWorkflowModule,
10
+ httpRequestAction,
11
+ sanitizeFunctionName,
12
+ sanitizeStepName,
13
+ sanitizeVarName
14
+ } from "../../chunk-XJ67EFQA.js";
15
+ import {
16
+ auth,
17
+ getAuthConfig
18
+ } from "../../chunk-P3DTV3QS.js";
19
+ import {
20
+ getErrorMessageAsync
21
+ } from "../../chunk-5YYA34YV.js";
22
+ import {
23
+ BOILERPLATE_PATH,
24
+ CODEGEN_TEMPLATES_PATH,
25
+ NON_ALPHANUMERIC_REGEX,
26
+ TEMPLATE_EXPORT_REGEX,
27
+ WHITESPACE_SPLIT_REGEX
28
+ } from "../../chunk-OQHML4II.js";
29
+ import {
30
+ createIntegration,
31
+ deleteIntegration,
32
+ getIntegration,
33
+ getIntegrations,
34
+ updateIntegration,
35
+ validateWorkflowIntegrations
36
+ } from "../../chunk-BNYDOC3I.js";
37
+ import {
38
+ findActionById,
39
+ getAllEnvVars,
40
+ getDependenciesForActions
41
+ } from "../../chunk-Z3BJJYHM.js";
42
+ import {
43
+ accounts,
44
+ apiKeys,
45
+ db,
46
+ generateId,
47
+ logWorkflowComplete,
48
+ users,
49
+ withStepLogging,
50
+ workflowExecutionLogs,
51
+ workflowExecutions,
52
+ workflows
53
+ } from "../../chunk-3MSAF2TH.js";
54
+
55
+ // src/server/api/index.ts
56
+ import { NextResponse as NextResponse5 } from "next/server";
57
+
58
+ // src/server/api/api-keys.ts
59
+ import { and, eq } from "drizzle-orm";
60
+ import { NextResponse } from "next/server";
61
+ import { createHash } from "crypto";
62
+
63
+ // src/server/auth/resolve-user.ts
64
+ async function resolveUser(request) {
65
+ const config = getAuthConfig();
66
+ if (config.getUser) {
67
+ return config.getUser(request);
68
+ }
69
+ const session = await auth.api.getSession({
70
+ headers: request.headers
71
+ });
72
+ if (!session?.user) return null;
73
+ return {
74
+ id: session.user.id,
75
+ email: session.user.email ?? void 0,
76
+ name: session.user.name ?? void 0
77
+ };
78
+ }
79
+
80
+ // src/server/api/api-keys.ts
81
+ async function handleGetApiKeys(request) {
82
+ try {
83
+ const user = await resolveUser(request);
84
+ if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
85
+ const keys = await db.query.apiKeys.findMany({
86
+ where: eq(apiKeys.userId, user.id),
87
+ columns: {
88
+ id: true,
89
+ name: true,
90
+ keyPrefix: true,
91
+ createdAt: true,
92
+ lastUsedAt: true
93
+ },
94
+ orderBy: (table, { desc: descFn }) => [descFn(table.createdAt)]
95
+ });
96
+ return NextResponse.json(keys);
97
+ } catch (error) {
98
+ console.error("Failed to list API keys:", error);
99
+ return NextResponse.json({ error: "Failed to list API keys" }, { status: 500 });
100
+ }
101
+ }
102
+ async function handleCreateApiKey(request) {
103
+ try {
104
+ const user = await resolveUser(request);
105
+ if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
106
+ const isAnonymous = user.name === "Anonymous" || user.email?.startsWith("temp-");
107
+ if (isAnonymous) {
108
+ return NextResponse.json(
109
+ { error: "Anonymous users cannot create API keys" },
110
+ { status: 403 }
111
+ );
112
+ }
113
+ const body = await request.json().catch(() => ({}));
114
+ const name = body.name || null;
115
+ const { randomBytes } = await import("crypto");
116
+ const randomPart = randomBytes(24).toString("base64url");
117
+ const key = `wfb_${randomPart}`;
118
+ const hash = createHash("sha256").update(key).digest("hex");
119
+ const prefix = key.slice(0, 11);
120
+ const [newKey] = await db.insert(apiKeys).values({
121
+ userId: user.id,
122
+ name,
123
+ keyHash: hash,
124
+ keyPrefix: prefix
125
+ }).returning({
126
+ id: apiKeys.id,
127
+ name: apiKeys.name,
128
+ keyPrefix: apiKeys.keyPrefix,
129
+ createdAt: apiKeys.createdAt
130
+ });
131
+ return NextResponse.json({ ...newKey, key });
132
+ } catch (error) {
133
+ console.error("Failed to create API key:", error);
134
+ return NextResponse.json({ error: "Failed to create API key" }, { status: 500 });
135
+ }
136
+ }
137
+ async function handleDeleteApiKey(request, keyId) {
138
+ try {
139
+ const user = await resolveUser(request);
140
+ if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
141
+ const result = await db.delete(apiKeys).where(and(eq(apiKeys.id, keyId), eq(apiKeys.userId, user.id))).returning({ id: apiKeys.id });
142
+ if (result.length === 0) {
143
+ return NextResponse.json({ error: "API key not found" }, { status: 404 });
144
+ }
145
+ return NextResponse.json({ success: true });
146
+ } catch (error) {
147
+ console.error("Failed to delete API key:", error);
148
+ return NextResponse.json({ error: "Failed to delete API key" }, { status: 500 });
149
+ }
150
+ }
151
+
152
+ // src/server/api/auth.ts
153
+ import { toNextJsHandler } from "better-auth/next-js";
154
+
155
+ // src/server/api/utils.ts
156
+ import { eq as eq2 } from "drizzle-orm";
157
+ import { readdir, readFile } from "fs/promises";
158
+ import { join } from "path";
159
+
160
+ // src/server/lib/condition-validator.ts
161
+ var DANGEROUS_PATTERNS = [
162
+ // Assignment operators
163
+ /(?<![=!<>])=(?!=)/g,
164
+ // = but not ==, ===, !=, !==, <=, >=
165
+ /\+=|-=|\*=|\/=|%=|\^=|\|=|&=/g,
166
+ // Code execution
167
+ /\beval\s*\(/gi,
168
+ /\bFunction\s*\(/gi,
169
+ /\bimport\s*\(/gi,
170
+ /\brequire\s*\(/gi,
171
+ /\bnew\s+\w/gi,
172
+ // Dangerous globals
173
+ /\bprocess\b/gi,
174
+ /\bglobal\b/gi,
175
+ /\bwindow\b/gi,
176
+ /\bdocument\b/gi,
177
+ /\bconstructor\b/gi,
178
+ /\b__proto__\b/gi,
179
+ /\bprototype\b/gi,
180
+ // Control flow that could be exploited
181
+ /\bwhile\s*\(/gi,
182
+ /\bfor\s*\(/gi,
183
+ /\bdo\s*\{/gi,
184
+ /\bswitch\s*\(/gi,
185
+ /\btry\s*\{/gi,
186
+ /\bcatch\s*\(/gi,
187
+ /\bfinally\s*\{/gi,
188
+ /\bthrow\s+/gi,
189
+ /\breturn\s+/gi,
190
+ // Template literals with expressions (could execute code)
191
+ /`[^`]*\$\{/g,
192
+ // Object literals (but NOT bracket property access)
193
+ /\{\s*\w+\s*:/g,
194
+ // Increment/decrement
195
+ /\+\+|--/g,
196
+ // Bitwise operators (rarely needed, often used in exploits)
197
+ /<<|>>|>>>/g,
198
+ // Comma operator (can chain expressions)
199
+ /,(?![^(]*\))/g,
200
+ // Comma not inside function call parentheses
201
+ // Semicolons (statement separator)
202
+ /;/g
203
+ ];
204
+ var ALLOWED_METHODS = /* @__PURE__ */ new Set([
205
+ "includes",
206
+ "startsWith",
207
+ "endsWith",
208
+ "toString",
209
+ "toLowerCase",
210
+ "toUpperCase",
211
+ "trim",
212
+ "length"
213
+ // Actually a property, but accessed like .length
214
+ ]);
215
+ var METHOD_CALL_PATTERN = /\.(\w+)\s*\(/g;
216
+ var BRACKET_EXPRESSION_PATTERN = /(\w+)\s*\[([^\]]+)\]/g;
217
+ var VALID_BRACKET_ACCESS_PATTERN = /^__v\d+$/;
218
+ var VALID_BRACKET_CONTENT_PATTERN = /^(\d+|'[^']*'|"[^"]*")$/;
219
+ var WHITESPACE_SPLIT_PATTERN = /\s+/;
220
+ var VARIABLE_TOKEN_PATTERN = /^__v\d+/;
221
+ var STRING_TOKEN_PATTERN = /^['"]/;
222
+ var NUMBER_TOKEN_PATTERN = /^\d/;
223
+ var LITERAL_TOKEN_PATTERN = /^(true|false|null|undefined)$/;
224
+ var OPERATOR_TOKEN_PATTERN = /^(===|!==|==|!=|>=|<=|>|<|&&|\|\||!|\(|\))$/;
225
+ var IDENTIFIER_TOKEN_PATTERN = /^[a-zA-Z_]\w*$/;
226
+ function checkDangerousPatterns(expression) {
227
+ for (const pattern of DANGEROUS_PATTERNS) {
228
+ pattern.lastIndex = 0;
229
+ if (pattern.test(expression)) {
230
+ pattern.lastIndex = 0;
231
+ const match = expression.match(pattern);
232
+ return {
233
+ valid: false,
234
+ error: `Condition contains disallowed syntax: "${match?.[0] || "unknown"}"`
235
+ };
236
+ }
237
+ }
238
+ return { valid: true };
239
+ }
240
+ function checkBracketExpressions(expression) {
241
+ BRACKET_EXPRESSION_PATTERN.lastIndex = 0;
242
+ let match = null;
243
+ while (true) {
244
+ match = BRACKET_EXPRESSION_PATTERN.exec(expression);
245
+ if (match === null) {
246
+ break;
247
+ }
248
+ const beforeBracket = match[1];
249
+ const insideBracket = match[2].trim();
250
+ if (!VALID_BRACKET_ACCESS_PATTERN.test(beforeBracket)) {
251
+ return {
252
+ valid: false,
253
+ error: `Bracket notation is only allowed on workflow variables. Found: "${beforeBracket}[...]"`
254
+ };
255
+ }
256
+ if (!VALID_BRACKET_CONTENT_PATTERN.test(insideBracket)) {
257
+ return {
258
+ valid: false,
259
+ error: `Invalid bracket content: "[${insideBracket}]". Only numeric indices or string literals are allowed.`
260
+ };
261
+ }
262
+ }
263
+ const standaloneArrayPattern = /(?:^|[=!<>&|(\s])\s*\[/g;
264
+ standaloneArrayPattern.lastIndex = 0;
265
+ if (standaloneArrayPattern.test(expression)) {
266
+ return {
267
+ valid: false,
268
+ error: "Array literals are not allowed in conditions. Use workflow variables instead."
269
+ };
270
+ }
271
+ return { valid: true };
272
+ }
273
+ function checkMethodCalls(expression) {
274
+ METHOD_CALL_PATTERN.lastIndex = 0;
275
+ let match = null;
276
+ while (true) {
277
+ match = METHOD_CALL_PATTERN.exec(expression);
278
+ if (match === null) {
279
+ break;
280
+ }
281
+ const methodName = match[1];
282
+ if (!ALLOWED_METHODS.has(methodName)) {
283
+ return {
284
+ valid: false,
285
+ error: `Method "${methodName}" is not allowed in conditions. Allowed methods: ${Array.from(ALLOWED_METHODS).join(", ")}`
286
+ };
287
+ }
288
+ }
289
+ return { valid: true };
290
+ }
291
+ function checkParentheses(expression) {
292
+ let parenDepth = 0;
293
+ for (const char of expression) {
294
+ if (char === "(") {
295
+ parenDepth += 1;
296
+ }
297
+ if (char === ")") {
298
+ parenDepth -= 1;
299
+ }
300
+ if (parenDepth < 0) {
301
+ return { valid: false, error: "Unbalanced parentheses in condition" };
302
+ }
303
+ }
304
+ if (parenDepth !== 0) {
305
+ return { valid: false, error: "Unbalanced parentheses in condition" };
306
+ }
307
+ return { valid: true };
308
+ }
309
+ function isValidToken(token) {
310
+ if (VARIABLE_TOKEN_PATTERN.test(token)) {
311
+ return true;
312
+ }
313
+ if (STRING_TOKEN_PATTERN.test(token)) {
314
+ return true;
315
+ }
316
+ if (NUMBER_TOKEN_PATTERN.test(token)) {
317
+ return true;
318
+ }
319
+ if (LITERAL_TOKEN_PATTERN.test(token)) {
320
+ return true;
321
+ }
322
+ if (OPERATOR_TOKEN_PATTERN.test(token)) {
323
+ return true;
324
+ }
325
+ return false;
326
+ }
327
+ function checkUnauthorizedIdentifiers(expression) {
328
+ const tokens = expression.split(WHITESPACE_SPLIT_PATTERN).filter(Boolean);
329
+ for (const token of tokens) {
330
+ if (isValidToken(token)) {
331
+ continue;
332
+ }
333
+ if (IDENTIFIER_TOKEN_PATTERN.test(token) && !token.startsWith("__v")) {
334
+ return {
335
+ valid: false,
336
+ error: `Unknown identifier "${token}" in condition. Use template variables like {{@nodeId:Label.field}} to reference workflow data.`
337
+ };
338
+ }
339
+ }
340
+ return { valid: true };
341
+ }
342
+ function validateConditionExpression(expression) {
343
+ if (!expression || expression.trim() === "") {
344
+ return { valid: false, error: "Condition expression cannot be empty" };
345
+ }
346
+ const dangerousCheck = checkDangerousPatterns(expression);
347
+ if (!dangerousCheck.valid) {
348
+ return dangerousCheck;
349
+ }
350
+ const bracketCheck = checkBracketExpressions(expression);
351
+ if (!bracketCheck.valid) {
352
+ return bracketCheck;
353
+ }
354
+ const methodCheck = checkMethodCalls(expression);
355
+ if (!methodCheck.valid) {
356
+ return methodCheck;
357
+ }
358
+ const parenCheck = checkParentheses(expression);
359
+ if (!parenCheck.valid) {
360
+ return parenCheck;
361
+ }
362
+ const identifierCheck = checkUnauthorizedIdentifiers(expression);
363
+ if (!identifierCheck.valid) {
364
+ return identifierCheck;
365
+ }
366
+ return { valid: true };
367
+ }
368
+ function preValidateConditionExpression(expression) {
369
+ if (!expression || typeof expression !== "string") {
370
+ return { valid: false, error: "Condition must be a non-empty string" };
371
+ }
372
+ const dangerousKeywords = [
373
+ "eval",
374
+ "Function",
375
+ "import",
376
+ "require",
377
+ "process",
378
+ "global",
379
+ "window",
380
+ "document",
381
+ "__proto__",
382
+ "constructor",
383
+ "prototype"
384
+ ];
385
+ const lowerExpression = expression.toLowerCase();
386
+ for (const keyword of dangerousKeywords) {
387
+ if (lowerExpression.includes(keyword.toLowerCase())) {
388
+ return {
389
+ valid: false,
390
+ error: `Condition contains disallowed keyword: "${keyword}"`
391
+ };
392
+ }
393
+ }
394
+ return { valid: true };
395
+ }
396
+
397
+ // src/server/lib/steps/trigger.ts
398
+ import "server-only";
399
+ function executeTrigger(input) {
400
+ return {
401
+ success: true,
402
+ data: input.triggerData
403
+ };
404
+ }
405
+ async function triggerStep(input) {
406
+ "use step";
407
+ if (input._workflowComplete) {
408
+ await logWorkflowComplete(input._workflowComplete);
409
+ return { success: true, data: {} };
410
+ }
411
+ return withStepLogging(input, () => Promise.resolve(executeTrigger(input)));
412
+ }
413
+ triggerStep.maxRetries = 0;
414
+
415
+ // src/server/lib/workflow-executor.workflow.ts
416
+ var SYSTEM_ACTIONS = {
417
+ "Database Query": {
418
+ // biome-ignore lint/suspicious/noExplicitAny: Dynamic module import
419
+ importer: () => import("../../database-query-GRWP3S3M.js"),
420
+ stepFunction: "databaseQueryStep"
421
+ },
422
+ "HTTP Request": {
423
+ // biome-ignore lint/suspicious/noExplicitAny: Dynamic module import
424
+ importer: () => import("../../http-request-2HVCXQHK.js"),
425
+ stepFunction: "httpRequestStep"
426
+ },
427
+ Condition: {
428
+ // biome-ignore lint/suspicious/noExplicitAny: Dynamic module import
429
+ importer: () => import("../../condition-SFT7Y5YJ.js"),
430
+ stepFunction: "conditionStep"
431
+ }
432
+ };
433
+ function replaceTemplateVariable(match, nodeId, rest, outputs, evalContext, varCounter) {
434
+ const sanitizedNodeId = nodeId.replace(/[^a-zA-Z0-9]/g, "_");
435
+ const output = outputs[sanitizedNodeId];
436
+ if (!output) {
437
+ console.log("[Condition] Output not found for node:", sanitizedNodeId);
438
+ return match;
439
+ }
440
+ const dotIndex = rest.indexOf(".");
441
+ let value;
442
+ if (dotIndex === -1) {
443
+ value = output.data;
444
+ } else if (output.data === null || output.data === void 0) {
445
+ value = void 0;
446
+ } else {
447
+ const fieldPath = rest.substring(dotIndex + 1);
448
+ const fields = fieldPath.split(".");
449
+ let current = output.data;
450
+ const firstField = fields[0];
451
+ if (current && typeof current === "object" && "success" in current && "data" in current && firstField !== "success" && firstField !== "data" && firstField !== "error") {
452
+ current = current.data;
453
+ }
454
+ for (const field of fields) {
455
+ if (current && typeof current === "object") {
456
+ current = current[field];
457
+ } else {
458
+ console.log("[Condition] Field access failed:", fieldPath);
459
+ value = void 0;
460
+ break;
461
+ }
462
+ }
463
+ if (value === void 0 && current !== void 0) {
464
+ value = current;
465
+ }
466
+ }
467
+ const varName = `__v${varCounter.value}`;
468
+ varCounter.value += 1;
469
+ evalContext[varName] = value;
470
+ return varName;
471
+ }
472
+ function evaluateConditionExpression(conditionExpression, outputs) {
473
+ console.log("[Condition] Original expression:", conditionExpression);
474
+ if (typeof conditionExpression === "boolean") {
475
+ return { result: conditionExpression, resolvedValues: {} };
476
+ }
477
+ if (typeof conditionExpression === "string") {
478
+ const preValidation = preValidateConditionExpression(conditionExpression);
479
+ if (!preValidation.valid) {
480
+ console.error("[Condition] Pre-validation failed:", preValidation.error);
481
+ console.error("[Condition] Expression was:", conditionExpression);
482
+ return { result: false, resolvedValues: {} };
483
+ }
484
+ try {
485
+ const evalContext = {};
486
+ const resolvedValues = {};
487
+ let transformedExpression = conditionExpression;
488
+ const templatePattern = /\{\{@([^:]+):([^}]+)\}\}/g;
489
+ const varCounter = { value: 0 };
490
+ transformedExpression = transformedExpression.replace(
491
+ templatePattern,
492
+ (match, nodeId, rest) => {
493
+ const varName = replaceTemplateVariable(
494
+ match,
495
+ nodeId,
496
+ rest,
497
+ outputs,
498
+ evalContext,
499
+ varCounter
500
+ );
501
+ resolvedValues[rest] = evalContext[varName];
502
+ return varName;
503
+ }
504
+ );
505
+ const validation = validateConditionExpression(transformedExpression);
506
+ if (!validation.valid) {
507
+ console.error("[Condition] Validation failed:", validation.error);
508
+ console.error("[Condition] Original expression:", conditionExpression);
509
+ console.error(
510
+ "[Condition] Transformed expression:",
511
+ transformedExpression
512
+ );
513
+ return { result: false, resolvedValues };
514
+ }
515
+ const varNames = Object.keys(evalContext);
516
+ const varValues = Object.values(evalContext);
517
+ const evalFunc = new Function(
518
+ ...varNames,
519
+ `return (${transformedExpression});`
520
+ );
521
+ const result = evalFunc(...varValues);
522
+ return { result: Boolean(result), resolvedValues };
523
+ } catch (error) {
524
+ console.error("[Condition] Failed to evaluate condition:", error);
525
+ console.error("[Condition] Expression was:", conditionExpression);
526
+ return { result: false, resolvedValues: {} };
527
+ }
528
+ }
529
+ return { result: Boolean(conditionExpression), resolvedValues: {} };
530
+ }
531
+ async function executeActionStep(input) {
532
+ const { actionType, config, outputs, context } = input;
533
+ const stepInput = {
534
+ ...config,
535
+ _context: context
536
+ };
537
+ if (actionType === "Condition") {
538
+ const systemAction2 = SYSTEM_ACTIONS.Condition;
539
+ const module = await systemAction2.importer();
540
+ const originalExpression = stepInput.condition;
541
+ const { result: evaluatedCondition, resolvedValues } = evaluateConditionExpression(originalExpression, outputs);
542
+ console.log("[Condition] Final result:", evaluatedCondition);
543
+ return await module[systemAction2.stepFunction]({
544
+ condition: evaluatedCondition,
545
+ // Include original expression and resolved values for logging purposes
546
+ expression: typeof originalExpression === "string" ? originalExpression : void 0,
547
+ values: Object.keys(resolvedValues).length > 0 ? resolvedValues : void 0,
548
+ _context: context
549
+ });
550
+ }
551
+ const systemAction = SYSTEM_ACTIONS[actionType];
552
+ if (systemAction) {
553
+ const module = await systemAction.importer();
554
+ const stepFunction = module[systemAction.stepFunction];
555
+ return await stepFunction(stepInput);
556
+ }
557
+ const { getStepImporter } = await import("virtual:workflow-builder-step-registry");
558
+ const stepImporter = getStepImporter(actionType);
559
+ if (stepImporter) {
560
+ const module = await stepImporter.importer();
561
+ const stepFunction = module[stepImporter.stepFunction];
562
+ if (stepFunction) {
563
+ return await stepFunction(stepInput);
564
+ }
565
+ return {
566
+ success: false,
567
+ error: `Step function "${stepImporter.stepFunction}" not found in module for action "${actionType}". Check that the plugin exports the correct function name.`
568
+ };
569
+ }
570
+ return {
571
+ success: false,
572
+ error: `Unknown action type: "${actionType}". This action is not registered in the plugin system. Available system actions: ${Object.keys(SYSTEM_ACTIONS).join(", ")}.`
573
+ };
574
+ }
575
+ function processTemplates(config, outputs) {
576
+ const processed = {};
577
+ for (const [key, value] of Object.entries(config)) {
578
+ if (typeof value === "string") {
579
+ let processedValue = value;
580
+ const templatePattern = /\{\{@([^:]+):([^}]+)\}\}/g;
581
+ processedValue = processedValue.replace(
582
+ templatePattern,
583
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Template processing requires nested logic
584
+ (match, nodeId, rest) => {
585
+ const sanitizedNodeId = nodeId.replace(/[^a-zA-Z0-9]/g, "_");
586
+ const output = outputs[sanitizedNodeId];
587
+ if (!output) {
588
+ return match;
589
+ }
590
+ const dotIndex = rest.indexOf(".");
591
+ if (dotIndex === -1) {
592
+ const data = output.data;
593
+ if (data === null || data === void 0) {
594
+ return "";
595
+ }
596
+ if (typeof data === "object") {
597
+ return JSON.stringify(data);
598
+ }
599
+ return String(data);
600
+ }
601
+ if (output.data === null || output.data === void 0) {
602
+ return "";
603
+ }
604
+ const fieldPath = rest.substring(dotIndex + 1);
605
+ const fields = fieldPath.split(".");
606
+ let current = output.data;
607
+ const firstField = fields[0];
608
+ if (current && typeof current === "object" && "success" in current && "data" in current && firstField !== "success" && firstField !== "data" && firstField !== "error") {
609
+ current = current.data;
610
+ }
611
+ for (const field of fields) {
612
+ if (current && typeof current === "object") {
613
+ current = current[field];
614
+ } else {
615
+ return "";
616
+ }
617
+ }
618
+ if (current === null || current === void 0) {
619
+ return "";
620
+ }
621
+ if (typeof current === "object") {
622
+ return JSON.stringify(current);
623
+ }
624
+ return String(current);
625
+ }
626
+ );
627
+ processed[key] = processedValue;
628
+ } else {
629
+ processed[key] = value;
630
+ }
631
+ }
632
+ return processed;
633
+ }
634
+ async function executeWorkflow(input) {
635
+ "use workflow";
636
+ console.log("[Workflow Executor] Starting workflow execution");
637
+ const { nodes, edges, triggerInput = {}, executionId, workflowId } = input;
638
+ console.log("[Workflow Executor] Input:", {
639
+ nodeCount: nodes.length,
640
+ edgeCount: edges.length,
641
+ hasExecutionId: !!executionId,
642
+ workflowId: workflowId || "none"
643
+ });
644
+ const outputs = {};
645
+ const results = {};
646
+ const nodeMap = new Map(nodes.map((n) => [n.id, n]));
647
+ const edgesBySource = /* @__PURE__ */ new Map();
648
+ for (const edge of edges) {
649
+ const targets = edgesBySource.get(edge.source) || [];
650
+ targets.push(edge.target);
651
+ edgesBySource.set(edge.source, targets);
652
+ }
653
+ const nodesWithIncoming = new Set(edges.map((e) => e.target));
654
+ const triggerNodes = nodes.filter(
655
+ (node) => node.data.type === "trigger" && !nodesWithIncoming.has(node.id)
656
+ );
657
+ console.log(
658
+ "[Workflow Executor] Found",
659
+ triggerNodes.length,
660
+ "trigger nodes"
661
+ );
662
+ const { getActionLabel } = await import("virtual:workflow-builder-step-registry");
663
+ function getNodeName(node) {
664
+ if (node.data.label) {
665
+ return node.data.label;
666
+ }
667
+ if (node.data.type === "action") {
668
+ const actionType = node.data.config?.actionType;
669
+ if (actionType) {
670
+ const label = getActionLabel(actionType);
671
+ if (label) {
672
+ return label;
673
+ }
674
+ }
675
+ return "Action";
676
+ }
677
+ if (node.data.type === "trigger") {
678
+ return node.data.config?.triggerType || "Trigger";
679
+ }
680
+ return node.data.type;
681
+ }
682
+ async function executeNode(nodeId, visited = /* @__PURE__ */ new Set()) {
683
+ console.log("[Workflow Executor] Executing node:", nodeId);
684
+ if (visited.has(nodeId)) {
685
+ console.log("[Workflow Executor] Node already visited, skipping");
686
+ return;
687
+ }
688
+ visited.add(nodeId);
689
+ const node = nodeMap.get(nodeId);
690
+ if (!node) {
691
+ console.log("[Workflow Executor] Node not found:", nodeId);
692
+ return;
693
+ }
694
+ if (node.data.enabled === false) {
695
+ console.log("[Workflow Executor] Skipping disabled node:", nodeId);
696
+ const sanitizedNodeId = nodeId.replace(/[^a-zA-Z0-9]/g, "_");
697
+ outputs[sanitizedNodeId] = {
698
+ label: node.data.label || nodeId,
699
+ data: null
700
+ };
701
+ const nextNodes = edgesBySource.get(nodeId) || [];
702
+ await Promise.all(
703
+ nextNodes.map((nextNodeId) => executeNode(nextNodeId, visited))
704
+ );
705
+ return;
706
+ }
707
+ try {
708
+ let result;
709
+ if (node.data.type === "trigger") {
710
+ console.log("[Workflow Executor] Executing trigger node");
711
+ const config = node.data.config || {};
712
+ const triggerType = config.triggerType;
713
+ let triggerData = {
714
+ triggered: true,
715
+ timestamp: Date.now()
716
+ };
717
+ if (triggerType === "Webhook" && config.webhookMockRequest && (!triggerInput || Object.keys(triggerInput).length === 0)) {
718
+ try {
719
+ const mockData = JSON.parse(config.webhookMockRequest);
720
+ triggerData = { ...triggerData, ...mockData };
721
+ console.log(
722
+ "[Workflow Executor] Using webhook mock request data:",
723
+ mockData
724
+ );
725
+ } catch (error) {
726
+ console.error(
727
+ "[Workflow Executor] Failed to parse webhook mock request:",
728
+ error
729
+ );
730
+ }
731
+ } else if (triggerInput && Object.keys(triggerInput).length > 0) {
732
+ triggerData = { ...triggerData, ...triggerInput };
733
+ }
734
+ const triggerContext = {
735
+ executionId,
736
+ nodeId: node.id,
737
+ nodeName: getNodeName(node),
738
+ nodeType: node.data.type
739
+ };
740
+ const triggerResult = await triggerStep({
741
+ triggerData,
742
+ _context: triggerContext
743
+ });
744
+ result = {
745
+ success: triggerResult.success,
746
+ data: triggerResult.data
747
+ };
748
+ } else if (node.data.type === "action") {
749
+ const config = node.data.config || {};
750
+ const actionType = config.actionType;
751
+ console.log("[Workflow Executor] Executing action node:", actionType);
752
+ if (!actionType) {
753
+ result = {
754
+ success: false,
755
+ error: `Action node "${node.data.label || node.id}" has no action type configured`
756
+ };
757
+ results[nodeId] = result;
758
+ return;
759
+ }
760
+ const configWithoutCondition = { ...config };
761
+ const originalCondition = config.condition;
762
+ configWithoutCondition.condition = void 0;
763
+ const processedConfig = processTemplates(
764
+ configWithoutCondition,
765
+ outputs
766
+ );
767
+ if (originalCondition !== void 0) {
768
+ processedConfig.condition = originalCondition;
769
+ }
770
+ const stepContext = {
771
+ executionId,
772
+ nodeId: node.id,
773
+ nodeName: getNodeName(node),
774
+ nodeType: actionType
775
+ };
776
+ console.log("[Workflow Executor] Calling executeActionStep");
777
+ const stepResult = await executeActionStep({
778
+ actionType,
779
+ config: processedConfig,
780
+ outputs,
781
+ context: stepContext
782
+ });
783
+ console.log("[Workflow Executor] Step result received:", {
784
+ hasResult: !!stepResult,
785
+ resultType: typeof stepResult
786
+ });
787
+ const isErrorResult = stepResult && typeof stepResult === "object" && "success" in stepResult && stepResult.success === false;
788
+ if (isErrorResult) {
789
+ const errorResult = stepResult;
790
+ const errorMessage = typeof errorResult.error === "string" ? errorResult.error : errorResult.error?.message || `Step "${actionType}" in node "${node.data.label || node.id}" failed without a specific error message.`;
791
+ console.error(`[Workflow Executor] Step "${actionType}" failed:`, errorMessage);
792
+ result = {
793
+ success: false,
794
+ error: errorMessage
795
+ };
796
+ } else {
797
+ result = {
798
+ success: true,
799
+ data: stepResult
800
+ };
801
+ }
802
+ } else {
803
+ console.log("[Workflow Executor] Unknown node type:", node.data.type);
804
+ result = {
805
+ success: false,
806
+ error: `Unknown node type "${node.data.type}" in node "${node.data.label || node.id}". Expected "trigger" or "action".`
807
+ };
808
+ }
809
+ results[nodeId] = result;
810
+ const sanitizedNodeId = nodeId.replace(/[^a-zA-Z0-9]/g, "_");
811
+ outputs[sanitizedNodeId] = {
812
+ label: node.data.label || nodeId,
813
+ data: result.data
814
+ };
815
+ console.log("[Workflow Executor] Node execution completed:", {
816
+ nodeId,
817
+ success: result.success
818
+ });
819
+ if (result.success) {
820
+ const isConditionNode = node.data.type === "action" && node.data.config?.actionType === "Condition";
821
+ if (isConditionNode) {
822
+ const conditionResult = result.data?.condition;
823
+ console.log(
824
+ "[Workflow Executor] Condition node result:",
825
+ conditionResult
826
+ );
827
+ if (conditionResult === true) {
828
+ const nextNodes = edgesBySource.get(nodeId) || [];
829
+ console.log(
830
+ "[Workflow Executor] Condition is true, executing",
831
+ nextNodes.length,
832
+ "next nodes in parallel"
833
+ );
834
+ await Promise.all(
835
+ nextNodes.map((nextNodeId) => executeNode(nextNodeId, visited))
836
+ );
837
+ } else {
838
+ console.log(
839
+ "[Workflow Executor] Condition is false, skipping next nodes"
840
+ );
841
+ }
842
+ } else {
843
+ const nextNodes = edgesBySource.get(nodeId) || [];
844
+ console.log(
845
+ "[Workflow Executor] Executing",
846
+ nextNodes.length,
847
+ "next nodes in parallel"
848
+ );
849
+ await Promise.all(
850
+ nextNodes.map((nextNodeId) => executeNode(nextNodeId, visited))
851
+ );
852
+ }
853
+ }
854
+ } catch (error) {
855
+ console.error("[Workflow Executor] Error executing node:", nodeId, error);
856
+ const errorMessage = await getErrorMessageAsync(error);
857
+ const errorResult = {
858
+ success: false,
859
+ error: errorMessage
860
+ };
861
+ results[nodeId] = errorResult;
862
+ }
863
+ }
864
+ try {
865
+ console.log("[Workflow Executor] Starting execution from trigger nodes");
866
+ const workflowStartTime = Date.now();
867
+ await Promise.all(triggerNodes.map((trigger) => executeNode(trigger.id)));
868
+ const finalSuccess = Object.values(results).every((r) => r.success);
869
+ const duration = Date.now() - workflowStartTime;
870
+ console.log("[Workflow Executor] Workflow execution completed:", {
871
+ success: finalSuccess,
872
+ resultCount: Object.keys(results).length,
873
+ duration
874
+ });
875
+ if (executionId) {
876
+ try {
877
+ await triggerStep({
878
+ triggerData: {},
879
+ _workflowComplete: {
880
+ executionId,
881
+ status: finalSuccess ? "success" : "error",
882
+ output: Object.values(results).at(-1)?.data,
883
+ error: Object.values(results).find((r) => !r.success)?.error,
884
+ startTime: workflowStartTime
885
+ }
886
+ });
887
+ console.log("[Workflow Executor] Updated execution record");
888
+ } catch (error) {
889
+ console.error(
890
+ "[Workflow Executor] Failed to update execution record:",
891
+ error
892
+ );
893
+ }
894
+ }
895
+ return {
896
+ success: finalSuccess,
897
+ results,
898
+ outputs
899
+ };
900
+ } catch (error) {
901
+ console.error(
902
+ "[Workflow Executor] Fatal error during workflow execution:",
903
+ error
904
+ );
905
+ const errorMessage = await getErrorMessageAsync(error);
906
+ if (executionId) {
907
+ try {
908
+ await triggerStep({
909
+ triggerData: {},
910
+ _workflowComplete: {
911
+ executionId,
912
+ status: "error",
913
+ error: errorMessage,
914
+ startTime: Date.now()
915
+ }
916
+ });
917
+ } catch (logError) {
918
+ console.error("[Workflow Executor] Failed to log error:", logError);
919
+ }
920
+ }
921
+ return {
922
+ success: false,
923
+ results,
924
+ outputs,
925
+ error: errorMessage
926
+ };
927
+ }
928
+ }
929
+
930
+ // src/server/api/utils.ts
931
+ var corsHeaders = {
932
+ "Access-Control-Allow-Origin": "*",
933
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
934
+ "Access-Control-Allow-Headers": "Content-Type, Authorization"
935
+ };
936
+ function extractPath(request) {
937
+ const url = new URL(request.url);
938
+ const pathname = url.pathname;
939
+ const prefix = "/api/workflow-builder/";
940
+ const idx = pathname.indexOf(prefix);
941
+ if (idx === -1) {
942
+ const apiIdx = pathname.indexOf("/api/");
943
+ if (apiIdx === -1) return [];
944
+ const afterApi = pathname.slice(apiIdx + 5);
945
+ return afterApi.split("/").filter(Boolean);
946
+ }
947
+ const afterPrefix = pathname.slice(idx + prefix.length);
948
+ return afterPrefix.split("/").filter(Boolean);
949
+ }
950
+ function rebuildRequest(request, newUrl) {
951
+ return new Request(newUrl, {
952
+ method: request.method,
953
+ headers: request.headers,
954
+ body: request.body,
955
+ duplex: "half"
956
+ });
957
+ }
958
+ async function executeWorkflowBackground(executionId, workflowId, nodes, edges, input) {
959
+ try {
960
+ await executeWorkflow({
961
+ nodes,
962
+ edges,
963
+ triggerInput: input,
964
+ executionId,
965
+ workflowId
966
+ });
967
+ } catch (error) {
968
+ console.error("[Workflow Execute] Error during execution:", error);
969
+ await db.update(workflowExecutions).set({
970
+ status: "error",
971
+ error: error instanceof Error ? error.message : "Unknown error",
972
+ completedAt: /* @__PURE__ */ new Date()
973
+ }).where(eq2(workflowExecutions.id, executionId));
974
+ }
975
+ }
976
+ function buildWorkflowUpdateData(body) {
977
+ const updateData = { updatedAt: /* @__PURE__ */ new Date() };
978
+ if (body.name !== void 0) updateData.name = body.name;
979
+ if (body.description !== void 0) updateData.description = body.description;
980
+ if (body.nodes !== void 0) updateData.nodes = body.nodes;
981
+ if (body.edges !== void 0) updateData.edges = body.edges;
982
+ if (body.visibility !== void 0) updateData.visibility = body.visibility;
983
+ return updateData;
984
+ }
985
+ function sanitizeNodesForPublicView(nodes) {
986
+ return nodes.map((node) => {
987
+ const sanitizedNode = { ...node };
988
+ if (sanitizedNode.data && typeof sanitizedNode.data === "object" && sanitizedNode.data !== null) {
989
+ const data = { ...sanitizedNode.data };
990
+ if (data.config && typeof data.config === "object" && data.config !== null) {
991
+ const { integrationId: _, ...configWithoutIntegration } = data.config;
992
+ data.config = configWithoutIntegration;
993
+ }
994
+ sanitizedNode.data = data;
995
+ }
996
+ return sanitizedNode;
997
+ });
998
+ }
999
+ async function readDirectoryRecursive(dirPath, baseDir = dirPath) {
1000
+ const files = {};
1001
+ const entries = await readdir(dirPath, { withFileTypes: true });
1002
+ for (const entry of entries) {
1003
+ const fullPath = join(dirPath, entry.name);
1004
+ if (entry.isDirectory()) {
1005
+ const subFiles = await readDirectoryRecursive(fullPath, baseDir);
1006
+ Object.assign(files, subFiles);
1007
+ } else if (entry.isFile()) {
1008
+ const content = await readFile(fullPath, "utf-8");
1009
+ const relativePath = fullPath.substring(baseDir.length + 1);
1010
+ files[relativePath] = content;
1011
+ }
1012
+ }
1013
+ return files;
1014
+ }
1015
+ function generateWorkflowFiles(workflow) {
1016
+ const files = {};
1017
+ const baseName = workflow.name.replace(NON_ALPHANUMERIC_REGEX, "").split(WHITESPACE_SPLIT_REGEX).map((word, i) => {
1018
+ if (i === 0) {
1019
+ return word.toLowerCase();
1020
+ }
1021
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
1022
+ }).join("") || "execute";
1023
+ const functionName = `${baseName}Workflow`;
1024
+ const workflowCode = generateWorkflowModule(
1025
+ workflow.name,
1026
+ workflow.nodes,
1027
+ workflow.edges,
1028
+ { functionName }
1029
+ );
1030
+ const fileName = sanitizeFileName(workflow.name);
1031
+ files[`workflows/${fileName}.ts`] = workflowCode;
1032
+ files[`app/api/workflows/${fileName}/route.ts`] = `import { start } from 'workflow/api';
1033
+ import { ${functionName} } from '@/workflows/${fileName}';
1034
+ import { NextResponse } from 'next/server';
1035
+
1036
+ export async function POST(request: Request) {
1037
+ try {
1038
+ const body = await request.json();
1039
+
1040
+ // Start the workflow execution
1041
+ await start(${functionName}, [body]);
1042
+
1043
+ return NextResponse.json({
1044
+ success: true,
1045
+ message: 'Workflow started successfully',
1046
+ });
1047
+ } catch (error) {
1048
+ return NextResponse.json(
1049
+ {
1050
+ success: false,
1051
+ error: error instanceof Error ? error.message : 'Unknown error',
1052
+ },
1053
+ { status: 500 }
1054
+ );
1055
+ }
1056
+ }
1057
+ `;
1058
+ files["app/page.tsx"] = `export default function Home() {
1059
+ return (
1060
+ <main className="p-8">
1061
+ <h1 className="text-2xl font-bold mb-4">Workflow: ${workflow.name}</h1>
1062
+ <p className="mb-4 text-gray-600">API endpoint:</p>
1063
+ <ul className="list-disc pl-6 space-y-2">
1064
+ <li>
1065
+ <a href="/api/workflows/${fileName}" className="text-blue-600 hover:underline">
1066
+ /api/workflows/${fileName}
1067
+ </a>
1068
+ </li>
1069
+ </ul>
1070
+ </main>
1071
+ );
1072
+ }
1073
+ `;
1074
+ return files;
1075
+ }
1076
+ function getIntegrationDependencies(nodes) {
1077
+ const actionTypes = nodes.filter((node) => node.data.type === "action").map((node) => node.data.config?.actionType).filter(Boolean);
1078
+ return getDependenciesForActions(actionTypes);
1079
+ }
1080
+ function generateEnvExample() {
1081
+ const lines = ["# Add your environment variables here"];
1082
+ lines.push("");
1083
+ lines.push("# For database integrations");
1084
+ lines.push("DATABASE_URL=your_database_url");
1085
+ const envVars = getAllEnvVars();
1086
+ const groupedByPrefix = {};
1087
+ for (const envVar of envVars) {
1088
+ const prefix = envVar.name.split("_")[0];
1089
+ if (!groupedByPrefix[prefix]) {
1090
+ groupedByPrefix[prefix] = [];
1091
+ }
1092
+ groupedByPrefix[prefix].push(envVar);
1093
+ }
1094
+ for (const [prefix, vars] of Object.entries(groupedByPrefix)) {
1095
+ lines.push(
1096
+ `# For ${prefix.charAt(0) + prefix.slice(1).toLowerCase()} integration`
1097
+ );
1098
+ for (const v of vars) {
1099
+ lines.push(`${v.name}=your_${v.name.toLowerCase()}`);
1100
+ }
1101
+ lines.push("");
1102
+ }
1103
+ return lines.join("\n");
1104
+ }
1105
+ function sanitizeFileName(name) {
1106
+ return name.toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
1107
+ }
1108
+
1109
+ // src/server/api/auth.ts
1110
+ var { GET: authGet, POST: authPost } = toNextJsHandler(auth);
1111
+ async function handleAuth(request, pathSegments) {
1112
+ const authPath = "/api/auth/" + pathSegments.slice(1).join("/");
1113
+ const url = new URL(request.url);
1114
+ const newUrl = `${url.origin}${authPath}${url.search}`;
1115
+ const newRequest = rebuildRequest(request, newUrl);
1116
+ if (request.method === "GET")
1117
+ return authGet(newRequest);
1118
+ return authPost(newRequest);
1119
+ }
1120
+
1121
+ // src/server/api/integrations.ts
1122
+ import { NextResponse as NextResponse2 } from "next/server";
1123
+ async function handleGetIntegrations(request) {
1124
+ try {
1125
+ const user = await resolveUser(request);
1126
+ if (!user) return NextResponse2.json({ error: "Unauthorized" }, { status: 401 });
1127
+ const { searchParams } = new URL(request.url);
1128
+ const typeFilter = searchParams.get("type");
1129
+ const intgList = await getIntegrations(user.id, typeFilter || void 0);
1130
+ return NextResponse2.json(
1131
+ intgList.map((i) => ({
1132
+ id: i.id,
1133
+ name: i.name,
1134
+ type: i.type,
1135
+ isManaged: i.isManaged ?? false,
1136
+ createdAt: i.createdAt.toISOString(),
1137
+ updatedAt: i.updatedAt.toISOString()
1138
+ }))
1139
+ );
1140
+ } catch (error) {
1141
+ console.error("Failed to get integrations:", error);
1142
+ return NextResponse2.json(
1143
+ { error: "Failed to get integrations", details: error instanceof Error ? error.message : "Unknown error" },
1144
+ { status: 500 }
1145
+ );
1146
+ }
1147
+ }
1148
+ async function handleCreateIntegration(request) {
1149
+ try {
1150
+ const user = await resolveUser(request);
1151
+ if (!user) return NextResponse2.json({ error: "Unauthorized" }, { status: 401 });
1152
+ const body = await request.json();
1153
+ if (!(body.type && body.config)) {
1154
+ return NextResponse2.json({ error: "Type and config are required" }, { status: 400 });
1155
+ }
1156
+ const integration = await createIntegration(
1157
+ user.id,
1158
+ body.name || "",
1159
+ body.type,
1160
+ body.config
1161
+ );
1162
+ return NextResponse2.json({
1163
+ id: integration.id,
1164
+ name: integration.name,
1165
+ type: integration.type,
1166
+ createdAt: integration.createdAt.toISOString(),
1167
+ updatedAt: integration.updatedAt.toISOString()
1168
+ });
1169
+ } catch (error) {
1170
+ console.error("Failed to create integration:", error);
1171
+ return NextResponse2.json(
1172
+ { error: "Failed to create integration", details: error instanceof Error ? error.message : "Unknown error" },
1173
+ { status: 500 }
1174
+ );
1175
+ }
1176
+ }
1177
+ async function handleGetIntegration(request, integrationId) {
1178
+ try {
1179
+ const user = await resolveUser(request);
1180
+ if (!user) return NextResponse2.json({ error: "Unauthorized" }, { status: 401 });
1181
+ const integration = await getIntegration(integrationId, user.id);
1182
+ if (!integration) {
1183
+ return NextResponse2.json({ error: "Integration not found" }, { status: 404 });
1184
+ }
1185
+ return NextResponse2.json({
1186
+ id: integration.id,
1187
+ name: integration.name,
1188
+ type: integration.type,
1189
+ config: integration.config,
1190
+ createdAt: integration.createdAt.toISOString(),
1191
+ updatedAt: integration.updatedAt.toISOString()
1192
+ });
1193
+ } catch (error) {
1194
+ console.error("Failed to get integration:", error);
1195
+ return NextResponse2.json(
1196
+ { error: "Failed to get integration", details: error instanceof Error ? error.message : "Unknown error" },
1197
+ { status: 500 }
1198
+ );
1199
+ }
1200
+ }
1201
+ async function handleUpdateIntegration(request, integrationId) {
1202
+ try {
1203
+ const user = await resolveUser(request);
1204
+ if (!user) return NextResponse2.json({ error: "Unauthorized" }, { status: 401 });
1205
+ const body = await request.json();
1206
+ const integration = await updateIntegration(integrationId, user.id, body);
1207
+ if (!integration) {
1208
+ return NextResponse2.json({ error: "Integration not found" }, { status: 404 });
1209
+ }
1210
+ return NextResponse2.json({
1211
+ id: integration.id,
1212
+ name: integration.name,
1213
+ type: integration.type,
1214
+ config: integration.config,
1215
+ createdAt: integration.createdAt.toISOString(),
1216
+ updatedAt: integration.updatedAt.toISOString()
1217
+ });
1218
+ } catch (error) {
1219
+ console.error("Failed to update integration:", error);
1220
+ return NextResponse2.json(
1221
+ { error: "Failed to update integration", details: error instanceof Error ? error.message : "Unknown error" },
1222
+ { status: 500 }
1223
+ );
1224
+ }
1225
+ }
1226
+ async function handleDeleteIntegration(request, integrationId) {
1227
+ try {
1228
+ const user = await resolveUser(request);
1229
+ if (!user) return NextResponse2.json({ error: "Unauthorized" }, { status: 401 });
1230
+ const success = await deleteIntegration(integrationId, user.id);
1231
+ if (!success) {
1232
+ return NextResponse2.json({ error: "Integration not found" }, { status: 404 });
1233
+ }
1234
+ return NextResponse2.json({ success: true });
1235
+ } catch (error) {
1236
+ console.error("Failed to delete integration:", error);
1237
+ return NextResponse2.json(
1238
+ { error: "Failed to delete integration", details: error instanceof Error ? error.message : "Unknown error" },
1239
+ { status: 500 }
1240
+ );
1241
+ }
1242
+ }
1243
+ async function handleTestIntegration(request, integrationId) {
1244
+ try {
1245
+ const user = await resolveUser(request);
1246
+ if (!user) return NextResponse2.json({ error: "Unauthorized" }, { status: 401 });
1247
+ const integration = await getIntegration(integrationId, user.id);
1248
+ if (!integration) {
1249
+ return NextResponse2.json({ error: "Integration not found" }, { status: 404 });
1250
+ }
1251
+ if (integration.type === "database") {
1252
+ return NextResponse2.json({ status: "success", message: "Integration exists" });
1253
+ }
1254
+ return NextResponse2.json({ status: "success", message: "Integration exists" });
1255
+ } catch (error) {
1256
+ console.error("Failed to test integration:", error);
1257
+ return NextResponse2.json(
1258
+ { status: "error", message: error instanceof Error ? error.message : "Failed to test connection" },
1259
+ { status: 500 }
1260
+ );
1261
+ }
1262
+ }
1263
+ async function handleTestIntegrationCredentials(request) {
1264
+ try {
1265
+ const user = await resolveUser(request);
1266
+ if (!user) return NextResponse2.json({ error: "Unauthorized" }, { status: 401 });
1267
+ const body = await request.json();
1268
+ if (!(body.type && body.config)) {
1269
+ return NextResponse2.json({ error: "Type and config are required" }, { status: 400 });
1270
+ }
1271
+ return NextResponse2.json({ status: "success", message: "Credentials accepted" });
1272
+ } catch (error) {
1273
+ console.error("Failed to test credentials:", error);
1274
+ return NextResponse2.json(
1275
+ { status: "error", message: error instanceof Error ? error.message : "Failed to test connection" },
1276
+ { status: 500 }
1277
+ );
1278
+ }
1279
+ }
1280
+
1281
+ // src/server/api/users.ts
1282
+ import { eq as eq3 } from "drizzle-orm";
1283
+ import { NextResponse as NextResponse3 } from "next/server";
1284
+ async function handleGetUser(request) {
1285
+ try {
1286
+ const user = await resolveUser(request);
1287
+ if (!user) return NextResponse3.json({ error: "Unauthorized" }, { status: 401 });
1288
+ const userData = await db.query.users.findFirst({
1289
+ where: eq3(users.id, user.id),
1290
+ columns: {
1291
+ id: true,
1292
+ name: true,
1293
+ email: true,
1294
+ image: true,
1295
+ isAnonymous: true
1296
+ }
1297
+ });
1298
+ if (!userData) {
1299
+ return NextResponse3.json({ error: "User not found" }, { status: 404 });
1300
+ }
1301
+ const userAccount = await db.query.accounts.findFirst({
1302
+ where: eq3(accounts.userId, user.id),
1303
+ columns: { providerId: true }
1304
+ });
1305
+ return NextResponse3.json({
1306
+ ...userData,
1307
+ providerId: userAccount?.providerId ?? null
1308
+ });
1309
+ } catch (error) {
1310
+ console.error("Failed to get user:", error);
1311
+ return NextResponse3.json(
1312
+ { error: error instanceof Error ? error.message : "Failed to get user" },
1313
+ { status: 500 }
1314
+ );
1315
+ }
1316
+ }
1317
+ async function handleUpdateUser(request) {
1318
+ try {
1319
+ const user = await resolveUser(request);
1320
+ if (!user) return NextResponse3.json({ error: "Unauthorized" }, { status: 401 });
1321
+ const userAccount = await db.query.accounts.findFirst({
1322
+ where: eq3(accounts.userId, user.id),
1323
+ columns: { providerId: true }
1324
+ });
1325
+ const oauthProviders = ["vercel", "github", "google"];
1326
+ if (userAccount && oauthProviders.includes(userAccount.providerId)) {
1327
+ return NextResponse3.json(
1328
+ { error: "Cannot update profile for OAuth users" },
1329
+ { status: 403 }
1330
+ );
1331
+ }
1332
+ const body = await request.json();
1333
+ const updates = {};
1334
+ if (body.name !== void 0) updates.name = body.name;
1335
+ if (body.email !== void 0) updates.email = body.email;
1336
+ await db.update(users).set(updates).where(eq3(users.id, user.id));
1337
+ return NextResponse3.json({ success: true });
1338
+ } catch (error) {
1339
+ console.error("Failed to update user:", error);
1340
+ return NextResponse3.json(
1341
+ { error: error instanceof Error ? error.message : "Failed to update user" },
1342
+ { status: 500 }
1343
+ );
1344
+ }
1345
+ }
1346
+
1347
+ // src/server/api/workflows.ts
1348
+ import { and as and2, desc, eq as eq4, inArray } from "drizzle-orm";
1349
+ import { NextResponse as NextResponse4 } from "next/server";
1350
+ import { createHash as createHash2 } from "crypto";
1351
+
1352
+ // src/client/lib/workflow-codegen-sdk.ts
1353
+ import "server-only";
1354
+ var SYSTEM_CODEGEN_TEMPLATES = {
1355
+ "Database Query": databaseQueryAction.codeGenerator,
1356
+ "HTTP Request": httpRequestAction.codeGenerator,
1357
+ Condition: conditionAction.codeGenerator
1358
+ };
1359
+ var FUNCTION_BODY_REGEX = /export\s+(?:async\s+)?function\s+\w+\s*\([^)]*\)\s*(?::\s*[^{]+)?\s*\{([\s\S]*)\}/;
1360
+ function loadStepImplementation(actionType) {
1361
+ let template = SYSTEM_CODEGEN_TEMPLATES[actionType];
1362
+ if (!template) {
1363
+ const action = findActionById(actionType);
1364
+ if (action?.codegenTemplate) {
1365
+ template = action.codegenTemplate;
1366
+ }
1367
+ }
1368
+ if (!template) {
1369
+ return null;
1370
+ }
1371
+ try {
1372
+ const functionMatch = template.match(FUNCTION_BODY_REGEX);
1373
+ if (functionMatch) {
1374
+ return functionMatch[1].trim();
1375
+ }
1376
+ return null;
1377
+ } catch {
1378
+ return null;
1379
+ }
1380
+ }
1381
+ function processNewFormatID(trimmed, match) {
1382
+ const withoutAt = trimmed.substring(1);
1383
+ const colonIndex = withoutAt.indexOf(":");
1384
+ if (colonIndex === -1) {
1385
+ return match;
1386
+ }
1387
+ const nodeId = withoutAt.substring(0, colonIndex);
1388
+ const rest = withoutAt.substring(colonIndex + 1);
1389
+ const dotIndex = rest.indexOf(".");
1390
+ const fieldPath = dotIndex !== -1 ? rest.substring(dotIndex + 1) : "";
1391
+ const sanitizedNodeId = nodeId.replace(/[^a-zA-Z0-9]/g, "_");
1392
+ if (!fieldPath) {
1393
+ return `\${outputs?.['${sanitizedNodeId}']?.data}`;
1394
+ }
1395
+ const accessPath = fieldPath.split(".").map((part) => {
1396
+ const arrayMatch = part.match(ARRAY_INDEX_PATTERN);
1397
+ if (arrayMatch) {
1398
+ return `?.${arrayMatch[1]}?.[${arrayMatch[2]}]`;
1399
+ }
1400
+ return `?.${part}`;
1401
+ }).join("");
1402
+ return `\${outputs?.['${sanitizedNodeId}']?.data${accessPath}}`;
1403
+ }
1404
+ function processLegacyDollarRef(trimmed) {
1405
+ const withoutDollar = trimmed.substring(1);
1406
+ if (!(withoutDollar.includes(".") || withoutDollar.includes("["))) {
1407
+ const sanitizedNodeId2 = withoutDollar.replace(/[^a-zA-Z0-9]/g, "_");
1408
+ return `\${outputs?.['${sanitizedNodeId2}']?.data}`;
1409
+ }
1410
+ const parts = withoutDollar.split(".");
1411
+ const nodeId = parts[0];
1412
+ const sanitizedNodeId = nodeId.replace(/[^a-zA-Z0-9]/g, "_");
1413
+ const fieldPath = parts.slice(1).join(".");
1414
+ if (!fieldPath) {
1415
+ return `\${outputs?.['${sanitizedNodeId}']?.data}`;
1416
+ }
1417
+ const accessPath = fieldPath.split(".").map((part) => {
1418
+ const arrayMatch = part.match(ARRAY_INDEX_PATTERN);
1419
+ if (arrayMatch) {
1420
+ return `?.${arrayMatch[1]}?.[${arrayMatch[2]}]`;
1421
+ }
1422
+ return `?.${part}`;
1423
+ }).join("");
1424
+ return `\${outputs?.['${sanitizedNodeId}']?.data${accessPath}}`;
1425
+ }
1426
+ function convertTemplateToJS(template) {
1427
+ if (!template || typeof template !== "string") {
1428
+ return template;
1429
+ }
1430
+ const pattern = /\{\{([^}]+)\}\}/g;
1431
+ return template.replace(pattern, (match, expression) => {
1432
+ const trimmed = expression.trim();
1433
+ if (trimmed.startsWith("@")) {
1434
+ return processNewFormatID(trimmed, match);
1435
+ }
1436
+ if (trimmed.startsWith("$")) {
1437
+ return processLegacyDollarRef(trimmed);
1438
+ }
1439
+ return match;
1440
+ });
1441
+ }
1442
+ function analyzeNodeUsageSDK(nodes) {
1443
+ const usedNodes = analyzeNodeUsage(nodes);
1444
+ const lastNode = nodes.at(-1);
1445
+ if (lastNode) {
1446
+ usedNodes.add(lastNode.id);
1447
+ }
1448
+ return usedNodes;
1449
+ }
1450
+ function createStepNameMapping(nodes) {
1451
+ const stepNameCounts = /* @__PURE__ */ new Map();
1452
+ const nodeToStepName = /* @__PURE__ */ new Map();
1453
+ for (const node of nodes) {
1454
+ if (node.data.type === "action") {
1455
+ const config = node.data.config || {};
1456
+ const actionType = config.actionType;
1457
+ const baseLabel = node.data.label || actionType || "UnnamedStep";
1458
+ const baseName = sanitizeStepName(baseLabel);
1459
+ const count = stepNameCounts.get(baseName) || 0;
1460
+ stepNameCounts.set(baseName, count + 1);
1461
+ const uniqueName = count > 0 ? `${baseName}${count + 1}` : baseName;
1462
+ nodeToStepName.set(node.id, uniqueName);
1463
+ }
1464
+ }
1465
+ return nodeToStepName;
1466
+ }
1467
+ function generateAllStepFunctions(nodes, nodeToStepName, generateStepFunc) {
1468
+ const stepFunctions = [];
1469
+ for (const node of nodes) {
1470
+ if (node.data.type === "action") {
1471
+ const uniqueName = nodeToStepName.get(node.id);
1472
+ stepFunctions.push(generateStepFunc(node, uniqueName));
1473
+ }
1474
+ }
1475
+ return stepFunctions;
1476
+ }
1477
+ function generateWorkflowSDKCode(workflowName, nodes, edges) {
1478
+ const imports = /* @__PURE__ */ new Set();
1479
+ const stepFunctions = [];
1480
+ const nodeMap = new Map(nodes.map((n) => [n.id, n]));
1481
+ const edgesBySource = buildEdgeMap(edges);
1482
+ const triggerNodes = findTriggerNodes(nodes, edges);
1483
+ const usedNodeOutputs = analyzeNodeUsageSDK(nodes);
1484
+ imports.add("import { sleep, FatalError } from 'workflow';");
1485
+ function buildEmailParams(config) {
1486
+ imports.add("import { Resend } from 'resend';");
1487
+ return [
1488
+ `fromEmail: process.env.RESEND_FROM_EMAIL || 'noreply@example.com'`,
1489
+ `emailTo: \`${convertTemplateToJS(config.emailTo || "user@example.com")}\``,
1490
+ `emailSubject: \`${convertTemplateToJS(config.emailSubject || "Notification")}\``,
1491
+ `emailBody: \`${convertTemplateToJS(config.emailBody || "No content")}\``,
1492
+ "apiKey: process.env.RESEND_API_KEY!"
1493
+ ];
1494
+ }
1495
+ function buildSlackParams(config) {
1496
+ imports.add("import { WebClient } from '@slack/web-api';");
1497
+ return [
1498
+ `slackChannel: \`${convertTemplateToJS(config.slackChannel || "#general")}\``,
1499
+ `slackMessage: \`${convertTemplateToJS(config.slackMessage || "No message")}\``,
1500
+ "apiKey: process.env.SLACK_API_KEY!"
1501
+ ];
1502
+ }
1503
+ function buildTicketParams(config) {
1504
+ imports.add("import { LinearClient } from '@linear/sdk';");
1505
+ const params = [
1506
+ `ticketTitle: \`${convertTemplateToJS(config.ticketTitle || "New Issue")}\``,
1507
+ `ticketDescription: \`${convertTemplateToJS(config.ticketDescription || "")}\``,
1508
+ "apiKey: process.env.LINEAR_API_KEY!"
1509
+ ];
1510
+ if (config.teamId) {
1511
+ params.push(`teamId: "${config.teamId}"`);
1512
+ }
1513
+ return params;
1514
+ }
1515
+ function buildAITextParams(config) {
1516
+ imports.add("import { generateText } from 'ai';");
1517
+ const modelId = config.aiModel || "meta/llama-4-scout";
1518
+ let modelString;
1519
+ if (modelId.includes("/")) {
1520
+ modelString = modelId;
1521
+ } else {
1522
+ let provider;
1523
+ if (modelId.startsWith("gpt-") || modelId.startsWith("o1-")) {
1524
+ provider = "openai";
1525
+ } else if (modelId.startsWith("claude-")) {
1526
+ provider = "anthropic";
1527
+ } else {
1528
+ provider = "openai";
1529
+ }
1530
+ modelString = `${provider}/${modelId}`;
1531
+ }
1532
+ return [
1533
+ `model: "${modelString}"`,
1534
+ `prompt: \`${convertTemplateToJS(config.aiPrompt || "")}\``,
1535
+ "apiKey: process.env.OPENAI_API_KEY!"
1536
+ ];
1537
+ }
1538
+ function buildAIImageParams(config) {
1539
+ imports.add(
1540
+ "import { experimental_generateImage as generateImage } from 'ai';"
1541
+ );
1542
+ const imageModel = config.imageModel || "google/imagen-4.0-generate";
1543
+ return [
1544
+ `model: "${imageModel}"`,
1545
+ `prompt: \`${convertTemplateToJS(config.imagePrompt || "")}\``,
1546
+ 'size: "1024x1024"',
1547
+ "providerOptions: { openai: { apiKey: process.env.AI_GATEWAY_API_KEY! } }"
1548
+ ];
1549
+ }
1550
+ function buildDatabaseParams(config) {
1551
+ return [
1552
+ `query: \`${convertTemplateToJS(config.dbQuery || "SELECT 1")}\``
1553
+ ];
1554
+ }
1555
+ function buildHttpParams(config) {
1556
+ const params = [
1557
+ `url: "${config.endpoint || "https://api.example.com/endpoint"}"`,
1558
+ `method: "${config.httpMethod || "POST"}"`,
1559
+ `headers: ${config.httpHeaders || "{}"}`
1560
+ ];
1561
+ if (config.httpBody) {
1562
+ params.push(`body: ${config.httpBody}`);
1563
+ }
1564
+ return params;
1565
+ }
1566
+ function buildConditionParams(config) {
1567
+ return [
1568
+ `condition: ${convertTemplateToJS(config.condition || "true")}`
1569
+ ];
1570
+ }
1571
+ function buildFirecrawlParams(actionType, config) {
1572
+ imports.add("import FirecrawlApp from '@mendable/firecrawl-js';");
1573
+ const mode = actionType === "Search" ? "search" : "scrape";
1574
+ const formats = config.formats ? JSON.stringify(config.formats) : "['markdown']";
1575
+ const params = [
1576
+ `mode: '${mode}'`,
1577
+ "apiKey: process.env.FIRECRAWL_API_KEY!",
1578
+ `formats: ${formats}`
1579
+ ];
1580
+ if (config.url) {
1581
+ params.push(
1582
+ `url: \`${convertTemplateToJS(config.url || "")}\``
1583
+ );
1584
+ }
1585
+ if (config.query) {
1586
+ params.push(
1587
+ `query: \`${convertTemplateToJS(config.query || "")}\``
1588
+ );
1589
+ }
1590
+ if (config.limit) {
1591
+ params.push(`limit: ${config.limit}`);
1592
+ }
1593
+ return params;
1594
+ }
1595
+ function buildV0CreateChatParams(config) {
1596
+ imports.add("import { createClient } from 'v0-sdk';");
1597
+ const params = [
1598
+ `message: \`${convertTemplateToJS(config.message || "")}\``,
1599
+ "apiKey: process.env.V0_API_KEY!"
1600
+ ];
1601
+ if (config.system) {
1602
+ params.push(
1603
+ `system: \`${convertTemplateToJS(config.system || "")}\``
1604
+ );
1605
+ }
1606
+ return params;
1607
+ }
1608
+ function buildV0SendMessageParams(config) {
1609
+ imports.add("import { createClient } from 'v0-sdk';");
1610
+ return [
1611
+ `chatId: \`${convertTemplateToJS(config.chatId || "")}\``,
1612
+ `message: \`${convertTemplateToJS(config.message || "")}\``,
1613
+ "apiKey: process.env.V0_API_KEY!"
1614
+ ];
1615
+ }
1616
+ function buildClerkGetUserParams(config) {
1617
+ return [
1618
+ `userId: \`${convertTemplateToJS(config.userId || "")}\``
1619
+ ];
1620
+ }
1621
+ function buildClerkCreateUserParams(config) {
1622
+ const params = [
1623
+ `emailAddress: \`${convertTemplateToJS(config.emailAddress || "")}\``
1624
+ ];
1625
+ if (config.password) {
1626
+ params.push(
1627
+ `password: \`${convertTemplateToJS(config.password)}\``
1628
+ );
1629
+ }
1630
+ if (config.firstName) {
1631
+ params.push(
1632
+ `firstName: \`${convertTemplateToJS(config.firstName)}\``
1633
+ );
1634
+ }
1635
+ if (config.lastName) {
1636
+ params.push(
1637
+ `lastName: \`${convertTemplateToJS(config.lastName)}\``
1638
+ );
1639
+ }
1640
+ if (config.publicMetadata) {
1641
+ params.push(
1642
+ `publicMetadata: \`${convertTemplateToJS(config.publicMetadata)}\``
1643
+ );
1644
+ }
1645
+ if (config.privateMetadata) {
1646
+ params.push(
1647
+ `privateMetadata: \`${convertTemplateToJS(config.privateMetadata)}\``
1648
+ );
1649
+ }
1650
+ return params;
1651
+ }
1652
+ function buildClerkUpdateUserParams(config) {
1653
+ const params = [
1654
+ `userId: \`${convertTemplateToJS(config.userId || "")}\``
1655
+ ];
1656
+ if (config.firstName) {
1657
+ params.push(
1658
+ `firstName: \`${convertTemplateToJS(config.firstName)}\``
1659
+ );
1660
+ }
1661
+ if (config.lastName) {
1662
+ params.push(
1663
+ `lastName: \`${convertTemplateToJS(config.lastName)}\``
1664
+ );
1665
+ }
1666
+ if (config.publicMetadata) {
1667
+ params.push(
1668
+ `publicMetadata: \`${convertTemplateToJS(config.publicMetadata)}\``
1669
+ );
1670
+ }
1671
+ if (config.privateMetadata) {
1672
+ params.push(
1673
+ `privateMetadata: \`${convertTemplateToJS(config.privateMetadata)}\``
1674
+ );
1675
+ }
1676
+ return params;
1677
+ }
1678
+ function buildClerkDeleteUserParams(config) {
1679
+ return [
1680
+ `userId: \`${convertTemplateToJS(config.userId || "")}\``
1681
+ ];
1682
+ }
1683
+ function buildStepInputParams(actionType, config) {
1684
+ const paramBuilders = {
1685
+ "Send Email": () => buildEmailParams(config),
1686
+ "Send Slack Message": () => buildSlackParams(config),
1687
+ "Create Ticket": () => buildTicketParams(config),
1688
+ "Generate Text": () => buildAITextParams(config),
1689
+ "Generate Image": () => buildAIImageParams(config),
1690
+ "Database Query": () => buildDatabaseParams(config),
1691
+ "HTTP Request": () => buildHttpParams(config),
1692
+ Condition: () => buildConditionParams(config),
1693
+ Scrape: () => buildFirecrawlParams(actionType, config),
1694
+ Search: () => buildFirecrawlParams(actionType, config),
1695
+ "Create Chat": () => buildV0CreateChatParams(config),
1696
+ "Send Message": () => buildV0SendMessageParams(config),
1697
+ // Clerk
1698
+ "Get User": () => buildClerkGetUserParams(config),
1699
+ "Create User": () => buildClerkCreateUserParams(config),
1700
+ "Update User": () => buildClerkUpdateUserParams(config),
1701
+ "Delete User": () => buildClerkDeleteUserParams(config)
1702
+ };
1703
+ const builder = paramBuilders[actionType];
1704
+ return builder ? builder() : [];
1705
+ }
1706
+ function generateStepFunction(node, uniqueStepName) {
1707
+ const config = node.data.config || {};
1708
+ const actionType = config.actionType;
1709
+ const label = node.data.label || actionType || "UnnamedStep";
1710
+ const stepName = uniqueStepName || sanitizeStepName(label);
1711
+ const stepImplementation = loadStepImplementation(actionType);
1712
+ let stepBody;
1713
+ if (stepImplementation && node.data.type === "action") {
1714
+ const inputParams = buildStepInputParams(actionType, config);
1715
+ stepBody = ` // Call step function with constructed input
1716
+ const stepInput = {
1717
+ ${inputParams.join(",\n ")}
1718
+ };
1719
+
1720
+ // Execute step implementation
1721
+ ${stepImplementation}`;
1722
+ } else {
1723
+ stepBody = " return { success: true };";
1724
+ }
1725
+ return `async function ${stepName}(input: Record<string, unknown> & { outputs?: Record<string, { label: string; data: unknown }> }) {
1726
+ "use step";
1727
+
1728
+ ${stepBody}
1729
+ }`;
1730
+ }
1731
+ const nodeToStepName = createStepNameMapping(nodes);
1732
+ stepFunctions.push(
1733
+ ...generateAllStepFunctions(nodes, nodeToStepName, generateStepFunction)
1734
+ );
1735
+ function generateTriggerCode(nodeId, label, indent) {
1736
+ if (!usedNodeOutputs.has(nodeId)) {
1737
+ return [`${indent}// Trigger (outputs not used)`];
1738
+ }
1739
+ const varName = `result_${sanitizeVarName(nodeId)}`;
1740
+ return [
1741
+ `${indent}// Triggered`,
1742
+ `${indent}let ${varName} = input;`,
1743
+ `${indent}outputs['${sanitizeVarName(nodeId)}'] = { label: '${label}', data: ${varName} };`
1744
+ ];
1745
+ }
1746
+ function generateActionTransformCode(nodeId, nodeConfig, label, indent) {
1747
+ const nodeActionType = nodeConfig.actionType;
1748
+ const nodeLabel = label || nodeActionType || "UnnamedStep";
1749
+ const stepFnName = nodeToStepName.get(nodeId) || sanitizeStepName(nodeLabel);
1750
+ const lines = [`${indent}// ${nodeLabel}`];
1751
+ const outputIsUsed = usedNodeOutputs.has(nodeId);
1752
+ if (outputIsUsed) {
1753
+ const varName = `result_${sanitizeVarName(nodeId)}`;
1754
+ lines.push(
1755
+ `${indent}const ${varName} = await ${stepFnName}({ ...input, outputs });`
1756
+ );
1757
+ lines.push(
1758
+ `${indent}outputs['${sanitizeVarName(nodeId)}'] = { label: '${nodeLabel}', data: ${varName} };`
1759
+ );
1760
+ } else {
1761
+ lines.push(`${indent}await ${stepFnName}({ ...input, outputs });`);
1762
+ }
1763
+ return lines;
1764
+ }
1765
+ function generateConditionCode(nodeId, node, indent, visitedLocal) {
1766
+ const condition = node.data.config?.condition || "true";
1767
+ const convertedCondition = convertTemplateToJS(condition);
1768
+ const nextNodes = edgesBySource.get(nodeId) || [];
1769
+ const conditionVarName = `conditionValue_${sanitizeVarName(nodeId)}`;
1770
+ const lines = [];
1771
+ if (nextNodes.length > 0) {
1772
+ lines.push(`${indent}// ${node.data.label}`);
1773
+ lines.push(
1774
+ `${indent}const ${conditionVarName} = \`${escapeForTemplateLiteral(convertedCondition)}\`;`
1775
+ );
1776
+ lines.push(`${indent}if (${conditionVarName}) {`);
1777
+ if (nextNodes[0]) {
1778
+ lines.push(
1779
+ ...generateWorkflowBody(nextNodes[0], `${indent} `, visitedLocal)
1780
+ );
1781
+ }
1782
+ if (nextNodes[1]) {
1783
+ lines.push(`${indent}} else {`);
1784
+ lines.push(
1785
+ ...generateWorkflowBody(nextNodes[1], `${indent} `, visitedLocal)
1786
+ );
1787
+ }
1788
+ lines.push(`${indent}}`);
1789
+ }
1790
+ return lines;
1791
+ }
1792
+ function generateWorkflowBody(nodeId, indent = " ", visitedLocal = /* @__PURE__ */ new Set()) {
1793
+ if (visitedLocal.has(nodeId)) {
1794
+ return [];
1795
+ }
1796
+ visitedLocal.add(nodeId);
1797
+ const node = nodeMap.get(nodeId);
1798
+ if (!node) {
1799
+ return [];
1800
+ }
1801
+ const lines = [];
1802
+ switch (node.data.type) {
1803
+ case "trigger":
1804
+ lines.push(...generateTriggerCode(nodeId, node.data.label, indent));
1805
+ break;
1806
+ case "action": {
1807
+ const actionType = node.data.config?.actionType;
1808
+ if (actionType === "Condition") {
1809
+ lines.push(
1810
+ ...generateConditionCode(nodeId, node, indent, visitedLocal)
1811
+ );
1812
+ return lines;
1813
+ }
1814
+ lines.push(
1815
+ ...generateActionTransformCode(
1816
+ nodeId,
1817
+ node.data.config || {},
1818
+ node.data.label,
1819
+ indent
1820
+ )
1821
+ );
1822
+ break;
1823
+ }
1824
+ default:
1825
+ lines.push(`${indent}// Unknown node type: ${node.data.type}`);
1826
+ break;
1827
+ }
1828
+ const nextNodes = edgesBySource.get(nodeId) || [];
1829
+ for (const nextNodeId of nextNodes) {
1830
+ lines.push(...generateWorkflowBody(nextNodeId, indent, visitedLocal));
1831
+ }
1832
+ return lines;
1833
+ }
1834
+ const workflowBody = [];
1835
+ if (triggerNodes.length === 0) {
1836
+ workflowBody.push(' return { error: "No trigger nodes" };');
1837
+ } else {
1838
+ workflowBody.push(
1839
+ " // Track outputs from each node for template processing"
1840
+ );
1841
+ workflowBody.push(
1842
+ " const outputs: Record<string, { label: string; data: unknown }> = {};"
1843
+ );
1844
+ workflowBody.push("");
1845
+ for (const trigger of triggerNodes) {
1846
+ workflowBody.push(...generateWorkflowBody(trigger.id));
1847
+ }
1848
+ const lastNode = nodes.at(-1);
1849
+ if (lastNode) {
1850
+ const lastVarName = `result_${sanitizeVarName(lastNode.id)}`;
1851
+ workflowBody.push("");
1852
+ workflowBody.push(` return ${lastVarName};`);
1853
+ }
1854
+ }
1855
+ const functionName = sanitizeFunctionName(workflowName);
1856
+ const mainFunction = `export async function ${functionName}() {
1857
+ "use workflow";
1858
+
1859
+ // Input from workflow trigger - replace with your trigger data
1860
+ const input: Record<string, unknown> = {};
1861
+
1862
+ ${workflowBody.join("\n")}
1863
+ }`;
1864
+ const code = `${Array.from(imports).join("\n")}
1865
+
1866
+ ${stepFunctions.join("\n\n")}
1867
+
1868
+ ${mainFunction}
1869
+ `;
1870
+ return code;
1871
+ }
1872
+
1873
+ // src/server/api/workflows.ts
1874
+ async function handleExecuteWorkflow(request, workflowId) {
1875
+ try {
1876
+ const user = await resolveUser(request);
1877
+ if (!user) return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
1878
+ const workflow = await db.query.workflows.findFirst({
1879
+ where: eq4(workflows.id, workflowId)
1880
+ });
1881
+ if (!workflow) {
1882
+ return NextResponse4.json({ error: "Workflow not found" }, { status: 404 });
1883
+ }
1884
+ if (workflow.userId !== user.id) {
1885
+ return NextResponse4.json({ error: "Forbidden" }, { status: 403 });
1886
+ }
1887
+ const validation = await validateWorkflowIntegrations(
1888
+ workflow.nodes,
1889
+ user.id
1890
+ );
1891
+ if (!validation.valid) {
1892
+ return NextResponse4.json(
1893
+ { error: "Workflow contains invalid integration references" },
1894
+ { status: 403 }
1895
+ );
1896
+ }
1897
+ const body = await request.json().catch(() => ({}));
1898
+ const input = body.input || {};
1899
+ const [execution] = await db.insert(workflowExecutions).values({
1900
+ workflowId,
1901
+ userId: user.id,
1902
+ status: "running",
1903
+ input
1904
+ }).returning();
1905
+ executeWorkflowBackground(
1906
+ execution.id,
1907
+ workflowId,
1908
+ workflow.nodes,
1909
+ workflow.edges,
1910
+ input
1911
+ );
1912
+ return NextResponse4.json({ executionId: execution.id, status: "running" });
1913
+ } catch (error) {
1914
+ console.error("Failed to start workflow execution:", error);
1915
+ return NextResponse4.json(
1916
+ { error: error instanceof Error ? error.message : "Failed to execute workflow" },
1917
+ { status: 500 }
1918
+ );
1919
+ }
1920
+ }
1921
+ async function handleGetWorkflow(request, workflowId) {
1922
+ try {
1923
+ const user = await resolveUser(request);
1924
+ const workflow = await db.query.workflows.findFirst({
1925
+ where: eq4(workflows.id, workflowId)
1926
+ });
1927
+ if (!workflow) {
1928
+ return NextResponse4.json({ error: "Workflow not found" }, { status: 404 });
1929
+ }
1930
+ const isOwner = user?.id === workflow.userId;
1931
+ if (!isOwner && workflow.visibility !== "public") {
1932
+ return NextResponse4.json({ error: "Workflow not found" }, { status: 404 });
1933
+ }
1934
+ return NextResponse4.json({
1935
+ ...workflow,
1936
+ nodes: isOwner ? workflow.nodes : sanitizeNodesForPublicView(workflow.nodes),
1937
+ createdAt: workflow.createdAt.toISOString(),
1938
+ updatedAt: workflow.updatedAt.toISOString(),
1939
+ isOwner
1940
+ });
1941
+ } catch (error) {
1942
+ console.error("Failed to get workflow:", error);
1943
+ return NextResponse4.json(
1944
+ { error: error instanceof Error ? error.message : "Failed to get workflow" },
1945
+ { status: 500 }
1946
+ );
1947
+ }
1948
+ }
1949
+ async function handleGetWorkflows(request) {
1950
+ try {
1951
+ const user = await resolveUser(request);
1952
+ if (!user) return NextResponse4.json([], { status: 200 });
1953
+ const userWorkflows = await db.select().from(workflows).where(eq4(workflows.userId, user.id)).orderBy(desc(workflows.updatedAt));
1954
+ return NextResponse4.json(
1955
+ userWorkflows.map((w) => ({
1956
+ ...w,
1957
+ createdAt: w.createdAt.toISOString(),
1958
+ updatedAt: w.updatedAt.toISOString()
1959
+ }))
1960
+ );
1961
+ } catch (error) {
1962
+ console.error("Failed to get workflows:", error);
1963
+ return NextResponse4.json(
1964
+ { error: error instanceof Error ? error.message : "Failed to get workflows" },
1965
+ { status: 500 }
1966
+ );
1967
+ }
1968
+ }
1969
+ async function handleCreateWorkflow(request) {
1970
+ try {
1971
+ const user = await resolveUser(request);
1972
+ if (!user) return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
1973
+ const body = await request.json();
1974
+ if (!(body.name && body.nodes && body.edges)) {
1975
+ return NextResponse4.json(
1976
+ { error: "Name, nodes, and edges are required" },
1977
+ { status: 400 }
1978
+ );
1979
+ }
1980
+ const validation = await validateWorkflowIntegrations(body.nodes, user.id);
1981
+ if (!validation.valid) {
1982
+ return NextResponse4.json(
1983
+ { error: "Invalid integration references in workflow" },
1984
+ { status: 403 }
1985
+ );
1986
+ }
1987
+ let nodes = body.nodes;
1988
+ if (nodes.length === 0) {
1989
+ nodes = [
1990
+ {
1991
+ id: generateId(),
1992
+ type: "trigger",
1993
+ position: { x: 0, y: 0 },
1994
+ data: {
1995
+ label: "",
1996
+ description: "",
1997
+ type: "trigger",
1998
+ config: { triggerType: "Manual" },
1999
+ status: "idle"
2000
+ }
2001
+ }
2002
+ ];
2003
+ }
2004
+ let workflowName = body.name;
2005
+ if (body.name === "Untitled Workflow") {
2006
+ const userWorkflows = await db.query.workflows.findMany({
2007
+ where: eq4(workflows.userId, user.id)
2008
+ });
2009
+ workflowName = `Untitled ${userWorkflows.length + 1}`;
2010
+ }
2011
+ const workflowId = generateId();
2012
+ const [newWorkflow] = await db.insert(workflows).values({
2013
+ id: workflowId,
2014
+ name: workflowName,
2015
+ description: body.description,
2016
+ nodes,
2017
+ edges: body.edges,
2018
+ userId: user.id
2019
+ }).returning();
2020
+ return NextResponse4.json({
2021
+ ...newWorkflow,
2022
+ createdAt: newWorkflow.createdAt.toISOString(),
2023
+ updatedAt: newWorkflow.updatedAt.toISOString()
2024
+ });
2025
+ } catch (error) {
2026
+ console.error("Failed to create workflow:", error);
2027
+ return NextResponse4.json(
2028
+ { error: error instanceof Error ? error.message : "Failed to create workflow" },
2029
+ { status: 500 }
2030
+ );
2031
+ }
2032
+ }
2033
+ var CURRENT_WORKFLOW_NAME = "~~__CURRENT__~~";
2034
+ async function handleGetCurrentWorkflow(request) {
2035
+ try {
2036
+ const user = await resolveUser(request);
2037
+ if (!user) return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
2038
+ const [currentWorkflow] = await db.select().from(workflows).where(and2(eq4(workflows.name, CURRENT_WORKFLOW_NAME), eq4(workflows.userId, user.id))).orderBy(desc(workflows.updatedAt)).limit(1);
2039
+ if (!currentWorkflow) {
2040
+ return NextResponse4.json({ nodes: [], edges: [] });
2041
+ }
2042
+ return NextResponse4.json({
2043
+ id: currentWorkflow.id,
2044
+ nodes: currentWorkflow.nodes,
2045
+ edges: currentWorkflow.edges
2046
+ });
2047
+ } catch (error) {
2048
+ console.error("Failed to get current workflow:", error);
2049
+ return NextResponse4.json(
2050
+ { error: error instanceof Error ? error.message : "Failed to get current workflow" },
2051
+ { status: 500 }
2052
+ );
2053
+ }
2054
+ }
2055
+ async function handleSaveCurrentWorkflow(request) {
2056
+ try {
2057
+ const user = await resolveUser(request);
2058
+ if (!user) return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
2059
+ const body = await request.json();
2060
+ const { nodes, edges } = body;
2061
+ if (!(nodes && edges)) {
2062
+ return NextResponse4.json({ error: "Nodes and edges are required" }, { status: 400 });
2063
+ }
2064
+ const [existing] = await db.select().from(workflows).where(and2(eq4(workflows.name, CURRENT_WORKFLOW_NAME), eq4(workflows.userId, user.id))).limit(1);
2065
+ if (existing) {
2066
+ const [updated] = await db.update(workflows).set({ nodes, edges, updatedAt: /* @__PURE__ */ new Date() }).where(eq4(workflows.id, existing.id)).returning();
2067
+ return NextResponse4.json({ id: updated.id, nodes: updated.nodes, edges: updated.edges });
2068
+ }
2069
+ const workflowId = generateId();
2070
+ const [saved] = await db.insert(workflows).values({
2071
+ id: workflowId,
2072
+ name: CURRENT_WORKFLOW_NAME,
2073
+ description: "Auto-saved current workflow",
2074
+ nodes,
2075
+ edges,
2076
+ userId: user.id
2077
+ }).returning();
2078
+ return NextResponse4.json({ id: saved.id, nodes: saved.nodes, edges: saved.edges });
2079
+ } catch (error) {
2080
+ console.error("Failed to save current workflow:", error);
2081
+ return NextResponse4.json(
2082
+ { error: error instanceof Error ? error.message : "Failed to save current workflow" },
2083
+ { status: 500 }
2084
+ );
2085
+ }
2086
+ }
2087
+ async function handleGetExecutionStatus(request, executionId) {
2088
+ try {
2089
+ const user = await resolveUser(request);
2090
+ if (!user) return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
2091
+ const execution = await db.query.workflowExecutions.findFirst({
2092
+ where: eq4(workflowExecutions.id, executionId),
2093
+ with: { workflow: true }
2094
+ });
2095
+ if (!execution) {
2096
+ return NextResponse4.json({ error: "Execution not found" }, { status: 404 });
2097
+ }
2098
+ if (execution.workflow.userId !== user.id) {
2099
+ return NextResponse4.json({ error: "Forbidden" }, { status: 403 });
2100
+ }
2101
+ const logs = await db.query.workflowExecutionLogs.findMany({
2102
+ where: eq4(workflowExecutionLogs.executionId, executionId)
2103
+ });
2104
+ const nodeStatuses = logs.map((log) => ({
2105
+ nodeId: log.nodeId,
2106
+ status: log.status
2107
+ }));
2108
+ return NextResponse4.json({ status: execution.status, nodeStatuses });
2109
+ } catch (error) {
2110
+ console.error("Failed to get execution status:", error);
2111
+ return NextResponse4.json(
2112
+ { error: error instanceof Error ? error.message : "Failed to get execution status" },
2113
+ { status: 500 }
2114
+ );
2115
+ }
2116
+ }
2117
+ async function handleGetExecutionLogs(request, executionId) {
2118
+ try {
2119
+ const user = await resolveUser(request);
2120
+ if (!user) return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
2121
+ const execution = await db.query.workflowExecutions.findFirst({
2122
+ where: eq4(workflowExecutions.id, executionId),
2123
+ with: { workflow: true }
2124
+ });
2125
+ if (!execution) {
2126
+ return NextResponse4.json({ error: "Execution not found" }, { status: 404 });
2127
+ }
2128
+ if (execution.workflow.userId !== user.id) {
2129
+ return NextResponse4.json({ error: "Forbidden" }, { status: 403 });
2130
+ }
2131
+ const logs = await db.query.workflowExecutionLogs.findMany({
2132
+ where: eq4(workflowExecutionLogs.executionId, executionId),
2133
+ orderBy: [desc(workflowExecutionLogs.timestamp)]
2134
+ });
2135
+ return NextResponse4.json({ execution, logs });
2136
+ } catch (error) {
2137
+ console.error("Failed to get execution logs:", error);
2138
+ return NextResponse4.json(
2139
+ { error: error instanceof Error ? error.message : "Failed to get execution logs" },
2140
+ { status: 500 }
2141
+ );
2142
+ }
2143
+ }
2144
+ async function handlePatchWorkflow(request, workflowId) {
2145
+ try {
2146
+ const user = await resolveUser(request);
2147
+ if (!user) return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
2148
+ const existingWorkflow = await db.query.workflows.findFirst({
2149
+ where: and2(eq4(workflows.id, workflowId), eq4(workflows.userId, user.id))
2150
+ });
2151
+ if (!existingWorkflow) {
2152
+ return NextResponse4.json({ error: "Workflow not found" }, { status: 404 });
2153
+ }
2154
+ const body = await request.json();
2155
+ if (Array.isArray(body.nodes)) {
2156
+ const validation = await validateWorkflowIntegrations(body.nodes, user.id);
2157
+ if (!validation.valid) {
2158
+ return NextResponse4.json(
2159
+ { error: "Invalid integration references in workflow" },
2160
+ { status: 403 }
2161
+ );
2162
+ }
2163
+ }
2164
+ if (body.visibility !== void 0 && body.visibility !== "private" && body.visibility !== "public") {
2165
+ return NextResponse4.json(
2166
+ { error: "Invalid visibility value. Must be 'private' or 'public'" },
2167
+ { status: 400 }
2168
+ );
2169
+ }
2170
+ const updateData = buildWorkflowUpdateData(body);
2171
+ const [updatedWorkflow] = await db.update(workflows).set(updateData).where(eq4(workflows.id, workflowId)).returning();
2172
+ if (!updatedWorkflow) {
2173
+ return NextResponse4.json({ error: "Workflow not found" }, { status: 404 });
2174
+ }
2175
+ return NextResponse4.json({
2176
+ ...updatedWorkflow,
2177
+ createdAt: updatedWorkflow.createdAt.toISOString(),
2178
+ updatedAt: updatedWorkflow.updatedAt.toISOString(),
2179
+ isOwner: true
2180
+ });
2181
+ } catch (error) {
2182
+ console.error("Failed to update workflow:", error);
2183
+ return NextResponse4.json(
2184
+ { error: error instanceof Error ? error.message : "Failed to update workflow" },
2185
+ { status: 500 }
2186
+ );
2187
+ }
2188
+ }
2189
+ async function handleDeleteWorkflow(request, workflowId) {
2190
+ try {
2191
+ const user = await resolveUser(request);
2192
+ if (!user) return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
2193
+ const existingWorkflow = await db.query.workflows.findFirst({
2194
+ where: and2(eq4(workflows.id, workflowId), eq4(workflows.userId, user.id))
2195
+ });
2196
+ if (!existingWorkflow) {
2197
+ return NextResponse4.json({ error: "Workflow not found" }, { status: 404 });
2198
+ }
2199
+ await db.delete(workflows).where(eq4(workflows.id, workflowId));
2200
+ return NextResponse4.json({ success: true });
2201
+ } catch (error) {
2202
+ console.error("Failed to delete workflow:", error);
2203
+ return NextResponse4.json(
2204
+ { error: error instanceof Error ? error.message : "Failed to delete workflow" },
2205
+ { status: 500 }
2206
+ );
2207
+ }
2208
+ }
2209
+ async function handleDuplicateWorkflow(request, workflowId) {
2210
+ try {
2211
+ const user = await resolveUser(request);
2212
+ if (!user) return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
2213
+ const sourceWorkflow = await db.query.workflows.findFirst({
2214
+ where: eq4(workflows.id, workflowId)
2215
+ });
2216
+ if (!sourceWorkflow) {
2217
+ return NextResponse4.json({ error: "Workflow not found" }, { status: 404 });
2218
+ }
2219
+ const isOwner = user.id === sourceWorkflow.userId;
2220
+ if (!isOwner && sourceWorkflow.visibility !== "public") {
2221
+ return NextResponse4.json({ error: "Workflow not found" }, { status: 404 });
2222
+ }
2223
+ const oldNodes = sourceWorkflow.nodes;
2224
+ const newNodes = oldNodes.map((node) => {
2225
+ const newNode = { ...node, id: generateId() };
2226
+ if (newNode.data) {
2227
+ const data = { ...newNode.data };
2228
+ if (data.config) {
2229
+ const { integrationId: _, ...configWithout } = data.config;
2230
+ data.config = configWithout;
2231
+ }
2232
+ data.status = "idle";
2233
+ newNode.data = data;
2234
+ }
2235
+ return newNode;
2236
+ });
2237
+ const idMap = new Map(oldNodes.map((n, i) => [n.id, newNodes[i].id]));
2238
+ const newEdges = sourceWorkflow.edges.map((edge) => ({
2239
+ ...edge,
2240
+ id: generateId(),
2241
+ source: idMap.get(edge.source) || edge.source,
2242
+ target: idMap.get(edge.target) || edge.target
2243
+ }));
2244
+ const userWorkflows = await db.query.workflows.findMany({
2245
+ where: eq4(workflows.userId, user.id)
2246
+ });
2247
+ const baseName = `${sourceWorkflow.name} (Copy)`;
2248
+ let workflowName = baseName;
2249
+ const existingNames = new Set(userWorkflows.map((w) => w.name));
2250
+ if (existingNames.has(workflowName)) {
2251
+ let counter = 2;
2252
+ while (existingNames.has(`${baseName} ${counter}`)) counter++;
2253
+ workflowName = `${baseName} ${counter}`;
2254
+ }
2255
+ const newWorkflowId = generateId();
2256
+ const [newWorkflow] = await db.insert(workflows).values({
2257
+ id: newWorkflowId,
2258
+ name: workflowName,
2259
+ description: sourceWorkflow.description,
2260
+ nodes: newNodes,
2261
+ edges: newEdges,
2262
+ userId: user.id,
2263
+ visibility: "private"
2264
+ }).returning();
2265
+ return NextResponse4.json({
2266
+ ...newWorkflow,
2267
+ createdAt: newWorkflow.createdAt.toISOString(),
2268
+ updatedAt: newWorkflow.updatedAt.toISOString(),
2269
+ isOwner: true
2270
+ });
2271
+ } catch (error) {
2272
+ console.error("Failed to duplicate workflow:", error);
2273
+ return NextResponse4.json(
2274
+ { error: error instanceof Error ? error.message : "Failed to duplicate workflow" },
2275
+ { status: 500 }
2276
+ );
2277
+ }
2278
+ }
2279
+ async function handleGetWorkflowExecutions(request, workflowId) {
2280
+ try {
2281
+ const user = await resolveUser(request);
2282
+ if (!user) return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
2283
+ const workflow = await db.query.workflows.findFirst({
2284
+ where: and2(eq4(workflows.id, workflowId), eq4(workflows.userId, user.id))
2285
+ });
2286
+ if (!workflow) {
2287
+ return NextResponse4.json({ error: "Workflow not found" }, { status: 404 });
2288
+ }
2289
+ const executions = await db.query.workflowExecutions.findMany({
2290
+ where: eq4(workflowExecutions.workflowId, workflowId),
2291
+ orderBy: [desc(workflowExecutions.startedAt)],
2292
+ limit: 50
2293
+ });
2294
+ return NextResponse4.json(executions);
2295
+ } catch (error) {
2296
+ console.error("Failed to get executions:", error);
2297
+ return NextResponse4.json(
2298
+ { error: error instanceof Error ? error.message : "Failed to get executions" },
2299
+ { status: 500 }
2300
+ );
2301
+ }
2302
+ }
2303
+ async function handleDeleteWorkflowExecutions(request, workflowId) {
2304
+ try {
2305
+ const user = await resolveUser(request);
2306
+ if (!user) return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
2307
+ const workflow = await db.query.workflows.findFirst({
2308
+ where: and2(eq4(workflows.id, workflowId), eq4(workflows.userId, user.id))
2309
+ });
2310
+ if (!workflow) {
2311
+ return NextResponse4.json({ error: "Workflow not found" }, { status: 404 });
2312
+ }
2313
+ const execList = await db.query.workflowExecutions.findMany({
2314
+ where: eq4(workflowExecutions.workflowId, workflowId),
2315
+ columns: { id: true }
2316
+ });
2317
+ const executionIds = execList.map((e) => e.id);
2318
+ if (executionIds.length > 0) {
2319
+ await db.delete(workflowExecutionLogs).where(inArray(workflowExecutionLogs.executionId, executionIds));
2320
+ await db.delete(workflowExecutions).where(eq4(workflowExecutions.workflowId, workflowId));
2321
+ }
2322
+ return NextResponse4.json({ success: true, deletedCount: executionIds.length });
2323
+ } catch (error) {
2324
+ console.error("Failed to delete executions:", error);
2325
+ return NextResponse4.json(
2326
+ { error: error instanceof Error ? error.message : "Failed to delete executions" },
2327
+ { status: 500 }
2328
+ );
2329
+ }
2330
+ }
2331
+ async function handleWebhookWorkflow(request, workflowId) {
2332
+ try {
2333
+ const workflow = await db.query.workflows.findFirst({
2334
+ where: eq4(workflows.id, workflowId)
2335
+ });
2336
+ if (!workflow) {
2337
+ return NextResponse4.json(
2338
+ { error: "Workflow not found" },
2339
+ { status: 404, headers: corsHeaders }
2340
+ );
2341
+ }
2342
+ const authHeader = request.headers.get("Authorization");
2343
+ if (!authHeader) {
2344
+ return NextResponse4.json(
2345
+ { error: "Missing Authorization header" },
2346
+ { status: 401, headers: corsHeaders }
2347
+ );
2348
+ }
2349
+ const key = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : authHeader;
2350
+ if (!key?.startsWith("wfb_")) {
2351
+ return NextResponse4.json(
2352
+ { error: "Invalid API key format" },
2353
+ { status: 401, headers: corsHeaders }
2354
+ );
2355
+ }
2356
+ const keyHash = createHash2("sha256").update(key).digest("hex");
2357
+ const apiKey = await db.query.apiKeys.findFirst({
2358
+ where: eq4(apiKeys.keyHash, keyHash)
2359
+ });
2360
+ if (!apiKey || apiKey.userId !== workflow.userId) {
2361
+ return NextResponse4.json(
2362
+ { error: "Invalid API key or insufficient permissions" },
2363
+ { status: 401, headers: corsHeaders }
2364
+ );
2365
+ }
2366
+ db.update(apiKeys).set({ lastUsedAt: /* @__PURE__ */ new Date() }).where(eq4(apiKeys.id, apiKey.id)).catch(() => {
2367
+ });
2368
+ const triggerNode = workflow.nodes.find(
2369
+ (node) => node.data.type === "trigger"
2370
+ );
2371
+ if (!triggerNode || triggerNode.data.config?.triggerType !== "Webhook") {
2372
+ return NextResponse4.json(
2373
+ { error: "This workflow is not configured for webhook triggers" },
2374
+ { status: 400, headers: corsHeaders }
2375
+ );
2376
+ }
2377
+ const validation = await validateWorkflowIntegrations(
2378
+ workflow.nodes,
2379
+ workflow.userId
2380
+ );
2381
+ if (!validation.valid) {
2382
+ return NextResponse4.json(
2383
+ { error: "Workflow contains invalid integration references" },
2384
+ { status: 403, headers: corsHeaders }
2385
+ );
2386
+ }
2387
+ const body = await request.json().catch(() => ({}));
2388
+ const [execution] = await db.insert(workflowExecutions).values({
2389
+ workflowId,
2390
+ userId: workflow.userId,
2391
+ status: "running",
2392
+ input: body
2393
+ }).returning();
2394
+ executeWorkflowBackground(
2395
+ execution.id,
2396
+ workflowId,
2397
+ workflow.nodes,
2398
+ workflow.edges,
2399
+ body
2400
+ );
2401
+ return NextResponse4.json(
2402
+ { executionId: execution.id, status: "running" },
2403
+ { headers: corsHeaders }
2404
+ );
2405
+ } catch (error) {
2406
+ console.error("Failed to start webhook execution:", error);
2407
+ return NextResponse4.json(
2408
+ { error: error instanceof Error ? error.message : "Failed to execute workflow" },
2409
+ { status: 500, headers: corsHeaders }
2410
+ );
2411
+ }
2412
+ }
2413
+ async function handleGetWorkflowCode(request, workflowId) {
2414
+ try {
2415
+ const session = await auth.api.getSession({
2416
+ headers: request.headers
2417
+ });
2418
+ if (!session?.user) {
2419
+ return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
2420
+ }
2421
+ const workflow = await db.query.workflows.findFirst({
2422
+ where: and2(
2423
+ eq4(workflows.id, workflowId),
2424
+ eq4(workflows.userId, session.user.id)
2425
+ )
2426
+ });
2427
+ if (!workflow) {
2428
+ return NextResponse4.json(
2429
+ { error: "Workflow not found" },
2430
+ { status: 404 }
2431
+ );
2432
+ }
2433
+ const code = generateWorkflowSDKCode(
2434
+ workflow.name,
2435
+ workflow.nodes,
2436
+ workflow.edges
2437
+ );
2438
+ return NextResponse4.json({
2439
+ code,
2440
+ workflowName: workflow.name
2441
+ });
2442
+ } catch (error) {
2443
+ console.error("Failed to get workflow code:", error);
2444
+ return NextResponse4.json(
2445
+ {
2446
+ error: error instanceof Error ? error.message : "Failed to get workflow code"
2447
+ },
2448
+ { status: 500 }
2449
+ );
2450
+ }
2451
+ }
2452
+ async function handleGetWorkflowDownload(request, workflowId) {
2453
+ try {
2454
+ const session = await auth.api.getSession({
2455
+ headers: request.headers
2456
+ });
2457
+ if (!session?.user) {
2458
+ return NextResponse4.json({ error: "Unauthorized" }, { status: 401 });
2459
+ }
2460
+ const workflow = await db.query.workflows.findFirst({
2461
+ where: and2(
2462
+ eq4(workflows.id, workflowId),
2463
+ eq4(workflows.userId, session.user.id)
2464
+ )
2465
+ });
2466
+ if (!workflow) {
2467
+ return NextResponse4.json(
2468
+ { error: "Workflow not found" },
2469
+ { status: 404 }
2470
+ );
2471
+ }
2472
+ const boilerplateFiles = await readDirectoryRecursive(BOILERPLATE_PATH);
2473
+ const templateFiles = await readDirectoryRecursive(CODEGEN_TEMPLATES_PATH);
2474
+ const stepFiles = {};
2475
+ for (const [path, content] of Object.entries(templateFiles)) {
2476
+ const templateMatch = content.match(TEMPLATE_EXPORT_REGEX);
2477
+ if (templateMatch) {
2478
+ stepFiles[`lib/steps/${path}`] = templateMatch[1];
2479
+ }
2480
+ }
2481
+ const workflowFiles = generateWorkflowFiles({
2482
+ name: workflow.name,
2483
+ nodes: workflow.nodes,
2484
+ edges: workflow.edges
2485
+ });
2486
+ const allFiles = { ...boilerplateFiles, ...stepFiles, ...workflowFiles };
2487
+ const packageJson = JSON.parse(allFiles["package.json"]);
2488
+ packageJson.dependencies = {
2489
+ ...packageJson.dependencies,
2490
+ workflow: "4.0.1-beta.7",
2491
+ ...getIntegrationDependencies(workflow.nodes)
2492
+ };
2493
+ allFiles["package.json"] = JSON.stringify(packageJson, null, 2);
2494
+ allFiles["next.config.ts"] = `import { withWorkflow } from 'workflow/next';
2495
+ import type { NextConfig } from 'next';
2496
+
2497
+ const nextConfig: NextConfig = {};
2498
+
2499
+ export default withWorkflow(nextConfig);
2500
+ `;
2501
+ const tsConfig = JSON.parse(allFiles["tsconfig.json"]);
2502
+ tsConfig.compilerOptions.plugins = [{ name: "next" }, { name: "workflow" }];
2503
+ allFiles["tsconfig.json"] = JSON.stringify(tsConfig, null, 2);
2504
+ allFiles["README.md"] = `# ${workflow.name}
2505
+
2506
+ This is a Next.js workflow project generated from Workflow Builder.
2507
+
2508
+ ## Getting Started
2509
+
2510
+ 1. Install dependencies:
2511
+ \`\`\`bash
2512
+ pnpm install
2513
+ \`\`\`
2514
+
2515
+ 2. Set up environment variables:
2516
+ \`\`\`bash
2517
+ cp .env.example .env.local
2518
+ \`\`\`
2519
+
2520
+ 3. Run the development server:
2521
+ \`\`\`bash
2522
+ pnpm dev
2523
+ \`\`\`
2524
+
2525
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
2526
+
2527
+ ## Workflow API
2528
+
2529
+ Your workflow is available at \`/api/workflows/${sanitizeFileName(workflow.name)}\`.
2530
+
2531
+ Send a POST request with a JSON body to trigger the workflow:
2532
+
2533
+ \`\`\`bash
2534
+ curl -X POST http://localhost:3000/api/workflows/${sanitizeFileName(workflow.name)} \\
2535
+ -H "Content-Type: application/json" \\
2536
+ -d '{"key": "value"}'
2537
+ \`\`\`
2538
+
2539
+ ## Deployment
2540
+
2541
+ Deploy your workflow to Vercel:
2542
+
2543
+ \`\`\`bash
2544
+ vercel deploy
2545
+ \`\`\`
2546
+
2547
+ For more information, visit the [Workflow documentation](https://workflow.is).
2548
+ `;
2549
+ allFiles[".env.example"] = generateEnvExample();
2550
+ return NextResponse4.json({
2551
+ success: true,
2552
+ files: allFiles
2553
+ });
2554
+ } catch (error) {
2555
+ console.error("Failed to prepare workflow download:", error);
2556
+ return NextResponse4.json(
2557
+ {
2558
+ success: false,
2559
+ error: error instanceof Error ? error.message : "Failed to prepare workflow download"
2560
+ },
2561
+ { status: 500 }
2562
+ );
2563
+ }
2564
+ }
2565
+
2566
+ // src/server/api/index.ts
2567
+ import "virtual:workflow-builder-plugins";
2568
+ async function route(request) {
2569
+ const segments = extractPath(request);
2570
+ const method = request.method.toUpperCase();
2571
+ if (segments.length === 0) {
2572
+ return NextResponse5.json({ error: "Not found" }, { status: 404 });
2573
+ }
2574
+ const [s0, s1, s2, s3] = segments;
2575
+ if (s0 === "auth") {
2576
+ return handleAuth(request, segments);
2577
+ }
2578
+ if (s0 === "workflows") {
2579
+ if (s1 === void 0) {
2580
+ if (method === "GET") return handleGetWorkflows(request);
2581
+ }
2582
+ if (s1 === "create") {
2583
+ if (method === "POST") return handleCreateWorkflow(request);
2584
+ }
2585
+ if (s1 === "current") {
2586
+ if (method === "GET") return handleGetCurrentWorkflow(request);
2587
+ if (method === "POST") return handleSaveCurrentWorkflow(request);
2588
+ }
2589
+ if (s1 === "executions" && s2) {
2590
+ const executionId = s2;
2591
+ if (s3 === "status" && method === "GET") return handleGetExecutionStatus(request, executionId);
2592
+ if (s3 === "logs" && method === "GET") return handleGetExecutionLogs(request, executionId);
2593
+ }
2594
+ if (s1 && s1 !== "create" && s1 !== "current" && s1 !== "executions") {
2595
+ const workflowId = s1;
2596
+ if (s2 === void 0) {
2597
+ if (method === "GET") return handleGetWorkflow(request, workflowId);
2598
+ if (method === "PATCH") return handlePatchWorkflow(request, workflowId);
2599
+ if (method === "DELETE") return handleDeleteWorkflow(request, workflowId);
2600
+ }
2601
+ if (s2 === "code" && method === "GET") return handleGetWorkflowCode(request, workflowId);
2602
+ if (s2 === "download" && method === "GET") return handleGetWorkflowDownload(request, workflowId);
2603
+ if (s2 === "duplicate" && method === "POST") return handleDuplicateWorkflow(request, workflowId);
2604
+ if (s2 === "executions") {
2605
+ if (method === "GET") return handleGetWorkflowExecutions(request, workflowId);
2606
+ if (method === "DELETE") return handleDeleteWorkflowExecutions(request, workflowId);
2607
+ }
2608
+ if (s2 === "webhook" && method === "POST") return handleWebhookWorkflow(request, workflowId);
2609
+ }
2610
+ }
2611
+ if (s0 === "workflow" && s1 && s2 === "execute") {
2612
+ if (method === "POST") return handleExecuteWorkflow(request, s1);
2613
+ }
2614
+ if (s0 === "integrations") {
2615
+ if (s1 === void 0) {
2616
+ if (method === "GET") return handleGetIntegrations(request);
2617
+ if (method === "POST") return handleCreateIntegration(request);
2618
+ }
2619
+ if (s1 === "test" && s2 === void 0) {
2620
+ if (method === "POST") return handleTestIntegrationCredentials(request);
2621
+ }
2622
+ if (s1 && s1 !== "test") {
2623
+ const integrationId = s1;
2624
+ if (s2 === void 0) {
2625
+ if (method === "GET") return handleGetIntegration(request, integrationId);
2626
+ if (method === "PUT") return handleUpdateIntegration(request, integrationId);
2627
+ if (method === "DELETE") return handleDeleteIntegration(request, integrationId);
2628
+ }
2629
+ if (s2 === "test" && method === "POST") return handleTestIntegration(request, integrationId);
2630
+ }
2631
+ }
2632
+ if (s0 === "user") {
2633
+ if (method === "GET") return handleGetUser(request);
2634
+ if (method === "PATCH") return handleUpdateUser(request);
2635
+ }
2636
+ if (s0 === "api-keys") {
2637
+ if (s1 === void 0) {
2638
+ if (method === "GET") return handleGetApiKeys(request);
2639
+ if (method === "POST") return handleCreateApiKey(request);
2640
+ }
2641
+ if (s1 && s2 === void 0) {
2642
+ if (method === "DELETE") return handleDeleteApiKey(request, s1);
2643
+ }
2644
+ }
2645
+ return NextResponse5.json({ error: "Not found" }, { status: 404 });
2646
+ }
2647
+ async function GET(request) {
2648
+ return route(request);
2649
+ }
2650
+ async function POST(request) {
2651
+ return route(request);
2652
+ }
2653
+ async function PUT(request) {
2654
+ return route(request);
2655
+ }
2656
+ async function PATCH(request) {
2657
+ return route(request);
2658
+ }
2659
+ async function DELETE(request) {
2660
+ return route(request);
2661
+ }
2662
+ async function OPTIONS(_request) {
2663
+ return NextResponse5.json({}, { headers: corsHeaders });
2664
+ }
2665
+ export {
2666
+ DELETE,
2667
+ GET,
2668
+ OPTIONS,
2669
+ PATCH,
2670
+ POST,
2671
+ PUT
2672
+ };