@burdenoff/fluidgrids-fe-sdk 2026.1031.7 → 2026.1101.2
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/{ExecutionDetailPage-4LPCNWM3.js → ExecutionDetailPage-Q77U6YJO.js} +11 -8
- package/dist/{ExecutionsListPage-HNPUKYD7.js → ExecutionsListPage-X4H4FVLN.js} +6 -5
- package/dist/{WorkflowBuilder-QFSCYCUC.js → WorkflowBuilder-RJPRUMQR.js} +11 -8
- package/dist/{WorkflowsList-RV4BRJO7.js → WorkflowsList-R2JJK7BL.js} +7 -6
- package/dist/agentTools-MISTH7JI.js +349 -0
- package/dist/{chunk-77HIU3CX.js → chunk-26HGYPBC.js} +1 -1
- package/dist/{chunk-76UUEMTN.js → chunk-37VUN74D.js} +4 -4
- package/dist/{chunk-QPZCJKII.js → chunk-3ERT6FQK.js} +3 -3
- package/dist/chunk-472HJ3S3.js +3929 -0
- package/dist/{chunk-WNZV4SLU.js → chunk-4QHCXTHW.js} +4 -4
- package/dist/chunk-AASHQI77.js +126 -0
- package/dist/chunk-CGXQVZGY.js +260 -0
- package/dist/chunk-CRDG2SQQ.js +185 -0
- package/dist/chunk-DQ2ZCXYX.js +21875 -0
- package/dist/chunk-F7DD7BOB.js +1164 -0
- package/dist/chunk-GTEBQQ7J.js +24 -0
- package/dist/{chunk-ALFMJSD6.js → chunk-H7XPXONT.js} +1 -1
- package/dist/{chunk-UO5DUKYN.js → chunk-HKKSXOFO.js} +8 -7
- package/dist/chunk-IA2ZCHNQ.js +1336 -0
- package/dist/{chunk-3M36XWAH.js → chunk-IGRIN7NL.js} +8 -7
- package/dist/chunk-JDLQ6QIB.js +283 -0
- package/dist/{chunk-AD7BICOT.js → chunk-JKBCLBHY.js} +1 -1
- package/dist/{chunk-6JX5UPDW.js → chunk-JNZCXFLD.js} +1 -1
- package/dist/{chunk-SEFOT2LD.js → chunk-LWLHPVIA.js} +1 -1
- package/dist/{chunk-JWROEOG5.js → chunk-LYTR6IQO.js} +1 -38
- package/dist/chunk-MR2B67NW.js +338 -0
- package/dist/{chunk-RIHYPPMQ.js → chunk-NLYQ3XDE.js} +1 -1
- package/dist/{chunk-6ILIBJFH.js → chunk-NZJWKVER.js} +7904 -7943
- package/dist/chunk-OCJTTHTS.js +46 -0
- package/dist/chunk-OSQ3OCR3.js +1193 -0
- package/dist/{chunk-VJL3DFJT.js → chunk-REO4S5MY.js} +2 -2
- package/dist/{chunk-XKQMCPSB.js → chunk-T2UUYYVJ.js} +4 -4
- package/dist/{chunk-BXR6BWXM.js → chunk-W7MTSATB.js} +5 -5
- package/dist/{chunk-A47PSD7A.js → chunk-WF6GMBR4.js} +7 -4
- package/dist/chunk-WYCB6OQL.js +19709 -0
- package/dist/{chunk-YOLPHVOE.js → chunk-YKFR7BZJ.js} +1 -1
- package/dist/{chunk-26NENGXA.js → chunk-YSXNJGCY.js} +4 -4
- package/dist/code-editor-field-JKCUR6BI.js +3 -0
- package/dist/executions.js +12 -9
- package/dist/history.js +4 -3
- package/dist/index.js +41 -32
- package/dist/node-detail-view-PCYFQX62.js +2034 -0
- package/dist/observability.js +5 -4
- package/dist/quick-config-panel-BRZZHOSI.js +402 -0
- package/dist/settings.js +6 -5
- package/dist/templates.js +10 -7
- package/dist/workflow-builder.js +12 -9
- package/dist/workflows-list.js +7 -6
- package/package.json +4 -1
- package/dist/chunk-NMEI7TQW.js +0 -2628
- package/dist/chunk-S7XV65DB.js +0 -50350
|
@@ -0,0 +1,1336 @@
|
|
|
1
|
+
import { structuredParameterType, parseParameterValue, liveInternalOperations, resolveNodeOperation, readDisplayOptions, getNodeDetail, useFlowStore, useNodeRegistryRevision, fieldsFromIO, pathToExpression, NODE_CONFIGS, resolveUiSpec, fieldsFromValue, declaredOutputFields, subscribeWorkflowSaved } from './chunk-NZJWKVER.js';
|
|
2
|
+
import { useBuilderReadOnly } from './chunk-OCJTTHTS.js';
|
|
3
|
+
import { EventRegistrationsDocument } from './chunk-IHEV5HTD.js';
|
|
4
|
+
import { useTr } from './chunk-UULVIT25.js';
|
|
5
|
+
import { getFluidGridsClientSafe } from './chunk-NTWG3CJX.js';
|
|
6
|
+
import { useMemo, useState, useEffect, useRef, useCallback } from 'react';
|
|
7
|
+
import { Icon } from '@iconify/react';
|
|
8
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
9
|
+
import { cn } from '@burdenoff/fe-libs/utils';
|
|
10
|
+
|
|
11
|
+
var FIELD_PATH_MIME = "application/x-fg-field-path";
|
|
12
|
+
var TYPE_ICON = {
|
|
13
|
+
string: "mdi:format-text",
|
|
14
|
+
number: "mdi:numeric",
|
|
15
|
+
integer: "mdi:numeric",
|
|
16
|
+
boolean: "mdi:toggle-switch-outline",
|
|
17
|
+
array: "mdi:code-brackets",
|
|
18
|
+
object: "mdi:code-braces",
|
|
19
|
+
any: "mdi:help-circle-outline"
|
|
20
|
+
};
|
|
21
|
+
var DEFAULT_EXPAND_DEPTH = 3;
|
|
22
|
+
function FieldRow({ field, depth, draggable }) {
|
|
23
|
+
const [open, setOpen] = useState(depth < DEFAULT_EXPAND_DEPTH);
|
|
24
|
+
const hasChildren = !!field.children && field.children.length > 0;
|
|
25
|
+
const handleDragStart = (e) => {
|
|
26
|
+
e.dataTransfer.setData(FIELD_PATH_MIME, field.path);
|
|
27
|
+
e.dataTransfer.setData("text/plain", pathToExpression(field.path));
|
|
28
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
29
|
+
};
|
|
30
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
31
|
+
/* @__PURE__ */ jsxs(
|
|
32
|
+
"div",
|
|
33
|
+
{
|
|
34
|
+
className: `group flex items-center gap-1.5 rounded-md py-1 pr-2 text-xs hover:bg-muted ${draggable ? "cursor-grab active:cursor-grabbing" : ""}`,
|
|
35
|
+
style: { paddingLeft: `${depth * 14 + 6}px` },
|
|
36
|
+
draggable,
|
|
37
|
+
onDragStart: draggable ? handleDragStart : void 0,
|
|
38
|
+
"data-field-path": field.path,
|
|
39
|
+
"data-schema-variant": field.schemaVariant,
|
|
40
|
+
title: field.description || field.path,
|
|
41
|
+
children: [
|
|
42
|
+
hasChildren ? /* @__PURE__ */ jsx(
|
|
43
|
+
"button",
|
|
44
|
+
{
|
|
45
|
+
type: "button",
|
|
46
|
+
onClick: () => setOpen((v) => !v),
|
|
47
|
+
className: "flex size-4 items-center justify-center text-muted-foreground hover:text-foreground",
|
|
48
|
+
children: /* @__PURE__ */ jsx(
|
|
49
|
+
Icon,
|
|
50
|
+
{
|
|
51
|
+
icon: open ? "mdi:chevron-down" : "mdi:chevron-right",
|
|
52
|
+
className: "size-3.5"
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
) : /* @__PURE__ */ jsx("span", { className: "inline-block size-4" }),
|
|
57
|
+
/* @__PURE__ */ jsx(
|
|
58
|
+
Icon,
|
|
59
|
+
{
|
|
60
|
+
icon: TYPE_ICON[field.type] ?? TYPE_ICON.any,
|
|
61
|
+
className: "size-3.5 shrink-0 text-primary/70"
|
|
62
|
+
}
|
|
63
|
+
),
|
|
64
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: field.key }),
|
|
65
|
+
/* @__PURE__ */ jsxs("span", { className: "ml-auto shrink-0 text-[10px] lowercase tracking-wide text-muted-foreground", children: [
|
|
66
|
+
field.type,
|
|
67
|
+
field.required ? " *" : ""
|
|
68
|
+
] })
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
),
|
|
72
|
+
hasChildren && open && /* @__PURE__ */ jsx("div", { children: field.children.map((child) => /* @__PURE__ */ jsx(
|
|
73
|
+
FieldRow,
|
|
74
|
+
{
|
|
75
|
+
field: child,
|
|
76
|
+
depth: depth + 1,
|
|
77
|
+
draggable
|
|
78
|
+
},
|
|
79
|
+
child.schemaVariant ?? child.path
|
|
80
|
+
)) })
|
|
81
|
+
] });
|
|
82
|
+
}
|
|
83
|
+
function FieldTree({
|
|
84
|
+
fields,
|
|
85
|
+
draggable = false,
|
|
86
|
+
emptyMessage = "No fields available."
|
|
87
|
+
}) {
|
|
88
|
+
if (fields.length === 0) {
|
|
89
|
+
return /* @__PURE__ */ jsx("div", { className: "px-2 py-4 text-xs text-card-foreground/50", children: emptyMessage });
|
|
90
|
+
}
|
|
91
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5", children: fields.map((field) => /* @__PURE__ */ jsx(
|
|
92
|
+
FieldRow,
|
|
93
|
+
{
|
|
94
|
+
field,
|
|
95
|
+
depth: 0,
|
|
96
|
+
draggable
|
|
97
|
+
},
|
|
98
|
+
field.schemaVariant ?? field.path
|
|
99
|
+
)) });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/builder/lib/mapping-input.ts
|
|
103
|
+
function latestNodeLog(logs, nodeId, runId) {
|
|
104
|
+
return nodeId && runId ? logs.filter((log) => log.nodeId === nodeId && log.runId === runId).at(-1) : void 0;
|
|
105
|
+
}
|
|
106
|
+
function resolveMappingInput({
|
|
107
|
+
source,
|
|
108
|
+
operation,
|
|
109
|
+
runId,
|
|
110
|
+
logs
|
|
111
|
+
}) {
|
|
112
|
+
const sourceLog = latestNodeLog(logs, source?.id, runId);
|
|
113
|
+
const data = sourceLog?.output ?? void 0;
|
|
114
|
+
return {
|
|
115
|
+
data,
|
|
116
|
+
fields: data !== void 0 ? fieldsFromValue(data) : source ? declaredOutputFields(source.type, operation) : []
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/builder/components/ndv/field-label.ts
|
|
121
|
+
var UPPERCASE_WORDS = /* @__PURE__ */ new Set([
|
|
122
|
+
"api",
|
|
123
|
+
"csv",
|
|
124
|
+
"html",
|
|
125
|
+
"http",
|
|
126
|
+
"https",
|
|
127
|
+
"id",
|
|
128
|
+
"ip",
|
|
129
|
+
"json",
|
|
130
|
+
"jwt",
|
|
131
|
+
"sql",
|
|
132
|
+
"ssl",
|
|
133
|
+
"url",
|
|
134
|
+
"uri",
|
|
135
|
+
"utc",
|
|
136
|
+
"xml"
|
|
137
|
+
]);
|
|
138
|
+
var PLURAL_ACRONYMS = /* @__PURE__ */ new Map([
|
|
139
|
+
["apis", "APIs"],
|
|
140
|
+
["ids", "IDs"],
|
|
141
|
+
["ips", "IPs"],
|
|
142
|
+
["uris", "URIs"],
|
|
143
|
+
["urls", "URLs"]
|
|
144
|
+
]);
|
|
145
|
+
function titleWord(word) {
|
|
146
|
+
const lower = word.toLowerCase();
|
|
147
|
+
const plural = PLURAL_ACRONYMS.get(lower);
|
|
148
|
+
if (plural) return plural;
|
|
149
|
+
if (UPPERCASE_WORDS.has(lower)) return lower.toUpperCase();
|
|
150
|
+
return lower.charAt(0).toUpperCase() + lower.slice(1);
|
|
151
|
+
}
|
|
152
|
+
function humanizeFieldName(name) {
|
|
153
|
+
const words = name.split(".").pop().replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean);
|
|
154
|
+
return words.length > 0 ? words.map(titleWord).join(" ") : name;
|
|
155
|
+
}
|
|
156
|
+
function fieldLabel(name, spec) {
|
|
157
|
+
for (const key of ["label", "displayName", "title"]) {
|
|
158
|
+
const value = spec?.[key];
|
|
159
|
+
if (typeof value === "string" && value.trim() !== "") return value.trim();
|
|
160
|
+
}
|
|
161
|
+
return humanizeFieldName(name);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/builder/lib/mapping-values.ts
|
|
165
|
+
function isMappingValue(value) {
|
|
166
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
170
|
+
if (Array.isArray(value)) return value.every(isMappingValue);
|
|
171
|
+
return typeof value === "object" && Object.values(value).every(isMappingValue);
|
|
172
|
+
}
|
|
173
|
+
function parseMappingValue(text) {
|
|
174
|
+
try {
|
|
175
|
+
const value = JSON.parse(text);
|
|
176
|
+
if (isMappingValue(value)) return { valid: true, value };
|
|
177
|
+
} catch {
|
|
178
|
+
return {
|
|
179
|
+
valid: false,
|
|
180
|
+
error: "Enter valid JSON before applying the mapping."
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return { valid: false, error: "JSON mapping numbers must be finite." };
|
|
184
|
+
}
|
|
185
|
+
function updateMappingDefinition(definition, field, value) {
|
|
186
|
+
if (value === void 0 || typeof value === "string" && !value.trim()) {
|
|
187
|
+
const next = { ...definition };
|
|
188
|
+
delete next[field];
|
|
189
|
+
return next;
|
|
190
|
+
}
|
|
191
|
+
return { ...definition, [field]: value };
|
|
192
|
+
}
|
|
193
|
+
function emptyGroup() {
|
|
194
|
+
return { type: "group", combinator: "AND", children: [] };
|
|
195
|
+
}
|
|
196
|
+
function emptyRule(field = "") {
|
|
197
|
+
return { type: "rule", field, operator: "==", value: "" };
|
|
198
|
+
}
|
|
199
|
+
var OPERATORS = {
|
|
200
|
+
string: [
|
|
201
|
+
{ value: "==", label: "equals", needsValue: true },
|
|
202
|
+
{ value: "!=", label: "not equals", needsValue: true },
|
|
203
|
+
{ value: "contains", label: "contains", needsValue: true },
|
|
204
|
+
{ value: "notContains", label: "does not contain", needsValue: true },
|
|
205
|
+
{ value: "startsWith", label: "starts with", needsValue: true },
|
|
206
|
+
{ value: "endsWith", label: "ends with", needsValue: true },
|
|
207
|
+
{ value: "regex", label: "matches regex", needsValue: true },
|
|
208
|
+
{ value: "isEmpty", label: "is empty", needsValue: false },
|
|
209
|
+
{ value: "isNotEmpty", label: "is not empty", needsValue: false },
|
|
210
|
+
{ value: "isNull", label: "is null", needsValue: false },
|
|
211
|
+
{ value: "isNotNull", label: "is not null", needsValue: false }
|
|
212
|
+
],
|
|
213
|
+
number: [
|
|
214
|
+
{ value: "==", label: "equals", needsValue: true },
|
|
215
|
+
{ value: "!=", label: "not equals", needsValue: true },
|
|
216
|
+
{ value: ">", label: "greater than", needsValue: true },
|
|
217
|
+
{ value: ">=", label: "greater or equal", needsValue: true },
|
|
218
|
+
{ value: "<", label: "less than", needsValue: true },
|
|
219
|
+
{ value: "<=", label: "less or equal", needsValue: true },
|
|
220
|
+
{ value: "isNull", label: "is null", needsValue: false },
|
|
221
|
+
{ value: "isNotNull", label: "is not null", needsValue: false }
|
|
222
|
+
],
|
|
223
|
+
boolean: [
|
|
224
|
+
{ value: "isTrue", label: "is true", needsValue: false },
|
|
225
|
+
{ value: "isFalse", label: "is false", needsValue: false },
|
|
226
|
+
{ value: "isNull", label: "is null", needsValue: false },
|
|
227
|
+
{ value: "isNotNull", label: "is not null", needsValue: false }
|
|
228
|
+
],
|
|
229
|
+
array: [
|
|
230
|
+
{ value: "contains", label: "contains", needsValue: true },
|
|
231
|
+
{ value: "notContains", label: "does not contain", needsValue: true },
|
|
232
|
+
{ value: "isEmpty", label: "is empty", needsValue: false },
|
|
233
|
+
{ value: "isNotEmpty", label: "is not empty", needsValue: false },
|
|
234
|
+
{ value: "isNull", label: "is null", needsValue: false },
|
|
235
|
+
{ value: "isNotNull", label: "is not null", needsValue: false }
|
|
236
|
+
],
|
|
237
|
+
object: [
|
|
238
|
+
{ value: "hasProperty", label: "has property", needsValue: true },
|
|
239
|
+
{ value: "isEmpty", label: "is empty", needsValue: false },
|
|
240
|
+
{ value: "isNotEmpty", label: "is not empty", needsValue: false },
|
|
241
|
+
{ value: "isNull", label: "is null", needsValue: false },
|
|
242
|
+
{ value: "isNotNull", label: "is not null", needsValue: false }
|
|
243
|
+
],
|
|
244
|
+
any: [
|
|
245
|
+
{ value: "==", label: "equals", needsValue: true },
|
|
246
|
+
{ value: "!=", label: "not equals", needsValue: true },
|
|
247
|
+
{ value: ">", label: "greater than", needsValue: true },
|
|
248
|
+
{ value: "<", label: "less than", needsValue: true },
|
|
249
|
+
{ value: "contains", label: "contains", needsValue: true },
|
|
250
|
+
{ value: "isEmpty", label: "is empty", needsValue: false },
|
|
251
|
+
{ value: "isNotEmpty", label: "is not empty", needsValue: false },
|
|
252
|
+
{ value: "isNull", label: "is null", needsValue: false },
|
|
253
|
+
{ value: "isNotNull", label: "is not null", needsValue: false }
|
|
254
|
+
]
|
|
255
|
+
};
|
|
256
|
+
function operatorsFor(type) {
|
|
257
|
+
return OPERATORS[type ?? "any"] ?? OPERATORS.any;
|
|
258
|
+
}
|
|
259
|
+
function flattenFields(nodes) {
|
|
260
|
+
const out = [];
|
|
261
|
+
const walk = (list) => {
|
|
262
|
+
for (const node of list) {
|
|
263
|
+
if (node.path) out.push({ path: node.path, type: node.type ?? "any" });
|
|
264
|
+
if (Array.isArray(node.children)) walk(node.children);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
walk(nodes);
|
|
268
|
+
return out.sort((a, b) => a.path.localeCompare(b.path));
|
|
269
|
+
}
|
|
270
|
+
var rowInput = "rounded-md border border-border-default bg-bg-surface px-2 py-1 text-xs text-text-primary scheme-normal dark:scheme-dark outline-none focus:border-action-primary-border [&_option]:bg-bg-surface [&_option]:text-text-primary";
|
|
271
|
+
function RuleRow({
|
|
272
|
+
rule,
|
|
273
|
+
fields,
|
|
274
|
+
onChange,
|
|
275
|
+
onRemove
|
|
276
|
+
}) {
|
|
277
|
+
const tr = useTr();
|
|
278
|
+
const fieldType = fields.find((f) => f.path === rule.field)?.type ?? "any";
|
|
279
|
+
const ops = operatorsFor(fieldType);
|
|
280
|
+
const currentOp = ops.find((o) => o.value === rule.operator) ?? ops[0];
|
|
281
|
+
const knownField = fields.some((f) => f.path === rule.field);
|
|
282
|
+
return /* @__PURE__ */ jsxs(
|
|
283
|
+
"div",
|
|
284
|
+
{
|
|
285
|
+
className: "flex flex-wrap items-center gap-1.5",
|
|
286
|
+
"data-testid": "condition-rule",
|
|
287
|
+
children: [
|
|
288
|
+
fields.length > 0 && (knownField || rule.field === "") ? /* @__PURE__ */ jsxs(
|
|
289
|
+
"select",
|
|
290
|
+
{
|
|
291
|
+
value: rule.field,
|
|
292
|
+
onChange: (e) => {
|
|
293
|
+
const nextField = e.target.value;
|
|
294
|
+
const nextType = fields.find((f) => f.path === nextField)?.type ?? "any";
|
|
295
|
+
const nextOps = operatorsFor(nextType);
|
|
296
|
+
const operator = nextOps.some((o) => o.value === rule.operator) ? rule.operator : nextOps[0].value;
|
|
297
|
+
onChange({ ...rule, field: nextField, operator });
|
|
298
|
+
},
|
|
299
|
+
className: cn(rowInput, "max-w-[180px] flex-1"),
|
|
300
|
+
"data-testid": "condition-field",
|
|
301
|
+
children: [
|
|
302
|
+
/* @__PURE__ */ jsx("option", { value: "", children: tr(
|
|
303
|
+
"fluidgrids.builder.conditionBuilder.selectField",
|
|
304
|
+
"Select field\u2026"
|
|
305
|
+
) }),
|
|
306
|
+
fields.map((f) => /* @__PURE__ */ jsxs("option", { value: f.path, children: [
|
|
307
|
+
f.path,
|
|
308
|
+
" (",
|
|
309
|
+
f.type,
|
|
310
|
+
")"
|
|
311
|
+
] }, f.path)),
|
|
312
|
+
/* @__PURE__ */ jsx("option", { value: "__custom__", children: tr(
|
|
313
|
+
"fluidgrids.builder.conditionBuilder.customPath",
|
|
314
|
+
"Custom path\u2026"
|
|
315
|
+
) })
|
|
316
|
+
]
|
|
317
|
+
}
|
|
318
|
+
) : /* @__PURE__ */ jsx(
|
|
319
|
+
"input",
|
|
320
|
+
{
|
|
321
|
+
type: "text",
|
|
322
|
+
value: rule.field === "__custom__" ? "" : rule.field,
|
|
323
|
+
placeholder: "field.path",
|
|
324
|
+
onChange: (e) => onChange({ ...rule, field: e.target.value }),
|
|
325
|
+
className: cn(rowInput, "max-w-[180px] flex-1 font-mono"),
|
|
326
|
+
"data-testid": "condition-field"
|
|
327
|
+
}
|
|
328
|
+
),
|
|
329
|
+
/* @__PURE__ */ jsx(
|
|
330
|
+
"select",
|
|
331
|
+
{
|
|
332
|
+
value: currentOp.value,
|
|
333
|
+
onChange: (e) => onChange({ ...rule, operator: e.target.value }),
|
|
334
|
+
className: rowInput,
|
|
335
|
+
"data-testid": "condition-operator",
|
|
336
|
+
children: ops.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
|
|
337
|
+
}
|
|
338
|
+
),
|
|
339
|
+
currentOp.needsValue && /* @__PURE__ */ jsx(
|
|
340
|
+
"input",
|
|
341
|
+
{
|
|
342
|
+
type: fieldType === "number" ? "number" : "text",
|
|
343
|
+
value: rule.value === null || rule.value === void 0 ? "" : String(rule.value),
|
|
344
|
+
placeholder: tr(
|
|
345
|
+
"fluidgrids.builder.conditionBuilder.valuePlaceholder",
|
|
346
|
+
"value"
|
|
347
|
+
),
|
|
348
|
+
onChange: (e) => {
|
|
349
|
+
const raw = e.target.value;
|
|
350
|
+
const value = fieldType === "number" && raw.trim() !== "" && !Number.isNaN(Number(raw)) ? Number(raw) : raw;
|
|
351
|
+
onChange({ ...rule, value });
|
|
352
|
+
},
|
|
353
|
+
className: cn(rowInput, "min-w-[90px] flex-1"),
|
|
354
|
+
"data-testid": "condition-value"
|
|
355
|
+
}
|
|
356
|
+
),
|
|
357
|
+
/* @__PURE__ */ jsx(
|
|
358
|
+
"button",
|
|
359
|
+
{
|
|
360
|
+
type: "button",
|
|
361
|
+
onClick: onRemove,
|
|
362
|
+
title: tr(
|
|
363
|
+
"fluidgrids.builder.conditionBuilder.removeCondition",
|
|
364
|
+
"Remove condition"
|
|
365
|
+
),
|
|
366
|
+
className: "rounded p-1 text-muted-foreground hover:text-[var(--color-status-error-text)]",
|
|
367
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "mynaui:trash", className: "size-3.5" })
|
|
368
|
+
}
|
|
369
|
+
)
|
|
370
|
+
]
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
function GroupEditor({
|
|
375
|
+
group,
|
|
376
|
+
fields,
|
|
377
|
+
depth,
|
|
378
|
+
onChange,
|
|
379
|
+
onRemove
|
|
380
|
+
}) {
|
|
381
|
+
const tr = useTr();
|
|
382
|
+
const setChild = (index, child) => {
|
|
383
|
+
const children = group.children.slice();
|
|
384
|
+
children[index] = child;
|
|
385
|
+
onChange({ ...group, children });
|
|
386
|
+
};
|
|
387
|
+
const removeChild = (index) => {
|
|
388
|
+
onChange({
|
|
389
|
+
...group,
|
|
390
|
+
children: group.children.filter((_, i) => i !== index)
|
|
391
|
+
});
|
|
392
|
+
};
|
|
393
|
+
return /* @__PURE__ */ jsxs(
|
|
394
|
+
"div",
|
|
395
|
+
{
|
|
396
|
+
className: cn(
|
|
397
|
+
"flex flex-col gap-1.5 rounded-md",
|
|
398
|
+
depth > 0 && "border border-border/70 bg-muted/20 p-2"
|
|
399
|
+
),
|
|
400
|
+
"data-testid": `condition-group-depth-${depth}`,
|
|
401
|
+
children: [
|
|
402
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
403
|
+
/* @__PURE__ */ jsx("div", { className: "flex overflow-hidden rounded-md border border-input text-[10px] font-semibold", children: ["AND", "OR"].map((c) => /* @__PURE__ */ jsx(
|
|
404
|
+
"button",
|
|
405
|
+
{
|
|
406
|
+
type: "button",
|
|
407
|
+
onClick: () => onChange({ ...group, combinator: c }),
|
|
408
|
+
className: cn(
|
|
409
|
+
"px-2 py-0.5",
|
|
410
|
+
group.combinator === c ? "bg-primary text-primary-foreground" : "bg-background text-muted-foreground hover:text-foreground"
|
|
411
|
+
),
|
|
412
|
+
"data-testid": `combinator-${c}`,
|
|
413
|
+
children: c
|
|
414
|
+
},
|
|
415
|
+
c
|
|
416
|
+
)) }),
|
|
417
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: group.combinator === "AND" ? tr(
|
|
418
|
+
"fluidgrids.builder.conditionBuilder.combinatorAndHint",
|
|
419
|
+
"all conditions must match"
|
|
420
|
+
) : tr(
|
|
421
|
+
"fluidgrids.builder.conditionBuilder.combinatorOrHint",
|
|
422
|
+
"any condition may match"
|
|
423
|
+
) }),
|
|
424
|
+
onRemove && /* @__PURE__ */ jsx(
|
|
425
|
+
"button",
|
|
426
|
+
{
|
|
427
|
+
type: "button",
|
|
428
|
+
onClick: onRemove,
|
|
429
|
+
title: tr(
|
|
430
|
+
"fluidgrids.builder.conditionBuilder.removeGroup",
|
|
431
|
+
"Remove group"
|
|
432
|
+
),
|
|
433
|
+
className: "ml-auto rounded p-1 text-muted-foreground hover:text-[var(--color-status-error-text)]",
|
|
434
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "mynaui:trash", className: "size-3.5" })
|
|
435
|
+
}
|
|
436
|
+
)
|
|
437
|
+
] }),
|
|
438
|
+
group.children.map(
|
|
439
|
+
(child, index) => child.type === "rule" ? /* @__PURE__ */ jsx(
|
|
440
|
+
RuleRow,
|
|
441
|
+
{
|
|
442
|
+
rule: child,
|
|
443
|
+
fields,
|
|
444
|
+
onChange: (next) => setChild(index, next),
|
|
445
|
+
onRemove: () => removeChild(index)
|
|
446
|
+
},
|
|
447
|
+
index
|
|
448
|
+
) : /* @__PURE__ */ jsx(
|
|
449
|
+
GroupEditor,
|
|
450
|
+
{
|
|
451
|
+
group: child,
|
|
452
|
+
fields,
|
|
453
|
+
depth: depth + 1,
|
|
454
|
+
onChange: (next) => setChild(index, next),
|
|
455
|
+
onRemove: () => removeChild(index)
|
|
456
|
+
},
|
|
457
|
+
index
|
|
458
|
+
)
|
|
459
|
+
),
|
|
460
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
461
|
+
/* @__PURE__ */ jsxs(
|
|
462
|
+
"button",
|
|
463
|
+
{
|
|
464
|
+
type: "button",
|
|
465
|
+
onClick: () => onChange({
|
|
466
|
+
...group,
|
|
467
|
+
children: [...group.children, emptyRule(fields[0]?.path ?? "")]
|
|
468
|
+
}),
|
|
469
|
+
className: "flex items-center gap-1 text-[11px] font-medium text-primary hover:underline",
|
|
470
|
+
"data-testid": "add-condition",
|
|
471
|
+
children: [
|
|
472
|
+
/* @__PURE__ */ jsx(Icon, { icon: "basil:add-solid", className: "size-3.5" }),
|
|
473
|
+
" ",
|
|
474
|
+
tr(
|
|
475
|
+
"fluidgrids.builder.conditionBuilder.addCondition",
|
|
476
|
+
"Add condition"
|
|
477
|
+
)
|
|
478
|
+
]
|
|
479
|
+
}
|
|
480
|
+
),
|
|
481
|
+
depth < 2 && /* @__PURE__ */ jsxs(
|
|
482
|
+
"button",
|
|
483
|
+
{
|
|
484
|
+
type: "button",
|
|
485
|
+
onClick: () => onChange({
|
|
486
|
+
...group,
|
|
487
|
+
children: [...group.children, emptyGroup()]
|
|
488
|
+
}),
|
|
489
|
+
className: "flex items-center gap-1 text-[11px] font-medium text-muted-foreground hover:text-foreground",
|
|
490
|
+
"data-testid": "add-group",
|
|
491
|
+
children: [
|
|
492
|
+
/* @__PURE__ */ jsx(Icon, { icon: "basil:add-solid", className: "size-3.5" }),
|
|
493
|
+
" ",
|
|
494
|
+
tr("fluidgrids.builder.conditionBuilder.addGroup", "Add group")
|
|
495
|
+
]
|
|
496
|
+
}
|
|
497
|
+
)
|
|
498
|
+
] })
|
|
499
|
+
]
|
|
500
|
+
}
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
function ConditionTreeBuilder({
|
|
504
|
+
value,
|
|
505
|
+
fields,
|
|
506
|
+
onChange
|
|
507
|
+
}) {
|
|
508
|
+
const tree = value && typeof value === "object" && value.type === "group" ? value : emptyGroup();
|
|
509
|
+
return /* @__PURE__ */ jsx("div", { "data-testid": "condition-tree-builder", children: /* @__PURE__ */ jsx(
|
|
510
|
+
GroupEditor,
|
|
511
|
+
{
|
|
512
|
+
group: tree,
|
|
513
|
+
fields,
|
|
514
|
+
depth: 0,
|
|
515
|
+
onChange: (next) => onChange(next.children.length === 0 ? void 0 : next)
|
|
516
|
+
}
|
|
517
|
+
) });
|
|
518
|
+
}
|
|
519
|
+
function newRuleHandle(existing) {
|
|
520
|
+
let n = existing.length;
|
|
521
|
+
const taken = new Set(existing.map((r) => r.handle));
|
|
522
|
+
let handle = `rule-${n}`;
|
|
523
|
+
while (taken.has(handle)) handle = `rule-${++n}`;
|
|
524
|
+
return handle;
|
|
525
|
+
}
|
|
526
|
+
function casesFromRules(rules) {
|
|
527
|
+
const cases = {};
|
|
528
|
+
rules.forEach((rule, index) => {
|
|
529
|
+
cases[rule.name || `Rule ${index + 1}`] = rule.handle;
|
|
530
|
+
});
|
|
531
|
+
return cases;
|
|
532
|
+
}
|
|
533
|
+
function SwitchRulesEditor({
|
|
534
|
+
rules,
|
|
535
|
+
fallbackHandle,
|
|
536
|
+
fields,
|
|
537
|
+
onChange
|
|
538
|
+
}) {
|
|
539
|
+
const tr = useTr();
|
|
540
|
+
const setRule = (index, rule) => {
|
|
541
|
+
const next = rules.slice();
|
|
542
|
+
next[index] = rule;
|
|
543
|
+
onChange({ rules: next, fallbackHandle });
|
|
544
|
+
};
|
|
545
|
+
const move = (index, dir) => {
|
|
546
|
+
const target = index + dir;
|
|
547
|
+
if (target < 0 || target >= rules.length) return;
|
|
548
|
+
const next = rules.slice();
|
|
549
|
+
const tmp = next[index];
|
|
550
|
+
next[index] = next[target];
|
|
551
|
+
next[target] = tmp;
|
|
552
|
+
onChange({ rules: next, fallbackHandle });
|
|
553
|
+
};
|
|
554
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", "data-testid": "switch-rules-editor", children: [
|
|
555
|
+
rules.map((rule, index) => /* @__PURE__ */ jsxs(
|
|
556
|
+
"div",
|
|
557
|
+
{
|
|
558
|
+
className: "rounded-md border border-border/70 bg-muted/10 p-2",
|
|
559
|
+
"data-testid": `switch-rule-${index}`,
|
|
560
|
+
children: [
|
|
561
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1.5 flex items-center gap-1.5", children: [
|
|
562
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[10px] font-semibold text-muted-foreground", children: [
|
|
563
|
+
index + 1,
|
|
564
|
+
"."
|
|
565
|
+
] }),
|
|
566
|
+
/* @__PURE__ */ jsx(
|
|
567
|
+
"input",
|
|
568
|
+
{
|
|
569
|
+
type: "text",
|
|
570
|
+
value: rule.name,
|
|
571
|
+
placeholder: `Rule ${index + 1}`,
|
|
572
|
+
onChange: (e) => setRule(index, { ...rule, name: e.target.value }),
|
|
573
|
+
className: cn(rowInput, "flex-1 font-medium"),
|
|
574
|
+
"data-testid": "switch-rule-name"
|
|
575
|
+
}
|
|
576
|
+
),
|
|
577
|
+
/* @__PURE__ */ jsx(
|
|
578
|
+
"button",
|
|
579
|
+
{
|
|
580
|
+
type: "button",
|
|
581
|
+
onClick: () => move(index, -1),
|
|
582
|
+
disabled: index === 0,
|
|
583
|
+
title: tr(
|
|
584
|
+
"fluidgrids.builder.switchRulesEditor.moveUp",
|
|
585
|
+
"Move up"
|
|
586
|
+
),
|
|
587
|
+
className: "rounded p-0.5 text-muted-foreground hover:text-foreground disabled:opacity-30",
|
|
588
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "mdi:chevron-up", className: "size-4" })
|
|
589
|
+
}
|
|
590
|
+
),
|
|
591
|
+
/* @__PURE__ */ jsx(
|
|
592
|
+
"button",
|
|
593
|
+
{
|
|
594
|
+
type: "button",
|
|
595
|
+
onClick: () => move(index, 1),
|
|
596
|
+
disabled: index === rules.length - 1,
|
|
597
|
+
title: tr(
|
|
598
|
+
"fluidgrids.builder.switchRulesEditor.moveDown",
|
|
599
|
+
"Move down"
|
|
600
|
+
),
|
|
601
|
+
className: "rounded p-0.5 text-muted-foreground hover:text-foreground disabled:opacity-30",
|
|
602
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "mdi:chevron-down", className: "size-4" })
|
|
603
|
+
}
|
|
604
|
+
),
|
|
605
|
+
/* @__PURE__ */ jsx(
|
|
606
|
+
"button",
|
|
607
|
+
{
|
|
608
|
+
type: "button",
|
|
609
|
+
onClick: () => onChange({
|
|
610
|
+
rules: rules.filter((_, i) => i !== index),
|
|
611
|
+
fallbackHandle
|
|
612
|
+
}),
|
|
613
|
+
title: tr(
|
|
614
|
+
"fluidgrids.builder.switchRulesEditor.removeRule",
|
|
615
|
+
"Remove rule"
|
|
616
|
+
),
|
|
617
|
+
className: "rounded p-0.5 text-muted-foreground hover:text-[var(--color-status-error-text)]",
|
|
618
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "mynaui:trash", className: "size-3.5" })
|
|
619
|
+
}
|
|
620
|
+
)
|
|
621
|
+
] }),
|
|
622
|
+
/* @__PURE__ */ jsx(
|
|
623
|
+
ConditionTreeBuilder,
|
|
624
|
+
{
|
|
625
|
+
value: rule.conditionTree,
|
|
626
|
+
fields,
|
|
627
|
+
onChange: (tree) => setRule(index, { ...rule, conditionTree: tree ?? emptyGroup() })
|
|
628
|
+
}
|
|
629
|
+
)
|
|
630
|
+
]
|
|
631
|
+
},
|
|
632
|
+
rule.handle
|
|
633
|
+
)),
|
|
634
|
+
/* @__PURE__ */ jsxs(
|
|
635
|
+
"button",
|
|
636
|
+
{
|
|
637
|
+
type: "button",
|
|
638
|
+
onClick: () => onChange({
|
|
639
|
+
rules: [
|
|
640
|
+
...rules,
|
|
641
|
+
{
|
|
642
|
+
name: `Rule ${rules.length + 1}`,
|
|
643
|
+
handle: newRuleHandle(rules),
|
|
644
|
+
conditionTree: emptyGroup()
|
|
645
|
+
}
|
|
646
|
+
],
|
|
647
|
+
fallbackHandle
|
|
648
|
+
}),
|
|
649
|
+
className: "flex w-fit items-center gap-1 text-[11px] font-medium text-primary hover:underline",
|
|
650
|
+
"data-testid": "add-switch-rule",
|
|
651
|
+
children: [
|
|
652
|
+
/* @__PURE__ */ jsx(Icon, { icon: "basil:add-solid", className: "size-3.5" }),
|
|
653
|
+
" ",
|
|
654
|
+
tr("fluidgrids.builder.switchRulesEditor.addRule", "Add rule")
|
|
655
|
+
]
|
|
656
|
+
}
|
|
657
|
+
),
|
|
658
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-2", children: [
|
|
659
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: tr(
|
|
660
|
+
"fluidgrids.builder.switchRulesEditor.fallbackLabel",
|
|
661
|
+
"Fallback output (no rule matches):"
|
|
662
|
+
) }),
|
|
663
|
+
/* @__PURE__ */ jsx(
|
|
664
|
+
"input",
|
|
665
|
+
{
|
|
666
|
+
type: "text",
|
|
667
|
+
value: fallbackHandle,
|
|
668
|
+
placeholder: "default",
|
|
669
|
+
onChange: (e) => onChange({ rules, fallbackHandle: e.target.value }),
|
|
670
|
+
className: cn(rowInput, "w-28"),
|
|
671
|
+
"data-testid": "switch-fallback"
|
|
672
|
+
}
|
|
673
|
+
)
|
|
674
|
+
] }),
|
|
675
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] leading-snug text-muted-foreground", children: tr(
|
|
676
|
+
"fluidgrids.builder.switchRulesEditor.rulesHint",
|
|
677
|
+
"Rules are checked top-down; the first matching rule routes the item to its output. Each rule renders as an output handle on the node."
|
|
678
|
+
) })
|
|
679
|
+
] });
|
|
680
|
+
}
|
|
681
|
+
function useWebhookRegistration(workflowKey, triggerNodeId, enabled = true) {
|
|
682
|
+
const key = enabled && workflowKey && triggerNodeId ? JSON.stringify([workflowKey, triggerNodeId]) : null;
|
|
683
|
+
const requestId = useRef(0);
|
|
684
|
+
const [state, setState] = useState({ key: null, registration: null, loading: false, error: null });
|
|
685
|
+
const fetchData = useCallback(async () => {
|
|
686
|
+
const currentRequest = ++requestId.current;
|
|
687
|
+
if (!key || !workflowKey || !triggerNodeId) return;
|
|
688
|
+
const client = getFluidGridsClientSafe();
|
|
689
|
+
if (!client) {
|
|
690
|
+
setState({
|
|
691
|
+
key,
|
|
692
|
+
registration: null,
|
|
693
|
+
loading: false,
|
|
694
|
+
error: new Error("Workflow client unavailable")
|
|
695
|
+
});
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
setState({ key, registration: null, loading: true, error: null });
|
|
699
|
+
try {
|
|
700
|
+
const result = await client.query({
|
|
701
|
+
query: EventRegistrationsDocument,
|
|
702
|
+
variables: { workflowKey },
|
|
703
|
+
fetchPolicy: "network-only"
|
|
704
|
+
});
|
|
705
|
+
const match = result.data?.eventRegistrations?.find(
|
|
706
|
+
(r) => r.triggerNodeId === triggerNodeId
|
|
707
|
+
) ?? null;
|
|
708
|
+
if (currentRequest === requestId.current)
|
|
709
|
+
setState({ key, registration: match, loading: false, error: null });
|
|
710
|
+
} catch (err) {
|
|
711
|
+
if (currentRequest === requestId.current)
|
|
712
|
+
setState({
|
|
713
|
+
key,
|
|
714
|
+
registration: null,
|
|
715
|
+
loading: false,
|
|
716
|
+
error: err instanceof Error ? err : new Error("Unable to load trigger URL")
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
}, [key, workflowKey, triggerNodeId]);
|
|
720
|
+
useEffect(() => {
|
|
721
|
+
fetchData();
|
|
722
|
+
return () => {
|
|
723
|
+
requestId.current += 1;
|
|
724
|
+
};
|
|
725
|
+
}, [fetchData]);
|
|
726
|
+
useEffect(() => {
|
|
727
|
+
if (!enabled || !workflowKey) return;
|
|
728
|
+
return subscribeWorkflowSaved(workflowKey, () => {
|
|
729
|
+
void fetchData();
|
|
730
|
+
});
|
|
731
|
+
}, [enabled, workflowKey, fetchData]);
|
|
732
|
+
const current = state.key === key && key !== null ? state : null;
|
|
733
|
+
const registration = current?.registration ?? null;
|
|
734
|
+
return {
|
|
735
|
+
registration,
|
|
736
|
+
webhookUrl: registration?.webhookUrl ?? null,
|
|
737
|
+
formUrl: registration?.formUrl ?? null,
|
|
738
|
+
loading: current?.loading ?? !!key,
|
|
739
|
+
error: current?.error ?? null,
|
|
740
|
+
refetch: fetchData
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// src/builder/components/ndv/node-option-values.ts
|
|
745
|
+
function isRecord(value) {
|
|
746
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
747
|
+
}
|
|
748
|
+
function isParameterName(value) {
|
|
749
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value) && !["__proto__", "prototype", "constructor"].includes(value);
|
|
750
|
+
}
|
|
751
|
+
function isStringOptionList(schema) {
|
|
752
|
+
if (structuredParameterType(schema) !== "array") return false;
|
|
753
|
+
const type = typeof schema === "string" ? schema : isRecord(schema) ? schema.type : void 0;
|
|
754
|
+
const inlineItem = typeof type === "string" ? type.trim().toLowerCase().match(/^array<(.+)>$/)?.[1] : void 0;
|
|
755
|
+
const item = (isRecord(schema) ? schema.items : void 0) ?? inlineItem;
|
|
756
|
+
if (item === void 0) return true;
|
|
757
|
+
const itemType = typeof item === "string" ? item : isRecord(item) ? item.type : void 0;
|
|
758
|
+
return typeof itemType === "string" && itemType.trim().toLowerCase() === "string";
|
|
759
|
+
}
|
|
760
|
+
function readLoadOptionsSpec(value) {
|
|
761
|
+
if (!isRecord(value) || typeof value.operation !== "string" || typeof value.labelField !== "string" || typeof value.valueField !== "string")
|
|
762
|
+
return void 0;
|
|
763
|
+
let parameterArgs;
|
|
764
|
+
if (value.parameterArgs !== void 0) {
|
|
765
|
+
if (!isRecord(value.parameterArgs)) return void 0;
|
|
766
|
+
parameterArgs = {};
|
|
767
|
+
for (const [argument, parameter] of Object.entries(value.parameterArgs)) {
|
|
768
|
+
if (!isParameterName(argument) || typeof parameter !== "string" || !isParameterName(parameter))
|
|
769
|
+
return void 0;
|
|
770
|
+
parameterArgs[argument] = parameter;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return {
|
|
774
|
+
operation: value.operation,
|
|
775
|
+
labelField: value.labelField,
|
|
776
|
+
valueField: value.valueField,
|
|
777
|
+
resultPath: typeof value.resultPath === "string" ? value.resultPath : void 0,
|
|
778
|
+
staticArgs: isRecord(value.staticArgs) ? value.staticArgs : void 0,
|
|
779
|
+
parameterArgs
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
function resolveLoadOptionsSpec(spec, values, defaults = {}, unavailableParameters = []) {
|
|
783
|
+
const args = { ...spec.staticArgs };
|
|
784
|
+
const missingParameters = /* @__PURE__ */ new Set();
|
|
785
|
+
for (const [argument, parameter] of Object.entries(
|
|
786
|
+
spec.parameterArgs ?? {}
|
|
787
|
+
)) {
|
|
788
|
+
if (!isParameterName(argument) || !isParameterName(parameter)) {
|
|
789
|
+
missingParameters.add(parameter);
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
const value = Object.hasOwn(values, parameter) && values[parameter] !== void 0 ? values[parameter] : Object.hasOwn(defaults, parameter) ? defaults[parameter] : void 0;
|
|
793
|
+
if (unavailableParameters.includes(parameter) || value === void 0 || value === null || containsExpression(value) || typeof value === "string" && !value.trim() || Array.isArray(value) && value.length === 0)
|
|
794
|
+
missingParameters.add(parameter);
|
|
795
|
+
else args[argument] = value;
|
|
796
|
+
}
|
|
797
|
+
return {
|
|
798
|
+
spec: missingParameters.size ? void 0 : { ...spec, staticArgs: args },
|
|
799
|
+
missingParameters: [...missingParameters]
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function containsExpression(value) {
|
|
803
|
+
if (typeof value === "string") return /\{\{|\$\{|^\$\./.test(value);
|
|
804
|
+
if (Array.isArray(value)) return value.some(containsExpression);
|
|
805
|
+
return isRecord(value) && Object.values(value).some(containsExpression);
|
|
806
|
+
}
|
|
807
|
+
function optionListValues(value) {
|
|
808
|
+
if (value === void 0 || value === null || value === "") return [];
|
|
809
|
+
if (typeof value === "string") {
|
|
810
|
+
const trimmed = value.trim();
|
|
811
|
+
if (!trimmed) return [];
|
|
812
|
+
if (trimmed.startsWith("{")) return void 0;
|
|
813
|
+
if (trimmed.startsWith("[")) {
|
|
814
|
+
try {
|
|
815
|
+
const parsed = JSON.parse(trimmed);
|
|
816
|
+
return Array.isArray(parsed) ? optionListValues(parsed) : void 0;
|
|
817
|
+
} catch {
|
|
818
|
+
return void 0;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return [
|
|
822
|
+
...new Set(
|
|
823
|
+
trimmed.split(",").map((entry) => entry.trim()).filter(Boolean)
|
|
824
|
+
)
|
|
825
|
+
];
|
|
826
|
+
}
|
|
827
|
+
if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string"))
|
|
828
|
+
return void 0;
|
|
829
|
+
return [...new Set(value.filter((entry) => entry !== ""))];
|
|
830
|
+
}
|
|
831
|
+
function optionListText(value) {
|
|
832
|
+
return typeof value === "string" ? value : JSON.stringify(value ?? []);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// src/builder/lib/multiline-parameters.ts
|
|
836
|
+
function isRecord2(value) {
|
|
837
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
838
|
+
}
|
|
839
|
+
function multilineParameters(operation) {
|
|
840
|
+
const names = /* @__PURE__ */ new Set();
|
|
841
|
+
if (!isRecord2(operation)) return names;
|
|
842
|
+
const legacyCard = operation.adaptive_card ?? operation.adaptiveCard;
|
|
843
|
+
const resolved = resolveUiSpec({
|
|
844
|
+
name: typeof operation.name === "string" ? operation.name : void 0,
|
|
845
|
+
ui: operation.ui,
|
|
846
|
+
adaptive_card: isRecord2(legacyCard) ? legacyCard : void 0
|
|
847
|
+
});
|
|
848
|
+
const pending = resolved?.spec.blocks.flatMap(
|
|
849
|
+
(block) => block.type === "adaptive_card" ? [block.card] : []
|
|
850
|
+
) ?? [];
|
|
851
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
852
|
+
while (pending.length > 0) {
|
|
853
|
+
const element = pending.pop();
|
|
854
|
+
if (typeof element !== "object" || element === null || visited.has(element))
|
|
855
|
+
continue;
|
|
856
|
+
visited.add(element);
|
|
857
|
+
if (Array.isArray(element)) {
|
|
858
|
+
pending.push(...element);
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
if (!isRecord2(element)) continue;
|
|
862
|
+
if (element.type === "Input.Text" && element.isMultiline === true && typeof element.id === "string") {
|
|
863
|
+
const name = element.id.replace(/^(?:args\.)?state\./, "");
|
|
864
|
+
if (name) names.add(name);
|
|
865
|
+
}
|
|
866
|
+
pending.push(
|
|
867
|
+
element.body,
|
|
868
|
+
element.items,
|
|
869
|
+
element.columns,
|
|
870
|
+
element.actions,
|
|
871
|
+
element.card
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
if (isRecord2(operation.input_fields)) {
|
|
875
|
+
for (const [name, field] of Object.entries(operation.input_fields)) {
|
|
876
|
+
if (!isRecord2(field)) continue;
|
|
877
|
+
if (field.isMultiline === true) names.add(name);
|
|
878
|
+
else if (field.isMultiline === false) names.delete(name);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
return names;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// src/builder/components/ndv/use-node-config.ts
|
|
885
|
+
function fixedParameterState(state, params, field, value) {
|
|
886
|
+
const parameter = params.find((candidate) => candidate.name === field);
|
|
887
|
+
const parsed = parseParameterValue(
|
|
888
|
+
parameter?.schema ?? parameter?.type,
|
|
889
|
+
value
|
|
890
|
+
);
|
|
891
|
+
const nextValue = parsed.valid ? parsed.value : value;
|
|
892
|
+
const optionDependency = params.some(
|
|
893
|
+
(candidate) => Object.values(candidate.loadOptions?.parameterArgs ?? {}).includes(field)
|
|
894
|
+
);
|
|
895
|
+
if (nextValue === void 0 || !optionDependency && (nextValue === null || nextValue === "")) {
|
|
896
|
+
const next = { ...state };
|
|
897
|
+
delete next[field];
|
|
898
|
+
return next;
|
|
899
|
+
}
|
|
900
|
+
return { ...state, [field]: nextValue };
|
|
901
|
+
}
|
|
902
|
+
function errorConfigOf(node) {
|
|
903
|
+
const config = asRecord(node?.data)?.errorConfig;
|
|
904
|
+
return config;
|
|
905
|
+
}
|
|
906
|
+
function coerceNumber(type, raw) {
|
|
907
|
+
if (raw.trim() === "") return void 0;
|
|
908
|
+
const n = type === "integer" ? parseInt(raw, 10) : parseFloat(raw);
|
|
909
|
+
return Number.isNaN(n) ? void 0 : n;
|
|
910
|
+
}
|
|
911
|
+
function asRecord(value) {
|
|
912
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
913
|
+
}
|
|
914
|
+
function fallbackOperations(type) {
|
|
915
|
+
if (!type) return void 0;
|
|
916
|
+
return NODE_CONFIGS.find((c) => c.type === type)?.operations;
|
|
917
|
+
}
|
|
918
|
+
function selectedOperationOf(data, type) {
|
|
919
|
+
const live = liveInternalOperations(type);
|
|
920
|
+
if (live) {
|
|
921
|
+
const selected = resolveNodeOperation(
|
|
922
|
+
{ operation_name: data?.operation_name },
|
|
923
|
+
live
|
|
924
|
+
).selected;
|
|
925
|
+
if (selected) return selected;
|
|
926
|
+
}
|
|
927
|
+
return resolveNodeOperation(data, fallbackOperations(type)).selected;
|
|
928
|
+
}
|
|
929
|
+
function readOptionLabels(map, options) {
|
|
930
|
+
const labels = /* @__PURE__ */ Object.create(null);
|
|
931
|
+
for (const [value, text] of Object.entries(asRecord(map) ?? {})) {
|
|
932
|
+
if (typeof text === "string" && text.trim() !== "") labels[value] = text;
|
|
933
|
+
}
|
|
934
|
+
if (Array.isArray(options)) {
|
|
935
|
+
for (const option of options) {
|
|
936
|
+
const rec = asRecord(option);
|
|
937
|
+
const value = rec?.value ?? rec?.name;
|
|
938
|
+
const text = [
|
|
939
|
+
rec?.title,
|
|
940
|
+
rec?.label,
|
|
941
|
+
rec?.value !== void 0 ? rec?.name : void 0
|
|
942
|
+
].find(
|
|
943
|
+
(candidate) => typeof candidate === "string" && candidate.trim() !== ""
|
|
944
|
+
);
|
|
945
|
+
if (typeof value === "string" && typeof text === "string" && text !== value) {
|
|
946
|
+
if (!Object.hasOwn(labels, value)) labels[value] = text;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
return Object.keys(labels).length > 0 ? labels : void 0;
|
|
951
|
+
}
|
|
952
|
+
function optionText(param, value) {
|
|
953
|
+
const labels = param.optionLabels;
|
|
954
|
+
return labels && Object.hasOwn(labels, value) ? labels[value] : value;
|
|
955
|
+
}
|
|
956
|
+
function paramsFromOperation(op) {
|
|
957
|
+
const io = asRecord(op?.input_fields);
|
|
958
|
+
if (!io) return [];
|
|
959
|
+
const multiline = multilineParameters(op);
|
|
960
|
+
return Object.entries(io).map(([name, spec]) => {
|
|
961
|
+
const rec = asRecord(spec);
|
|
962
|
+
const optsRaw = rec?.options ?? rec?.enum;
|
|
963
|
+
const options = Array.isArray(optsRaw) ? optsRaw.map(
|
|
964
|
+
(o) => typeof o === "string" ? o : asRecord(o)?.value ?? asRecord(o)?.name
|
|
965
|
+
).filter((v) => typeof v === "string") : void 0;
|
|
966
|
+
const loadOptions = readLoadOptionsSpec(rec?.loadOptions);
|
|
967
|
+
const optionLabels = readOptionLabels(rec?.optionLabels, optsRaw);
|
|
968
|
+
const fields = asRecord(rec?.fields);
|
|
969
|
+
return {
|
|
970
|
+
name,
|
|
971
|
+
label: fieldLabel(name, rec),
|
|
972
|
+
placeholder: typeof rec?.placeholder === "string" && rec.placeholder.trim() !== "" ? rec.placeholder : void 0,
|
|
973
|
+
...optionLabels ? { optionLabels } : {},
|
|
974
|
+
type: typeof spec === "string" ? spec : typeof rec?.type === "string" ? rec.type : "any",
|
|
975
|
+
required: rec?.required === true,
|
|
976
|
+
description: typeof rec?.description === "string" ? rec.description : void 0,
|
|
977
|
+
options,
|
|
978
|
+
defaultValue: rec?.default,
|
|
979
|
+
isMultiline: multiline.has(name),
|
|
980
|
+
schema: spec,
|
|
981
|
+
loadOptions,
|
|
982
|
+
...rec?.secret === true ? { secret: true } : {},
|
|
983
|
+
displayOptions: readDisplayOptions(rec),
|
|
984
|
+
fields
|
|
985
|
+
};
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
function nodeTitle(node) {
|
|
989
|
+
if (!node) return "Node";
|
|
990
|
+
const data = asRecord(node.data);
|
|
991
|
+
const label = typeof data?.label === "string" ? data.label : "";
|
|
992
|
+
return label || getNodeDetail(node.type).title || node.id;
|
|
993
|
+
}
|
|
994
|
+
function nodeSubtitle(node) {
|
|
995
|
+
const data = asRecord(node?.data);
|
|
996
|
+
return typeof data?.operation_name === "string" ? data.operation_name : void 0;
|
|
997
|
+
}
|
|
998
|
+
function leaveUnchanged() {
|
|
999
|
+
}
|
|
1000
|
+
function readOnlyNodeConfig(api) {
|
|
1001
|
+
return {
|
|
1002
|
+
...api,
|
|
1003
|
+
writeFixed: leaveUnchanged,
|
|
1004
|
+
writeMapping: leaveUnchanged,
|
|
1005
|
+
handleConnectionsChange: leaveUnchanged,
|
|
1006
|
+
toggleMode: leaveUnchanged,
|
|
1007
|
+
// A field dropped on a read-only form is refused, not opened as a link.
|
|
1008
|
+
handleDropOnParam: () => (e) => e.preventDefault(),
|
|
1009
|
+
writeErrorConfig: leaveUnchanged,
|
|
1010
|
+
writeNotes: leaveUnchanged,
|
|
1011
|
+
writeExecutionSettings: leaveUnchanged,
|
|
1012
|
+
writeNodeDataField: leaveUnchanged,
|
|
1013
|
+
readOnly: true
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
function runOnOf(executionSettings) {
|
|
1017
|
+
const runOn = asRecord(executionSettings?.runOn);
|
|
1018
|
+
return runOn?.kind === "runner" && typeof runOn.runnerId === "string" ? { kind: "runner", runnerId: runOn.runnerId } : null;
|
|
1019
|
+
}
|
|
1020
|
+
function useNodeConfig(target, options) {
|
|
1021
|
+
const nodes = useFlowStore((s) => s.workflow.nodes);
|
|
1022
|
+
const edges = useFlowStore((s) => s.workflow.edges);
|
|
1023
|
+
const workflowKey = useFlowStore((s) => s.workflow.workflowKey);
|
|
1024
|
+
const readOnly = useBuilderReadOnly();
|
|
1025
|
+
const executionLogs = useFlowStore((s) => s.execution.logs);
|
|
1026
|
+
const currentRunId = useFlowStore((s) => s.execution.currentRunId);
|
|
1027
|
+
const nodeId = target?.nodeId;
|
|
1028
|
+
const node = useMemo(
|
|
1029
|
+
() => nodeId ? nodes.find((n) => n.id === nodeId) : void 0,
|
|
1030
|
+
[nodeId, nodes]
|
|
1031
|
+
);
|
|
1032
|
+
const incoming = useMemo(
|
|
1033
|
+
() => nodeId ? edges.filter((e) => e.target === nodeId) : [],
|
|
1034
|
+
[edges, nodeId]
|
|
1035
|
+
);
|
|
1036
|
+
const sourceIds = useMemo(() => incoming.map((e) => e.source), [incoming]);
|
|
1037
|
+
const sourceKey = sourceIds.join(",");
|
|
1038
|
+
const [selectedSource, setSelectedSource] = useState();
|
|
1039
|
+
const [dragField, setDragField] = useState(null);
|
|
1040
|
+
const [modeByField, setModeByField] = useState({});
|
|
1041
|
+
useEffect(() => {
|
|
1042
|
+
setSelectedSource(
|
|
1043
|
+
(prev) => prev && sourceIds.includes(prev) ? prev : sourceIds[0]
|
|
1044
|
+
);
|
|
1045
|
+
}, [nodeId, sourceKey, sourceIds]);
|
|
1046
|
+
useEffect(() => {
|
|
1047
|
+
setModeByField({});
|
|
1048
|
+
}, [nodeId, selectedSource]);
|
|
1049
|
+
const sourceNode = useMemo(
|
|
1050
|
+
() => nodes.find((n) => n.id === selectedSource),
|
|
1051
|
+
[nodes, selectedSource]
|
|
1052
|
+
);
|
|
1053
|
+
const currentEdge = useMemo(
|
|
1054
|
+
() => incoming.find((e) => e.source === selectedSource),
|
|
1055
|
+
[incoming, selectedSource]
|
|
1056
|
+
);
|
|
1057
|
+
const registryRevision = useNodeRegistryRevision();
|
|
1058
|
+
const currentOp = useMemo(() => {
|
|
1059
|
+
return selectedOperationOf(asRecord(node?.data), node?.type);
|
|
1060
|
+
}, [node, registryRevision]);
|
|
1061
|
+
const sourceOp = useMemo(() => {
|
|
1062
|
+
return selectedOperationOf(asRecord(sourceNode?.data), sourceNode?.type);
|
|
1063
|
+
}, [sourceNode, registryRevision]);
|
|
1064
|
+
const { fields: inputFields, data: inputData } = useMemo(
|
|
1065
|
+
() => resolveMappingInput({
|
|
1066
|
+
source: sourceNode,
|
|
1067
|
+
operation: sourceOp,
|
|
1068
|
+
runId: currentRunId,
|
|
1069
|
+
logs: executionLogs
|
|
1070
|
+
}),
|
|
1071
|
+
[sourceNode, sourceOp, currentRunId, executionLogs]
|
|
1072
|
+
);
|
|
1073
|
+
const outputFields = useMemo(
|
|
1074
|
+
() => fieldsFromIO(currentOp?.output_fields),
|
|
1075
|
+
[currentOp]
|
|
1076
|
+
);
|
|
1077
|
+
const params = useMemo(() => paramsFromOperation(currentOp), [currentOp]);
|
|
1078
|
+
const isWebhookTrigger = useMemo(
|
|
1079
|
+
() => !!asRecord(currentOp?.output_fields)?.webhookUrl,
|
|
1080
|
+
[currentOp]
|
|
1081
|
+
);
|
|
1082
|
+
const isFormTrigger = useMemo(
|
|
1083
|
+
() => !!asRecord(currentOp?.output_fields)?.formUrl,
|
|
1084
|
+
[currentOp]
|
|
1085
|
+
);
|
|
1086
|
+
const {
|
|
1087
|
+
webhookUrl,
|
|
1088
|
+
formUrl,
|
|
1089
|
+
error: triggerUrlError,
|
|
1090
|
+
loading: webhookUrlLoading
|
|
1091
|
+
} = useWebhookRegistration(
|
|
1092
|
+
workflowKey || void 0,
|
|
1093
|
+
node?.id,
|
|
1094
|
+
(isWebhookTrigger || isFormTrigger) && (options?.webhookEnabled ?? true)
|
|
1095
|
+
);
|
|
1096
|
+
const pickerFields = useMemo(() => flattenFields(inputFields), [inputFields]);
|
|
1097
|
+
const mapping = currentEdge?.mapping?.definition ?? {};
|
|
1098
|
+
const argsState = useMemo(() => {
|
|
1099
|
+
const data = asRecord(node?.data);
|
|
1100
|
+
const args = asRecord(data?.args);
|
|
1101
|
+
return asRecord(args?.state) ?? {};
|
|
1102
|
+
}, [node]);
|
|
1103
|
+
const isWebhookWait = useMemo(() => {
|
|
1104
|
+
const type = asRecord(asRecord(node?.data)?.args)?.context;
|
|
1105
|
+
const nodeType = asRecord(asRecord(type)?.node)?.type;
|
|
1106
|
+
return nodeType === "internal:wait" && argsState.mode === "webhook";
|
|
1107
|
+
}, [node, argsState]);
|
|
1108
|
+
const connectionAdapters = useMemo(() => {
|
|
1109
|
+
const op = asRecord(currentOp);
|
|
1110
|
+
const manifest = asRecord(asRecord(node?.data)?.nodeData);
|
|
1111
|
+
const raw = op?.connectionAdapters ?? op?.credentialAdapters ?? manifest?.connectionAdapters ?? manifest?.credentialAdapters;
|
|
1112
|
+
if (!Array.isArray(raw)) return [];
|
|
1113
|
+
return raw.map((a) => asRecord(a)).filter((a) => !!a).map((a) => ({
|
|
1114
|
+
name: String(a.name ?? ""),
|
|
1115
|
+
provider: String(a.provider ?? ""),
|
|
1116
|
+
...a.required === false ? { required: false } : {}
|
|
1117
|
+
})).filter((a) => a.name && a.provider);
|
|
1118
|
+
}, [currentOp, node]);
|
|
1119
|
+
const selectedConnections = useMemo(() => {
|
|
1120
|
+
const args = asRecord(asRecord(node?.data)?.args);
|
|
1121
|
+
const fromList = (list, idKey) => {
|
|
1122
|
+
if (!Array.isArray(list)) return [];
|
|
1123
|
+
return list.map((c) => asRecord(c)).filter((c) => !!c).map((c) => ({
|
|
1124
|
+
connectionId: String(c[idKey] ?? ""),
|
|
1125
|
+
adapter: String(c.adapter ?? "")
|
|
1126
|
+
})).filter((c) => c.connectionId);
|
|
1127
|
+
};
|
|
1128
|
+
const connections = fromList(args?.connections, "connectionId");
|
|
1129
|
+
if (connections.length > 0) return connections;
|
|
1130
|
+
return fromList(args?.credentials, "id");
|
|
1131
|
+
}, [node]);
|
|
1132
|
+
const handleConnectionsChange = (connections) => {
|
|
1133
|
+
if (!nodeId) return;
|
|
1134
|
+
const store = useFlowStore.getState();
|
|
1135
|
+
const next = store.workflow.nodes.map((n) => {
|
|
1136
|
+
if (n.id !== nodeId) return n;
|
|
1137
|
+
const data = { ...asRecord(n.data) ?? {} };
|
|
1138
|
+
const args = { ...asRecord(data.args) ?? {} };
|
|
1139
|
+
delete args.credentials;
|
|
1140
|
+
args.connections = connections.map((c) => ({
|
|
1141
|
+
connectionId: c.connectionId,
|
|
1142
|
+
adapter: c.adapter
|
|
1143
|
+
}));
|
|
1144
|
+
data.args = args;
|
|
1145
|
+
return { ...n, data };
|
|
1146
|
+
});
|
|
1147
|
+
store.actions.nodes.setNodes(next);
|
|
1148
|
+
};
|
|
1149
|
+
const writeMapping = (field, value) => {
|
|
1150
|
+
if (!currentEdge) return;
|
|
1151
|
+
const store = useFlowStore.getState();
|
|
1152
|
+
const freshEdges = store.workflow.edges;
|
|
1153
|
+
const next = freshEdges.map((e) => {
|
|
1154
|
+
if (e.id !== currentEdge.id) return e;
|
|
1155
|
+
const def = updateMappingDefinition(
|
|
1156
|
+
e.mapping?.definition ?? {},
|
|
1157
|
+
field,
|
|
1158
|
+
value
|
|
1159
|
+
);
|
|
1160
|
+
return {
|
|
1161
|
+
...e,
|
|
1162
|
+
mapping: {
|
|
1163
|
+
definition: def,
|
|
1164
|
+
comment: e.mapping?.comment ?? `Map ${selectedSource} \u2192 ${nodeId ?? ""}`
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
});
|
|
1168
|
+
store.actions.edges.setEdges(next);
|
|
1169
|
+
};
|
|
1170
|
+
const clearMappingFromAllIncoming = (field) => {
|
|
1171
|
+
if (!nodeId) return;
|
|
1172
|
+
const store = useFlowStore.getState();
|
|
1173
|
+
const fresh = store.workflow.edges;
|
|
1174
|
+
let changed = false;
|
|
1175
|
+
const next = fresh.map((e) => {
|
|
1176
|
+
if (e.target !== nodeId || !e.mapping?.definition) return e;
|
|
1177
|
+
if (!(field in e.mapping.definition)) return e;
|
|
1178
|
+
const def = { ...e.mapping.definition };
|
|
1179
|
+
delete def[field];
|
|
1180
|
+
changed = true;
|
|
1181
|
+
return { ...e, mapping: { ...e.mapping, definition: def } };
|
|
1182
|
+
});
|
|
1183
|
+
if (changed) store.actions.edges.setEdges(next);
|
|
1184
|
+
};
|
|
1185
|
+
const writeFixed = (field, value) => {
|
|
1186
|
+
if (!nodeId) return;
|
|
1187
|
+
const store = useFlowStore.getState();
|
|
1188
|
+
const next = store.workflow.nodes.map((n) => {
|
|
1189
|
+
if (n.id !== nodeId) return n;
|
|
1190
|
+
const data = { ...asRecord(n.data) ?? {} };
|
|
1191
|
+
const args = { ...asRecord(data.args) ?? {} };
|
|
1192
|
+
args.state = fixedParameterState(
|
|
1193
|
+
asRecord(args.state) ?? {},
|
|
1194
|
+
params,
|
|
1195
|
+
field,
|
|
1196
|
+
value
|
|
1197
|
+
);
|
|
1198
|
+
data.args = args;
|
|
1199
|
+
return { ...n, data };
|
|
1200
|
+
});
|
|
1201
|
+
store.actions.nodes.setNodes(next);
|
|
1202
|
+
clearMappingFromAllIncoming(field);
|
|
1203
|
+
};
|
|
1204
|
+
const writeErrorConfig = (patch) => {
|
|
1205
|
+
if (!nodeId) return;
|
|
1206
|
+
const store = useFlowStore.getState();
|
|
1207
|
+
const next = store.workflow.nodes.map((n) => {
|
|
1208
|
+
if (n.id !== nodeId) return n;
|
|
1209
|
+
const data = { ...asRecord(n.data) ?? {} };
|
|
1210
|
+
const prev = errorConfigOf(n) ?? {};
|
|
1211
|
+
const merged = { ...prev, ...patch };
|
|
1212
|
+
if (patch.retryConfig !== void 0) {
|
|
1213
|
+
merged.retryConfig = patch.retryConfig ? { ...prev.retryConfig, ...patch.retryConfig } : void 0;
|
|
1214
|
+
}
|
|
1215
|
+
if (patch.timeout !== void 0) {
|
|
1216
|
+
merged.timeout = patch.timeout ? { ...prev.timeout, ...patch.timeout } : void 0;
|
|
1217
|
+
}
|
|
1218
|
+
data.errorConfig = merged;
|
|
1219
|
+
return { ...n, data };
|
|
1220
|
+
});
|
|
1221
|
+
store.actions.nodes.setNodes(next);
|
|
1222
|
+
};
|
|
1223
|
+
const writeNodeDataField = (field, value) => {
|
|
1224
|
+
if (!nodeId) return;
|
|
1225
|
+
const store = useFlowStore.getState();
|
|
1226
|
+
const next = store.workflow.nodes.map((n) => {
|
|
1227
|
+
if (n.id !== nodeId) return n;
|
|
1228
|
+
const data = { ...asRecord(n.data) ?? {} };
|
|
1229
|
+
if (value === void 0 || value === "" || value === false) {
|
|
1230
|
+
delete data[field];
|
|
1231
|
+
} else {
|
|
1232
|
+
data[field] = value;
|
|
1233
|
+
}
|
|
1234
|
+
return { ...n, data };
|
|
1235
|
+
});
|
|
1236
|
+
store.actions.nodes.setNodes(next);
|
|
1237
|
+
};
|
|
1238
|
+
const writeNotes = (value) => writeNodeDataField("notes", value);
|
|
1239
|
+
const writeExecutionSettings = (patch) => {
|
|
1240
|
+
if (!nodeId) return;
|
|
1241
|
+
const store = useFlowStore.getState();
|
|
1242
|
+
const next = store.workflow.nodes.map((n) => {
|
|
1243
|
+
if (n.id !== nodeId) return n;
|
|
1244
|
+
const data = { ...asRecord(n.data) ?? {} };
|
|
1245
|
+
const merged = {
|
|
1246
|
+
...asRecord(data.executionSettings) ?? {},
|
|
1247
|
+
...patch
|
|
1248
|
+
};
|
|
1249
|
+
if (merged.runOn === null) delete merged.runOn;
|
|
1250
|
+
if (!merged.alwaysOutputData && !merged.executeOnce && !merged.runOn) {
|
|
1251
|
+
delete data.executionSettings;
|
|
1252
|
+
} else {
|
|
1253
|
+
data.executionSettings = merged;
|
|
1254
|
+
}
|
|
1255
|
+
return { ...n, data };
|
|
1256
|
+
});
|
|
1257
|
+
store.actions.nodes.setNodes(next);
|
|
1258
|
+
};
|
|
1259
|
+
const toggleMode = (field, current) => {
|
|
1260
|
+
if (current === "expression") {
|
|
1261
|
+
clearMappingFromAllIncoming(field);
|
|
1262
|
+
}
|
|
1263
|
+
setModeByField((m) => ({
|
|
1264
|
+
...m,
|
|
1265
|
+
[field]: current === "expression" ? "fixed" : "expression"
|
|
1266
|
+
}));
|
|
1267
|
+
};
|
|
1268
|
+
const errorConfig = useMemo(() => errorConfigOf(node), [node]);
|
|
1269
|
+
const notes = useMemo(() => {
|
|
1270
|
+
const data = asRecord(node?.data);
|
|
1271
|
+
return typeof data?.notes === "string" ? data.notes : "";
|
|
1272
|
+
}, [node]);
|
|
1273
|
+
const executionSettings = useMemo(
|
|
1274
|
+
() => asRecord(asRecord(node?.data)?.executionSettings),
|
|
1275
|
+
[node]
|
|
1276
|
+
);
|
|
1277
|
+
const alwaysOutputData = executionSettings?.alwaysOutputData === true;
|
|
1278
|
+
const executeOnce = executionSettings?.executeOnce === true;
|
|
1279
|
+
const runOn = useMemo(() => runOnOf(executionSettings), [executionSettings]);
|
|
1280
|
+
const handleDropOnParam = (field) => (e) => {
|
|
1281
|
+
e.preventDefault();
|
|
1282
|
+
setDragField(null);
|
|
1283
|
+
const path = e.dataTransfer.getData(FIELD_PATH_MIME);
|
|
1284
|
+
if (path) {
|
|
1285
|
+
writeMapping(field, pathToExpression(path));
|
|
1286
|
+
setModeByField((m) => ({ ...m, [field]: "expression" }));
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
const api = {
|
|
1290
|
+
node,
|
|
1291
|
+
nodes,
|
|
1292
|
+
incoming,
|
|
1293
|
+
sourceIds,
|
|
1294
|
+
selectedSource,
|
|
1295
|
+
setSelectedSource,
|
|
1296
|
+
currentEdge,
|
|
1297
|
+
currentOp,
|
|
1298
|
+
params,
|
|
1299
|
+
argsState,
|
|
1300
|
+
mapping,
|
|
1301
|
+
inputFields,
|
|
1302
|
+
inputData,
|
|
1303
|
+
outputFields,
|
|
1304
|
+
pickerFields,
|
|
1305
|
+
connectionAdapters,
|
|
1306
|
+
selectedConnections,
|
|
1307
|
+
isWebhookTrigger,
|
|
1308
|
+
isFormTrigger,
|
|
1309
|
+
formUrl,
|
|
1310
|
+
triggerUrlError,
|
|
1311
|
+
isWebhookWait,
|
|
1312
|
+
webhookUrl,
|
|
1313
|
+
webhookUrlLoading,
|
|
1314
|
+
modeByField,
|
|
1315
|
+
dragField,
|
|
1316
|
+
setDragField,
|
|
1317
|
+
writeFixed,
|
|
1318
|
+
writeMapping,
|
|
1319
|
+
handleConnectionsChange,
|
|
1320
|
+
toggleMode,
|
|
1321
|
+
handleDropOnParam,
|
|
1322
|
+
errorConfig,
|
|
1323
|
+
writeErrorConfig,
|
|
1324
|
+
notes,
|
|
1325
|
+
writeNotes,
|
|
1326
|
+
alwaysOutputData,
|
|
1327
|
+
executeOnce,
|
|
1328
|
+
runOn,
|
|
1329
|
+
writeExecutionSettings,
|
|
1330
|
+
writeNodeDataField,
|
|
1331
|
+
readOnly: false
|
|
1332
|
+
};
|
|
1333
|
+
return readOnly ? readOnlyNodeConfig(api) : api;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
export { ConditionTreeBuilder, FieldTree, SwitchRulesEditor, asRecord, casesFromRules, coerceNumber, fixedParameterState, isStringOptionList, latestNodeLog, nodeSubtitle, nodeTitle, optionListText, optionListValues, optionText, paramsFromOperation, parseMappingValue, resolveLoadOptionsSpec, selectedOperationOf, useNodeConfig };
|