@burdenoff/fluidgrids-fe-sdk 2026.1110.11 → 2026.1110.13

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