@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,8 +1,8 @@
1
1
  import { useBuilderReadOnly } from './chunk-SDZEAIIE.js';
2
- import { STICKY_NOTE_TYPE, useFlowStore, resolveStickyColor, STICKY_COLORS, stickySurfaceStyle, nodeAdaptersOf, resolveOperationName, synchronizeOperationUpdate, isNodeTrigger, resolveOutputHandles, resolveInputHandles, outputHandlesSpacerHeight, WAIT_FOR_SIGNAL_NODE_TYPE, CODE_WORKFLOW_NODE_TYPE, readSignalName, LogicNodeCard, NodeCard, NodeCardHeader, NodeCardBody, InputHandles, OutputHandles, hasNodeManifest, NodeTypeIcon, nodeAccent, readCodeWorkflowState, interfaceFields, interfaceHandlers, partitionStickyNotes, DEFAULT_STICKY_COLOR, alignPlainOutputEdges } from './chunk-JT6NAHAX.js';
2
+ import { STICKY_NOTE_TYPE, useFlowStore, resolveStickyColor, STICKY_COLORS, stickySurfaceStyle, nodeAdaptersOf, resolveOperationName, synchronizeOperationUpdate, isNodeTrigger, resolveOutputHandles, resolveInputHandles, outputHandlesSpacerHeight, WAIT_FOR_SIGNAL_NODE_TYPE, CODE_WORKFLOW_NODE_TYPE, readSignalName, LogicNodeCard, NodeCard, NodeCardHeader, NodeCardBody, InputHandles, OutputHandles, hasNodeManifest, NodeTypeIcon, nodeAccent, readCodeWorkflowState, interfaceFields, interfaceHandlers, partitionStickyNotes, DEFAULT_STICKY_COLOR, alignPlainOutputEdges } from './chunk-ATTUQMTG.js';
3
3
  import { getGlobalStoreClient, createGlobalStoreClient, setGlobalStoreClient, resetGlobalStoreClient } from './chunk-TYI2XOSI.js';
4
4
  import { GetMyNodeInstallationsDocument } from './chunk-4FPY66GH.js';
5
- import { fetchAllNodeEndpointConfigs, NodeManifestDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowRunDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, WorkflowDefinitionDocument } from './chunk-AL5QPU3W.js';
5
+ import { fetchAllNodeEndpointConfigs, NodeManifestDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowRunDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, WorkflowDefinitionDocument, EventRegistrationsDocument } from './chunk-AL5QPU3W.js';
6
6
  import { useTr, ENGLISH_FALLBACK_TRANSLATOR, FluidGridsTranslationProvider } from './chunk-UULVIT25.js';
7
7
  import { waitForFluidGridsClient, getFluidGridsClient, getFluidGridsClientSafe, createFluidGridsClient, setFluidGridsClient, resetFluidGridsClient } from './chunk-PDODDDHY.js';
8
8
  import { throwIfResultError, describeOperationError, emitFluidGridsEvent, emitOperationErrorEvent, emitRunCompletedOnce } from './chunk-JKLZ6YZ3.js';
@@ -1967,14 +1967,14 @@ function normalizeOperations(raw) {
1967
1967
  }
1968
1968
  return [];
1969
1969
  }
