next-workflow-builder 0.4.7 → 0.5.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.
- package/dist/{chunk-MIBRBQIJ.js → chunk-EMCA7GLF.js} +145 -2
- package/dist/client/index.js +247 -190
- package/dist/server/api/index.js +46 -334
- package/package.json +1 -1
|
@@ -1038,6 +1038,142 @@ ${code}
|
|
|
1038
1038
|
`;
|
|
1039
1039
|
}
|
|
1040
1040
|
|
|
1041
|
+
// src/plugins/condition/operators.ts
|
|
1042
|
+
var DATA_TYPE_OPTIONS = [
|
|
1043
|
+
{ value: "string", label: "String" },
|
|
1044
|
+
{ value: "number", label: "Number" },
|
|
1045
|
+
{ value: "boolean", label: "Boolean" },
|
|
1046
|
+
{ value: "datetime", label: "Date & Time" }
|
|
1047
|
+
];
|
|
1048
|
+
var OPERATORS = {
|
|
1049
|
+
string: [
|
|
1050
|
+
{ value: "exists", label: "exists", unary: true },
|
|
1051
|
+
{ value: "doesNotExist", label: "does not exist", unary: true },
|
|
1052
|
+
{ value: "isEmpty", label: "is empty", unary: true },
|
|
1053
|
+
{ value: "isNotEmpty", label: "is not empty", unary: true },
|
|
1054
|
+
{ value: "equals", label: "equals", unary: false },
|
|
1055
|
+
{ value: "notEquals", label: "does not equal", unary: false },
|
|
1056
|
+
{ value: "contains", label: "contains", unary: false },
|
|
1057
|
+
{ value: "doesNotContain", label: "does not contain", unary: false },
|
|
1058
|
+
{ value: "startsWith", label: "starts with", unary: false },
|
|
1059
|
+
{ value: "doesNotStartWith", label: "does not start with", unary: false },
|
|
1060
|
+
{ value: "endsWith", label: "ends with", unary: false },
|
|
1061
|
+
{ value: "doesNotEndWith", label: "does not end with", unary: false },
|
|
1062
|
+
{ value: "matchesRegex", label: "matches regex", unary: false },
|
|
1063
|
+
{ value: "doesNotMatchRegex", label: "does not match regex", unary: false }
|
|
1064
|
+
],
|
|
1065
|
+
number: [
|
|
1066
|
+
{ value: "equals", label: "equals", unary: false },
|
|
1067
|
+
{ value: "notEquals", label: "does not equal", unary: false },
|
|
1068
|
+
{ value: "greaterThan", label: "greater than", unary: false },
|
|
1069
|
+
{ value: "lessThan", label: "less than", unary: false },
|
|
1070
|
+
{ value: "greaterThanOrEqual", label: "greater than or equal", unary: false },
|
|
1071
|
+
{ value: "lessThanOrEqual", label: "less than or equal", unary: false }
|
|
1072
|
+
],
|
|
1073
|
+
boolean: [
|
|
1074
|
+
{ value: "isTrue", label: "is true", unary: true },
|
|
1075
|
+
{ value: "isFalse", label: "is false", unary: true },
|
|
1076
|
+
{ value: "exists", label: "exists", unary: true },
|
|
1077
|
+
{ value: "doesNotExist", label: "does not exist", unary: true }
|
|
1078
|
+
],
|
|
1079
|
+
datetime: [
|
|
1080
|
+
{ value: "isBefore", label: "is before", unary: false },
|
|
1081
|
+
{ value: "isAfter", label: "is after", unary: false },
|
|
1082
|
+
{ value: "equals", label: "equals", unary: false }
|
|
1083
|
+
]
|
|
1084
|
+
};
|
|
1085
|
+
function evaluateOperator(dataType, operator, leftValue, rightValue) {
|
|
1086
|
+
switch (operator) {
|
|
1087
|
+
case "exists":
|
|
1088
|
+
return leftValue !== null && leftValue !== void 0;
|
|
1089
|
+
case "doesNotExist":
|
|
1090
|
+
return leftValue === null || leftValue === void 0;
|
|
1091
|
+
case "isEmpty":
|
|
1092
|
+
return leftValue === null || leftValue === void 0 || String(leftValue) === "";
|
|
1093
|
+
case "isNotEmpty":
|
|
1094
|
+
return leftValue !== null && leftValue !== void 0 && String(leftValue) !== "";
|
|
1095
|
+
case "isTrue":
|
|
1096
|
+
return Boolean(leftValue) === true;
|
|
1097
|
+
case "isFalse":
|
|
1098
|
+
return Boolean(leftValue) === false;
|
|
1099
|
+
}
|
|
1100
|
+
switch (dataType) {
|
|
1101
|
+
case "string": {
|
|
1102
|
+
const left = String(leftValue ?? "");
|
|
1103
|
+
const right = String(rightValue ?? "");
|
|
1104
|
+
switch (operator) {
|
|
1105
|
+
case "equals":
|
|
1106
|
+
return left === right;
|
|
1107
|
+
case "notEquals":
|
|
1108
|
+
return left !== right;
|
|
1109
|
+
case "contains":
|
|
1110
|
+
return left.includes(right);
|
|
1111
|
+
case "doesNotContain":
|
|
1112
|
+
return !left.includes(right);
|
|
1113
|
+
case "startsWith":
|
|
1114
|
+
return left.startsWith(right);
|
|
1115
|
+
case "doesNotStartWith":
|
|
1116
|
+
return !left.startsWith(right);
|
|
1117
|
+
case "endsWith":
|
|
1118
|
+
return left.endsWith(right);
|
|
1119
|
+
case "doesNotEndWith":
|
|
1120
|
+
return !left.endsWith(right);
|
|
1121
|
+
case "matchesRegex":
|
|
1122
|
+
try {
|
|
1123
|
+
return new RegExp(right).test(left);
|
|
1124
|
+
} catch {
|
|
1125
|
+
return false;
|
|
1126
|
+
}
|
|
1127
|
+
case "doesNotMatchRegex":
|
|
1128
|
+
try {
|
|
1129
|
+
return !new RegExp(right).test(left);
|
|
1130
|
+
} catch {
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
break;
|
|
1135
|
+
}
|
|
1136
|
+
case "number": {
|
|
1137
|
+
const left = Number(leftValue);
|
|
1138
|
+
const right = Number(rightValue);
|
|
1139
|
+
if (Number.isNaN(left) || Number.isNaN(right)) return false;
|
|
1140
|
+
switch (operator) {
|
|
1141
|
+
case "equals":
|
|
1142
|
+
return left === right;
|
|
1143
|
+
case "notEquals":
|
|
1144
|
+
return left !== right;
|
|
1145
|
+
case "greaterThan":
|
|
1146
|
+
return left > right;
|
|
1147
|
+
case "lessThan":
|
|
1148
|
+
return left < right;
|
|
1149
|
+
case "greaterThanOrEqual":
|
|
1150
|
+
return left >= right;
|
|
1151
|
+
case "lessThanOrEqual":
|
|
1152
|
+
return left <= right;
|
|
1153
|
+
}
|
|
1154
|
+
break;
|
|
1155
|
+
}
|
|
1156
|
+
case "boolean":
|
|
1157
|
+
break;
|
|
1158
|
+
case "datetime": {
|
|
1159
|
+
const left = new Date(leftValue).getTime();
|
|
1160
|
+
const right = new Date(rightValue).getTime();
|
|
1161
|
+
if (Number.isNaN(left) || Number.isNaN(right)) return false;
|
|
1162
|
+
switch (operator) {
|
|
1163
|
+
case "isBefore":
|
|
1164
|
+
return left < right;
|
|
1165
|
+
case "isAfter":
|
|
1166
|
+
return left > right;
|
|
1167
|
+
case "equals":
|
|
1168
|
+
return left === right;
|
|
1169
|
+
}
|
|
1170
|
+
break;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
console.warn(`[Condition] Unknown operator "${operator}" for data type "${dataType}"`);
|
|
1174
|
+
return false;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1041
1177
|
// src/plugins/condition/index.tsx
|
|
1042
1178
|
import { GitBranch } from "lucide-react";
|
|
1043
1179
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -1049,10 +1185,14 @@ var conditionAction = {
|
|
|
1049
1185
|
icon: /* @__PURE__ */ jsx(GitBranch, { className: "size-12 text-pink-300", strokeWidth: 1.5 }),
|
|
1050
1186
|
codeGenerator: `export async function conditionStep(input: {
|
|
1051
1187
|
condition: boolean;
|
|
1188
|
+
dataType?: string;
|
|
1189
|
+
operator?: string;
|
|
1190
|
+
leftValue?: unknown;
|
|
1191
|
+
rightValue?: unknown;
|
|
1052
1192
|
}) {
|
|
1053
1193
|
"use step";
|
|
1054
|
-
|
|
1055
|
-
// Evaluate condition
|
|
1194
|
+
|
|
1195
|
+
// Evaluate structured condition
|
|
1056
1196
|
return { condition: input.condition };
|
|
1057
1197
|
}`
|
|
1058
1198
|
};
|
|
@@ -1156,6 +1296,9 @@ export {
|
|
|
1156
1296
|
sanitizeVarName,
|
|
1157
1297
|
generateWorkflowCode,
|
|
1158
1298
|
generateWorkflowModule,
|
|
1299
|
+
DATA_TYPE_OPTIONS,
|
|
1300
|
+
OPERATORS,
|
|
1301
|
+
evaluateOperator,
|
|
1159
1302
|
conditionAction,
|
|
1160
1303
|
databaseQueryAction,
|
|
1161
1304
|
httpRequestAction
|
package/dist/client/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
|
+
DATA_TYPE_OPTIONS,
|
|
4
|
+
OPERATORS,
|
|
3
5
|
conditionAction,
|
|
4
6
|
databaseQueryAction,
|
|
5
7
|
generateWorkflowCode,
|
|
6
8
|
httpRequestAction
|
|
7
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-EMCA7GLF.js";
|
|
8
10
|
import {
|
|
9
11
|
cn
|
|
10
12
|
} from "../chunk-5YYA34YV.js";
|
|
@@ -1176,6 +1178,166 @@ import { useAtomValue as useAtomValue5, useSetAtom as useSetAtom5 } from "jotai"
|
|
|
1176
1178
|
import { HelpCircle as HelpCircle2, Plus as Plus3, Settings as Settings2 } from "lucide-react";
|
|
1177
1179
|
import { useEffect as useEffect8, useMemo as useMemo4, useState as useState11 } from "react";
|
|
1178
1180
|
|
|
1181
|
+
// src/client/components/ui/select.tsx
|
|
1182
|
+
import { Select as SelectPrimitive } from "radix-ui";
|
|
1183
|
+
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
1184
|
+
import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1185
|
+
function Select({
|
|
1186
|
+
...props
|
|
1187
|
+
}) {
|
|
1188
|
+
return /* @__PURE__ */ jsx7(SelectPrimitive.Root, { "data-slot": "select", ...props });
|
|
1189
|
+
}
|
|
1190
|
+
function SelectGroup({
|
|
1191
|
+
...props
|
|
1192
|
+
}) {
|
|
1193
|
+
return /* @__PURE__ */ jsx7(SelectPrimitive.Group, { "data-slot": "select-group", ...props });
|
|
1194
|
+
}
|
|
1195
|
+
function SelectValue({
|
|
1196
|
+
...props
|
|
1197
|
+
}) {
|
|
1198
|
+
return /* @__PURE__ */ jsx7(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
|
|
1199
|
+
}
|
|
1200
|
+
function SelectTrigger({
|
|
1201
|
+
className,
|
|
1202
|
+
size = "default",
|
|
1203
|
+
children,
|
|
1204
|
+
...props
|
|
1205
|
+
}) {
|
|
1206
|
+
return /* @__PURE__ */ jsxs2(
|
|
1207
|
+
SelectPrimitive.Trigger,
|
|
1208
|
+
{
|
|
1209
|
+
"data-slot": "select-trigger",
|
|
1210
|
+
"data-size": size,
|
|
1211
|
+
className: cn(
|
|
1212
|
+
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive bg-transparent hover:bg-accent/50 flex w-fit items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1213
|
+
className
|
|
1214
|
+
),
|
|
1215
|
+
...props,
|
|
1216
|
+
children: [
|
|
1217
|
+
children,
|
|
1218
|
+
/* @__PURE__ */ jsx7(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx7(ChevronDownIcon, { className: "size-4 opacity-50" }) })
|
|
1219
|
+
]
|
|
1220
|
+
}
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
function SelectContent({
|
|
1224
|
+
className,
|
|
1225
|
+
children,
|
|
1226
|
+
position = "popper",
|
|
1227
|
+
align = "center",
|
|
1228
|
+
...props
|
|
1229
|
+
}) {
|
|
1230
|
+
return /* @__PURE__ */ jsx7(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs2(
|
|
1231
|
+
SelectPrimitive.Content,
|
|
1232
|
+
{
|
|
1233
|
+
"data-slot": "select-content",
|
|
1234
|
+
className: cn(
|
|
1235
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
|
1236
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
1237
|
+
className
|
|
1238
|
+
),
|
|
1239
|
+
position,
|
|
1240
|
+
align,
|
|
1241
|
+
...props,
|
|
1242
|
+
children: [
|
|
1243
|
+
/* @__PURE__ */ jsx7(SelectScrollUpButton, {}),
|
|
1244
|
+
/* @__PURE__ */ jsx7(
|
|
1245
|
+
SelectPrimitive.Viewport,
|
|
1246
|
+
{
|
|
1247
|
+
className: cn(
|
|
1248
|
+
"p-1",
|
|
1249
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
|
1250
|
+
),
|
|
1251
|
+
children
|
|
1252
|
+
}
|
|
1253
|
+
),
|
|
1254
|
+
/* @__PURE__ */ jsx7(SelectScrollDownButton, {})
|
|
1255
|
+
]
|
|
1256
|
+
}
|
|
1257
|
+
) });
|
|
1258
|
+
}
|
|
1259
|
+
function SelectLabel({
|
|
1260
|
+
className,
|
|
1261
|
+
...props
|
|
1262
|
+
}) {
|
|
1263
|
+
return /* @__PURE__ */ jsx7(
|
|
1264
|
+
SelectPrimitive.Label,
|
|
1265
|
+
{
|
|
1266
|
+
"data-slot": "select-label",
|
|
1267
|
+
className: cn("text-muted-foreground px-2 py-1.5 text-xs", className),
|
|
1268
|
+
...props
|
|
1269
|
+
}
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
function SelectItem({
|
|
1273
|
+
className,
|
|
1274
|
+
children,
|
|
1275
|
+
...props
|
|
1276
|
+
}) {
|
|
1277
|
+
return /* @__PURE__ */ jsxs2(
|
|
1278
|
+
SelectPrimitive.Item,
|
|
1279
|
+
{
|
|
1280
|
+
"data-slot": "select-item",
|
|
1281
|
+
className: cn(
|
|
1282
|
+
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
1283
|
+
className
|
|
1284
|
+
),
|
|
1285
|
+
...props,
|
|
1286
|
+
children: [
|
|
1287
|
+
/* @__PURE__ */ jsx7("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx7(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx7(CheckIcon, { className: "size-4" }) }) }),
|
|
1288
|
+
/* @__PURE__ */ jsx7(SelectPrimitive.ItemText, { children })
|
|
1289
|
+
]
|
|
1290
|
+
}
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
function SelectSeparator({
|
|
1294
|
+
className,
|
|
1295
|
+
...props
|
|
1296
|
+
}) {
|
|
1297
|
+
return /* @__PURE__ */ jsx7(
|
|
1298
|
+
SelectPrimitive.Separator,
|
|
1299
|
+
{
|
|
1300
|
+
"data-slot": "select-separator",
|
|
1301
|
+
className: cn("bg-border pointer-events-none -mx-1 my-1 h-px", className),
|
|
1302
|
+
...props
|
|
1303
|
+
}
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
function SelectScrollUpButton({
|
|
1307
|
+
className,
|
|
1308
|
+
...props
|
|
1309
|
+
}) {
|
|
1310
|
+
return /* @__PURE__ */ jsx7(
|
|
1311
|
+
SelectPrimitive.ScrollUpButton,
|
|
1312
|
+
{
|
|
1313
|
+
"data-slot": "select-scroll-up-button",
|
|
1314
|
+
className: cn(
|
|
1315
|
+
"flex cursor-default items-center justify-center py-1",
|
|
1316
|
+
className
|
|
1317
|
+
),
|
|
1318
|
+
...props,
|
|
1319
|
+
children: /* @__PURE__ */ jsx7(ChevronUpIcon, { className: "size-4" })
|
|
1320
|
+
}
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
function SelectScrollDownButton({
|
|
1324
|
+
className,
|
|
1325
|
+
...props
|
|
1326
|
+
}) {
|
|
1327
|
+
return /* @__PURE__ */ jsx7(
|
|
1328
|
+
SelectPrimitive.ScrollDownButton,
|
|
1329
|
+
{
|
|
1330
|
+
"data-slot": "select-scroll-down-button",
|
|
1331
|
+
className: cn(
|
|
1332
|
+
"flex cursor-default items-center justify-center py-1",
|
|
1333
|
+
className
|
|
1334
|
+
),
|
|
1335
|
+
...props,
|
|
1336
|
+
children: /* @__PURE__ */ jsx7(ChevronDownIcon, { className: "size-4" })
|
|
1337
|
+
}
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1179
1341
|
// src/client/components/ui/template-badge-input.tsx
|
|
1180
1342
|
import { useAtom as useAtom2 } from "jotai";
|
|
1181
1343
|
import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
|
|
@@ -1185,7 +1347,7 @@ import { useAtom } from "jotai";
|
|
|
1185
1347
|
import { Check } from "lucide-react";
|
|
1186
1348
|
import { useEffect as useEffect2, useRef, useState as useState2 } from "react";
|
|
1187
1349
|
import { createPortal } from "react-dom";
|
|
1188
|
-
import { Fragment, jsx as
|
|
1350
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1189
1351
|
var getNodeDisplayName = (node) => {
|
|
1190
1352
|
if (node.data.label) {
|
|
1191
1353
|
return node.data.label;
|
|
@@ -1404,7 +1566,7 @@ function TemplateAutocomplete({
|
|
|
1404
1566
|
left: Math.min(position.left, window.innerWidth - 320)
|
|
1405
1567
|
// Keep menu (320px wide) within viewport
|
|
1406
1568
|
};
|
|
1407
|
-
const menuContent = /* @__PURE__ */
|
|
1569
|
+
const menuContent = /* @__PURE__ */ jsx8(
|
|
1408
1570
|
"div",
|
|
1409
1571
|
{
|
|
1410
1572
|
className: "fixed z-[9999] w-80 rounded-lg border bg-popover p-1 text-popover-foreground shadow-md",
|
|
@@ -1413,7 +1575,7 @@ function TemplateAutocomplete({
|
|
|
1413
1575
|
top: `${adjustedPosition.top}px`,
|
|
1414
1576
|
left: `${adjustedPosition.left}px`
|
|
1415
1577
|
},
|
|
1416
|
-
children: /* @__PURE__ */
|
|
1578
|
+
children: /* @__PURE__ */ jsx8("div", { className: "max-h-60 overflow-y-auto", children: filteredOptions.map((option, index) => /* @__PURE__ */ jsxs3(
|
|
1417
1579
|
"div",
|
|
1418
1580
|
{
|
|
1419
1581
|
className: cn(
|
|
@@ -1423,17 +1585,17 @@ function TemplateAutocomplete({
|
|
|
1423
1585
|
onClick: () => onSelect(option.template),
|
|
1424
1586
|
onMouseEnter: () => setSelectedIndex(index),
|
|
1425
1587
|
children: [
|
|
1426
|
-
/* @__PURE__ */
|
|
1427
|
-
/* @__PURE__ */
|
|
1428
|
-
/* @__PURE__ */
|
|
1588
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex-1", children: [
|
|
1589
|
+
/* @__PURE__ */ jsx8("div", { className: "font-medium", children: option.type === "node" ? option.nodeName : /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
1590
|
+
/* @__PURE__ */ jsxs3("span", { className: "text-muted-foreground", children: [
|
|
1429
1591
|
option.nodeName,
|
|
1430
1592
|
"."
|
|
1431
1593
|
] }),
|
|
1432
1594
|
option.field
|
|
1433
1595
|
] }) }),
|
|
1434
|
-
option.description && /* @__PURE__ */
|
|
1596
|
+
option.description && /* @__PURE__ */ jsx8("div", { className: "text-muted-foreground text-xs", children: option.description })
|
|
1435
1597
|
] }),
|
|
1436
|
-
index === selectedIndex && /* @__PURE__ */
|
|
1598
|
+
index === selectedIndex && /* @__PURE__ */ jsx8(Check, { className: "h-4 w-4" })
|
|
1437
1599
|
]
|
|
1438
1600
|
},
|
|
1439
1601
|
`${option.nodeId}-${option.field || "root"}`
|
|
@@ -1444,7 +1606,7 @@ function TemplateAutocomplete({
|
|
|
1444
1606
|
}
|
|
1445
1607
|
|
|
1446
1608
|
// src/client/components/ui/template-badge-input.tsx
|
|
1447
|
-
import { Fragment as Fragment2, jsx as
|
|
1609
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1448
1610
|
function doesNodeExist(template, nodes) {
|
|
1449
1611
|
const match = template.match(/\{\{@([^:]+):([^}]+)\}\}/);
|
|
1450
1612
|
if (!match) return false;
|
|
@@ -1795,8 +1957,8 @@ function TemplateBadgeInput({
|
|
|
1795
1957
|
updateDisplay();
|
|
1796
1958
|
}
|
|
1797
1959
|
}, [internalValue, isFocused]);
|
|
1798
|
-
return /* @__PURE__ */
|
|
1799
|
-
/* @__PURE__ */
|
|
1960
|
+
return /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1961
|
+
/* @__PURE__ */ jsx9(
|
|
1800
1962
|
"div",
|
|
1801
1963
|
{
|
|
1802
1964
|
className: cn(
|
|
@@ -1804,7 +1966,7 @@ function TemplateBadgeInput({
|
|
|
1804
1966
|
disabled && "cursor-not-allowed opacity-50",
|
|
1805
1967
|
className
|
|
1806
1968
|
),
|
|
1807
|
-
children: /* @__PURE__ */
|
|
1969
|
+
children: /* @__PURE__ */ jsx9(
|
|
1808
1970
|
"div",
|
|
1809
1971
|
{
|
|
1810
1972
|
className: "w-full outline-none",
|
|
@@ -1821,7 +1983,7 @@ function TemplateBadgeInput({
|
|
|
1821
1983
|
)
|
|
1822
1984
|
}
|
|
1823
1985
|
),
|
|
1824
|
-
/* @__PURE__ */
|
|
1986
|
+
/* @__PURE__ */ jsx9(
|
|
1825
1987
|
TemplateAutocomplete,
|
|
1826
1988
|
{
|
|
1827
1989
|
currentNodeId: selectedNodeId || void 0,
|
|
@@ -1836,193 +1998,88 @@ function TemplateBadgeInput({
|
|
|
1836
1998
|
}
|
|
1837
1999
|
|
|
1838
2000
|
// src/plugins/condition/fields.tsx
|
|
1839
|
-
import { jsx as
|
|
2001
|
+
import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1840
2002
|
function ConditionFields({
|
|
1841
2003
|
config,
|
|
1842
2004
|
onUpdateConfig,
|
|
1843
2005
|
disabled
|
|
1844
2006
|
}) {
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
2007
|
+
const dataType = config?.dataType || "string";
|
|
2008
|
+
const operator = config?.operator || OPERATORS[dataType][0].value;
|
|
2009
|
+
const operatorDefs = OPERATORS[dataType];
|
|
2010
|
+
const selectedOp = operatorDefs.find((op) => op.value === operator);
|
|
2011
|
+
const isUnary = selectedOp?.unary ?? false;
|
|
2012
|
+
function handleDataTypeChange(value) {
|
|
2013
|
+
onUpdateConfig("dataType", value);
|
|
2014
|
+
const newOps = OPERATORS[value];
|
|
2015
|
+
if (newOps?.[0]) {
|
|
2016
|
+
onUpdateConfig("operator", newOps[0].value);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
return /* @__PURE__ */ jsxs5("div", { className: "space-y-4", children: [
|
|
2020
|
+
/* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
|
|
2021
|
+
/* @__PURE__ */ jsx10(Label, { htmlFor: "leftValue", children: "Value to Test" }),
|
|
2022
|
+
/* @__PURE__ */ jsx10(
|
|
2023
|
+
TemplateBadgeInput,
|
|
2024
|
+
{
|
|
2025
|
+
disabled,
|
|
2026
|
+
id: "leftValue",
|
|
2027
|
+
onChange: (value) => onUpdateConfig("leftValue", value),
|
|
2028
|
+
placeholder: "e.g., {{PreviousNode.status}}",
|
|
2029
|
+
value: config?.leftValue || ""
|
|
2030
|
+
}
|
|
2031
|
+
),
|
|
2032
|
+
/* @__PURE__ */ jsx10("p", { className: "text-muted-foreground text-xs", children: "Use @ to reference previous node outputs." })
|
|
2033
|
+
] }),
|
|
2034
|
+
/* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
|
|
2035
|
+
/* @__PURE__ */ jsx10(Label, { htmlFor: "dataType", children: "Data Type" }),
|
|
2036
|
+
/* @__PURE__ */ jsxs5(
|
|
2037
|
+
Select,
|
|
2038
|
+
{
|
|
2039
|
+
disabled,
|
|
2040
|
+
onValueChange: handleDataTypeChange,
|
|
2041
|
+
value: dataType,
|
|
2042
|
+
children: [
|
|
2043
|
+
/* @__PURE__ */ jsx10(SelectTrigger, { id: "dataType", children: /* @__PURE__ */ jsx10(SelectValue, { placeholder: "Select data type" }) }),
|
|
2044
|
+
/* @__PURE__ */ jsx10(SelectContent, { children: DATA_TYPE_OPTIONS.map((opt) => /* @__PURE__ */ jsx10(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })
|
|
2045
|
+
]
|
|
2046
|
+
}
|
|
2047
|
+
)
|
|
2048
|
+
] }),
|
|
2049
|
+
/* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
|
|
2050
|
+
/* @__PURE__ */ jsx10(Label, { htmlFor: "operator", children: "Operator" }),
|
|
2051
|
+
/* @__PURE__ */ jsxs5(
|
|
2052
|
+
Select,
|
|
2053
|
+
{
|
|
2054
|
+
disabled,
|
|
2055
|
+
onValueChange: (value) => onUpdateConfig("operator", value),
|
|
2056
|
+
value: operator,
|
|
2057
|
+
children: [
|
|
2058
|
+
/* @__PURE__ */ jsx10(SelectTrigger, { id: "operator", children: /* @__PURE__ */ jsx10(SelectValue, { placeholder: "Select operator" }) }),
|
|
2059
|
+
/* @__PURE__ */ jsx10(SelectContent, { children: operatorDefs.map((op) => /* @__PURE__ */ jsx10(SelectItem, { value: op.value, children: op.label }, op.value)) })
|
|
2060
|
+
]
|
|
2061
|
+
}
|
|
2062
|
+
)
|
|
2063
|
+
] }),
|
|
2064
|
+
!isUnary && /* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
|
|
2065
|
+
/* @__PURE__ */ jsx10(Label, { htmlFor: "rightValue", children: "Compare Value" }),
|
|
2066
|
+
/* @__PURE__ */ jsx10(
|
|
2067
|
+
TemplateBadgeInput,
|
|
2068
|
+
{
|
|
2069
|
+
disabled,
|
|
2070
|
+
id: "rightValue",
|
|
2071
|
+
onChange: (value) => onUpdateConfig("rightValue", value),
|
|
2072
|
+
placeholder: "e.g., 200 or {{OtherNode.field}}",
|
|
2073
|
+
value: config?.rightValue || ""
|
|
2074
|
+
}
|
|
2075
|
+
)
|
|
2076
|
+
] })
|
|
1858
2077
|
] });
|
|
1859
2078
|
}
|
|
1860
2079
|
|
|
1861
2080
|
// src/client/components/workflow/config/schema-builder.tsx
|
|
1862
2081
|
import { Plus, Trash2 } from "lucide-react";
|
|
1863
2082
|
import { nanoid } from "nanoid";
|
|
1864
|
-
|
|
1865
|
-
// src/client/components/ui/select.tsx
|
|
1866
|
-
import { Select as SelectPrimitive } from "radix-ui";
|
|
1867
|
-
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
1868
|
-
import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1869
|
-
function Select({
|
|
1870
|
-
...props
|
|
1871
|
-
}) {
|
|
1872
|
-
return /* @__PURE__ */ jsx10(SelectPrimitive.Root, { "data-slot": "select", ...props });
|
|
1873
|
-
}
|
|
1874
|
-
function SelectGroup({
|
|
1875
|
-
...props
|
|
1876
|
-
}) {
|
|
1877
|
-
return /* @__PURE__ */ jsx10(SelectPrimitive.Group, { "data-slot": "select-group", ...props });
|
|
1878
|
-
}
|
|
1879
|
-
function SelectValue({
|
|
1880
|
-
...props
|
|
1881
|
-
}) {
|
|
1882
|
-
return /* @__PURE__ */ jsx10(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
|
|
1883
|
-
}
|
|
1884
|
-
function SelectTrigger({
|
|
1885
|
-
className,
|
|
1886
|
-
size = "default",
|
|
1887
|
-
children,
|
|
1888
|
-
...props
|
|
1889
|
-
}) {
|
|
1890
|
-
return /* @__PURE__ */ jsxs5(
|
|
1891
|
-
SelectPrimitive.Trigger,
|
|
1892
|
-
{
|
|
1893
|
-
"data-slot": "select-trigger",
|
|
1894
|
-
"data-size": size,
|
|
1895
|
-
className: cn(
|
|
1896
|
-
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive bg-transparent hover:bg-accent/50 flex w-fit items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1897
|
-
className
|
|
1898
|
-
),
|
|
1899
|
-
...props,
|
|
1900
|
-
children: [
|
|
1901
|
-
children,
|
|
1902
|
-
/* @__PURE__ */ jsx10(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx10(ChevronDownIcon, { className: "size-4 opacity-50" }) })
|
|
1903
|
-
]
|
|
1904
|
-
}
|
|
1905
|
-
);
|
|
1906
|
-
}
|
|
1907
|
-
function SelectContent({
|
|
1908
|
-
className,
|
|
1909
|
-
children,
|
|
1910
|
-
position = "popper",
|
|
1911
|
-
align = "center",
|
|
1912
|
-
...props
|
|
1913
|
-
}) {
|
|
1914
|
-
return /* @__PURE__ */ jsx10(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs5(
|
|
1915
|
-
SelectPrimitive.Content,
|
|
1916
|
-
{
|
|
1917
|
-
"data-slot": "select-content",
|
|
1918
|
-
className: cn(
|
|
1919
|
-
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
|
1920
|
-
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
1921
|
-
className
|
|
1922
|
-
),
|
|
1923
|
-
position,
|
|
1924
|
-
align,
|
|
1925
|
-
...props,
|
|
1926
|
-
children: [
|
|
1927
|
-
/* @__PURE__ */ jsx10(SelectScrollUpButton, {}),
|
|
1928
|
-
/* @__PURE__ */ jsx10(
|
|
1929
|
-
SelectPrimitive.Viewport,
|
|
1930
|
-
{
|
|
1931
|
-
className: cn(
|
|
1932
|
-
"p-1",
|
|
1933
|
-
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
|
1934
|
-
),
|
|
1935
|
-
children
|
|
1936
|
-
}
|
|
1937
|
-
),
|
|
1938
|
-
/* @__PURE__ */ jsx10(SelectScrollDownButton, {})
|
|
1939
|
-
]
|
|
1940
|
-
}
|
|
1941
|
-
) });
|
|
1942
|
-
}
|
|
1943
|
-
function SelectLabel({
|
|
1944
|
-
className,
|
|
1945
|
-
...props
|
|
1946
|
-
}) {
|
|
1947
|
-
return /* @__PURE__ */ jsx10(
|
|
1948
|
-
SelectPrimitive.Label,
|
|
1949
|
-
{
|
|
1950
|
-
"data-slot": "select-label",
|
|
1951
|
-
className: cn("text-muted-foreground px-2 py-1.5 text-xs", className),
|
|
1952
|
-
...props
|
|
1953
|
-
}
|
|
1954
|
-
);
|
|
1955
|
-
}
|
|
1956
|
-
function SelectItem({
|
|
1957
|
-
className,
|
|
1958
|
-
children,
|
|
1959
|
-
...props
|
|
1960
|
-
}) {
|
|
1961
|
-
return /* @__PURE__ */ jsxs5(
|
|
1962
|
-
SelectPrimitive.Item,
|
|
1963
|
-
{
|
|
1964
|
-
"data-slot": "select-item",
|
|
1965
|
-
className: cn(
|
|
1966
|
-
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
1967
|
-
className
|
|
1968
|
-
),
|
|
1969
|
-
...props,
|
|
1970
|
-
children: [
|
|
1971
|
-
/* @__PURE__ */ jsx10("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx10(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx10(CheckIcon, { className: "size-4" }) }) }),
|
|
1972
|
-
/* @__PURE__ */ jsx10(SelectPrimitive.ItemText, { children })
|
|
1973
|
-
]
|
|
1974
|
-
}
|
|
1975
|
-
);
|
|
1976
|
-
}
|
|
1977
|
-
function SelectSeparator({
|
|
1978
|
-
className,
|
|
1979
|
-
...props
|
|
1980
|
-
}) {
|
|
1981
|
-
return /* @__PURE__ */ jsx10(
|
|
1982
|
-
SelectPrimitive.Separator,
|
|
1983
|
-
{
|
|
1984
|
-
"data-slot": "select-separator",
|
|
1985
|
-
className: cn("bg-border pointer-events-none -mx-1 my-1 h-px", className),
|
|
1986
|
-
...props
|
|
1987
|
-
}
|
|
1988
|
-
);
|
|
1989
|
-
}
|
|
1990
|
-
function SelectScrollUpButton({
|
|
1991
|
-
className,
|
|
1992
|
-
...props
|
|
1993
|
-
}) {
|
|
1994
|
-
return /* @__PURE__ */ jsx10(
|
|
1995
|
-
SelectPrimitive.ScrollUpButton,
|
|
1996
|
-
{
|
|
1997
|
-
"data-slot": "select-scroll-up-button",
|
|
1998
|
-
className: cn(
|
|
1999
|
-
"flex cursor-default items-center justify-center py-1",
|
|
2000
|
-
className
|
|
2001
|
-
),
|
|
2002
|
-
...props,
|
|
2003
|
-
children: /* @__PURE__ */ jsx10(ChevronUpIcon, { className: "size-4" })
|
|
2004
|
-
}
|
|
2005
|
-
);
|
|
2006
|
-
}
|
|
2007
|
-
function SelectScrollDownButton({
|
|
2008
|
-
className,
|
|
2009
|
-
...props
|
|
2010
|
-
}) {
|
|
2011
|
-
return /* @__PURE__ */ jsx10(
|
|
2012
|
-
SelectPrimitive.ScrollDownButton,
|
|
2013
|
-
{
|
|
2014
|
-
"data-slot": "select-scroll-down-button",
|
|
2015
|
-
className: cn(
|
|
2016
|
-
"flex cursor-default items-center justify-center py-1",
|
|
2017
|
-
className
|
|
2018
|
-
),
|
|
2019
|
-
...props,
|
|
2020
|
-
children: /* @__PURE__ */ jsx10(ChevronDownIcon, { className: "size-4" })
|
|
2021
|
-
}
|
|
2022
|
-
);
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
// src/client/components/workflow/config/schema-builder.tsx
|
|
2026
2083
|
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2027
2084
|
function SchemaBuilder({
|
|
2028
2085
|
schema,
|
package/dist/server/api/index.js
CHANGED
|
@@ -5,13 +5,14 @@ import {
|
|
|
5
5
|
conditionAction,
|
|
6
6
|
databaseQueryAction,
|
|
7
7
|
escapeForTemplateLiteral,
|
|
8
|
+
evaluateOperator,
|
|
8
9
|
findTriggerNodes,
|
|
9
10
|
generateWorkflowModule,
|
|
10
11
|
httpRequestAction,
|
|
11
12
|
sanitizeFunctionName,
|
|
12
13
|
sanitizeStepName,
|
|
13
14
|
sanitizeVarName
|
|
14
|
-
} from "../../chunk-
|
|
15
|
+
} from "../../chunk-EMCA7GLF.js";
|
|
15
16
|
import {
|
|
16
17
|
auth,
|
|
17
18
|
getAuthConfig
|
|
@@ -157,243 +158,6 @@ import { eq as eq2 } from "drizzle-orm";
|
|
|
157
158
|
import { readdir, readFile } from "fs/promises";
|
|
158
159
|
import { join } from "path";
|
|
159
160
|
|
|
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
161
|
// src/server/lib/steps/trigger.ts
|
|
398
162
|
import "server-only";
|
|
399
163
|
function executeTrigger(input) {
|
|
@@ -445,20 +209,18 @@ var SYSTEM_ACTIONS = {
|
|
|
445
209
|
stepFunction: "mergeStep"
|
|
446
210
|
}
|
|
447
211
|
};
|
|
448
|
-
function
|
|
449
|
-
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
value = void 0;
|
|
461
|
-
} else {
|
|
212
|
+
function resolveTemplateValue(value, outputs) {
|
|
213
|
+
if (value === void 0 || value === "") return void 0;
|
|
214
|
+
const templatePattern = /\{\{@([^:]+):([^}]+)\}\}/g;
|
|
215
|
+
const matches = [...value.matchAll(templatePattern)];
|
|
216
|
+
if (matches.length === 0) return value;
|
|
217
|
+
function extractValue(nodeId, rest) {
|
|
218
|
+
const sanitizedNodeId = nodeId.replace(/[^a-zA-Z0-9]/g, "_");
|
|
219
|
+
const output = outputs[sanitizedNodeId];
|
|
220
|
+
if (!output) return void 0;
|
|
221
|
+
const dotIndex = rest.indexOf(".");
|
|
222
|
+
if (dotIndex === -1) return output.data;
|
|
223
|
+
if (output.data === null || output.data === void 0) return void 0;
|
|
462
224
|
const fieldPath = rest.substring(dotIndex + 1);
|
|
463
225
|
const fields = fieldPath.split(".");
|
|
464
226
|
let current = output.data;
|
|
@@ -470,78 +232,37 @@ function replaceTemplateVariable(match, nodeId, rest, outputs, evalContext, varC
|
|
|
470
232
|
if (current && typeof current === "object") {
|
|
471
233
|
current = current[field];
|
|
472
234
|
} else {
|
|
473
|
-
|
|
474
|
-
value = void 0;
|
|
475
|
-
break;
|
|
235
|
+
return void 0;
|
|
476
236
|
}
|
|
477
237
|
}
|
|
478
|
-
|
|
479
|
-
value = current;
|
|
480
|
-
}
|
|
238
|
+
return current;
|
|
481
239
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
evalContext[varName] = value;
|
|
485
|
-
return varName;
|
|
486
|
-
}
|
|
487
|
-
function evaluateConditionExpression(conditionExpression, outputs) {
|
|
488
|
-
console.log("[Condition] Original expression:", conditionExpression);
|
|
489
|
-
if (typeof conditionExpression === "boolean") {
|
|
490
|
-
return { result: conditionExpression, resolvedValues: {} };
|
|
240
|
+
if (matches.length === 1 && matches[0][0] === value) {
|
|
241
|
+
return extractValue(matches[0][1], matches[0][2]);
|
|
491
242
|
}
|
|
492
|
-
|
|
493
|
-
const
|
|
494
|
-
if (
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
evalContext,
|
|
514
|
-
varCounter
|
|
515
|
-
);
|
|
516
|
-
resolvedValues[rest] = evalContext[varName];
|
|
517
|
-
return varName;
|
|
518
|
-
}
|
|
519
|
-
);
|
|
520
|
-
const validation = validateConditionExpression(transformedExpression);
|
|
521
|
-
if (!validation.valid) {
|
|
522
|
-
console.error("[Condition] Validation failed:", validation.error);
|
|
523
|
-
console.error("[Condition] Original expression:", conditionExpression);
|
|
524
|
-
console.error(
|
|
525
|
-
"[Condition] Transformed expression:",
|
|
526
|
-
transformedExpression
|
|
527
|
-
);
|
|
528
|
-
return { result: false, resolvedValues };
|
|
529
|
-
}
|
|
530
|
-
const varNames = Object.keys(evalContext);
|
|
531
|
-
const varValues = Object.values(evalContext);
|
|
532
|
-
const evalFunc = new Function(
|
|
533
|
-
...varNames,
|
|
534
|
-
`return (${transformedExpression});`
|
|
535
|
-
);
|
|
536
|
-
const result = evalFunc(...varValues);
|
|
537
|
-
return { result: Boolean(result), resolvedValues };
|
|
538
|
-
} catch (error) {
|
|
539
|
-
console.error("[Condition] Failed to evaluate condition:", error);
|
|
540
|
-
console.error("[Condition] Expression was:", conditionExpression);
|
|
541
|
-
return { result: false, resolvedValues: {} };
|
|
243
|
+
return value.replace(templatePattern, (match, nodeId, rest) => {
|
|
244
|
+
const val = extractValue(nodeId, rest);
|
|
245
|
+
if (val === null || val === void 0) return "";
|
|
246
|
+
if (typeof val === "object") return JSON.stringify(val);
|
|
247
|
+
return String(val);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
function evaluateStructuredCondition(config, outputs) {
|
|
251
|
+
const dataType = config.dataType || "string";
|
|
252
|
+
const operator = config.operator;
|
|
253
|
+
const leftRaw = config.leftValue;
|
|
254
|
+
const rightRaw = config.rightValue;
|
|
255
|
+
const leftResolved = resolveTemplateValue(leftRaw, outputs);
|
|
256
|
+
const rightResolved = resolveTemplateValue(rightRaw, outputs);
|
|
257
|
+
console.log("[Condition] Evaluating:", { dataType, operator, leftResolved, rightResolved });
|
|
258
|
+
const result = evaluateOperator(dataType, operator, leftResolved, rightResolved);
|
|
259
|
+
return {
|
|
260
|
+
result,
|
|
261
|
+
resolvedValues: {
|
|
262
|
+
leftValue: leftResolved,
|
|
263
|
+
rightValue: rightResolved
|
|
542
264
|
}
|
|
543
|
-
}
|
|
544
|
-
return { result: Boolean(conditionExpression), resolvedValues: {} };
|
|
265
|
+
};
|
|
545
266
|
}
|
|
546
267
|
async function executeActionStep(input) {
|
|
547
268
|
const { actionType, config, outputs, context } = input;
|
|
@@ -552,14 +273,14 @@ async function executeActionStep(input) {
|
|
|
552
273
|
if (actionType === "Condition") {
|
|
553
274
|
const systemAction2 = SYSTEM_ACTIONS.Condition;
|
|
554
275
|
const module = await systemAction2.importer();
|
|
555
|
-
const
|
|
556
|
-
const { result: evaluatedCondition, resolvedValues } = evaluateConditionExpression(originalExpression, outputs);
|
|
276
|
+
const { result: evaluatedCondition, resolvedValues } = evaluateStructuredCondition(config, outputs);
|
|
557
277
|
console.log("[Condition] Final result:", evaluatedCondition);
|
|
558
278
|
return await module[systemAction2.stepFunction]({
|
|
559
279
|
condition: evaluatedCondition,
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
280
|
+
dataType: config.dataType,
|
|
281
|
+
operator: config.operator,
|
|
282
|
+
leftValue: resolvedValues.leftValue,
|
|
283
|
+
rightValue: resolvedValues.rightValue,
|
|
563
284
|
_context: context
|
|
564
285
|
});
|
|
565
286
|
}
|
|
@@ -776,16 +497,7 @@ async function executeWorkflow(input) {
|
|
|
776
497
|
results[nodeId] = result;
|
|
777
498
|
return;
|
|
778
499
|
}
|
|
779
|
-
const
|
|
780
|
-
const originalCondition = config.condition;
|
|
781
|
-
configWithoutCondition.condition = void 0;
|
|
782
|
-
const processedConfig = processTemplates(
|
|
783
|
-
configWithoutCondition,
|
|
784
|
-
outputs
|
|
785
|
-
);
|
|
786
|
-
if (originalCondition !== void 0) {
|
|
787
|
-
processedConfig.condition = originalCondition;
|
|
788
|
-
}
|
|
500
|
+
const processedConfig = processTemplates(config, outputs);
|
|
789
501
|
const stepContext = {
|
|
790
502
|
executionId,
|
|
791
503
|
nodeId: node.id,
|