1970
- function resolveNodeOperation(data, fallbackOperations) {
1970
+ function resolveNodeOperation(data, fallbackOperations2) {
1971
1971
  const nodeData = isRecord4(data?.nodeData) ? data?.nodeData : void 0;
1972
1972
  let operations = normalizeOperations(nodeData?.operations);
1973
1973
  if (operations.length === 0) {
1974
1974
  operations = normalizeOperations(data?.operations);
1975
1975
  }
1976
- if (operations.length === 0 && fallbackOperations !== void 0) {
1977
- operations = normalizeOperations(fallbackOperations);
1976
+ if (operations.length === 0 && fallbackOperations2 !== void 0) {
1977
+ operations = normalizeOperations(fallbackOperations2);
1978
1978
  }
1979
1979
  const opName = typeof data?.operation_name === "string" ? data.operation_name : void 0;
1980
1980
  const selected = operations.find((op) => op.name === opName) ?? operations[0];
@@ -2066,8 +2066,8 @@ function formatParameterValue(value) {
2066
2066
  if (value === void 0 || value === null) return "";
2067
2067
  return typeof value === "string" ? value : JSON.stringify(value, null, 2);
2068
2068
  }
2069
- function normalizeNodeParameters(data, fallbackOperations, mappedFields = /* @__PURE__ */ new Set()) {
2070
- const operation = resolveNodeOperation(data, fallbackOperations).selected;
2069
+ function normalizeNodeParameters(data, fallbackOperations2, mappedFields = /* @__PURE__ */ new Set()) {
2070
+ const operation = resolveNodeOperation(data, fallbackOperations2).selected;
2071
2071
  const fields = record(operation?.input_fields);
2072
2072
  const args = record(data.args);
2073
2073
  const state = record(args?.state) ?? {};
@@ -2091,7 +2091,7 @@ function normalizeNodeParameters(data, fallbackOperations, mappedFields = /* @__
2091
2091
  }
2092
2092
  return { ...data, args: { ...args, state: Object.fromEntries(normalized) } };
2093
2093
  }
2094
- function normalizeWorkflowParameters(nodes, edges, fallbackOperations) {
2094
+ function normalizeWorkflowParameters(nodes, edges, fallbackOperations2) {
2095
2095
  return nodes.map((node) => {
2096
2096
  if (node.data.disabled === true) return node;
2097
2097
  const mappedFields = new Set(
@@ -2108,7 +2108,7 @@ function normalizeWorkflowParameters(nodes, edges, fallbackOperations) {
2108
2108
  ...node,
2109
2109
  data: normalizeNodeParameters(
2110
2110
  node.data,
2111
- fallbackOperations(node.type),
2111
+ fallbackOperations2(node.type),
2112
2112
  mappedFields
2113
2113
  )
2114
2114
  };
@@ -3941,11 +3941,1627 @@ function useGuardedNavigate() {
3941
3941
  [navigate]
3942
3942
  );
3943
3943
  }
3944
- function useRunGuard(startRun) {
3944
+
3945
+ // src/builder/lib/required-parameters.ts
3946
+ function isEmptyParameterValue(value) {
3947
+ if (value === void 0 || value === null) return true;
3948
+ if (typeof value === "string") return value.trim() === "";
3949
+ if (Array.isArray(value)) return value.length === 0;
3950
+ return false;
3951
+ }
3952
+ function missingRequiredParameters(params, state, boundFields = /* @__PURE__ */ new Set()) {
3953
+ const defaults = {};
3954
+ for (const param of params) {
3955
+ if (param.defaultValue !== void 0)
3956
+ defaults[param.name] = param.defaultValue;
3957
+ }
3958
+ const missing = [];
3959
+ for (const param of params) {
3960
+ if (param.required !== true || boundFields.has(param.name)) continue;
3961
+ if (!isParamVisible(param, state, defaults)) continue;
3962
+ const value = state[param.name] === void 0 ? param.defaultValue : state[param.name];
3963
+ if (!isEmptyParameterValue(value)) continue;
3964
+ missing.push({
3965
+ name: param.name,
3966
+ label: typeof param.label === "string" && param.label.trim() !== "" ? param.label : param.name
3967
+ });
3968
+ }
3969
+ return missing;
3970
+ }
3971
+ function asRecord3(value) {
3972
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
3973
+ }
3974
+ function nodeStateOf(node) {
3975
+ return asRecord3(asRecord3(asRecord3(node.data)?.args)?.state) ?? {};
3976
+ }
3977
+ function isDisabledNode(node) {
3978
+ return asRecord3(node.data)?.disabled === true;
3979
+ }
3980
+ function boundFieldsOf(nodeId, edges) {
3981
+ const bound = /* @__PURE__ */ new Set();
3982
+ for (const edge of edges) {
3983
+ if (edge.target !== nodeId) continue;
3984
+ const definition = asRecord3(edge.mapping?.definition) ?? asRecord3(asRecord3(asRecord3(edge.data)?.mapping)?.definition);
3985
+ for (const field of Object.keys(definition ?? {})) bound.add(field);
3986
+ }
3987
+ return bound;
3988
+ }
3989
+ function nodeParameterIssues(nodes, edges, paramsOf, labelOf) {
3990
+ const issues = [];
3991
+ for (const node of nodes) {
3992
+ if (isDisabledNode(node)) continue;
3993
+ const missing = missingRequiredParameters(
3994
+ paramsOf(node),
3995
+ nodeStateOf(node),
3996
+ boundFieldsOf(node.id, edges)
3997
+ );
3998
+ if (missing.length > 0)
3999
+ issues.push({ nodeId: node.id, nodeLabel: labelOf(node), missing });
4000
+ }
4001
+ return issues;
4002
+ }
4003
+
4004
+ // src/builder/components/ndv/field-label.ts
4005
+ var UPPERCASE_WORDS = /* @__PURE__ */ new Set([
4006
+ "api",
4007
+ "csv",
4008
+ "html",
4009
+ "http",
4010
+ "https",
4011
+ "id",
4012
+ "ip",
4013
+ "json",
4014
+ "jwt",
4015
+ "sql",
4016
+ "ssl",
4017
+ "url",
4018
+ "uri",
4019
+ "utc",
4020
+ "xml"
4021
+ ]);
4022
+ var PLURAL_ACRONYMS = /* @__PURE__ */ new Map([
4023
+ ["apis", "APIs"],
4024
+ ["ids", "IDs"],
4025
+ ["ips", "IPs"],
4026
+ ["uris", "URIs"],
4027
+ ["urls", "URLs"]
4028
+ ]);
4029
+ function titleWord(word) {
4030
+ const lower = word.toLowerCase();
4031
+ const plural = PLURAL_ACRONYMS.get(lower);
4032
+ if (plural) return plural;
4033
+ if (UPPERCASE_WORDS.has(lower)) return lower.toUpperCase();
4034
+ return lower.charAt(0).toUpperCase() + lower.slice(1);
4035
+ }
4036
+ function humanizeFieldName(name) {
4037
+ 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);
4038
+ return words.length > 0 ? words.map(titleWord).join(" ") : name;
4039
+ }
4040
+ function fieldLabel(name, spec) {
4041
+ for (const key of ["label", "displayName", "title"]) {
4042
+ const value = spec?.[key];
4043
+ if (typeof value === "string" && value.trim() !== "") return value.trim();
4044
+ }
4045
+ return humanizeFieldName(name);
4046
+ }
4047
+
4048
+ // src/builder/lib/mapping-input.ts
4049
+ function latestNodeLog(logs, nodeId, runId) {
4050
+ return nodeId && runId ? logs.filter((log) => log.nodeId === nodeId && log.runId === runId).at(-1) : void 0;
4051
+ }
4052
+ function resolveMappingInput({
4053
+ source,
4054
+ operation,
4055
+ runId,
4056
+ logs
4057
+ }) {
4058
+ const sourceLog = latestNodeLog(logs, source?.id, runId);
4059
+ const data = sourceLog?.output ?? void 0;
4060
+ return {
4061
+ data,
4062
+ fields: data !== void 0 ? fieldsFromValue(data) : source ? declaredOutputFields(source.type, operation) : []
4063
+ };
4064
+ }
4065
+
4066
+ // src/builder/lib/mapping-values.ts
4067
+ function isMappingValue(value) {
4068
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
4069
+ return true;
4070
+ }
4071
+ if (typeof value === "number") return Number.isFinite(value);
4072
+ if (Array.isArray(value)) return value.every(isMappingValue);
4073
+ return typeof value === "object" && Object.values(value).every(isMappingValue);
4074
+ }
4075
+ function parseMappingValue(text) {
4076
+ try {
4077
+ const value = JSON.parse(text);
4078
+ if (isMappingValue(value)) return { valid: true, value };
4079
+ } catch {
4080
+ return {
4081
+ valid: false,
4082
+ error: "Enter valid JSON before applying the mapping."
4083
+ };
4084
+ }
4085
+ return { valid: false, error: "JSON mapping numbers must be finite." };
4086
+ }
4087
+ function updateMappingDefinition(definition, field, value) {
4088
+ if (value === void 0 || typeof value === "string" && !value.trim()) {
4089
+ const next = { ...definition };
4090
+ delete next[field];
4091
+ return next;
4092
+ }
4093
+ return { ...definition, [field]: value };
4094
+ }
4095
+ var FIELD_PATH_MIME = "application/x-fg-field-path";
4096
+ var TYPE_ICON = {
4097
+ string: "mdi:format-text",
4098
+ number: "mdi:numeric",
4099
+ integer: "mdi:numeric",
4100
+ boolean: "mdi:toggle-switch-outline",
4101
+ array: "mdi:code-brackets",
4102
+ object: "mdi:code-braces",
4103
+ any: "mdi:help-circle-outline"
4104
+ };
4105
+ var DEFAULT_EXPAND_DEPTH = 3;
4106
+ function FieldRow({ field, depth, draggable }) {
4107
+ const [open, setOpen] = useState(depth < DEFAULT_EXPAND_DEPTH);
4108
+ const hasChildren = !!field.children && field.children.length > 0;
4109
+ const handleDragStart = (e) => {
4110
+ e.dataTransfer.setData(FIELD_PATH_MIME, field.path);
4111
+ e.dataTransfer.setData("text/plain", pathToExpression(field.path));
4112
+ e.dataTransfer.effectAllowed = "copy";
4113
+ };
4114
+ return /* @__PURE__ */ jsxs("div", { children: [
4115
+ /* @__PURE__ */ jsxs(
4116
+ "div",
4117
+ {
4118
+ 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" : ""}`,
4119
+ style: { paddingLeft: `${depth * 14 + 6}px` },
4120
+ draggable,
4121
+ onDragStart: draggable ? handleDragStart : void 0,
4122
+ "data-field-path": field.path,
4123
+ "data-schema-variant": field.schemaVariant,
4124
+ title: field.description || field.path,
4125
+ children: [
4126
+ hasChildren ? /* @__PURE__ */ jsx(
4127
+ "button",
4128
+ {
4129
+ type: "button",
4130
+ onClick: () => setOpen((v) => !v),
4131
+ className: "flex size-4 items-center justify-center text-muted-foreground hover:text-foreground",
4132
+ children: /* @__PURE__ */ jsx(
4133
+ Icon,
4134
+ {
4135
+ icon: open ? "mdi:chevron-down" : "mdi:chevron-right",
4136
+ className: "size-3.5"
4137
+ }
4138
+ )
4139
+ }
4140
+ ) : /* @__PURE__ */ jsx("span", { className: "inline-block size-4" }),
4141
+ /* @__PURE__ */ jsx(
4142
+ Icon,
4143
+ {
4144
+ icon: TYPE_ICON[field.type] ?? TYPE_ICON.any,
4145
+ className: "size-3.5 shrink-0 text-primary/70"
4146
+ }
4147
+ ),
4148
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: field.key }),
4149
+ /* @__PURE__ */ jsxs("span", { className: "ml-auto shrink-0 text-[10px] lowercase tracking-wide text-muted-foreground", children: [
4150
+ field.type,
4151
+ field.required ? " *" : ""
4152
+ ] })
4153
+ ]
4154
+ }
4155
+ ),
4156
+ hasChildren && open && /* @__PURE__ */ jsx("div", { children: field.children.map((child) => /* @__PURE__ */ jsx(
4157
+ FieldRow,
4158
+ {
4159
+ field: child,
4160
+ depth: depth + 1,
4161
+ draggable
4162
+ },
4163
+ child.schemaVariant ?? child.path
4164
+ )) })
4165
+ ] });
4166
+ }
4167
+ function FieldTree({
4168
+ fields,
4169
+ draggable = false,
4170
+ emptyMessage = "No fields available."
4171
+ }) {
4172
+ if (fields.length === 0) {
4173
+ return /* @__PURE__ */ jsx("div", { className: "px-2 py-4 text-xs text-card-foreground/50", children: emptyMessage });
4174
+ }
4175
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5", children: fields.map((field) => /* @__PURE__ */ jsx(
4176
+ FieldRow,
4177
+ {
4178
+ field,
4179
+ depth: 0,
4180
+ draggable
4181
+ },
4182
+ field.schemaVariant ?? field.path
4183
+ )) });
4184
+ }
4185
+ function emptyGroup() {
4186
+ return { type: "group", combinator: "AND", children: [] };
4187
+ }
4188
+ function emptyRule(field = "") {
4189
+ return { type: "rule", field, operator: "==", value: "" };
4190
+ }
4191
+ var OPERATORS = {
4192
+ string: [
4193
+ { value: "==", label: "equals", needsValue: true },
4194
+ { value: "!=", label: "not equals", needsValue: true },
4195
+ { value: "contains", label: "contains", needsValue: true },
4196
+ { value: "notContains", label: "does not contain", needsValue: true },
4197
+ { value: "startsWith", label: "starts with", needsValue: true },
4198
+ { value: "endsWith", label: "ends with", needsValue: true },
4199
+ { value: "regex", label: "matches regex", needsValue: true },
4200
+ { value: "isEmpty", label: "is empty", needsValue: false },
4201
+ { value: "isNotEmpty", label: "is not empty", needsValue: false },
4202
+ { value: "isNull", label: "is null", needsValue: false },
4203
+ { value: "isNotNull", label: "is not null", needsValue: false }
4204
+ ],
4205
+ number: [
4206
+ { value: "==", label: "equals", needsValue: true },
4207
+ { value: "!=", label: "not equals", needsValue: true },
4208
+ { value: ">", label: "greater than", needsValue: true },
4209
+ { value: ">=", label: "greater or equal", needsValue: true },
4210
+ { value: "<", label: "less than", needsValue: true },
4211
+ { value: "<=", label: "less or equal", needsValue: true },
4212
+ { value: "isNull", label: "is null", needsValue: false },
4213
+ { value: "isNotNull", label: "is not null", needsValue: false }
4214
+ ],
4215
+ boolean: [
4216
+ { value: "isTrue", label: "is true", needsValue: false },
4217
+ { value: "isFalse", label: "is false", needsValue: false },
4218
+ { value: "isNull", label: "is null", needsValue: false },
4219
+ { value: "isNotNull", label: "is not null", needsValue: false }
4220
+ ],
4221
+ array: [
4222
+ { value: "contains", label: "contains", needsValue: true },
4223
+ { value: "notContains", label: "does not contain", needsValue: true },
4224
+ { value: "isEmpty", label: "is empty", needsValue: false },
4225
+ { value: "isNotEmpty", label: "is not empty", needsValue: false },
4226
+ { value: "isNull", label: "is null", needsValue: false },
4227
+ { value: "isNotNull", label: "is not null", needsValue: false }
4228
+ ],
4229
+ object: [
4230
+ { value: "hasProperty", label: "has property", needsValue: true },
4231
+ { value: "isEmpty", label: "is empty", needsValue: false },
4232
+ { value: "isNotEmpty", label: "is not empty", needsValue: false },
4233
+ { value: "isNull", label: "is null", needsValue: false },
4234
+ { value: "isNotNull", label: "is not null", needsValue: false }
4235
+ ],
4236
+ any: [
4237
+ { value: "==", label: "equals", needsValue: true },
4238
+ { value: "!=", label: "not equals", needsValue: true },
4239
+ { value: ">", label: "greater than", needsValue: true },
4240
+ { value: "<", label: "less than", needsValue: true },
4241
+ { value: "contains", label: "contains", needsValue: true },
4242
+ { value: "isEmpty", label: "is empty", needsValue: false },
4243
+ { value: "isNotEmpty", label: "is not empty", needsValue: false },
4244
+ { value: "isNull", label: "is null", needsValue: false },
4245
+ { value: "isNotNull", label: "is not null", needsValue: false }
4246
+ ]
4247
+ };
4248
+ function operatorsFor(type) {
4249
+ return OPERATORS[type ?? "any"] ?? OPERATORS.any;
4250
+ }
4251
+ function flattenFields(nodes) {
4252
+ const out = [];
4253
+ const walk = (list) => {
4254
+ for (const node of list) {
4255
+ if (node.path) out.push({ path: node.path, type: node.type ?? "any" });
4256
+ if (Array.isArray(node.children)) walk(node.children);
4257
+ }
4258
+ };
4259
+ walk(nodes);
4260
+ return out.sort((a, b) => a.path.localeCompare(b.path));
4261
+ }
4262
+ 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";
4263
+ function RuleRow({
4264
+ rule,
4265
+ fields,
4266
+ onChange,
4267
+ onRemove
4268
+ }) {
4269
+ const tr = useTr();
4270
+ const fieldType = fields.find((f) => f.path === rule.field)?.type ?? "any";
4271
+ const ops = operatorsFor(fieldType);
4272
+ const currentOp = ops.find((o) => o.value === rule.operator) ?? ops[0];
4273
+ const knownField = fields.some((f) => f.path === rule.field);
4274
+ return /* @__PURE__ */ jsxs(
4275
+ "div",
4276
+ {
4277
+ className: "flex flex-wrap items-center gap-1.5",
4278
+ "data-testid": "condition-rule",
4279
+ children: [
4280
+ fields.length > 0 && (knownField || rule.field === "") ? /* @__PURE__ */ jsxs(
4281
+ "select",
4282
+ {
4283
+ value: rule.field,
4284
+ onChange: (e) => {
4285
+ const nextField = e.target.value;
4286
+ const nextType = fields.find((f) => f.path === nextField)?.type ?? "any";
4287
+ const nextOps = operatorsFor(nextType);
4288
+ const operator = nextOps.some((o) => o.value === rule.operator) ? rule.operator : nextOps[0].value;
4289
+ onChange({ ...rule, field: nextField, operator });
4290
+ },
4291
+ className: cn(rowInput, "max-w-[180px] flex-1"),
4292
+ "data-testid": "condition-field",
4293
+ children: [
4294
+ /* @__PURE__ */ jsx("option", { value: "", children: tr(
4295
+ "fluidgrids.builder.conditionBuilder.selectField",
4296
+ "Select field\u2026"
4297
+ ) }),
4298
+ fields.map((f) => /* @__PURE__ */ jsxs("option", { value: f.path, children: [
4299
+ f.path,
4300
+ " (",
4301
+ f.type,
4302
+ ")"
4303
+ ] }, f.path)),
4304
+ /* @__PURE__ */ jsx("option", { value: "__custom__", children: tr(
4305
+ "fluidgrids.builder.conditionBuilder.customPath",
4306
+ "Custom path\u2026"
4307
+ ) })
4308
+ ]
4309
+ }
4310
+ ) : /* @__PURE__ */ jsx(
4311
+ "input",
4312
+ {
4313
+ type: "text",
4314
+ value: rule.field === "__custom__" ? "" : rule.field,
4315
+ placeholder: "field.path",
4316
+ onChange: (e) => onChange({ ...rule, field: e.target.value }),
4317
+ className: cn(rowInput, "max-w-[180px] flex-1 font-mono"),
4318
+ "data-testid": "condition-field"
4319
+ }
4320
+ ),
4321
+ /* @__PURE__ */ jsx(
4322
+ "select",
4323
+ {
4324
+ value: currentOp.value,
4325
+ onChange: (e) => onChange({ ...rule, operator: e.target.value }),
4326
+ className: rowInput,
4327
+ "data-testid": "condition-operator",
4328
+ children: ops.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
4329
+ }
4330
+ ),
4331
+ currentOp.needsValue && /* @__PURE__ */ jsx(
4332
+ "input",
4333
+ {
4334
+ type: fieldType === "number" ? "number" : "text",
4335
+ value: rule.value === null || rule.value === void 0 ? "" : String(rule.value),
4336
+ placeholder: tr(
4337
+ "fluidgrids.builder.conditionBuilder.valuePlaceholder",
4338
+ "value"
4339
+ ),
4340
+ onChange: (e) => {
4341
+ const raw = e.target.value;
4342
+ const value = fieldType === "number" && raw.trim() !== "" && !Number.isNaN(Number(raw)) ? Number(raw) : raw;
4343
+ onChange({ ...rule, value });
4344
+ },
4345
+ className: cn(rowInput, "min-w-[90px] flex-1"),
4346
+ "data-testid": "condition-value"
4347
+ }
4348
+ ),
4349
+ /* @__PURE__ */ jsx(
4350
+ "button",
4351
+ {
4352
+ type: "button",
4353
+ onClick: onRemove,
4354
+ title: tr(
4355
+ "fluidgrids.builder.conditionBuilder.removeCondition",
4356
+ "Remove condition"
4357
+ ),
4358
+ className: "rounded p-1 text-muted-foreground hover:text-[var(--color-status-error-text)]",
4359
+ children: /* @__PURE__ */ jsx(Icon, { icon: "mynaui:trash", className: "size-3.5" })
4360
+ }
4361
+ )
4362
+ ]
4363
+ }
4364
+ );
4365
+ }
4366
+ function GroupEditor({
4367
+ group,
4368
+ fields,
4369
+ depth,
4370
+ onChange,
4371
+ onRemove
4372
+ }) {
4373
+ const tr = useTr();
4374
+ const setChild = (index, child) => {
4375
+ const children = group.children.slice();
4376
+ children[index] = child;
4377
+ onChange({ ...group, children });
4378
+ };
4379
+ const removeChild = (index) => {
4380
+ onChange({
4381
+ ...group,
4382
+ children: group.children.filter((_, i) => i !== index)
4383
+ });
4384
+ };
4385
+ return /* @__PURE__ */ jsxs(
4386
+ "div",
4387
+ {
4388
+ className: cn(
4389
+ "flex flex-col gap-1.5 rounded-md",
4390
+ depth > 0 && "border border-border/70 bg-muted/20 p-2"
4391
+ ),
4392
+ "data-testid": `condition-group-depth-${depth}`,
4393
+ children: [
4394
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
4395
+ /* @__PURE__ */ jsx("div", { className: "flex overflow-hidden rounded-md border border-input text-[10px] font-semibold", children: ["AND", "OR"].map((c) => /* @__PURE__ */ jsx(
4396
+ "button",
4397
+ {
4398
+ type: "button",
4399
+ onClick: () => onChange({ ...group, combinator: c }),
4400
+ className: cn(
4401
+ "px-2 py-0.5",
4402
+ group.combinator === c ? "bg-primary text-primary-foreground" : "bg-background text-muted-foreground hover:text-foreground"
4403
+ ),
4404
+ "data-testid": `combinator-${c}`,
4405
+ children: c
4406
+ },
4407
+ c
4408
+ )) }),
4409
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: group.combinator === "AND" ? tr(
4410
+ "fluidgrids.builder.conditionBuilder.combinatorAndHint",
4411
+ "all conditions must match"
4412
+ ) : tr(
4413
+ "fluidgrids.builder.conditionBuilder.combinatorOrHint",
4414
+ "any condition may match"
4415
+ ) }),
4416
+ onRemove && /* @__PURE__ */ jsx(
4417
+ "button",
4418
+ {
4419
+ type: "button",
4420
+ onClick: onRemove,
4421
+ title: tr(
4422
+ "fluidgrids.builder.conditionBuilder.removeGroup",
4423
+ "Remove group"
4424
+ ),
4425
+ className: "ml-auto rounded p-1 text-muted-foreground hover:text-[var(--color-status-error-text)]",
4426
+ children: /* @__PURE__ */ jsx(Icon, { icon: "mynaui:trash", className: "size-3.5" })
4427
+ }
4428
+ )
4429
+ ] }),
4430
+ group.children.map(
4431
+ (child, index) => child.type === "rule" ? /* @__PURE__ */ jsx(
4432
+ RuleRow,
4433
+ {
4434
+ rule: child,
4435
+ fields,
4436
+ onChange: (next) => setChild(index, next),
4437
+ onRemove: () => removeChild(index)
4438
+ },
4439
+ index
4440
+ ) : /* @__PURE__ */ jsx(
4441
+ GroupEditor,
4442
+ {
4443
+ group: child,
4444
+ fields,
4445
+ depth: depth + 1,
4446
+ onChange: (next) => setChild(index, next),
4447
+ onRemove: () => removeChild(index)
4448
+ },
4449
+ index
4450
+ )
4451
+ ),
4452
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
4453
+ /* @__PURE__ */ jsxs(
4454
+ "button",
4455
+ {
4456
+ type: "button",
4457
+ onClick: () => onChange({
4458
+ ...group,
4459
+ children: [...group.children, emptyRule(fields[0]?.path ?? "")]
4460
+ }),
4461
+ className: "flex items-center gap-1 text-[11px] font-medium text-primary hover:underline",
4462
+ "data-testid": "add-condition",
4463
+ children: [
4464
+ /* @__PURE__ */ jsx(Icon, { icon: "basil:add-solid", className: "size-3.5" }),
4465
+ " ",
4466
+ tr(
4467
+ "fluidgrids.builder.conditionBuilder.addCondition",
4468
+ "Add condition"
4469
+ )
4470
+ ]
4471
+ }
4472
+ ),
4473
+ depth < 2 && /* @__PURE__ */ jsxs(
4474
+ "button",
4475
+ {
4476
+ type: "button",
4477
+ onClick: () => onChange({
4478
+ ...group,
4479
+ children: [...group.children, emptyGroup()]
4480
+ }),
4481
+ className: "flex items-center gap-1 text-[11px] font-medium text-muted-foreground hover:text-foreground",
4482
+ "data-testid": "add-group",
4483
+ children: [
4484
+ /* @__PURE__ */ jsx(Icon, { icon: "basil:add-solid", className: "size-3.5" }),
4485
+ " ",
4486
+ tr("fluidgrids.builder.conditionBuilder.addGroup", "Add group")
4487
+ ]
4488
+ }
4489
+ )
4490
+ ] })
4491
+ ]
4492
+ }
4493
+ );
4494
+ }
4495
+ function ConditionTreeBuilder({
4496
+ value,
4497
+ fields,
4498
+ onChange
4499
+ }) {
4500
+ const tree = value && typeof value === "object" && value.type === "group" ? value : emptyGroup();
4501
+ return /* @__PURE__ */ jsx("div", { "data-testid": "condition-tree-builder", children: /* @__PURE__ */ jsx(
4502
+ GroupEditor,
4503
+ {
4504
+ group: tree,
4505
+ fields,
4506
+ depth: 0,
4507
+ onChange: (next) => onChange(next.children.length === 0 ? void 0 : next)
4508
+ }
4509
+ ) });
4510
+ }
4511
+ function newRuleHandle(existing) {
4512
+ let n = existing.length;
4513
+ const taken = new Set(existing.map((r) => r.handle));
4514
+ let handle = `rule-${n}`;
4515
+ while (taken.has(handle)) handle = `rule-${++n}`;
4516
+ return handle;
4517
+ }
4518
+ function casesFromRules(rules) {
4519
+ const cases = {};
4520
+ rules.forEach((rule, index) => {
4521
+ cases[rule.name || `Rule ${index + 1}`] = rule.handle;
4522
+ });
4523
+ return cases;
4524
+ }
4525
+ function SwitchRulesEditor({
4526
+ rules,
4527
+ fallbackHandle,
4528
+ fields,
4529
+ onChange
4530
+ }) {
4531
+ const tr = useTr();
4532
+ const setRule = (index, rule) => {
4533
+ const next = rules.slice();
4534
+ next[index] = rule;
4535
+ onChange({ rules: next, fallbackHandle });
4536
+ };
4537
+ const move = (index, dir) => {
4538
+ const target = index + dir;
4539
+ if (target < 0 || target >= rules.length) return;
4540
+ const next = rules.slice();
4541
+ const tmp = next[index];
4542
+ next[index] = next[target];
4543
+ next[target] = tmp;
4544
+ onChange({ rules: next, fallbackHandle });
4545
+ };
4546
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", "data-testid": "switch-rules-editor", children: [
4547
+ rules.map((rule, index) => /* @__PURE__ */ jsxs(
4548
+ "div",
4549
+ {
4550
+ className: "rounded-md border border-border/70 bg-muted/10 p-2",
4551
+ "data-testid": `switch-rule-${index}`,
4552
+ children: [
4553
+ /* @__PURE__ */ jsxs("div", { className: "mb-1.5 flex items-center gap-1.5", children: [
4554
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-semibold text-muted-foreground", children: [
4555
+ index + 1,
4556
+ "."
4557
+ ] }),
4558
+ /* @__PURE__ */ jsx(
4559
+ "input",
4560
+ {
4561
+ type: "text",
4562
+ value: rule.name,
4563
+ placeholder: `Rule ${index + 1}`,
4564
+ onChange: (e) => setRule(index, { ...rule, name: e.target.value }),
4565
+ className: cn(rowInput, "flex-1 font-medium"),
4566
+ "data-testid": "switch-rule-name"
4567
+ }
4568
+ ),
4569
+ /* @__PURE__ */ jsx(
4570
+ "button",
4571
+ {
4572
+ type: "button",
4573
+ onClick: () => move(index, -1),
4574
+ disabled: index === 0,
4575
+ title: tr(
4576
+ "fluidgrids.builder.switchRulesEditor.moveUp",
4577
+ "Move up"
4578
+ ),
4579
+ className: "rounded p-0.5 text-muted-foreground hover:text-foreground disabled:opacity-30",
4580
+ children: /* @__PURE__ */ jsx(Icon, { icon: "mdi:chevron-up", className: "size-4" })
4581
+ }
4582
+ ),
4583
+ /* @__PURE__ */ jsx(
4584
+ "button",
4585
+ {
4586
+ type: "button",
4587
+ onClick: () => move(index, 1),
4588
+ disabled: index === rules.length - 1,
4589
+ title: tr(
4590
+ "fluidgrids.builder.switchRulesEditor.moveDown",
4591
+ "Move down"
4592
+ ),
4593
+ className: "rounded p-0.5 text-muted-foreground hover:text-foreground disabled:opacity-30",
4594
+ children: /* @__PURE__ */ jsx(Icon, { icon: "mdi:chevron-down", className: "size-4" })
4595
+ }
4596
+ ),
4597
+ /* @__PURE__ */ jsx(
4598
+ "button",
4599
+ {
4600
+ type: "button",
4601
+ onClick: () => onChange({
4602
+ rules: rules.filter((_, i) => i !== index),
4603
+ fallbackHandle
4604
+ }),
4605
+ title: tr(
4606
+ "fluidgrids.builder.switchRulesEditor.removeRule",
4607
+ "Remove rule"
4608
+ ),
4609
+ className: "rounded p-0.5 text-muted-foreground hover:text-[var(--color-status-error-text)]",
4610
+ children: /* @__PURE__ */ jsx(Icon, { icon: "mynaui:trash", className: "size-3.5" })
4611
+ }
4612
+ )
4613
+ ] }),
4614
+ /* @__PURE__ */ jsx(
4615
+ ConditionTreeBuilder,
4616
+ {
4617
+ value: rule.conditionTree,
4618
+ fields,
4619
+ onChange: (tree) => setRule(index, { ...rule, conditionTree: tree ?? emptyGroup() })
4620
+ }
4621
+ )
4622
+ ]
4623
+ },
4624
+ rule.handle
4625
+ )),
4626
+ /* @__PURE__ */ jsxs(
4627
+ "button",
4628
+ {
4629
+ type: "button",
4630
+ onClick: () => onChange({
4631
+ rules: [
4632
+ ...rules,
4633
+ {
4634
+ name: `Rule ${rules.length + 1}`,
4635
+ handle: newRuleHandle(rules),
4636
+ conditionTree: emptyGroup()
4637
+ }
4638
+ ],
4639
+ fallbackHandle
4640
+ }),
4641
+ className: "flex w-fit items-center gap-1 text-[11px] font-medium text-primary hover:underline",
4642
+ "data-testid": "add-switch-rule",
4643
+ children: [
4644
+ /* @__PURE__ */ jsx(Icon, { icon: "basil:add-solid", className: "size-3.5" }),
4645
+ " ",
4646
+ tr("fluidgrids.builder.switchRulesEditor.addRule", "Add rule")
4647
+ ]
4648
+ }
4649
+ ),
4650
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-2", children: [
4651
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: tr(
4652
+ "fluidgrids.builder.switchRulesEditor.fallbackLabel",
4653
+ "Fallback output (no rule matches):"
4654
+ ) }),
4655
+ /* @__PURE__ */ jsx(
4656
+ "input",
4657
+ {
4658
+ type: "text",
4659
+ value: fallbackHandle,
4660
+ placeholder: "default",
4661
+ onChange: (e) => onChange({ rules, fallbackHandle: e.target.value }),
4662
+ className: cn(rowInput, "w-28"),
4663
+ "data-testid": "switch-fallback"
4664
+ }
4665
+ )
4666
+ ] }),
4667
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] leading-snug text-muted-foreground", children: tr(
4668
+ "fluidgrids.builder.switchRulesEditor.rulesHint",
4669
+ "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."
4670
+ ) })
4671
+ ] });
4672
+ }
4673
+ function useWebhookRegistration(workflowKey, triggerNodeId, enabled = true) {
4674
+ const key = enabled && workflowKey && triggerNodeId ? JSON.stringify([workflowKey, triggerNodeId]) : null;
4675
+ const requestId = useRef(0);
4676
+ const [state, setState] = useState({ key: null, registration: null, loading: false, error: null });
4677
+ const fetchData = useCallback(async () => {
4678
+ const currentRequest = ++requestId.current;
4679
+ if (!key || !workflowKey || !triggerNodeId) return;
4680
+ const client = getFluidGridsClientSafe();
4681
+ if (!client) {
4682
+ setState({
4683
+ key,
4684
+ registration: null,
4685
+ loading: false,
4686
+ error: new Error("Workflow client unavailable")
4687
+ });
4688
+ return;
4689
+ }
4690
+ setState({ key, registration: null, loading: true, error: null });
4691
+ try {
4692
+ const result = await client.query({
4693
+ query: EventRegistrationsDocument,
4694
+ variables: { workflowKey },
4695
+ fetchPolicy: "network-only"
4696
+ });
4697
+ const match = result.data?.eventRegistrations?.find(
4698
+ (r) => r.triggerNodeId === triggerNodeId
4699
+ ) ?? null;
4700
+ if (currentRequest === requestId.current)
4701
+ setState({ key, registration: match, loading: false, error: null });
4702
+ } catch (err) {
4703
+ if (currentRequest === requestId.current)
4704
+ setState({
4705
+ key,
4706
+ registration: null,
4707
+ loading: false,
4708
+ error: err instanceof Error ? err : new Error("Unable to load trigger URL")
4709
+ });
4710
+ }
4711
+ }, [key, workflowKey, triggerNodeId]);
4712
+ useEffect(() => {
4713
+ fetchData();
4714
+ return () => {
4715
+ requestId.current += 1;
4716
+ };
4717
+ }, [fetchData]);
4718
+ useEffect(() => {
4719
+ if (!enabled || !workflowKey) return;
4720
+ return subscribeWorkflowSaved(workflowKey, () => {
4721
+ void fetchData();
4722
+ });
4723
+ }, [enabled, workflowKey, fetchData]);
4724
+ const current = state.key === key && key !== null ? state : null;
4725
+ const registration = current?.registration ?? null;
4726
+ return {
4727
+ registration,
4728
+ webhookUrl: registration?.webhookUrl ?? null,
4729
+ formUrl: registration?.formUrl ?? null,
4730
+ loading: current?.loading ?? !!key,
4731
+ error: current?.error ?? null,
4732
+ refetch: fetchData
4733
+ };
4734
+ }
4735
+
4736
+ // src/builder/components/ndv/node-option-values.ts
4737
+ function isRecord6(value) {
4738
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4739
+ }
4740
+ function isParameterName(value) {
4741
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value) && !["__proto__", "prototype", "constructor"].includes(value);
4742
+ }
4743
+ function isStringOptionList(schema) {
4744
+ if (structuredParameterType(schema) !== "array") return false;
4745
+ const type = typeof schema === "string" ? schema : isRecord6(schema) ? schema.type : void 0;
4746
+ const inlineItem = typeof type === "string" ? type.trim().toLowerCase().match(/^array<(.+)>$/)?.[1] : void 0;
4747
+ const item = (isRecord6(schema) ? schema.items : void 0) ?? inlineItem;
4748
+ if (item === void 0) return true;
4749
+ const itemType = typeof item === "string" ? item : isRecord6(item) ? item.type : void 0;
4750
+ return typeof itemType === "string" && itemType.trim().toLowerCase() === "string";
4751
+ }
4752
+ function readLoadOptionsSpec(value) {
4753
+ if (!isRecord6(value) || typeof value.operation !== "string" || typeof value.labelField !== "string" || typeof value.valueField !== "string")
4754
+ return void 0;
4755
+ let parameterArgs;
4756
+ if (value.parameterArgs !== void 0) {
4757
+ if (!isRecord6(value.parameterArgs)) return void 0;
4758
+ parameterArgs = {};
4759
+ for (const [argument, parameter] of Object.entries(value.parameterArgs)) {
4760
+ if (!isParameterName(argument) || typeof parameter !== "string" || !isParameterName(parameter))
4761
+ return void 0;
4762
+ parameterArgs[argument] = parameter;
4763
+ }
4764
+ }
4765
+ return {
4766
+ operation: value.operation,
4767
+ labelField: value.labelField,
4768
+ valueField: value.valueField,
4769
+ resultPath: typeof value.resultPath === "string" ? value.resultPath : void 0,
4770
+ staticArgs: isRecord6(value.staticArgs) ? value.staticArgs : void 0,
4771
+ parameterArgs
4772
+ };
4773
+ }
4774
+ function resolveLoadOptionsSpec(spec, values, defaults = {}, unavailableParameters = []) {
4775
+ const args = { ...spec.staticArgs };
4776
+ const missingParameters = /* @__PURE__ */ new Set();
4777
+ for (const [argument, parameter] of Object.entries(
4778
+ spec.parameterArgs ?? {}
4779
+ )) {
4780
+ if (!isParameterName(argument) || !isParameterName(parameter)) {
4781
+ missingParameters.add(parameter);
4782
+ continue;
4783
+ }
4784
+ const value = Object.hasOwn(values, parameter) && values[parameter] !== void 0 ? values[parameter] : Object.hasOwn(defaults, parameter) ? defaults[parameter] : void 0;
4785
+ if (unavailableParameters.includes(parameter) || value === void 0 || value === null || containsExpression(value) || typeof value === "string" && !value.trim() || Array.isArray(value) && value.length === 0)
4786
+ missingParameters.add(parameter);
4787
+ else args[argument] = value;
4788
+ }
4789
+ return {
4790
+ spec: missingParameters.size ? void 0 : { ...spec, staticArgs: args },
4791
+ missingParameters: [...missingParameters]
4792
+ };
4793
+ }
4794
+ function containsExpression(value) {
4795
+ if (typeof value === "string") return /\{\{|\$\{|^\$\./.test(value);
4796
+ if (Array.isArray(value)) return value.some(containsExpression);
4797
+ return isRecord6(value) && Object.values(value).some(containsExpression);
4798
+ }
4799
+ function optionListValues(value) {
4800
+ if (value === void 0 || value === null || value === "") return [];
4801
+ if (typeof value === "string") {
4802
+ const trimmed = value.trim();
4803
+ if (!trimmed) return [];
4804
+ if (trimmed.startsWith("{")) return void 0;
4805
+ if (trimmed.startsWith("[")) {
4806
+ try {
4807
+ const parsed = JSON.parse(trimmed);
4808
+ return Array.isArray(parsed) ? optionListValues(parsed) : void 0;
4809
+ } catch {
4810
+ return void 0;
4811
+ }
4812
+ }
4813
+ return [
4814
+ ...new Set(
4815
+ trimmed.split(",").map((entry) => entry.trim()).filter(Boolean)
4816
+ )
4817
+ ];
4818
+ }
4819
+ if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string"))
4820
+ return void 0;
4821
+ return [...new Set(value.filter((entry) => entry !== ""))];
4822
+ }
4823
+ function optionListText(value) {
4824
+ return typeof value === "string" ? value : JSON.stringify(value ?? []);
4825
+ }
4826
+
4827
+ // src/modules/ui-blocks/parse.ts
4828
+ function isRecord7(value) {
4829
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4830
+ }
4831
+ function humanizeFieldName2(name) {
4832
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().replace(/\b\w/g, (c) => c.toUpperCase());
4833
+ }
4834
+ function synthesizeCardFromInputFields(inputFields) {
4835
+ const names = Object.keys(inputFields);
4836
+ if (names.length === 0) return null;
4837
+ const body = [];
4838
+ for (const name of names) {
4839
+ const field = isRecord7(inputFields[name]) ? inputFields[name] : {};
4840
+ const description = typeof field.description === "string" ? field.description : void 0;
4841
+ const required = field.required === true;
4842
+ const secret = field.secret === true;
4843
+ const multiline = name === "body" || name === "htmlBody";
4844
+ const label = humanizeFieldName2(name);
4845
+ body.push({
4846
+ type: "TextBlock",
4847
+ text: required ? `${label} *` : label,
4848
+ wrap: true
4849
+ });
4850
+ body.push({
4851
+ type: "Input.Text",
4852
+ id: `args.state.${name}`,
4853
+ value: `\${${name}}`,
4854
+ ...description ? { placeholder: description } : {},
4855
+ // A secret field renders masked (and agent tools treat a password
4856
+ // input as sensitive).
4857
+ ...secret ? { style: "Password" } : {},
4858
+ ...multiline && !secret ? { isMultiline: true } : {},
4859
+ ...required ? { isRequired: true, errorMessage: `${label} is required` } : {}
4860
+ });
4861
+ }
4862
+ body.push({
4863
+ type: "ActionSet",
4864
+ actions: [{ type: "Action.Submit", title: "Apply" }]
4865
+ });
4866
+ return { type: "AdaptiveCard", version: "1.5", body };
4867
+ }
4868
+ function secretFieldNames(inputFields) {
4869
+ const names = /* @__PURE__ */ new Set();
4870
+ if (!isRecord7(inputFields)) return names;
4871
+ for (const [name, field] of Object.entries(inputFields)) {
4872
+ if (isRecord7(field) && field.secret === true) names.add(name);
4873
+ }
4874
+ return names;
4875
+ }
4876
+ function fieldOfInputId(id) {
4877
+ if (typeof id !== "string") return null;
4878
+ const name = id.replace(/^(?:args\.)?state\./, "");
4879
+ return name.length > 0 ? name : null;
4880
+ }
4881
+ function maskSecretInputs(card, secrets) {
4882
+ if (secrets.size === 0) return card;
4883
+ const visit = (value) => {
4884
+ if (Array.isArray(value)) return value.map(visit);
4885
+ if (!isRecord7(value)) return value;
4886
+ const copy = {};
4887
+ for (const [key, nested] of Object.entries(value))
4888
+ copy[key] = visit(nested);
4889
+ const field = copy.type === "Input.Text" ? fieldOfInputId(copy.id) : null;
4890
+ if (field && secrets.has(field)) {
4891
+ copy.style = "Password";
4892
+ delete copy.isMultiline;
4893
+ }
4894
+ return copy;
4895
+ };
4896
+ return visit(card);
4897
+ }
4898
+ function validateUiSpec(raw) {
4899
+ if (!isRecord7(raw)) return "ui is not an object";
4900
+ if (raw.version !== 1)
4901
+ return `unsupported ui.version: ${String(raw.version)}`;
4902
+ if (!Array.isArray(raw.blocks) || raw.blocks.length === 0) {
4903
+ return "ui.blocks must be a non-empty array";
4904
+ }
4905
+ const seenIds = /* @__PURE__ */ new Set();
4906
+ const blocks = [];
4907
+ for (const [index, rawBlock] of raw.blocks.entries()) {
4908
+ if (!isRecord7(rawBlock)) return `ui.blocks[${index}] is not an object`;
4909
+ const id = rawBlock.id;
4910
+ if (typeof id !== "string" || id.length === 0) {
4911
+ return `ui.blocks[${index}] is missing a non-empty string id`;
4912
+ }
4913
+ if (seenIds.has(id)) return `ui.blocks has duplicate id "${id}"`;
4914
+ seenIds.add(id);
4915
+ if (rawBlock.type === "adaptive_card") {
4916
+ if (!isRecord7(rawBlock.card)) {
4917
+ return `ui.blocks[${index}] ("${id}") has no card object`;
4918
+ }
4919
+ blocks.push({ id, type: "adaptive_card", card: rawBlock.card });
4920
+ } else if (rawBlock.type === "custom") {
4921
+ if (typeof rawBlock.component !== "string" || !rawBlock.component) {
4922
+ return `ui.blocks[${index}] ("${id}") has no component name`;
4923
+ }
4924
+ blocks.push({
4925
+ id,
4926
+ type: "custom",
4927
+ component: rawBlock.component,
4928
+ props: isRecord7(rawBlock.props) ? rawBlock.props : void 0
4929
+ });
4930
+ } else {
4931
+ return `ui.blocks[${index}] ("${id}") has unknown type "${String(rawBlock.type)}"`;
4932
+ }
4933
+ }
4934
+ return {
4935
+ version: 1,
4936
+ blocks,
4937
+ ...typeof raw.applyLabel === "string" && raw.applyLabel.trim().length > 0 ? { applyLabel: raw.applyLabel } : {}
4938
+ };
4939
+ }
4940
+ function resolveUiSpec(operation) {
4941
+ if (!operation) return null;
4942
+ const secrets = secretFieldNames(operation.input_fields);
4943
+ if (operation.ui !== void 0) {
4944
+ const result = validateUiSpec(operation.ui);
4945
+ if (typeof result !== "string") {
4946
+ const blocks = result.blocks.map(
4947
+ (block) => block.type === "adaptive_card" ? { ...block, card: maskSecretInputs(block.card, secrets) } : block
4948
+ );
4949
+ return { spec: { ...result, blocks }, synthesized: false };
4950
+ }
4951
+ console.warn(
4952
+ `ui-blocks: invalid ui declaration on operation "${operation.name ?? "?"}" (${result}); falling back to adaptive_card`
4953
+ );
4954
+ }
4955
+ if (isRecord7(operation.adaptive_card)) {
4956
+ return {
4957
+ spec: {
4958
+ version: 1,
4959
+ blocks: [
4960
+ {
4961
+ id: "main",
4962
+ type: "adaptive_card",
4963
+ card: maskSecretInputs(operation.adaptive_card, secrets)
4964
+ }
4965
+ ]
4966
+ },
4967
+ synthesized: true
4968
+ };
4969
+ }
4970
+ if (isRecord7(operation.input_fields)) {
4971
+ const card = synthesizeCardFromInputFields(operation.input_fields);
4972
+ if (card) {
4973
+ return {
4974
+ spec: {
4975
+ version: 1,
4976
+ blocks: [{ id: "main", type: "adaptive_card", card }]
4977
+ },
4978
+ synthesized: true
4979
+ };
4980
+ }
4981
+ }
4982
+ return null;
4983
+ }
4984
+ function isSubmitAction(action) {
4985
+ return isRecord7(action) && action.type === "Action.Submit";
4986
+ }
4987
+ function stripFromActions(actions) {
4988
+ return actions.filter((action) => !isSubmitAction(action)).map(
4989
+ (action) => isRecord7(action) && isRecord7(action.card) ? { ...action, card: stripSubmitActions(action.card) } : action
4990
+ );
4991
+ }
4992
+ function stripFromElements(elements) {
4993
+ const result = [];
4994
+ for (const element of elements) {
4995
+ if (!isRecord7(element)) {
4996
+ result.push(element);
4997
+ continue;
4998
+ }
4999
+ const el = { ...element };
5000
+ if (el.type === "ActionSet" && Array.isArray(el.actions)) {
5001
+ const remaining = stripFromActions(el.actions);
5002
+ if (remaining.length === 0) continue;
5003
+ el.actions = remaining;
5004
+ }
5005
+ if (Array.isArray(el.items)) {
5006
+ el.items = stripFromElements(el.items);
5007
+ }
5008
+ if (Array.isArray(el.columns)) {
5009
+ el.columns = el.columns.map(
5010
+ (column) => isRecord7(column) && Array.isArray(column.items) ? { ...column, items: stripFromElements(column.items) } : column
5011
+ );
5012
+ }
5013
+ result.push(el);
5014
+ }
5015
+ return result;
5016
+ }
5017
+ function stripSubmitActions(card) {
5018
+ const result = { ...card };
5019
+ if (Array.isArray(result.actions)) {
5020
+ result.actions = stripFromActions(result.actions);
5021
+ }
5022
+ if (Array.isArray(result.body)) {
5023
+ result.body = stripFromElements(result.body);
5024
+ }
5025
+ return result;
5026
+ }
5027
+
5028
+ // src/builder/lib/multiline-parameters.ts
5029
+ function isRecord8(value) {
5030
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5031
+ }
5032
+ function multilineParameters(operation) {
5033
+ const names = /* @__PURE__ */ new Set();
5034
+ if (!isRecord8(operation)) return names;
5035
+ const legacyCard = operation.adaptive_card ?? operation.adaptiveCard;
5036
+ const resolved = resolveUiSpec({
5037
+ name: typeof operation.name === "string" ? operation.name : void 0,
5038
+ ui: operation.ui,
5039
+ adaptive_card: isRecord8(legacyCard) ? legacyCard : void 0
5040
+ });
5041
+ const pending = resolved?.spec.blocks.flatMap(
5042
+ (block) => block.type === "adaptive_card" ? [block.card] : []
5043
+ ) ?? [];
5044
+ const visited = /* @__PURE__ */ new WeakSet();
5045
+ while (pending.length > 0) {
5046
+ const element = pending.pop();
5047
+ if (typeof element !== "object" || element === null || visited.has(element))
5048
+ continue;
5049
+ visited.add(element);
5050
+ if (Array.isArray(element)) {
5051
+ pending.push(...element);
5052
+ continue;
5053
+ }
5054
+ if (!isRecord8(element)) continue;
5055
+ if (element.type === "Input.Text" && element.isMultiline === true && typeof element.id === "string") {
5056
+ const name = element.id.replace(/^(?:args\.)?state\./, "");
5057
+ if (name) names.add(name);
5058
+ }
5059
+ pending.push(
5060
+ element.body,
5061
+ element.items,
5062
+ element.columns,
5063
+ element.actions,
5064
+ element.card
5065
+ );
5066
+ }
5067
+ if (isRecord8(operation.input_fields)) {
5068
+ for (const [name, field] of Object.entries(operation.input_fields)) {
5069
+ if (!isRecord8(field)) continue;
5070
+ if (field.isMultiline === true) names.add(name);
5071
+ else if (field.isMultiline === false) names.delete(name);
5072
+ }
5073
+ }
5074
+ return names;
5075
+ }
5076
+
5077
+ // src/builder/components/ndv/use-node-config.ts
5078
+ function fixedParameterState(state, params, field, value) {
5079
+ const parameter = params.find((candidate) => candidate.name === field);
5080
+ const parsed = parseParameterValue(
5081
+ parameter?.schema ?? parameter?.type,
5082
+ value
5083
+ );
5084
+ const nextValue = parsed.valid ? parsed.value : value;
5085
+ const optionDependency = params.some(
5086
+ (candidate) => Object.values(candidate.loadOptions?.parameterArgs ?? {}).includes(field)
5087
+ );
5088
+ if (nextValue === void 0 || !optionDependency && (nextValue === null || nextValue === "")) {
5089
+ const next = { ...state };
5090
+ delete next[field];
5091
+ return next;
5092
+ }
5093
+ return { ...state, [field]: nextValue };
5094
+ }
5095
+ function errorConfigOf(node) {
5096
+ const config = asRecord4(node?.data)?.errorConfig;
5097
+ return config;
5098
+ }
5099
+ function coerceNumber(type, raw) {
5100
+ if (raw.trim() === "") return void 0;
5101
+ const n = type === "integer" ? parseInt(raw, 10) : parseFloat(raw);
5102
+ return Number.isNaN(n) ? void 0 : n;
5103
+ }
5104
+ function asRecord4(value) {
5105
+ return typeof value === "object" && value !== null ? value : void 0;
5106
+ }
5107
+ function fallbackOperations(type) {
5108
+ if (!type) return void 0;
5109
+ return NODE_CONFIGS.find((c) => c.type === type)?.operations;
5110
+ }
5111
+ function selectedOperationOf(data, type) {
5112
+ const live = liveInternalOperations(type);
5113
+ if (live) {
5114
+ const selected = resolveNodeOperation(
5115
+ { operation_name: data?.operation_name },
5116
+ live
5117
+ ).selected;
5118
+ if (selected) return selected;
5119
+ }
5120
+ return resolveNodeOperation(data, fallbackOperations(type)).selected;
5121
+ }
5122
+ function readOptionLabels(map, options) {
5123
+ const labels = /* @__PURE__ */ Object.create(null);
5124
+ for (const [value, text] of Object.entries(asRecord4(map) ?? {})) {
5125
+ if (typeof text === "string" && text.trim() !== "") labels[value] = text;
5126
+ }
5127
+ if (Array.isArray(options)) {
5128
+ for (const option of options) {
5129
+ const rec = asRecord4(option);
5130
+ const value = rec?.value ?? rec?.name;
5131
+ const text = [
5132
+ rec?.title,
5133
+ rec?.label,
5134
+ rec?.value !== void 0 ? rec?.name : void 0
5135
+ ].find(
5136
+ (candidate) => typeof candidate === "string" && candidate.trim() !== ""
5137
+ );
5138
+ if (typeof value === "string" && typeof text === "string" && text !== value) {
5139
+ if (!Object.hasOwn(labels, value)) labels[value] = text;
5140
+ }
5141
+ }
5142
+ }
5143
+ return Object.keys(labels).length > 0 ? labels : void 0;
5144
+ }
5145
+ function optionText(param, value) {
5146
+ const labels = param.optionLabels;
5147
+ return labels && Object.hasOwn(labels, value) ? labels[value] : value;
5148
+ }
5149
+ function paramsFromOperation(op) {
5150
+ const io = asRecord4(op?.input_fields);
5151
+ if (!io) return [];
5152
+ const multiline = multilineParameters(op);
5153
+ return Object.entries(io).map(([name, spec]) => {
5154
+ const rec = asRecord4(spec);
5155
+ const optsRaw = rec?.options ?? rec?.enum;
5156
+ const options = Array.isArray(optsRaw) ? optsRaw.map(
5157
+ (o) => typeof o === "string" ? o : asRecord4(o)?.value ?? asRecord4(o)?.name
5158
+ ).filter((v) => typeof v === "string") : void 0;
5159
+ const loadOptions = readLoadOptionsSpec(rec?.loadOptions);
5160
+ const optionLabels = readOptionLabels(rec?.optionLabels, optsRaw);
5161
+ const fields = asRecord4(rec?.fields);
5162
+ return {
5163
+ name,
5164
+ label: fieldLabel(name, rec),
5165
+ placeholder: typeof rec?.placeholder === "string" && rec.placeholder.trim() !== "" ? rec.placeholder : void 0,
5166
+ ...optionLabels ? { optionLabels } : {},
5167
+ type: typeof spec === "string" ? spec : typeof rec?.type === "string" ? rec.type : "any",
5168
+ required: rec?.required === true,
5169
+ description: typeof rec?.description === "string" ? rec.description : void 0,
5170
+ options,
5171
+ defaultValue: rec?.default,
5172
+ isMultiline: multiline.has(name),
5173
+ schema: spec,
5174
+ loadOptions,
5175
+ ...rec?.secret === true ? { secret: true } : {},
5176
+ displayOptions: readDisplayOptions(rec),
5177
+ fields
5178
+ };
5179
+ });
5180
+ }
5181
+ function nodeTitle(node) {
5182
+ if (!node) return "Node";
5183
+ const data = asRecord4(node.data);
5184
+ const label = typeof data?.label === "string" ? data.label : "";
5185
+ return label || getNodeDetail(node.type).title || node.id;
5186
+ }
5187
+ function nodeSubtitle(node) {
5188
+ const data = asRecord4(node?.data);
5189
+ return typeof data?.operation_name === "string" ? data.operation_name : void 0;
5190
+ }
5191
+ function leaveUnchanged() {
5192
+ }
5193
+ function readOnlyNodeConfig(api) {
5194
+ return {
5195
+ ...api,
5196
+ writeFixed: leaveUnchanged,
5197
+ writeMapping: leaveUnchanged,
5198
+ handleConnectionsChange: leaveUnchanged,
5199
+ toggleMode: leaveUnchanged,
5200
+ // A field dropped on a read-only form is refused, not opened as a link.
5201
+ handleDropOnParam: () => (e) => e.preventDefault(),
5202
+ writeErrorConfig: leaveUnchanged,
5203
+ writeNotes: leaveUnchanged,
5204
+ writeExecutionSettings: leaveUnchanged,
5205
+ writeNodeDataField: leaveUnchanged,
5206
+ readOnly: true
5207
+ };
5208
+ }
5209
+ function runOnOf(executionSettings) {
5210
+ const runOn = asRecord4(executionSettings?.runOn);
5211
+ return runOn?.kind === "runner" && typeof runOn.runnerId === "string" ? { kind: "runner", runnerId: runOn.runnerId } : null;
5212
+ }
5213
+ function useNodeConfig(target, options) {
5214
+ const nodes = useFlowStore((s) => s.workflow.nodes);
5215
+ const edges = useFlowStore((s) => s.workflow.edges);
5216
+ const workflowKey = useFlowStore((s) => s.workflow.workflowKey);
5217
+ const readOnly = useBuilderReadOnly();
5218
+ const executionLogs = useFlowStore((s) => s.execution.logs);
5219
+ const currentRunId = useFlowStore((s) => s.execution.currentRunId);
5220
+ const nodeId = target?.nodeId;
5221
+ const node = useMemo(
5222
+ () => nodeId ? nodes.find((n) => n.id === nodeId) : void 0,
5223
+ [nodeId, nodes]
5224
+ );
5225
+ const incoming = useMemo(
5226
+ () => nodeId ? edges.filter((e) => e.target === nodeId) : [],
5227
+ [edges, nodeId]
5228
+ );
5229
+ const sourceIds = useMemo(() => incoming.map((e) => e.source), [incoming]);
5230
+ const sourceKey = sourceIds.join(",");
5231
+ const [selectedSource, setSelectedSource] = useState();
5232
+ const [dragField, setDragField] = useState(null);
5233
+ const [modeByField, setModeByField] = useState({});
5234
+ useEffect(() => {
5235
+ setSelectedSource(
5236
+ (prev) => prev && sourceIds.includes(prev) ? prev : sourceIds[0]
5237
+ );
5238
+ }, [nodeId, sourceKey, sourceIds]);
5239
+ useEffect(() => {
5240
+ setModeByField({});
5241
+ }, [nodeId, selectedSource]);
5242
+ const sourceNode = useMemo(
5243
+ () => nodes.find((n) => n.id === selectedSource),
5244
+ [nodes, selectedSource]
5245
+ );
5246
+ const currentEdge = useMemo(
5247
+ () => incoming.find((e) => e.source === selectedSource),
5248
+ [incoming, selectedSource]
5249
+ );
5250
+ const registryRevision = useNodeRegistryRevision();
5251
+ const currentOp = useMemo(() => {
5252
+ return selectedOperationOf(asRecord4(node?.data), node?.type);
5253
+ }, [node, registryRevision]);
5254
+ const sourceOp = useMemo(() => {
5255
+ return selectedOperationOf(asRecord4(sourceNode?.data), sourceNode?.type);
5256
+ }, [sourceNode, registryRevision]);
5257
+ const { fields: inputFields, data: inputData } = useMemo(
5258
+ () => resolveMappingInput({
5259
+ source: sourceNode,
5260
+ operation: sourceOp,
5261
+ runId: currentRunId,
5262
+ logs: executionLogs
5263
+ }),
5264
+ [sourceNode, sourceOp, currentRunId, executionLogs]
5265
+ );
5266
+ const outputFields = useMemo(
5267
+ () => fieldsFromIO(currentOp?.output_fields),
5268
+ [currentOp]
5269
+ );
5270
+ const params = useMemo(() => paramsFromOperation(currentOp), [currentOp]);
5271
+ const isWebhookTrigger = useMemo(
5272
+ () => !!asRecord4(currentOp?.output_fields)?.webhookUrl,
5273
+ [currentOp]
5274
+ );
5275
+ const isFormTrigger = useMemo(
5276
+ () => !!asRecord4(currentOp?.output_fields)?.formUrl,
5277
+ [currentOp]
5278
+ );
5279
+ const {
5280
+ webhookUrl,
5281
+ formUrl,
5282
+ error: triggerUrlError,
5283
+ loading: webhookUrlLoading
5284
+ } = useWebhookRegistration(
5285
+ workflowKey || void 0,
5286
+ node?.id,
5287
+ (isWebhookTrigger || isFormTrigger) && (options?.webhookEnabled ?? true)
5288
+ );
5289
+ const pickerFields = useMemo(() => flattenFields(inputFields), [inputFields]);
5290
+ const mapping = currentEdge?.mapping?.definition ?? {};
5291
+ const argsState = useMemo(() => {
5292
+ const data = asRecord4(node?.data);
5293
+ const args = asRecord4(data?.args);
5294
+ return asRecord4(args?.state) ?? {};
5295
+ }, [node]);
5296
+ const isWebhookWait = useMemo(() => {
5297
+ const type = asRecord4(asRecord4(node?.data)?.args)?.context;
5298
+ const nodeType = asRecord4(asRecord4(type)?.node)?.type;
5299
+ return nodeType === "internal:wait" && argsState.mode === "webhook";
5300
+ }, [node, argsState]);
5301
+ const connectionAdapters = useMemo(() => {
5302
+ const op = asRecord4(currentOp);
5303
+ const manifest = asRecord4(asRecord4(node?.data)?.nodeData);
5304
+ const raw = op?.connectionAdapters ?? op?.credentialAdapters ?? manifest?.connectionAdapters ?? manifest?.credentialAdapters;
5305
+ if (!Array.isArray(raw)) return [];
5306
+ return raw.map((a) => asRecord4(a)).filter((a) => !!a).map((a) => ({
5307
+ name: String(a.name ?? ""),
5308
+ provider: String(a.provider ?? ""),
5309
+ ...a.required === false ? { required: false } : {}
5310
+ })).filter((a) => a.name && a.provider);
5311
+ }, [currentOp, node]);
5312
+ const selectedConnections = useMemo(() => {
5313
+ const args = asRecord4(asRecord4(node?.data)?.args);
5314
+ const fromList = (list, idKey) => {
5315
+ if (!Array.isArray(list)) return [];
5316
+ return list.map((c) => asRecord4(c)).filter((c) => !!c).map((c) => ({
5317
+ connectionId: String(c[idKey] ?? ""),
5318
+ adapter: String(c.adapter ?? "")
5319
+ })).filter((c) => c.connectionId);
5320
+ };
5321
+ const connections = fromList(args?.connections, "connectionId");
5322
+ if (connections.length > 0) return connections;
5323
+ return fromList(args?.credentials, "id");
5324
+ }, [node]);
5325
+ const handleConnectionsChange = (connections) => {
5326
+ if (!nodeId) return;
5327
+ const store = useFlowStore.getState();
5328
+ const next = store.workflow.nodes.map((n) => {
5329
+ if (n.id !== nodeId) return n;
5330
+ const data = { ...asRecord4(n.data) ?? {} };
5331
+ const args = { ...asRecord4(data.args) ?? {} };
5332
+ delete args.credentials;
5333
+ args.connections = connections.map((c) => ({
5334
+ connectionId: c.connectionId,
5335
+ adapter: c.adapter
5336
+ }));
5337
+ data.args = args;
5338
+ return { ...n, data };
5339
+ });
5340
+ store.actions.nodes.setNodes(next);
5341
+ };
5342
+ const writeMapping = (field, value) => {
5343
+ if (!currentEdge) return;
5344
+ const store = useFlowStore.getState();
5345
+ const freshEdges = store.workflow.edges;
5346
+ const next = freshEdges.map((e) => {
5347
+ if (e.id !== currentEdge.id) return e;
5348
+ const def = updateMappingDefinition(
5349
+ e.mapping?.definition ?? {},
5350
+ field,
5351
+ value
5352
+ );
5353
+ return {
5354
+ ...e,
5355
+ mapping: {
5356
+ definition: def,
5357
+ comment: e.mapping?.comment ?? `Map ${selectedSource} \u2192 ${nodeId ?? ""}`
5358
+ }
5359
+ };
5360
+ });
5361
+ store.actions.edges.setEdges(next);
5362
+ };
5363
+ const clearMappingFromAllIncoming = (field) => {
5364
+ if (!nodeId) return;
5365
+ const store = useFlowStore.getState();
5366
+ const fresh = store.workflow.edges;
5367
+ let changed = false;
5368
+ const next = fresh.map((e) => {
5369
+ if (e.target !== nodeId || !e.mapping?.definition) return e;
5370
+ if (!(field in e.mapping.definition)) return e;
5371
+ const def = { ...e.mapping.definition };
5372
+ delete def[field];
5373
+ changed = true;
5374
+ return { ...e, mapping: { ...e.mapping, definition: def } };
5375
+ });
5376
+ if (changed) store.actions.edges.setEdges(next);
5377
+ };
5378
+ const writeFixed = (field, value) => {
5379
+ if (!nodeId) return;
5380
+ const store = useFlowStore.getState();
5381
+ const next = store.workflow.nodes.map((n) => {
5382
+ if (n.id !== nodeId) return n;
5383
+ const data = { ...asRecord4(n.data) ?? {} };
5384
+ const args = { ...asRecord4(data.args) ?? {} };
5385
+ args.state = fixedParameterState(
5386
+ asRecord4(args.state) ?? {},
5387
+ params,
5388
+ field,
5389
+ value
5390
+ );
5391
+ data.args = args;
5392
+ return { ...n, data };
5393
+ });
5394
+ store.actions.nodes.setNodes(next);
5395
+ clearMappingFromAllIncoming(field);
5396
+ };
5397
+ const writeErrorConfig = (patch) => {
5398
+ if (!nodeId) return;
5399
+ const store = useFlowStore.getState();
5400
+ const next = store.workflow.nodes.map((n) => {
5401
+ if (n.id !== nodeId) return n;
5402
+ const data = { ...asRecord4(n.data) ?? {} };
5403
+ const prev = errorConfigOf(n) ?? {};
5404
+ const merged = { ...prev, ...patch };
5405
+ if (patch.retryConfig !== void 0) {
5406
+ merged.retryConfig = patch.retryConfig ? { ...prev.retryConfig, ...patch.retryConfig } : void 0;
5407
+ }
5408
+ if (patch.timeout !== void 0) {
5409
+ merged.timeout = patch.timeout ? { ...prev.timeout, ...patch.timeout } : void 0;
5410
+ }
5411
+ data.errorConfig = merged;
5412
+ return { ...n, data };
5413
+ });
5414
+ store.actions.nodes.setNodes(next);
5415
+ };
5416
+ const writeNodeDataField = (field, value) => {
5417
+ if (!nodeId) return;
5418
+ const store = useFlowStore.getState();
5419
+ const next = store.workflow.nodes.map((n) => {
5420
+ if (n.id !== nodeId) return n;
5421
+ const data = { ...asRecord4(n.data) ?? {} };
5422
+ if (value === void 0 || value === "" || value === false) {
5423
+ delete data[field];
5424
+ } else {
5425
+ data[field] = value;
5426
+ }
5427
+ return { ...n, data };
5428
+ });
5429
+ store.actions.nodes.setNodes(next);
5430
+ };
5431
+ const writeNotes = (value) => writeNodeDataField("notes", value);
5432
+ const writeExecutionSettings = (patch) => {
5433
+ if (!nodeId) return;
5434
+ const store = useFlowStore.getState();
5435
+ const next = store.workflow.nodes.map((n) => {
5436
+ if (n.id !== nodeId) return n;
5437
+ const data = { ...asRecord4(n.data) ?? {} };
5438
+ const merged = {
5439
+ ...asRecord4(data.executionSettings) ?? {},
5440
+ ...patch
5441
+ };
5442
+ if (merged.runOn === null) delete merged.runOn;
5443
+ if (!merged.alwaysOutputData && !merged.executeOnce && !merged.runOn) {
5444
+ delete data.executionSettings;
5445
+ } else {
5446
+ data.executionSettings = merged;
5447
+ }
5448
+ return { ...n, data };
5449
+ });
5450
+ store.actions.nodes.setNodes(next);
5451
+ };
5452
+ const toggleMode = (field, current) => {
5453
+ if (current === "expression") {
5454
+ clearMappingFromAllIncoming(field);
5455
+ }
5456
+ setModeByField((m) => ({
5457
+ ...m,
5458
+ [field]: current === "expression" ? "fixed" : "expression"
5459
+ }));
5460
+ };
5461
+ const errorConfig = useMemo(() => errorConfigOf(node), [node]);
5462
+ const notes = useMemo(() => {
5463
+ const data = asRecord4(node?.data);
5464
+ return typeof data?.notes === "string" ? data.notes : "";
5465
+ }, [node]);
5466
+ const executionSettings = useMemo(
5467
+ () => asRecord4(asRecord4(node?.data)?.executionSettings),
5468
+ [node]
5469
+ );
5470
+ const alwaysOutputData = executionSettings?.alwaysOutputData === true;
5471
+ const executeOnce = executionSettings?.executeOnce === true;
5472
+ const runOn = useMemo(() => runOnOf(executionSettings), [executionSettings]);
5473
+ const handleDropOnParam = (field) => (e) => {
5474
+ e.preventDefault();
5475
+ setDragField(null);
5476
+ const path = e.dataTransfer.getData(FIELD_PATH_MIME);
5477
+ if (path) {
5478
+ writeMapping(field, pathToExpression(path));
5479
+ setModeByField((m) => ({ ...m, [field]: "expression" }));
5480
+ }
5481
+ };
5482
+ const api = {
5483
+ node,
5484
+ nodes,
5485
+ incoming,
5486
+ sourceIds,
5487
+ selectedSource,
5488
+ setSelectedSource,
5489
+ currentEdge,
5490
+ currentOp,
5491
+ params,
5492
+ argsState,
5493
+ mapping,
5494
+ inputFields,
5495
+ inputData,
5496
+ outputFields,
5497
+ pickerFields,
5498
+ connectionAdapters,
5499
+ selectedConnections,
5500
+ isWebhookTrigger,
5501
+ isFormTrigger,
5502
+ formUrl,
5503
+ triggerUrlError,
5504
+ isWebhookWait,
5505
+ webhookUrl,
5506
+ webhookUrlLoading,
5507
+ modeByField,
5508
+ dragField,
5509
+ setDragField,
5510
+ writeFixed,
5511
+ writeMapping,
5512
+ handleConnectionsChange,
5513
+ toggleMode,
5514
+ handleDropOnParam,
5515
+ errorConfig,
5516
+ writeErrorConfig,
5517
+ notes,
5518
+ writeNotes,
5519
+ alwaysOutputData,
5520
+ executeOnce,
5521
+ runOn,
5522
+ writeExecutionSettings,
5523
+ writeNodeDataField,
5524
+ readOnly: false
5525
+ };
5526
+ return readOnly ? readOnlyNodeConfig(api) : api;
5527
+ }
5528
+
5529
+ // src/builder/components/ndv/node-parameter-issues.ts
5530
+ function nodeParamsOf(node) {
5531
+ return paramsFromOperation(
5532
+ selectedOperationOf(asRecord4(node.data), node.type)
5533
+ );
5534
+ }
5535
+ function nodeDisplayName(node) {
5536
+ const data = asRecord4(node.data);
5537
+ const label = data?.label;
5538
+ if (typeof label === "string" && label.trim() !== "") return label;
5539
+ const name = asRecord4(data?.nodeData)?.name;
5540
+ if (typeof name === "string" && name.trim() !== "") return name;
5541
+ return node.type || node.id;
5542
+ }
5543
+ function workflowParameterIssues(nodes, edges, nodeIds) {
5544
+ const only = nodeIds ? new Set(nodeIds) : void 0;
5545
+ const checked = only ? nodes.filter((node) => only.has(node.id)) : nodes;
5546
+ return nodeParameterIssues(checked, edges, nodeParamsOf, nodeDisplayName);
5547
+ }
5548
+ function useRunGuard(startRun, options = {}) {
3945
5549
  const { saveToBackend } = useSaveWorkflow();
3946
5550
  const [confirmOpen, setConfirmOpen] = useState(false);
3947
5551
  const [preparing, setPreparing] = useState(false);
5552
+ const [issues, setIssues] = useState([]);
5553
+ const stepNodeId = options.nodeId;
3948
5554
  const requestRun = useCallback(async () => {
5555
+ const { nodes, edges } = useFlowStore.getState().workflow;
5556
+ const found = workflowParameterIssues(
5557
+ nodes,
5558
+ edges,
5559
+ stepNodeId ? [stepNodeId] : void 0
5560
+ );
5561
+ if (found.length > 0) {
5562
+ setIssues(found);
5563
+ return;
5564
+ }
3949
5565
  const preparation = runPreparation();
3950
5566
  if (preparation === "run") {
3951
5567
  startRun();
@@ -3965,23 +5581,62 @@ function useRunGuard(startRun) {
3965
5581
  }
3966
5582
  }
3967
5583
  setConfirmOpen(true);
3968
- }, [saveToBackend, startRun]);
3969
- const dialog = /* @__PURE__ */ jsx(
3970
- UnsavedRunDialog,
5584
+ }, [saveToBackend, startRun, stepNodeId]);
5585
+ const dialog = /* @__PURE__ */ jsxs(Fragment, { children: [
5586
+ /* @__PURE__ */ jsx(
5587
+ UnsavedRunDialog,
5588
+ {
5589
+ open: confirmOpen,
5590
+ onOpenChange: setConfirmOpen,
5591
+ onRunSaved: () => {
5592
+ setConfirmOpen(false);
5593
+ startRun();
5594
+ },
5595
+ onSavedAndRunnable: () => {
5596
+ setConfirmOpen(false);
5597
+ startRun();
5598
+ }
5599
+ }
5600
+ ),
5601
+ /* @__PURE__ */ jsx(MissingParametersDialog, { issues, onClose: () => setIssues([]) })
5602
+ ] });
5603
+ return { requestRun, preparing, dialog };
5604
+ }
5605
+ function MissingParametersDialog({
5606
+ issues,
5607
+ onClose,
5608
+ title
5609
+ }) {
5610
+ const tr = useTr();
5611
+ return /* @__PURE__ */ jsx(
5612
+ AlertDialog,
3971
5613
  {
3972
- open: confirmOpen,
3973
- onOpenChange: setConfirmOpen,
3974
- onRunSaved: () => {
3975
- setConfirmOpen(false);
3976
- startRun();
5614
+ open: issues.length > 0,
5615
+ onOpenChange: (open) => {
5616
+ if (!open) onClose();
3977
5617
  },
3978
- onSavedAndRunnable: () => {
3979
- setConfirmOpen(false);
3980
- startRun();
3981
- }
5618
+ children: /* @__PURE__ */ jsxs(AlertDialogContent, { "data-testid": "missing-parameters-dialog", children: [
5619
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
5620
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: title ?? tr(
5621
+ "fluidgrids.builder.requiredParameters.runBlockedTitle",
5622
+ "Fill in the required parameters first"
5623
+ ) }),
5624
+ /* @__PURE__ */ jsx(AlertDialogDescription, { children: tr(
5625
+ "fluidgrids.builder.requiredParameters.runBlockedDescription",
5626
+ "These nodes have required parameters that are still empty. The workflow is saved as it is; it can run once they are filled in."
5627
+ ) })
5628
+ ] }),
5629
+ /* @__PURE__ */ jsx("ul", { className: "space-y-1 text-sm", "data-testid": "missing-parameters-list", children: issues.map((issue) => /* @__PURE__ */ jsxs("li", { children: [
5630
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: issue.nodeLabel }),
5631
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
5632
+ ": ",
5633
+ issue.missing.map((field) => field.label).join(", ")
5634
+ ] })
5635
+ ] }, issue.nodeId)) }),
5636
+ /* @__PURE__ */ jsx(AlertDialogFooter, { children: /* @__PURE__ */ jsx(AlertDialogCancel, { onClick: onClose, children: tr("fluidgrids.builder.requiredParameters.close", "Close") }) })
5637
+ ] })
3982
5638
  }
3983
5639
  );
3984
- return { requestRun, preparing, dialog };
3985
5640
  }
3986
5641
  function UnsavedRunDialog({
3987
5642
  open,
@@ -4643,7 +6298,7 @@ var getNodeMetadata = (value) => {
4643
6298
  console.error("[NodeComponent] Execute step error:", error);
4644
6299
  });
4645
6300
  }, [executeStep, id]);
4646
- const stepGuard = useRunGuard(handleExecuteStep);
6301
+ const stepGuard = useRunGuard(handleExecuteStep, { nodeId: id });
4647
6302
  const missingCredentials = useMemo(() => {
4648
6303
  const visibleAdapters = getVisibleConnectionAdapters(
4649
6304
  String(value?.type ?? ""),
@@ -4655,6 +6310,18 @@ var getNodeMetadata = (value) => {
4655
6310
  const selected2 = data?.args?.connections ?? data?.args?.credentials ?? [];
4656
6311
  return missingRequiredAdapters(visibleAdapters, selected2).length > 0;
4657
6312
  }, [data]);
6313
+ const boundFields = useFlowStore(
6314
+ useShallow((s) => [...boundFieldsOf(id, s.workflow.edges)].sort())
6315
+ );
6316
+ const missingParameters = useMemo(() => {
6317
+ if (data?.disabled === true)
6318
+ return [];
6319
+ return missingRequiredParameters(
6320
+ nodeParamsOf({ type: value?.type, data }),
6321
+ nodeStateOf({ data }),
6322
+ new Set(boundFields)
6323
+ ).map((field) => field.label);
6324
+ }, [boundFields, data, id, value?.type]);
4658
6325
  if (isAdapterSubNode) {
4659
6326
  return /* @__PURE__ */ jsx(SubNodeComponent, { ...props, nodeType: value?.type });
4660
6327
  }
@@ -4779,6 +6446,7 @@ var getNodeMetadata = (value) => {
4779
6446
  appName,
4780
6447
  accentKey: value?.type || meta.title,
4781
6448
  missingCredentials,
6449
+ missingParameters,
4782
6450
  label: nodeLabel,
4783
6451
  isRenaming: isRenamingThisNode && !readOnly,
4784
6452
  onStartRename: readOnly ? void 0 : handleStartRename,
@@ -5170,4 +6838,4 @@ function createNodeWithDefaultData(type, data) {
5170
6838
  return Object.assign(createNodeData(type, defaultData), data);
5171
6839
  }
5172
6840
 
5173
- export { AUTOSAVE_DEBOUNCE_MS, AUTOSAVE_RETRY_DELAYS_MS, AVAILABLE_NODES, BILLING_PLANS_PATH, DURABLE_FUNCTION_NODE_TYPES, EDIT_SESSION_ID, FLUIDGRIDS_FLAGS, FLUIDGRIDS_FLAG_DEFAULTS, FluidGridsContext, FluidGridsProvider, NODES_METADATA, NODE_CONFIGS, NODE_TYPES, PHASE_C_FLAGS_OFF, RUNS_QUOTA_NAME, RUN_CONTROL_NODE_TYPES, WORKFLOWS_QUOTA_NAME, WORKFLOW_TEST_NODE_TYPES, autosaveAllowedFor, bannerOf, blankWorkflowCreateInput, buildOperationSeed, buildStickyNode, builderPathOf, canAutosave, canSignalRun, classifyCreateRefusal, clearCodeWorkflowDefinitionCache, codeBuildIdOf, connectionsForAdapter, createConfiguredNode, createThenOpen, declaredOutputFields, declaredSignals, departingSave, durableInterfaceOf, editStateFor, editingLeaseLocked, emitNodeInstalled, emitRunFailed, emitRunLimitHit, emitRunStarted, emitTemplateUsed, emitUpgradeClicked, emitWorkflowCreated, emitWorkflowLimitHit, emitWorkflowPublished, ensureNodeConfigsLoaded, fieldsFromIO, fieldsFromValue, filterPaletteNodes, findQuotaRefusal, findSaveConflict, forgetSessionForReload, formatElapsedMs, formatParameterValue, getNodeDetail, getNodeMetadata, inAppLeaveTarget, isDurableFunctionNodeType, isFlowComplete, isLocked, isNodeConfigsInitialized, isNodeTypeHiddenFromPalette, isOptionalAdapter, isParamVisible, isQuotaExhaustedFailure, isRunControlNodeType, leaseCallQueue, leaseCallSequence, leavingLosesChanges, liveInternalOperations, loadDefinitionJson, matchesAdapterProvider, newConnectionProviderKey, nodeStateDurationMs, nodesOf, onNodeTypesUpdate, openRunQuotaUpgradePrompt, parseParameterValue, parseRawSignalPayload, pathToExpression, payloadFromCardValues, phaseAfterAcquire, phaseAfterTakeOver, readDisplayOptions, refetchStoreNodes, refusalOfQuota, registerWorkflowNodeTypes, registryNodeData, releasesOnClose, rememberOwnStatusChange, rememberRevision, rememberSessionForReload, replaceAdapterConnections, resolveFluidGridsFlagsSettled, resolveNodeOperation, resolvePhaseCFlags, runDurationMs, runSignalTargets, runTriggerFromMode, saveFailureActionOf, saveIndicatorOf, savedEverythingBeforeLeaving, sessionBeforeReload, shouldReleaseForIdle, signalPayloadCard, structuredParameterType, subscribeWorkflowSaved, tickAction, touchesSavedFields, useCanvasEdges, useCodeWorkflowInterface, useContinuousOptimisationEnabled, useDurableFunctionNodesEnabled, useDurableParityEnabled, useEditLeaseLocked, useEditLeaseStore, useFlowValidator, useFluidGridsAnalytics, useFluidGridsContext, useFluidGridsFlag, useFluidGridsFlagsSettled, useGuardedNavigate, useInsertNode, useManualSave, useNodeExecution, useNodeRegistryRevision, usePaletteGates, usePhaseCFlags, useRunGuard, useSaveStatusStore, useSaveWorkflow, useSelfHostedWorkersEnabled, useWorkflowTestsEnabled, watchLeavingLosesChanges, workflowFingerprint };
6841
+ export { AUTOSAVE_DEBOUNCE_MS, AUTOSAVE_RETRY_DELAYS_MS, AVAILABLE_NODES, BILLING_PLANS_PATH, ConditionTreeBuilder, DURABLE_FUNCTION_NODE_TYPES, EDIT_SESSION_ID, FLUIDGRIDS_FLAGS, FLUIDGRIDS_FLAG_DEFAULTS, FieldTree, FluidGridsContext, FluidGridsProvider, MissingParametersDialog, NODES_METADATA, NODE_CONFIGS, NODE_TYPES, PHASE_C_FLAGS_OFF, RUNS_QUOTA_NAME, RUN_CONTROL_NODE_TYPES, SwitchRulesEditor, WORKFLOWS_QUOTA_NAME, WORKFLOW_TEST_NODE_TYPES, asRecord4 as asRecord, autosaveAllowedFor, bannerOf, blankWorkflowCreateInput, boundFieldsOf, buildOperationSeed, buildStickyNode, builderPathOf, canAutosave, canSignalRun, casesFromRules, classifyCreateRefusal, clearCodeWorkflowDefinitionCache, codeBuildIdOf, coerceNumber, connectionsForAdapter, createConfiguredNode, createThenOpen, declaredSignals, departingSave, durableInterfaceOf, editStateFor, editingLeaseLocked, emitNodeInstalled, emitRunFailed, emitRunLimitHit, emitRunStarted, emitTemplateUsed, emitUpgradeClicked, emitWorkflowCreated, emitWorkflowLimitHit, emitWorkflowPublished, ensureNodeConfigsLoaded, fieldsFromValue, filterPaletteNodes, findQuotaRefusal, findSaveConflict, fixedParameterState, forgetSessionForReload, formatElapsedMs, formatParameterValue, getNodeDetail, getNodeMetadata, inAppLeaveTarget, isDurableFunctionNodeType, isFlowComplete, isLocked, isNodeConfigsInitialized, isNodeTypeHiddenFromPalette, isOptionalAdapter, isParamVisible, isQuotaExhaustedFailure, isRunControlNodeType, isStringOptionList, latestNodeLog, leaseCallQueue, leaseCallSequence, leavingLosesChanges, loadDefinitionJson, matchesAdapterProvider, missingRequiredParameters, newConnectionProviderKey, nodeStateDurationMs, nodeSubtitle, nodeTitle, nodesOf, onNodeTypesUpdate, openRunQuotaUpgradePrompt, optionListText, optionListValues, optionText, paramsFromOperation, parseMappingValue, parseParameterValue, parseRawSignalPayload, payloadFromCardValues, phaseAfterAcquire, phaseAfterTakeOver, refetchStoreNodes, refusalOfQuota, registerWorkflowNodeTypes, registryNodeData, releasesOnClose, rememberOwnStatusChange, rememberRevision, rememberSessionForReload, replaceAdapterConnections, resolveFluidGridsFlagsSettled, resolveLoadOptionsSpec, resolveNodeOperation, resolvePhaseCFlags, resolveUiSpec, runDurationMs, runSignalTargets, runTriggerFromMode, saveFailureActionOf, saveIndicatorOf, savedEverythingBeforeLeaving, selectedOperationOf, sessionBeforeReload, shouldReleaseForIdle, signalPayloadCard, stripSubmitActions, structuredParameterType, tickAction, touchesSavedFields, useCanvasEdges, useCodeWorkflowInterface, useContinuousOptimisationEnabled, useDurableFunctionNodesEnabled, useDurableParityEnabled, useEditLeaseLocked, useEditLeaseStore, useFlowValidator, useFluidGridsAnalytics, useFluidGridsContext, useFluidGridsFlag, useFluidGridsFlagsSettled, useGuardedNavigate, useInsertNode, useManualSave, useNodeConfig, useNodeExecution, usePaletteGates, usePhaseCFlags, useRunGuard, useSaveStatusStore, useSaveWorkflow, useSelfHostedWorkersEnabled, useWorkflowTestsEnabled, watchLeavingLosesChanges, workflowFingerprint, workflowParameterIssues };