@burdenoff/fluidgrids-fe-sdk 2026.1105.2 → 2026.1105.4

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 (69) hide show
  1. package/README.md +42 -3
  2. package/dist/ExecutionDetailPage-OOHJQR2L.js +30 -0
  3. package/dist/ExecutionsListPage-6HS5VQOV.js +24 -0
  4. package/dist/WorkflowBuilder-PICIS7FI.js +35 -0
  5. package/dist/WorkflowsList-SD5XQC4Q.js +23 -0
  6. package/dist/{agentTools-5N3ZEZDW.js → agentTools-UH4LU6LA.js} +9 -7
  7. package/dist/{chunk-6ZK4MRVA.js → chunk-43GSC5Y6.js} +1 -1
  8. package/dist/{chunk-CH3YUGWR.js → chunk-4XAXF5VP.js} +4 -4
  9. package/dist/{chunk-6IS7EXIY.js → chunk-5GG2FEKW.js} +2 -2
  10. package/dist/{chunk-64JN2TVX.js → chunk-5Z4F24QT.js} +10 -39
  11. package/dist/chunk-6VIG44VF.js +32 -0
  12. package/dist/{chunk-QFC6E3WD.js → chunk-ASYYYJG2.js} +4 -4
  13. package/dist/{chunk-OXXXUAWM.js → chunk-CTKWMPQQ.js} +1 -1
  14. package/dist/{chunk-QFGZXWTM.js → chunk-EIMTOK6E.js} +3 -3
  15. package/dist/{chunk-AMXJLVZN.js → chunk-EXJW7EKR.js} +2 -2
  16. package/dist/{chunk-O7CJJNW6.js → chunk-GBJWPSLJ.js} +70 -5
  17. package/dist/{chunk-X25NCWG2.js → chunk-GBQC5WDH.js} +16 -8
  18. package/dist/{chunk-3XUWPDKJ.js → chunk-GPABUQTD.js} +650 -636
  19. package/dist/{chunk-3QCRKKTI.js → chunk-HSTP2RX3.js} +267 -2149
  20. package/dist/{chunk-US5QSLPV.js → chunk-JKLZ6YZ3.js} +22 -11
  21. package/dist/{chunk-GKFMK7FF.js → chunk-JRLBPR5U.js} +3 -3
  22. package/dist/{chunk-IL343FKX.js → chunk-KHS2DZHH.js} +1 -1
  23. package/dist/{chunk-SALZ3LQB.js → chunk-KJT5TAOE.js} +3 -3
  24. package/dist/{chunk-AASHQI77.js → chunk-KZCNJAMK.js} +2 -1
  25. package/dist/{chunk-TNFBRXU7.js → chunk-LLCVVRCE.js} +9 -8
  26. package/dist/{chunk-5CFCFA6O.js → chunk-M72SMQOS.js} +8 -7
  27. package/dist/{chunk-TCIBTMJI.js → chunk-NZMUKAXG.js} +3 -69
  28. package/dist/{chunk-2KUZBEWW.js → chunk-OLRIWGYM.js} +8 -7
  29. package/dist/{chunk-62CCBMDC.js → chunk-OTOQFZFO.js} +40 -39
  30. package/dist/{chunk-EYQ7GJRW.js → chunk-PASKZCII.js} +3 -3
  31. package/dist/{chunk-3IXU7WUC.js → chunk-PDODDDHY.js} +1 -1
  32. package/dist/chunk-SDZEAIIE.js +9 -0
  33. package/dist/chunk-TGWDFLEX.js +1932 -0
  34. package/dist/{chunk-AU7LACOY.js → chunk-TYI2XOSI.js} +1 -1
  35. package/dist/{chunk-L2N6DDOI.js → chunk-U7UNWR3D.js} +2 -2
  36. package/dist/{chunk-HK76ZUAK.js → chunk-UIS7KGHW.js} +4 -4
  37. package/dist/{chunk-QPLFALVV.js → chunk-UWCD7JUU.js} +8 -7
  38. package/dist/{chunk-CNKTQ5MG.js → chunk-UYTPURFW.js} +16 -50
  39. package/dist/{chunk-AJVU5YW6.js → chunk-WBRW67HX.js} +4 -3
  40. package/dist/{chunk-J3YQCEP3.js → chunk-WC526MLS.js} +3 -3
  41. package/dist/{chunk-XFH6SLDK.js → chunk-XNARCHMT.js} +2 -2
  42. package/dist/{chunk-W5WML3OO.js → chunk-XWINH6CR.js} +1 -1
  43. package/dist/{chunk-Y6M7K4UG.js → chunk-Y4UCHFGK.js} +1 -1
  44. package/dist/{chunk-OCJTTHTS.js → chunk-Y7FKJJMF.js} +1 -9
  45. package/dist/{client-YVZMLEQX.js → client-62VHXHKN.js} +2 -2
  46. package/dist/code-editor-field-WWPDMF4O.js +3 -0
  47. package/dist/executions.js +17 -15
  48. package/dist/{global-client-N5EIFW6T.js → global-client-PJ5LANJA.js} +2 -2
  49. package/dist/history.js +11 -8
  50. package/dist/index.d.ts +10 -4
  51. package/dist/index.js +69 -52
  52. package/dist/{node-detail-view-WIWW5N6N.js → node-detail-view-P5NV5CMO.js} +14 -12
  53. package/dist/nodes.js +3 -3
  54. package/dist/observability.js +10 -8
  55. package/dist/{quick-config-panel-O64TXXK3.js → quick-config-panel-ZNSBVF4G.js} +11 -9
  56. package/dist/settings.js +11 -9
  57. package/dist/templates.js +17 -14
  58. package/dist/{workflow-builder-7vv9dCdp.d.ts → workflow-builder-C-ekFbwR.d.ts} +37 -4
  59. package/dist/workflow-builder.d.ts +1 -1
  60. package/dist/workflow-builder.js +19 -16
  61. package/dist/workflow-preview.d.ts +158 -0
  62. package/dist/workflow-preview.js +721 -0
  63. package/dist/workflows-list.js +12 -10
  64. package/package.json +5 -1
  65. package/dist/ExecutionDetailPage-HQB2SWBP.js +0 -28
  66. package/dist/ExecutionsListPage-RSLL634L.js +0 -22
  67. package/dist/WorkflowBuilder-E3PNZX74.js +0 -32
  68. package/dist/WorkflowsList-473FRRP2.js +0 -21
  69. package/dist/code-editor-field-XWUVOKTA.js +0 -2
@@ -1,27 +1,25 @@
1
1
  import { SdkPermissionGate, FLUIDGRIDS_PERMISSIONS } from './chunk-5375NUIO.js';
2
- import { useBuilderReadOnly, editorSeverity } from './chunk-OCJTTHTS.js';
3
- import { getGlobalStoreClient, createGlobalStoreClient, setGlobalStoreClient, resetGlobalStoreClient } from './chunk-AU7LACOY.js';
2
+ import { useBuilderReadOnly } from './chunk-SDZEAIIE.js';
3
+ import { STICKY_NOTE_TYPE, useFlowStore, resolveOperationName, synchronizeOperationUpdate, isNodeTrigger, resolveOutputHandles, resolveInputHandles, outputHandlesSpacerHeight, WAIT_FOR_SIGNAL_NODE_TYPE, CODE_WORKFLOW_NODE_TYPE, readSignalName, LogicNodeCard, NodeCard, NodeCardHeader, NodeCardBody, InputHandles, OutputHandles, NodeTypeIcon, nodeAccent, readCodeWorkflowState, partitionStickyNotes, interfaceFields, interfaceHandlers } from './chunk-TGWDFLEX.js';
4
+ import { getGlobalStoreClient, createGlobalStoreClient, setGlobalStoreClient, resetGlobalStoreClient } from './chunk-TYI2XOSI.js';
4
5
  import { GetMyNodeInstallationsDocument } from './chunk-G77IGC3Y.js';
5
6
  import { fetchAllNodeEndpointConfigs, NodeManifestDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowDefinitionDocument, WorkflowRunDocument } from './chunk-Z3ABLERJ.js';
6
7
  import { useTr, ENGLISH_FALLBACK_TRANSLATOR, FluidGridsTranslationProvider } from './chunk-UULVIT25.js';
7
- import { waitForFluidGridsClient, getFluidGridsClient, getFluidGridsClientSafe, createFluidGridsClient, setFluidGridsClient, resetFluidGridsClient } from './chunk-3IXU7WUC.js';
8
- import { throwIfResultError, emitFluidGridsEvent, emitOperationErrorEvent, describeOperationError, emitRunCompletedOnce } from './chunk-US5QSLPV.js';
8
+ import { waitForFluidGridsClient, getFluidGridsClient, getFluidGridsClientSafe, createFluidGridsClient, setFluidGridsClient, resetFluidGridsClient } from './chunk-PDODDDHY.js';
9
+ import { throwIfResultError, emitFluidGridsEvent, emitOperationErrorEvent, describeOperationError, emitRunCompletedOnce } from './chunk-JKLZ6YZ3.js';
9
10
  import { getFluidGridsConfig, validateTrustedOrigin, resetFluidGridsExecutionState, resetFluidGridsConfig, setFluidGridsConfig } from './chunk-UEG2WC3G.js';
10
- import * as React from 'react';
11
11
  import { memo, forwardRef, useState, useImperativeHandle, useRef, useEffect, useMemo, createContext, useCallback, useSyncExternalStore, useContext } from 'react';
12
12
  import { useOptionalFeatureFlags } from '@burdenoff/fe-libs/shared/feature-flags';
13
13
  import { dispatchUpgradePrompt } from '@burdenoff/fe-libs/shared/components';
14
- import { nanoid } from 'nanoid/non-secure';
15
- import { addEdge, applyEdgeChanges, applyNodeChanges, useReactFlow, NodeResizer, getConnectedEdges, useStore, Handle, Position, useUpdateNodeInternals, useNodeId } from '@xyflow/react';
16
- import { create } from 'zustand';
17
- import { produce } from 'immer';
14
+ import { nanoid as nanoid$1 } from 'nanoid/non-secure';
15
+ import { useReactFlow, NodeResizer, getConnectedEdges, useStore, Handle, Position } from '@xyflow/react';
18
16
  import { Icon } from '@iconify/react';
19
- import { nanoid as nanoid$1 } from 'nanoid';
17
+ import { nanoid } from 'nanoid';
20
18
  import ReactMarkdown from 'react-markdown';
21
19
  import { cn } from '@burdenoff/fe-libs/utils';
22
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
20
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
23
21
  import { toast } from 'sonner';
24
- import { nodeCategoryColor, chartSeriesColor, chartSeriesIndex } from '@burdenoff/fe-libs/shared/utils';
22
+ import { nodeCategoryColor } from '@burdenoff/fe-libs/shared/utils';
25
23
  import { gql } from '@apollo/client';
26
24
  import { Textarea, Input, Button, Card, Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuItem, DropdownMenuSeparator, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel } from '@burdenoff/fe-libs/ui';
27
25
  import * as AdaptiveCards from 'adaptivecards';
@@ -31,6 +29,7 @@ import { v4 } from 'uuid';
31
29
  import { useParams, useNavigate } from 'react-router';
32
30
  import { useEventBus } from '@burdenoff/fe-libs/shared/events';
33
31
  import { ApolloProvider } from '@apollo/client/react';
32
+ import { create } from 'zustand';
34
33
  import { useShallow } from 'zustand/shallow';
35
34
  import { InfoCircledIcon } from '@radix-ui/react-icons';
36
35
  import { Button as Button$1 } from '@burdenoff/fe-libs/button';
@@ -276,6 +275,35 @@ function usePaletteGates() {
276
275
  return useMemo(() => resolvePaletteGates(context), [context]);
277
276
  }
278
277
 
278
+ // src/modules/executions/utils/duration.ts
279
+ function spanMs(start, end) {
280
+ if (!start || !end) return null;
281
+ const ms = new Date(end).getTime() - new Date(start).getTime();
282
+ return Number.isFinite(ms) && ms >= 0 ? ms : null;
283
+ }
284
+ function secondsToMs(seconds) {
285
+ return typeof seconds === "number" && Number.isFinite(seconds) && seconds >= 0 ? Math.round(seconds * 1e3) : null;
286
+ }
287
+ function nodeStateDurationMs(node) {
288
+ return spanMs(node.startTime, node.endTime) ?? secondsToMs(node.duration);
289
+ }
290
+ function runDurationMs(run) {
291
+ return spanMs(run.startedAt, run.completedAt) ?? secondsToMs(run.duration);
292
+ }
293
+ function formatElapsedMs(ms, empty = "-") {
294
+ if (ms === null || ms === void 0 || !Number.isFinite(ms) || ms < 0) {
295
+ return empty;
296
+ }
297
+ if (ms < 999.5) return `${Math.round(ms)}ms`;
298
+ if (ms < 59950) return `${(ms / 1e3).toFixed(1)}s`;
299
+ const totalSeconds = Math.round(ms / 1e3);
300
+ if (totalSeconds < 3600) {
301
+ return `${Math.floor(totalSeconds / 60)}m ${totalSeconds % 60}s`;
302
+ }
303
+ const totalMinutes = Math.floor(totalSeconds / 60);
304
+ return `${Math.floor(totalMinutes / 60)}h ${totalMinutes % 60}m`;
305
+ }
306
+
279
307
  // src/billing/quota.ts
280
308
  var RUNS_QUOTA_NAME = "fluidgrids.runs.monthly";
281
309
  var WORKFLOWS_QUOTA_NAME = "fluidgrids.workflows.count";
@@ -510,901 +538,6 @@ function openRunQuotaUpgradePrompt(error, tr = ENGLISH_FALLBACK_TRANSLATOR, anal
510
538
  });
511
539
  return true;
512
540
  }
513
-
514
- // src/builder/lib/node-role.ts
515
- function isRecord(value) {
516
- return typeof value === "object" && value !== null && !Array.isArray(value);
517
- }
518
- function readRecord(value) {
519
- return isRecord(value) ? value : void 0;
520
- }
521
- function readString(value) {
522
- return typeof value === "string" ? value.trim().toLowerCase() : "";
523
- }
524
- function operationRole(operation) {
525
- const metadata = readRecord(operation);
526
- const role = readString(metadata?.role);
527
- if (role) return role === "trigger" ? "trigger" : "action";
528
- const type = readString(metadata?.type);
529
- if (type === "trigger" || type === "subscription" || readString(metadata?.delivery)) {
530
- return "trigger";
531
- }
532
- if (type === "action" || type === "query" || type === "mutation") {
533
- return "action";
534
- }
535
- return void 0;
536
- }
537
- function isTriggerOperation(operation) {
538
- return operationRole(operation) === "trigger";
539
- }
540
- function isLegacyTriggerType(type) {
541
- return type === "trigger" || type.startsWith("trigger:") || type.startsWith("internal:schedule") || type.startsWith("internal:webhook") || type.startsWith("internal:api") || type.startsWith("internal:form") || type.startsWith("internal:mcp") || type.endsWith("_trigger") || type.endsWith("Trigger") || type.endsWith("-trigger");
542
- }
543
- function getSelectedOperationRole(nodeType, nodeData) {
544
- const data = readRecord(nodeData);
545
- const operationName = data?.operation_name;
546
- if (typeof operationName !== "string" || !operationName.trim())
547
- return void 0;
548
- const manifest = readRecord(data?.nodeData);
549
- const declared = manifest?.operations;
550
- const operations = Array.isArray(declared) ? declared : readRecord(declared)?.operations;
551
- const operation = Array.isArray(operations) ? operations.find(
552
- (entry) => readRecord(entry)?.name === operationName
553
- ) : void 0;
554
- const selectedRole = operationRole(operation) ?? operationRole({ type: data?.operation_type });
555
- if (selectedRole) return selectedRole;
556
- if (nodeType === "integration/channel" || nodeType?.startsWith("store/")) {
557
- return operationName.startsWith("on") ? "trigger" : "action";
558
- }
559
- return void 0;
560
- }
561
- function isNodeTrigger(node) {
562
- const data = readRecord(node.data);
563
- const selectedRole = getSelectedOperationRole(node.type, data);
564
- if (selectedRole) return selectedRole === "trigger";
565
- const state = readRecord(readRecord(data?.args)?.state);
566
- for (const legacyRole of [data?.role, state?.role]) {
567
- const explicitRole = readString(legacyRole);
568
- if (explicitRole === "trigger" || explicitRole === "action") {
569
- return explicitRole === "trigger";
570
- }
571
- }
572
- const savedRole = operationRole({ type: data?.operation_type });
573
- if (savedRole) return savedRole === "trigger";
574
- const type = node.type ?? "";
575
- if (isLegacyTriggerType(type)) return true;
576
- const manifest = readRecord(data?.nodeData);
577
- if (!data?.operation_name && Array.isArray(manifest?.kind) && manifest.kind.includes("trigger")) {
578
- return true;
579
- }
580
- return false;
581
- }
582
-
583
- // src/builder/hooks/use-is-valid-connection.ts
584
- function isLoopController(source) {
585
- const type = source?.type?.toLowerCase();
586
- const disabled = source?.data?.disabled === true || source !== void 0 && "disabled" in source && source.disabled === true;
587
- return (type === "internal:loop" || type === "loop") && !disabled;
588
- }
589
- function isRecord2(value) {
590
- return typeof value === "object" && value !== null && !Array.isArray(value);
591
- }
592
- function readRecord2(value) {
593
- return isRecord2(value) ? value : void 0;
594
- }
595
- function loopBodyHandle(source) {
596
- const node = readRecord2(source);
597
- const data = readRecord2(node?.data);
598
- const parameters = {
599
- ...data,
600
- ...readRecord2(node?.config),
601
- ...readRecord2(readRecord2(data?.args)?.state),
602
- ...readRecord2(readRecord2(node?.args)?.state)
603
- };
604
- return typeof parameters.loopHandle === "string" && parameters.loopHandle ? parameters.loopHandle : "loop";
605
- }
606
- function isLoopBodyEdge(edge, nodes) {
607
- const source = nodes.find((node) => node.id === edge.source);
608
- return source !== void 0 && isLoopController(source) && edge.sourceHandle === loopBodyHandle(source);
609
- }
610
- function adjacency(edges) {
611
- const outgoing = /* @__PURE__ */ new Map();
612
- for (const edge of edges) {
613
- const targets = outgoing.get(edge.source) ?? /* @__PURE__ */ new Set();
614
- targets.add(edge.target);
615
- outgoing.set(edge.source, targets);
616
- }
617
- return outgoing;
618
- }
619
- function reachable(starts, outgoing, blocked = /* @__PURE__ */ new Set()) {
620
- const pending = [...starts];
621
- const visited = /* @__PURE__ */ new Set();
622
- while (pending.length > 0) {
623
- const current = pending.pop();
624
- if (current === void 0 || visited.has(current) || blocked.has(current))
625
- continue;
626
- visited.add(current);
627
- pending.push(...outgoing.get(current) ?? []);
628
- }
629
- return visited;
630
- }
631
- function controlledReturns(nodes, edges) {
632
- const outgoing = adjacency(edges);
633
- const incomingTargets = new Set(edges.map((edge) => edge.target));
634
- const entries = nodes.filter((node) => !incomingTargets.has(node.id) || isNodeTrigger(node)).map((node) => node.id);
635
- const returns = /* @__PURE__ */ new Set();
636
- for (const controller of nodes.filter(isLoopController)) {
637
- const bodyHandle = loopBodyHandle(controller);
638
- const controllerEdges = edges.filter(
639
- (edge) => edge.source === controller.id
640
- );
641
- const outside = reachable(
642
- [
643
- ...entries,
644
- ...controllerEdges.filter((edge) => edge.sourceHandle !== bodyHandle).map((edge) => edge.target)
645
- ],
646
- outgoing,
647
- /* @__PURE__ */ new Set([controller.id])
648
- );
649
- const body = reachable(
650
- controllerEdges.filter((edge) => edge.sourceHandle === bodyHandle).map((edge) => edge.target),
651
- outgoing,
652
- /* @__PURE__ */ new Set([controller.id, ...outside])
653
- );
654
- for (const edge of edges) {
655
- if (edge.target === controller.id && body.has(edge.source))
656
- returns.add(edge);
657
- }
658
- }
659
- return returns;
660
- }
661
- function isValidWorkflowConnection(nodes, edges, connection) {
662
- if (connection.target === connection.source) return false;
663
- const candidates = [...edges, connection];
664
- const previousReturns = controlledReturns(nodes, edges);
665
- const nextReturns = controlledReturns(nodes, candidates);
666
- const outgoing = adjacency(
667
- candidates.filter((edge) => !nextReturns.has(edge))
668
- );
669
- const invalidated = [connection, ...previousReturns].filter(
670
- (edge) => !nextReturns.has(edge)
671
- );
672
- return invalidated.every(
673
- (edge) => !reachable([edge.target], outgoing).has(edge.source)
674
- );
675
- }
676
- function useIsValidConnection(nodes, edges) {
677
- return useCallback(
678
- (connection) => isValidWorkflowConnection(nodes, edges, connection),
679
- [nodes, edges]
680
- );
681
- }
682
-
683
- // src/builder/stores/flow-store.ts
684
- var HISTORY_LIMIT = 100;
685
- var CLONE_OFFSET = 48;
686
- function cloneValue(value) {
687
- try {
688
- return structuredClone(value);
689
- } catch {
690
- return JSON.parse(JSON.stringify(value));
691
- }
692
- }
693
- var TAGS = [
694
- { value: "marketing", label: "Marketing", color: "var(--color-chart-5)" },
695
- { value: "support", label: "Support", color: "var(--color-chart-5)" },
696
- { value: "lead", label: "Lead", color: "var(--color-chart-4)" },
697
- { value: "new", label: "New", color: "var(--color-chart-7)" }
698
- ];
699
- var INITIAL_EXECUTION = {
700
- isExecuting: false,
701
- executingNodeId: null,
702
- currentRunId: null,
703
- sourceRun: null,
704
- stepPlan: null,
705
- logs: [],
706
- selectedLogId: null,
707
- panelExpanded: false,
708
- nodeStatuses: {},
709
- nodeItemCounts: {},
710
- followRequest: null
711
- };
712
- var INITIAL_HISTORY = { past: [], future: [] };
713
- var useFlowStore = create()((set, get) => {
714
- let lastCheckpointKind = null;
715
- let lastCheckpointAt = 0;
716
- function pushCheckpoint(kind, coalesce) {
717
- const now = Date.now();
718
- const isCoalesced = coalesce && kind === lastCheckpointKind && now - lastCheckpointAt < 800;
719
- if (!isCoalesced) {
720
- const state = get();
721
- const snapshot = {
722
- nodes: cloneValue(state.workflow.nodes),
723
- edges: cloneValue(state.workflow.edges)
724
- };
725
- set((s) => ({
726
- history: {
727
- past: [...s.history.past, snapshot].slice(-HISTORY_LIMIT),
728
- future: []
729
- }
730
- }));
731
- } else if (get().history.future.length > 0) {
732
- set((s) => ({ history: { ...s.history, future: [] } }));
733
- }
734
- lastCheckpointKind = kind;
735
- lastCheckpointAt = now;
736
- noteEdit();
737
- }
738
- function noteEdit() {
739
- set((s) => ({ editRevision: s.editRevision + 1 }));
740
- }
741
- const REMOVAL_KINDS = /* @__PURE__ */ new Set(["nodes:remove", "edges:remove"]);
742
- const SAME_ACTION_WINDOW_MS = 150;
743
- function joinsPrecedingRemoval(kind) {
744
- return lastCheckpointKind !== null && lastCheckpointKind !== kind && REMOVAL_KINDS.has(kind) && REMOVAL_KINDS.has(lastCheckpointKind) && Date.now() - lastCheckpointAt < SAME_ACTION_WINDOW_MS;
745
- }
746
- function resetCoalescing() {
747
- lastCheckpointKind = null;
748
- lastCheckpointAt = 0;
749
- }
750
- return {
751
- tags: TAGS,
752
- history: { ...INITIAL_HISTORY },
753
- editRevision: 0,
754
- loadGeneration: 0,
755
- workflow: {
756
- id: nanoid(),
757
- workflowKey: "",
758
- name: "",
759
- version: "",
760
- alias: "",
761
- edges: [],
762
- nodes: [],
763
- ndv: null,
764
- quickConfig: null,
765
- pendingConnection: null,
766
- renamingNodeId: null,
767
- sidebar: {
768
- active: "none",
769
- panels: {
770
- nodeProperties: {
771
- selectedNode: null
772
- }
773
- }
774
- }
775
- },
776
- execution: { ...INITIAL_EXECUTION },
777
- actions: {
778
- saveWorkflow: () => {
779
- const { workflow } = get();
780
- const { id, name, version, alias, dbId, status, nodes, edges } = workflow;
781
- set({ workflow });
782
- return { id, name, version, alias, dbId, status, nodes, edges };
783
- },
784
- setWorkflow: (workflow) => {
785
- const current = get().workflow;
786
- if (workflow.name !== void 0 && workflow.name !== current.name || workflow.alias !== void 0 && workflow.alias !== current.alias) {
787
- noteEdit();
788
- }
789
- const nodes = workflow.nodes?.map(
790
- (node) => node.data === void 0 || node.data === null ? { ...node, data: {} } : node
791
- );
792
- set((state) => ({
793
- workflow: {
794
- ...state.workflow,
795
- ...workflow,
796
- ...nodes ? { nodes } : {}
797
- }
798
- }));
799
- },
800
- resetWorkflow: () => {
801
- resetCoalescing();
802
- set((s) => ({
803
- loadGeneration: s.loadGeneration + 1,
804
- workflow: {
805
- id: nanoid(),
806
- workflowKey: "",
807
- name: "",
808
- version: "",
809
- alias: "",
810
- dbId: void 0,
811
- status: void 0,
812
- codeBuildId: void 0,
813
- edges: [],
814
- nodes: [],
815
- ndv: null,
816
- quickConfig: null,
817
- pendingConnection: null,
818
- renamingNodeId: null,
819
- sidebar: {
820
- active: "none",
821
- panels: {
822
- nodeProperties: {
823
- selectedNode: null
824
- }
825
- }
826
- }
827
- },
828
- // Wipe run state too — resetWorkflow fires when the builder loads a
829
- // (different) workflow, so the previous workflow's logs must not carry
830
- // over into this one's Logs panel.
831
- execution: { ...INITIAL_EXECUTION },
832
- // A freshly loaded workflow must not be undo-able back to empty —
833
- // undo/redo only ever applies within the currently open workflow.
834
- history: { ...INITIAL_HISTORY }
835
- }));
836
- },
837
- history: {
838
- undo: () => {
839
- const { past, future } = get().history;
840
- const previous = past[past.length - 1];
841
- if (!previous) return;
842
- const { nodes, edges } = get().workflow;
843
- const current = {
844
- nodes: cloneValue(nodes),
845
- edges: cloneValue(edges)
846
- };
847
- resetCoalescing();
848
- noteEdit();
849
- set((s) => ({
850
- workflow: {
851
- ...s.workflow,
852
- nodes: previous.nodes,
853
- edges: previous.edges
854
- },
855
- history: {
856
- past: past.slice(0, -1),
857
- future: [current, ...future].slice(0, HISTORY_LIMIT)
858
- }
859
- }));
860
- },
861
- redo: () => {
862
- const { past, future } = get().history;
863
- const next = future[0];
864
- if (!next) return;
865
- const { nodes, edges } = get().workflow;
866
- const current = {
867
- nodes: cloneValue(nodes),
868
- edges: cloneValue(edges)
869
- };
870
- resetCoalescing();
871
- noteEdit();
872
- set((s) => ({
873
- workflow: {
874
- ...s.workflow,
875
- nodes: next.nodes,
876
- edges: next.edges
877
- },
878
- history: {
879
- past: [...past, current].slice(-HISTORY_LIMIT),
880
- future: future.slice(1)
881
- }
882
- }));
883
- },
884
- clear: () => {
885
- resetCoalescing();
886
- set({ history: { ...INITIAL_HISTORY } });
887
- }
888
- },
889
- ndv: {
890
- // Opening the full NDV supersedes the floating quick-config panel — the
891
- // two edit the same node data, so only one should be visible at a time.
892
- open: (node) => set((state) => ({
893
- workflow: { ...state.workflow, ndv: node, quickConfig: null }
894
- })),
895
- close: () => set((state) => ({
896
- workflow: { ...state.workflow, ndv: null }
897
- }))
898
- },
899
- quickConfig: {
900
- open: (node) => set((state) => ({
901
- workflow: { ...state.workflow, quickConfig: node }
902
- })),
903
- close: () => set((state) => ({
904
- workflow: { ...state.workflow, quickConfig: null }
905
- }))
906
- },
907
- pendingConnection: {
908
- start: (connection) => set((state) => ({
909
- workflow: {
910
- ...state.workflow,
911
- pendingConnection: connection,
912
- sidebar: { ...state.workflow.sidebar, active: "store-browse" }
913
- }
914
- })),
915
- clear: () => set((state) => ({
916
- workflow: { ...state.workflow, pendingConnection: null }
917
- }))
918
- },
919
- sidebar: {
920
- // Leaving the store panel abandons any armed "+" attach mode — the
921
- // pending handle would otherwise silently capture a later insert.
922
- setActivePanel: (panel) => set((state) => ({
923
- workflow: {
924
- ...state.workflow,
925
- sidebar: { ...state.workflow.sidebar, active: panel },
926
- ...panel !== "store-browse" ? { pendingConnection: null } : {}
927
- }
928
- })),
929
- showNodePropertiesOf: (node) => {
930
- set((state) => ({
931
- workflow: {
932
- ...state.workflow,
933
- pendingConnection: null,
934
- sidebar: {
935
- ...state.workflow.sidebar,
936
- active: "node-properties",
937
- panels: {
938
- ...state.workflow.sidebar.panels,
939
- nodeProperties: {
940
- ...state.workflow.sidebar.panels.nodeProperties,
941
- selectedNode: node
942
- }
943
- }
944
- }
945
- }
946
- }));
947
- },
948
- panels: {
949
- nodeProperties: {
950
- setSelectedNode: (node) => set((state) => ({
951
- workflow: {
952
- ...state.workflow,
953
- sidebar: {
954
- ...state.workflow.sidebar,
955
- panels: {
956
- ...state.workflow.sidebar.panels,
957
- nodeProperties: {
958
- ...state.workflow.sidebar.panels.nodeProperties,
959
- selectedNode: node
960
- }
961
- }
962
- }
963
- }
964
- }))
965
- },
966
- tags: {
967
- setTags: (tags) => set({ tags }),
968
- createTag: (tag) => set((state) => ({
969
- tags: [...state.tags, tag]
970
- })),
971
- updateTag: (tag, newTag) => set((state) => ({
972
- tags: state.tags.map(
973
- (f) => f.value === tag.value ? newTag : f
974
- )
975
- })),
976
- deleteTag: (tag) => set((state) => ({
977
- tags: state.tags.filter((f) => f.value !== tag.value)
978
- }))
979
- }
980
- }
981
- },
982
- nodes: {
983
- onNodesChange: (changes) => {
984
- const removedIds = new Set(
985
- changes.filter(
986
- (c) => c.type === "remove"
987
- ).map((c) => c.id)
988
- );
989
- const addedCount = changes.filter((c) => c.type === "add").length;
990
- if (removedIds.size > 0) {
991
- const stillPresent = get().workflow.nodes.some(
992
- (n) => removedIds.has(n.id)
993
- );
994
- if (stillPresent && !joinsPrecedingRemoval("nodes:remove")) {
995
- pushCheckpoint("nodes:remove", false);
996
- }
997
- } else if (addedCount > 0) {
998
- pushCheckpoint("nodes:add", false);
999
- } else {
1000
- const startsDrag = changes.some(
1001
- (c) => c.type === "position" && c.dragging === true && !get().workflow.nodes.find((n) => n.id === c.id)?.dragging
1002
- );
1003
- if (startsDrag) noteEdit();
1004
- const finishedDragIds = changes.filter(
1005
- (c) => c.type === "position" && c.dragging === false
1006
- );
1007
- const actuallyMoved = finishedDragIds.some((c) => {
1008
- const current = get().workflow.nodes.find((n) => n.id === c.id);
1009
- return current && c.position && (current.position.x !== c.position.x || current.position.y !== c.position.y);
1010
- });
1011
- if (actuallyMoved) pushCheckpoint("nodes:move", false);
1012
- }
1013
- set(
1014
- (state) => produce(state, (draft) => {
1015
- const updatedNodes = applyNodeChanges(
1016
- changes,
1017
- draft.workflow.nodes
1018
- );
1019
- draft.workflow.nodes = updatedNodes;
1020
- })
1021
- );
1022
- },
1023
- setNodes: (nodes, opts) => {
1024
- if (!opts?.skipHistory) {
1025
- pushCheckpoint(
1026
- opts?.historyKind ?? "nodes:set",
1027
- !!opts?.historyKind
1028
- );
1029
- }
1030
- set({ workflow: { ...get().workflow, nodes } });
1031
- },
1032
- deleteNode: (node) => {
1033
- const exists = get().workflow.nodes.some((n) => n.id === node.id);
1034
- if (exists) pushCheckpoint("nodes:delete", false);
1035
- set((state) => ({
1036
- workflow: {
1037
- ...state.workflow,
1038
- nodes: state.workflow.nodes.filter((n) => n.id !== node.id),
1039
- // Drop any UI state pointing at the removed node.
1040
- ...state.workflow.quickConfig?.nodeId === node.id ? { quickConfig: null } : {},
1041
- ...state.workflow.pendingConnection?.nodeId === node.id ? { pendingConnection: null } : {}
1042
- }
1043
- }));
1044
- },
1045
- deleteNodes: (nodes) => {
1046
- const ids = new Set(nodes.map((n) => n.id));
1047
- if (ids.size === 0) return;
1048
- const anyExist = get().workflow.nodes.some((n) => ids.has(n.id));
1049
- if (anyExist) pushCheckpoint("nodes:delete", false);
1050
- set((state) => ({
1051
- workflow: {
1052
- ...state.workflow,
1053
- nodes: state.workflow.nodes.filter((n) => !ids.has(n.id)),
1054
- edges: state.workflow.edges.filter(
1055
- (e) => !ids.has(e.source) && !ids.has(e.target)
1056
- ),
1057
- ...state.workflow.quickConfig && ids.has(state.workflow.quickConfig.nodeId) ? { quickConfig: null } : {},
1058
- ...state.workflow.pendingConnection && ids.has(state.workflow.pendingConnection.nodeId) ? { pendingConnection: null } : {}
1059
- }
1060
- }));
1061
- },
1062
- duplicateNodes: (nodeIds) => {
1063
- const { nodes, edges } = get().workflow;
1064
- const idSet = new Set(nodeIds);
1065
- const selected = nodes.filter((n) => idSet.has(n.id));
1066
- if (selected.length === 0) return;
1067
- pushCheckpoint("nodes:duplicate", false);
1068
- const idMap = /* @__PURE__ */ new Map();
1069
- const clones = selected.map((n) => {
1070
- const newId = nanoid();
1071
- idMap.set(n.id, newId);
1072
- const cloned = cloneValue(n);
1073
- return {
1074
- ...cloned,
1075
- id: newId,
1076
- position: {
1077
- x: cloned.position.x + CLONE_OFFSET,
1078
- y: cloned.position.y + CLONE_OFFSET
1079
- },
1080
- selected: true
1081
- };
1082
- });
1083
- const clonedEdges = edges.filter((e) => idMap.has(e.source) && idMap.has(e.target)).map((e) => {
1084
- const cloned = cloneValue(e);
1085
- return {
1086
- ...cloned,
1087
- id: nanoid(),
1088
- source: idMap.get(e.source),
1089
- target: idMap.get(e.target),
1090
- selected: false
1091
- };
1092
- });
1093
- set((state) => ({
1094
- workflow: {
1095
- ...state.workflow,
1096
- nodes: [
1097
- ...state.workflow.nodes.map(
1098
- (n) => idSet.has(n.id) ? { ...n, selected: false } : n
1099
- ),
1100
- ...clones
1101
- ],
1102
- edges: [...state.workflow.edges, ...clonedEdges]
1103
- }
1104
- }));
1105
- },
1106
- pasteNodes: (payload) => {
1107
- const pasteNodesIn = payload.nodes ?? [];
1108
- if (pasteNodesIn.length === 0) return;
1109
- pushCheckpoint("nodes:paste", false);
1110
- const idMap = /* @__PURE__ */ new Map();
1111
- const clones = pasteNodesIn.map((n) => {
1112
- const newId = nanoid();
1113
- idMap.set(n.id, newId);
1114
- const cloned = cloneValue(n);
1115
- return {
1116
- ...cloned,
1117
- id: newId,
1118
- position: {
1119
- x: (cloned.position?.x ?? 0) + CLONE_OFFSET,
1120
- y: (cloned.position?.y ?? 0) + CLONE_OFFSET
1121
- },
1122
- selected: true
1123
- };
1124
- });
1125
- const clonedEdges = (payload.edges ?? []).filter((e) => idMap.has(e.source) && idMap.has(e.target)).map((e) => {
1126
- const cloned = cloneValue(e);
1127
- return {
1128
- ...cloned,
1129
- id: nanoid(),
1130
- source: idMap.get(e.source),
1131
- target: idMap.get(e.target),
1132
- selected: false
1133
- };
1134
- });
1135
- set((state) => ({
1136
- workflow: {
1137
- ...state.workflow,
1138
- nodes: [
1139
- ...state.workflow.nodes.map((n) => ({ ...n, selected: false })),
1140
- ...clones
1141
- ],
1142
- edges: [...state.workflow.edges, ...clonedEdges]
1143
- }
1144
- }));
1145
- },
1146
- toggleDisabled: (nodeId) => {
1147
- const { nodes, edges } = get().workflow;
1148
- const updatedNodes = nodes.map((node) => {
1149
- if (node.id !== nodeId) return node;
1150
- const data = node.data ?? {};
1151
- return {
1152
- ...node,
1153
- data: { ...data, disabled: !(data.disabled === true) }
1154
- };
1155
- });
1156
- const breaksLoop = edges.some(
1157
- (edge) => edge.source === nodeId && isLoopBodyEdge(edge, nodes) && !isValidWorkflowConnection(updatedNodes, edges, edge)
1158
- );
1159
- if (breaksLoop) return false;
1160
- pushCheckpoint("nodes:disable", false);
1161
- set((state) => ({
1162
- workflow: {
1163
- ...state.workflow,
1164
- nodes: updatedNodes
1165
- }
1166
- }));
1167
- return true;
1168
- },
1169
- renameNode: (nodeId, label) => {
1170
- pushCheckpoint("nodes:rename", false);
1171
- set((state) => ({
1172
- workflow: {
1173
- ...state.workflow,
1174
- nodes: state.workflow.nodes.map(
1175
- (n) => n.id === nodeId ? { ...n, data: { ...n.data ?? {}, label } } : n
1176
- ),
1177
- renamingNodeId: null
1178
- }
1179
- }));
1180
- },
1181
- startRename: (nodeId) => {
1182
- set((state) => ({
1183
- workflow: { ...state.workflow, renamingNodeId: nodeId }
1184
- }));
1185
- },
1186
- endRename: () => {
1187
- set((state) => ({
1188
- workflow: { ...state.workflow, renamingNodeId: null }
1189
- }));
1190
- }
1191
- },
1192
- edges: {
1193
- onEdgesChange: (changes) => {
1194
- const removedIds = new Set(
1195
- changes.filter(
1196
- (c) => c.type === "remove"
1197
- ).map((c) => c.id)
1198
- );
1199
- if (removedIds.size > 0) {
1200
- const stillPresent = get().workflow.edges.some(
1201
- (e) => removedIds.has(e.id)
1202
- );
1203
- if (stillPresent && !joinsPrecedingRemoval("edges:remove")) {
1204
- pushCheckpoint("edges:remove", false);
1205
- }
1206
- } else if (changes.some((c) => c.type === "add")) {
1207
- pushCheckpoint("edges:add", false);
1208
- }
1209
- set(
1210
- (state) => produce(state, (draft) => {
1211
- const updatedEdges = applyEdgeChanges(
1212
- changes,
1213
- draft.workflow.edges
1214
- );
1215
- draft.workflow.edges = updatedEdges;
1216
- })
1217
- );
1218
- },
1219
- onConnect: (connection) => {
1220
- pushCheckpoint("edges:connect", false);
1221
- const edge = {
1222
- ...connection,
1223
- id: nanoid(),
1224
- type: "deletable"
1225
- };
1226
- set({
1227
- workflow: {
1228
- ...get().workflow,
1229
- edges: addEdge(edge, get().workflow.edges)
1230
- }
1231
- });
1232
- },
1233
- setEdges: (edges, opts) => {
1234
- if (!opts?.skipHistory) {
1235
- pushCheckpoint(
1236
- opts?.historyKind ?? "edges:set",
1237
- !!opts?.historyKind
1238
- );
1239
- }
1240
- set({ workflow: { ...get().workflow, edges } });
1241
- },
1242
- deleteEdge: (edge) => {
1243
- const exists = get().workflow.edges.some((e) => e.id === edge.id);
1244
- if (exists) pushCheckpoint("edges:delete", false);
1245
- set((state) => ({
1246
- workflow: {
1247
- ...state.workflow,
1248
- edges: state.workflow.edges.filter((e) => e.id !== edge.id)
1249
- }
1250
- }));
1251
- }
1252
- },
1253
- execution: {
1254
- startExecution: (nodeId, runId, metadata) => {
1255
- set((state) => ({
1256
- execution: {
1257
- ...state.execution,
1258
- isExecuting: true,
1259
- executingNodeId: nodeId,
1260
- currentRunId: runId,
1261
- sourceRun: metadata ? { runId, ...metadata } : { runId, workflowKey: "", version: "" },
1262
- panelExpanded: true,
1263
- nodeStatuses: {
1264
- ...state.execution.nodeStatuses,
1265
- [nodeId]: "running"
1266
- }
1267
- }
1268
- }));
1269
- },
1270
- setRunMetadata: (metadata) => {
1271
- set((state) => {
1272
- if (state.execution.currentRunId !== metadata.runId && state.execution.sourceRun?.runId !== metadata.runId) {
1273
- return state;
1274
- }
1275
- return {
1276
- execution: {
1277
- ...state.execution,
1278
- currentRunId: metadata.runId,
1279
- sourceRun: metadata
1280
- }
1281
- };
1282
- });
1283
- },
1284
- setStepPlan: (plan) => {
1285
- set((state) => ({
1286
- execution: {
1287
- ...state.execution,
1288
- stepPlan: plan
1289
- }
1290
- }));
1291
- },
1292
- addLog: (log) => {
1293
- set((state) => ({
1294
- execution: {
1295
- ...state.execution,
1296
- logs: [...state.execution.logs, log],
1297
- selectedLogId: log.id
1298
- }
1299
- }));
1300
- },
1301
- updateLog: (logId, updates) => {
1302
- set((state) => ({
1303
- execution: {
1304
- ...state.execution,
1305
- logs: state.execution.logs.map(
1306
- (log) => log.id === logId ? { ...log, ...updates } : log
1307
- )
1308
- }
1309
- }));
1310
- },
1311
- setNodeStatus: (nodeId, status) => {
1312
- set((state) => ({
1313
- execution: {
1314
- ...state.execution,
1315
- nodeStatuses: {
1316
- ...state.execution.nodeStatuses,
1317
- [nodeId]: status
1318
- }
1319
- }
1320
- }));
1321
- },
1322
- setNodeItemCount: (nodeId, count) => {
1323
- set((state) => ({
1324
- execution: {
1325
- ...state.execution,
1326
- nodeItemCounts: {
1327
- ...state.execution.nodeItemCounts,
1328
- [nodeId]: count
1329
- }
1330
- }
1331
- }));
1332
- },
1333
- selectLog: (logId) => {
1334
- set((state) => ({
1335
- execution: {
1336
- ...state.execution,
1337
- selectedLogId: logId
1338
- }
1339
- }));
1340
- },
1341
- togglePanel: () => {
1342
- set((state) => ({
1343
- execution: {
1344
- ...state.execution,
1345
- panelExpanded: !state.execution.panelExpanded
1346
- }
1347
- }));
1348
- },
1349
- clearLogs: () => {
1350
- set((state) => ({
1351
- execution: {
1352
- ...state.execution,
1353
- logs: [],
1354
- stepPlan: null,
1355
- currentRunId: null,
1356
- sourceRun: null,
1357
- selectedLogId: null,
1358
- nodeStatuses: {},
1359
- nodeItemCounts: {}
1360
- }
1361
- }));
1362
- },
1363
- completeExecution: () => {
1364
- set((state) => ({
1365
- execution: {
1366
- ...state.execution,
1367
- isExecuting: false,
1368
- executingNodeId: null
1369
- }
1370
- }));
1371
- },
1372
- requestFollowRun: (runId, triggerNodeId) => {
1373
- set((state) => ({
1374
- execution: {
1375
- ...state.execution,
1376
- followRequest: { runId, triggerNodeId }
1377
- }
1378
- }));
1379
- },
1380
- clearFollowRequest: () => {
1381
- set((state) => ({
1382
- execution: { ...state.execution, followRequest: null }
1383
- }));
1384
- },
1385
- loadDebugRun: (runId, logs, metadata) => {
1386
- const nodeStatuses = {};
1387
- for (const log of logs) {
1388
- nodeStatuses[log.nodeId] = log.status === "completed" ? "success" : log.status === "failed" ? "error" : log.status === "running" ? "running" : "idle";
1389
- }
1390
- set(() => ({
1391
- execution: {
1392
- ...INITIAL_EXECUTION,
1393
- currentRunId: runId,
1394
- sourceRun: metadata ? { runId, ...metadata } : { runId, workflowKey: "", version: "" },
1395
- logs,
1396
- nodeStatuses,
1397
- panelExpanded: true
1398
- }
1399
- }));
1400
- }
1401
- }
1402
- }
1403
- };
1404
- });
1405
- if (typeof window !== "undefined" && import.meta.env?.DEV) {
1406
- window.__fgFlowStore = useFlowStore;
1407
- }
1408
541
  function useDeleteNode() {
1409
542
  const { getNode, getEdges, deleteElements } = useReactFlow();
1410
543
  const deleteNodes = useFlowStore((state) => state.actions.nodes.deleteNodes);
@@ -1420,11 +553,6 @@ function useDeleteNode() {
1420
553
  [deleteElements, getEdges, getNode, deleteNodes]
1421
554
  );
1422
555
  }
1423
-
1424
- // src/builder/contants/symbols.ts
1425
- var NODE_TYPE_DRAG_DATA_FORMAT = "application/flow-builder.node-type";
1426
- var NODE_OPERATION_DRAG_DATA_FORMAT = "application/flow-builder.node-operation";
1427
- var STICKY_NOTE_TYPE = "sticky-note";
1428
556
  var STICKY_COLORS = {
1429
557
  yellow: "var(--color-chart-4)",
1430
558
  orange: "var(--color-status-warning-bg)",
@@ -1520,7 +648,7 @@ function StickyNoteNodeComponent({
1520
648
  if (!source) return;
1521
649
  const clone = {
1522
650
  ...source,
1523
- id: nanoid$1(),
651
+ id: nanoid(),
1524
652
  position: {
1525
653
  x: source.position.x + 24,
1526
654
  y: source.position.y + 24
@@ -1644,1158 +772,204 @@ function StickyNoteNodeComponent({
1644
772
  /* @__PURE__ */ jsx(
1645
773
  "div",
1646
774
  {
1647
- className: "flex h-full w-full flex-col overflow-hidden rounded-lg border",
1648
- style: {
1649
- backgroundColor: `${accent}1F`,
1650
- // Same border whether selected or not — selection shows only via the
1651
- // floating toolbar (no extra border/outline on click).
1652
- borderColor: `${accent}59`
1653
- },
1654
- onDoubleClick: (e) => {
1655
- e.stopPropagation();
1656
- if (!readOnly) startEditing();
1657
- },
1658
- children: editing ? /* @__PURE__ */ jsx(
1659
- "textarea",
1660
- {
1661
- ref: textareaRef,
1662
- readOnly,
1663
- value: draft,
1664
- onChange: (e) => setDraft(e.target.value),
1665
- onBlur: commitEdit,
1666
- onKeyDown: (e) => {
1667
- e.stopPropagation();
1668
- if (e.key === "Escape") cancelEdit();
1669
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) commitEdit();
1670
- },
1671
- placeholder: tr(
1672
- "fluidgrids.builder.stickyNote.editorPlaceholder",
1673
- "# Note\nSupports **markdown** \u2014 headings, lists, links\u2026"
1674
- ),
1675
- className: "nodrag nowheel h-full w-full resize-none bg-transparent p-3 font-mono text-xs leading-relaxed text-foreground outline-none placeholder:text-muted-foreground/60"
1676
- }
1677
- ) : content.trim() ? /* @__PURE__ */ jsx(
1678
- "div",
1679
- {
1680
- className: cn(
1681
- "nowheel h-full w-full overflow-auto p-3 text-xs leading-relaxed text-foreground",
1682
- // Compact markdown typography scoped to the note body.
1683
- "[&_h1]:mb-1.5 [&_h1]:text-sm [&_h1]:font-bold",
1684
- "[&_h2]:mb-1 [&_h2]:text-[13px] [&_h2]:font-bold",
1685
- "[&_h3]:mb-1 [&_h3]:text-xs [&_h3]:font-semibold",
1686
- "[&_p]:mb-1.5 [&_ul]:mb-1.5 [&_ul]:list-disc [&_ul]:pl-4",
1687
- "[&_ol]:mb-1.5 [&_ol]:list-decimal [&_ol]:pl-4 [&_li]:mb-0.5",
1688
- "[&_a]:underline [&_code]:rounded [&_code]:bg-muted [&_code]:px-1"
1689
- ),
1690
- children: /* @__PURE__ */ jsx(ReactMarkdown, { children: content })
1691
- }
1692
- ) : /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center p-3 text-xs text-muted-foreground/70", children: tr(
1693
- "fluidgrids.builder.stickyNote.emptyStateHint",
1694
- "Double-click to edit"
1695
- ) })
1696
- }
1697
- )
1698
- ] })
1699
- );
1700
- }
1701
- var StickyNoteNode = memo(StickyNoteNodeComponent);
1702
- function buildStickyNode(position) {
1703
- return {
1704
- id: nanoid$1(),
1705
- type: STICKY_NOTE_TYPE,
1706
- position,
1707
- width: STICKY_DEFAULT_SIZE.width,
1708
- height: STICKY_DEFAULT_SIZE.height,
1709
- // Render under regular nodes and edges — notes are a background layer.
1710
- zIndex: -1,
1711
- selected: true,
1712
- data: { content: "", color: DEFAULT_STICKY_COLOR }
1713
- };
1714
- }
1715
-
1716
- // src/modules/connections/utils/adapter-selection.ts
1717
- var ANY_PROVIDER = "*";
1718
- function isOptionalAdapter(adapter) {
1719
- return adapter.required === false;
1720
- }
1721
- function newConnectionProviderKey(adapters) {
1722
- if (adapters.length !== 1) return void 0;
1723
- const provider = adapters[0]?.provider;
1724
- return provider && provider !== ANY_PROVIDER ? provider : void 0;
1725
- }
1726
- function matchesAdapterProvider(adapter, providerKey) {
1727
- const normalizedKey = providerKey.trim().toLowerCase();
1728
- if (normalizedKey.length === 0) return false;
1729
- if (adapter.provider.trim() === ANY_PROVIDER) return true;
1730
- return adapter.provider.trim().toLowerCase() === normalizedKey || adapter.name.trim().toLowerCase() === normalizedKey;
1731
- }
1732
- function connectionsForAdapter(connections, adapterName) {
1733
- return connections.filter(
1734
- (connection) => connection.adapter.toLowerCase() === adapterName.toLowerCase()
1735
- );
1736
- }
1737
- function replaceAdapterConnections(connections, adapterName, connectionIds = []) {
1738
- const otherConnections = connections.filter(
1739
- (connection) => connection.adapter.toLowerCase() !== adapterName.toLowerCase()
1740
- );
1741
- const uniqueIds = [...new Set(connectionIds.filter(Boolean))];
1742
- return [
1743
- ...otherConnections,
1744
- ...uniqueIds.map((connectionId) => ({
1745
- connectionId,
1746
- adapter: adapterName
1747
- }))
1748
- ];
1749
- }
1750
- function missingRequiredAdapters(adapters, selected) {
1751
- return adapters.filter(
1752
- (adapter) => !isOptionalAdapter(adapter) && !selected.some((connection) => connection.adapter === adapter.name)
1753
- );
1754
- }
1755
- var NODE_DOCS_URL = gql`
1756
- query NodeDocsUrlLookup($nodeType: String!, $nodeVersion: String!) {
1757
- nodeEndpointConfig(nodeType: $nodeType, nodeVersion: $nodeVersion) {
1758
- docsUrl
1759
- }
1760
- }
1761
- `;
1762
- var cache = /* @__PURE__ */ new Map();
1763
- var inFlight = /* @__PURE__ */ new Map();
1764
- function fetchDocsUrl(nodeType, nodeVersion) {
1765
- const key = `${nodeType}@${nodeVersion}`;
1766
- if (cache.has(key)) return Promise.resolve(cache.get(key));
1767
- const existing = inFlight.get(key);
1768
- if (existing) return existing;
1769
- const client = getFluidGridsClientSafe();
1770
- if (!client) {
1771
- cache.set(key, void 0);
1772
- return Promise.resolve(void 0);
1773
- }
1774
- const promise = client.query({
1775
- query: NODE_DOCS_URL,
1776
- variables: { nodeType, nodeVersion },
1777
- fetchPolicy: "cache-first"
1778
- }).then((result) => result.data?.nodeEndpointConfig?.docsUrl ?? void 0).catch(() => void 0).finally(() => inFlight.delete(key));
1779
- inFlight.set(key, promise);
1780
- return promise.then((url) => {
1781
- cache.set(key, url);
1782
- return url;
1783
- });
1784
- }
1785
- function useNodeDocsUrl(nodeType, nodeVersion) {
1786
- const [docsUrl, setDocsUrl] = useState(
1787
- () => nodeType && nodeVersion ? cache.get(`${nodeType}@${nodeVersion}`) : void 0
1788
- );
1789
- useEffect(() => {
1790
- if (!nodeType || !nodeVersion) {
1791
- setDocsUrl(void 0);
1792
- return;
1793
- }
1794
- let cancelled = false;
1795
- fetchDocsUrl(nodeType, nodeVersion).then((url) => {
1796
- if (!cancelled) setDocsUrl(url);
1797
- });
1798
- return () => {
1799
- cancelled = true;
1800
- };
1801
- }, [nodeType, nodeVersion]);
1802
- return docsUrl;
1803
- }
1804
- function isImageIcon(icon) {
1805
- return !!icon && (/^https?:\/\//.test(icon) || icon.startsWith("data:"));
1806
- }
1807
- function NodeTypeIcon({
1808
- icon,
1809
- alt,
1810
- className,
1811
- fallbackIcon = "mynaui:puzzle"
1812
- }) {
1813
- if (isImageIcon(icon)) {
1814
- return /* @__PURE__ */ jsx(
1815
- "img",
1816
- {
1817
- src: icon,
1818
- alt: alt ?? "",
1819
- className: cn("object-contain", className)
1820
- }
1821
- );
1822
- }
1823
- return /* @__PURE__ */ jsx(Icon, { icon: icon || fallbackIcon, className });
1824
- }
1825
- var PROVIDER_ACCENTS = [
1826
- [/gmail|google/, "var(--color-chart-5)"],
1827
- [/slack/, "var(--color-chart-3)"],
1828
- [/github|gitlab/, nodeCategoryColor("utility")],
1829
- [/stripe/, "var(--color-chart-12)"],
1830
- [/discord|telegram/, "var(--color-chart-1)"],
1831
- [/notion/, "var(--color-text-primary)"],
1832
- [/whatsapp|sheet|excel/, "var(--color-chart-7)"],
1833
- [/openai|anthropic|claude/, nodeCategoryColor("ai")],
1834
- [/twilio/, "var(--color-chart-10)"],
1835
- [/http|api|webhook|rest/, nodeCategoryColor("integration")],
1836
- [/db|sql|postgres|mysql|mongo/, nodeCategoryColor("data")]
1837
- ];
1838
- function nodeAccent(key) {
1839
- const k = (key || "").toLowerCase();
1840
- for (const [re, color] of PROVIDER_ACCENTS) if (re.test(k)) return color;
1841
- return chartSeriesColor(chartSeriesIndex(k));
1842
- }
1843
- function tint(color, pct = 14) {
1844
- return `color-mix(in srgb, ${color} ${pct}%, transparent)`;
1845
- }
1846
- function InlineEditableLabel({
1847
- value,
1848
- placeholder,
1849
- editing,
1850
- onStartEdit,
1851
- onCommit,
1852
- onCancel,
1853
- className,
1854
- inputClassName
1855
- }) {
1856
- const tr = useTr();
1857
- const [draft, setDraft] = React.useState(value);
1858
- const inputRef = React.useRef(null);
1859
- React.useEffect(() => {
1860
- if (!editing) return;
1861
- setDraft(value);
1862
- const raf = requestAnimationFrame(() => {
1863
- inputRef.current?.focus();
1864
- inputRef.current?.select();
1865
- });
1866
- return () => cancelAnimationFrame(raf);
1867
- }, [editing, value]);
1868
- if (editing) {
1869
- return /* @__PURE__ */ jsx(
1870
- "input",
1871
- {
1872
- ref: inputRef,
1873
- value: draft,
1874
- onChange: (e) => setDraft(e.target.value),
1875
- onClick: (e) => e.stopPropagation(),
1876
- onDoubleClick: (e) => e.stopPropagation(),
1877
- onMouseDown: (e) => e.stopPropagation(),
1878
- onKeyDown: (e) => {
1879
- e.stopPropagation();
1880
- if (e.key === "Enter") {
1881
- e.preventDefault();
1882
- onCommit(draft.trim());
1883
- } else if (e.key === "Escape") {
1884
- e.preventDefault();
1885
- onCancel();
1886
- }
1887
- },
1888
- onBlur: () => onCommit(draft.trim()),
1889
- className: cn(
1890
- "min-w-0 flex-1 rounded border border-primary/50 bg-card px-1 text-inherit outline-none",
1891
- inputClassName
1892
- )
1893
- }
1894
- );
1895
- }
1896
- return /* @__PURE__ */ jsx(
1897
- "span",
1898
- {
1899
- className: cn("cursor-text truncate", className),
1900
- title: tr(
1901
- "fluidgrids.builder.nodeCard.renameTooltip",
1902
- "Double-click or press F2 to rename"
1903
- ),
1904
- onDoubleClick: (e) => {
1905
- e.stopPropagation();
1906
- onStartEdit();
1907
- },
1908
- children: value || placeholder
1909
- }
1910
- );
1911
- }
1912
- var NodeCard = React.forwardRef(
1913
- ({
1914
- className,
1915
- selected = false,
1916
- executionStatus = "idle",
1917
- itemCount,
1918
- isTrigger = false,
1919
- accent = nodeCategoryColor("action"),
1920
- onSettings,
1921
- onExecute,
1922
- onDelete,
1923
- isExecuting = false,
1924
- infoButton,
1925
- disabled = false,
1926
- onToggleDisabled,
1927
- children,
1928
- ...props
1929
- }, ref) => {
1930
- const tr = useTr();
1931
- const [hovered, setHovered] = React.useState(false);
1932
- const statusRing = executionStatus === "running" ? "ring-2 ring-[var(--color-status-success-border)] animate-pulse-border" : executionStatus === "success" ? "ring-2 ring-[var(--color-status-success-border)]" : executionStatus === "error" ? "ring-2 ring-[var(--color-status-error-border)]" : "";
1933
- return /* @__PURE__ */ jsxs(
1934
- "div",
1935
- {
1936
- className: "group/node relative",
1937
- onMouseEnter: () => setHovered(true),
1938
- onMouseLeave: () => setHovered(false),
1939
- children: [
1940
- itemCount !== void 0 && itemCount > 0 && /* @__PURE__ */ jsx("div", { className: "absolute -top-7 left-1/2 -translate-x-1/2 z-10", children: /* @__PURE__ */ jsxs("div", { className: "rounded-full border border-border bg-card px-2 py-0.5 text-[10px] font-medium text-muted-foreground shadow-sm", children: [
1941
- itemCount,
1942
- " ",
1943
- itemCount !== 1 ? tr("fluidgrids.builder.nodeCard.itemCountOther", "items") : tr("fluidgrids.builder.nodeCard.itemCountOne", "item")
1944
- ] }) }),
1945
- (onSettings || onExecute || onDelete || onToggleDisabled || infoButton) && /* @__PURE__ */ jsxs(
1946
- "div",
1947
- {
1948
- className: cn(
1949
- "absolute right-1 z-20 flex items-center gap-0.5 rounded-lg border border-border bg-card p-0.5 shadow-md transition-all duration-150",
1950
- hovered ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
1951
- ),
1952
- style: {
1953
- // Inline offset (not a `-top-*` class) so it survives the host
1954
- // app's Tailwind scan of fe-sdk source, which drops some spacing
1955
- // utilities. The toolbar is ~30px tall; top:-15px centers it on
1956
- // the card's top edge (half outside, half over the card).
1957
- top: -15,
1958
- transform: hovered ? "translateY(0)" : "translateY(4px)"
1959
- },
1960
- onClick: (e) => e.stopPropagation(),
1961
- onDoubleClick: (e) => e.stopPropagation(),
1962
- children: [
1963
- onSettings && /* @__PURE__ */ jsx(
1964
- ToolbarButton2,
1965
- {
1966
- title: tr(
1967
- "fluidgrids.builder.nodeCard.configureTooltip",
1968
- "Configure"
1969
- ),
1970
- onClick: onSettings,
1971
- children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:sliders-horizontal", className: "size-4" })
1972
- }
1973
- ),
1974
- onExecute && /* @__PURE__ */ jsx(
1975
- ToolbarButton2,
1976
- {
1977
- "data-agent": "confirm",
1978
- title: tr(
1979
- "fluidgrids.builder.nodeCard.executeTooltip",
1980
- "Execute step"
1981
- ),
1982
- onClick: onExecute,
1983
- disabled: isExecuting,
1984
- children: isExecuting ? /* @__PURE__ */ jsx(
1985
- Icon,
1986
- {
1987
- icon: "svg-spinners:ring-resize",
1988
- className: "size-4 text-[var(--color-status-success-text)]"
1989
- }
1990
- ) : /* @__PURE__ */ jsx(
1991
- Icon,
1992
- {
1993
- icon: "mdi:play",
1994
- className: "size-4 text-[var(--color-status-success-text)]"
1995
- }
1996
- )
1997
- }
1998
- ),
1999
- infoButton,
2000
- onToggleDisabled && /* @__PURE__ */ jsx(
2001
- ToolbarButton2,
2002
- {
2003
- title: disabled ? tr(
2004
- "fluidgrids.builder.nodeCard.enableTooltip",
2005
- "Enable node"
2006
- ) : tr(
2007
- "fluidgrids.builder.nodeCard.disableTooltip",
2008
- "Disable node (skip on run)"
2009
- ),
2010
- onClick: onToggleDisabled,
2011
- children: /* @__PURE__ */ jsx(
2012
- Icon,
2013
- {
2014
- icon: disabled ? "mdi:power" : "mdi:power-off",
2015
- className: cn(
2016
- "size-4",
2017
- disabled ? "text-[var(--color-status-warning-text)]" : "text-muted-foreground"
2018
- )
2019
- }
2020
- )
2021
- }
2022
- ),
2023
- onDelete && /* @__PURE__ */ jsx(
2024
- ToolbarButton2,
2025
- {
2026
- title: tr(
2027
- "fluidgrids.builder.nodeCard.deleteTooltip",
2028
- "Delete"
2029
- ),
2030
- onClick: onDelete,
2031
- children: /* @__PURE__ */ jsx(
2032
- Icon,
2033
- {
2034
- icon: "mynaui:trash",
2035
- className: "size-4 text-[var(--color-status-error-text)]"
2036
- }
2037
- )
2038
- }
2039
- )
2040
- ]
2041
- }
2042
- ),
2043
- disabled && /* @__PURE__ */ jsx("div", { className: "absolute -top-2 right-2 z-10", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 rounded-full border border-[var(--color-status-warning-border)] bg-[var(--color-status-warning-bg)] px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-[var(--color-status-warning-text)] shadow-sm", children: [
2044
- /* @__PURE__ */ jsx(Icon, { icon: "mdi:power-off", className: "size-2.5" }),
2045
- tr("fluidgrids.builder.nodeCard.disabledBadge", "Disabled")
2046
- ] }) }),
2047
- /* @__PURE__ */ jsxs(
2048
- "div",
2049
- {
2050
- ref,
2051
- "data-selected": selected,
2052
- className: cn(
2053
- "relative w-72 rounded-xl border border-border bg-card shadow-sm will-change-transform",
2054
- statusRing,
2055
- executionStatus === "success" && "animate-success-glow",
2056
- disabled && "opacity-50 grayscale-[0.6]",
2057
- className
2058
- ),
2059
- style: {
2060
- borderTop: `3px solid ${accent}`,
2061
- transition: "transform 0.15s ease, box-shadow 0.15s ease",
2062
- transform: hovered ? "translateY(-2px)" : "none",
2063
- boxShadow: hovered ? "var(--shadow-float)" : void 0
2064
- },
2065
- ...props,
2066
- children: [
2067
- /* @__PURE__ */ jsx(
2068
- "div",
2069
- {
2070
- className: "pointer-events-none absolute -inset-0.5 transition-opacity duration-150",
2071
- style: {
2072
- borderRadius: 14,
2073
- opacity: selected || hovered ? 1 : 0,
2074
- boxShadow: selected ? `0 0 0 2px ${accent}, 0 0 0 5px ${tint(accent, 18)}` : `0 0 0 2px ${accent}`
2075
- }
2076
- }
2077
- ),
2078
- isTrigger && /* @__PURE__ */ jsx(
2079
- "div",
2080
- {
2081
- className: "absolute -top-2 -left-2 z-10 flex size-5 items-center justify-center rounded-full shadow-md ring-2 ring-card",
2082
- style: { backgroundColor: "var(--color-status-success-text)" },
2083
- children: /* @__PURE__ */ jsx(
2084
- Icon,
2085
- {
2086
- icon: "mdi:lightning-bolt",
2087
- className: "size-3",
2088
- style: { color: "var(--color-status-success-text-inverse)" }
2089
- }
2090
- )
2091
- }
2092
- ),
2093
- children
2094
- ]
2095
- }
2096
- ),
2097
- executionStatus === "success" && /* @__PURE__ */ jsx(StatusCorner, { icon: "mdi:check", tone: "success" }),
2098
- executionStatus === "error" && /* @__PURE__ */ jsx(StatusCorner, { icon: "mdi:close", tone: "error" }),
2099
- executionStatus === "running" && /* @__PURE__ */ jsx(StatusCorner, { icon: "svg-spinners:ring-resize", tone: "success" })
2100
- ]
2101
- }
2102
- );
2103
- }
2104
- );
2105
- NodeCard.displayName = "NodeCard";
2106
- function ToolbarButton2({
2107
- title,
2108
- onClick,
2109
- disabled,
2110
- children,
2111
- "data-agent": agent
2112
- }) {
2113
- return /* @__PURE__ */ jsx(
2114
- "button",
2115
- {
2116
- type: "button",
2117
- "data-agent": agent,
2118
- title,
2119
- disabled,
2120
- onClick: (e) => {
2121
- e.stopPropagation();
2122
- onClick?.();
2123
- },
2124
- className: "flex size-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-50",
2125
- children
2126
- }
2127
- );
2128
- }
2129
- function StatusCorner({
2130
- icon,
2131
- tone
2132
- }) {
2133
- const bg = tone === "success" ? "var(--color-status-success-bg)" : "var(--color-status-error-bg)";
2134
- const fg = tone === "success" ? "var(--color-status-success-text-inverse)" : "var(--color-status-error-text-inverse)";
2135
- return /* @__PURE__ */ jsx("div", { className: "absolute -bottom-1.5 -right-1.5 z-10", children: /* @__PURE__ */ jsx(
2136
- "div",
2137
- {
2138
- className: "rounded-full p-0.5 shadow-md animate-in zoom-in duration-200",
2139
- style: { backgroundColor: bg },
2140
- children: /* @__PURE__ */ jsx(Icon, { icon, className: "size-3", style: { color: fg } })
2141
- }
2142
- ) });
2143
- }
2144
- var NodeCardHeader = React.forwardRef(
2145
- ({
2146
- icon,
2147
- appName,
2148
- accentKey,
2149
- missingCredentials = false,
2150
- label,
2151
- isRenaming = false,
2152
- onStartRename,
2153
- onCommitRename,
2154
- onCancelRename
2155
- }, ref) => {
2156
- const tr = useTr();
2157
- const accent = nodeAccent(accentKey);
2158
- return /* @__PURE__ */ jsxs("div", { ref, className: "flex items-center gap-2.5 px-3 pb-3 pt-4", children: [
2159
- /* @__PURE__ */ jsx(
2160
- "div",
2161
- {
2162
- className: "flex size-8 shrink-0 items-center justify-center rounded-lg",
2163
- style: {
2164
- backgroundColor: tint(accent, 13),
2165
- color: accent,
2166
- border: `1px solid ${tint(accent, 26)}`
2167
- },
2168
- children: /* @__PURE__ */ jsx(NodeTypeIcon, { icon, alt: appName, className: "size-[18px]" })
2169
- }
2170
- ),
2171
- onStartRename && onCommitRename && onCancelRename ? /* @__PURE__ */ jsx(
2172
- InlineEditableLabel,
2173
- {
2174
- value: label ?? "",
2175
- placeholder: appName,
2176
- editing: isRenaming,
2177
- onStartEdit: onStartRename,
2178
- onCommit: onCommitRename,
2179
- onCancel: onCancelRename,
2180
- className: "min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wide text-muted-foreground",
2181
- inputClassName: "text-[11px] font-semibold uppercase tracking-wide"
2182
- }
2183
- ) : /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: label || appName }),
2184
- missingCredentials && /* @__PURE__ */ jsx(
2185
- "span",
2186
- {
2187
- title: tr(
2188
- "fluidgrids.builder.nodeCard.missingCredentialsTooltip",
2189
- "Credentials not configured"
2190
- ),
2191
- className: "flex items-center text-[var(--color-status-warning-text)]",
2192
- "aria-label": tr(
2193
- "fluidgrids.builder.nodeCard.missingCredentialsAriaLabel",
2194
- "Credentials not configured"
2195
- ),
2196
- children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:alert-triangle", className: "size-4" })
2197
- }
2198
- )
2199
- ] });
2200
- }
2201
- );
2202
- NodeCardHeader.displayName = "NodeCardHeader";
2203
- var NodeCardBody = React.forwardRef(
2204
- ({ operationName, description }, ref) => {
2205
- const tr = useTr();
2206
- return /* @__PURE__ */ jsxs(
2207
- "div",
2208
- {
2209
- ref,
2210
- className: "rounded-b-xl border-t border-border/60 px-3 pb-3 pt-2.5",
2211
- children: [
2212
- /* @__PURE__ */ jsx("div", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: tr("fluidgrids.builder.nodeCard.operationLabel", "Operation") }),
2213
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 truncate font-mono text-[13px] font-bold text-card-foreground", children: operationName }),
2214
- description ? /* @__PURE__ */ jsx("p", { className: "mt-1 line-clamp-2 text-xs leading-snug text-muted-foreground", children: description }) : null
2215
- ]
2216
- }
2217
- );
2218
- }
2219
- );
2220
- NodeCardBody.displayName = "NodeCardBody";
2221
- function CustomHandle({
2222
- className,
2223
- isConnectable,
2224
- accent,
2225
- ...props
2226
- }) {
2227
- const { nodeLookup, edges } = useStore(({ nodeLookup: nodeLookup2, edges: edges2 }) => ({
2228
- nodeLookup: nodeLookup2,
2229
- edges: edges2
2230
- }));
2231
- const nodeId = useNodeId();
2232
- const isHandleConnectable = useMemo(() => {
2233
- if (!nodeId) return false;
2234
- const node = nodeLookup.get(nodeId);
2235
- if (!node) return false;
2236
- const connectedEdges = getConnectedEdges([node], edges);
2237
- if (typeof isConnectable === "function")
2238
- return isConnectable({ node, connectedEdges });
2239
- if (typeof isConnectable === "number")
2240
- return connectedEdges.length < isConnectable;
2241
- return isConnectable;
2242
- }, [edges, isConnectable, nodeId, nodeLookup]);
2243
- return /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
2244
- Handle,
2245
- {
2246
- className: cn(
2247
- "cursor-crosshair transition-transform hover:scale-110",
2248
- !accent && "size-3 border-2 border-card-foreground/40 hover:border-primary/50",
2249
- className
2250
- ),
2251
- isConnectable: isHandleConnectable,
2252
- style: accent ? {
2253
- width: 14,
2254
- height: 14,
2255
- borderRadius: "50%",
2256
- background: "var(--color-node-bg)",
2257
- border: `2.5px solid ${accent}`,
2258
- boxShadow: `0 0 0 3px color-mix(in srgb, ${accent} 12%, transparent)`
2259
- } : void 0,
2260
- ...props
2261
- }
2262
- ) });
2263
- }
2264
-
2265
- // src/builder/components/flow-builder/components/blocks/nodes/node/handle-spec.ts
2266
- function isDynamicSpec(spec) {
2267
- return !!spec && !Array.isArray(spec) && typeof spec.fromStateRecord === "string";
2268
- }
2269
- var DEFAULT_HANDLES = [{ id: "source", label: "" }];
2270
- var BUILTIN_OUTPUT_HANDLES = {
2271
- "internal:wait-for-signal": [
2272
- { id: "received", label: "Received", tone: "success" },
2273
- { id: "timedOut", label: "Timed out", tone: "warning" }
2274
- ]
2275
- };
2276
- function resolveOutputHandles(data, nodeType) {
2277
- const nodeData = data?.nodeData;
2278
- const operationsBlob = Array.isArray(nodeData?.operations) ? void 0 : nodeData?.operations;
2279
- const operationList = Array.isArray(nodeData?.operations) ? nodeData.operations : operationsBlob?.operations ?? [];
2280
- const operationName = typeof data?.operation_name === "string" ? data.operation_name : void 0;
2281
- const operationSpec = operationName ? operationList.find((op) => op?.name === operationName)?.outputHandles : void 0;
2282
- const spec = operationSpec ?? nodeData?.outputHandles ?? operationsBlob?.outputHandles;
2283
- if (!spec)
2284
- return nodeType && BUILTIN_OUTPUT_HANDLES[nodeType] || DEFAULT_HANDLES;
2285
- if (isDynamicSpec(spec)) {
2286
- const args = data?.args;
2287
- const record2 = args?.state?.[spec.fromStateRecord] ?? data?.[spec.fromStateRecord] ?? {};
2288
- const dynamic = Object.entries(record2).filter(([, handleId]) => typeof handleId === "string" && handleId).map(([label, handleId]) => ({ id: handleId, label }));
2289
- return [...dynamic, ...spec.extra ?? []];
2290
- }
2291
- if (spec.length === 0) return DEFAULT_HANDLES;
2292
- return spec;
2293
- }
2294
- var DEFAULT_INPUT_HANDLES = [{ id: "target", label: "" }];
2295
- function resolveInputHandles(data) {
2296
- const nodeData = data?.nodeData;
2297
- const spec = nodeData?.inputHandles ?? nodeData?.operations?.inputHandles;
2298
- if (!Array.isArray(spec) || spec.length === 0) return DEFAULT_INPUT_HANDLES;
2299
- return spec;
2300
- }
2301
- function outputHandlesSpacerHeight(count) {
2302
- return count > 2 ? (count - 2) * 26 : 0;
2303
- }
2304
- var TONE_COLORS = {
2305
- success: "var(--color-status-success-text)",
2306
- error: "var(--color-status-error-text)",
2307
- warning: "var(--color-status-warning-text)",
2308
- info: "var(--color-status-info-text)",
2309
- neutral: "var(--color-text-muted)"
2310
- };
2311
- function HandlePlusStub({
2312
- nodeId,
2313
- handleId,
2314
- color,
2315
- isConnectable,
2316
- isOnlyHandle,
2317
- anchor
2318
- }) {
2319
- const tr = useTr();
2320
- const startAttach = useFlowStore((s) => s.actions.pendingConnection.start);
2321
- const isArmedHere = useFlowStore(
2322
- (s) => s.workflow.pendingConnection?.nodeId === nodeId && s.workflow.pendingConnection?.handleId === handleId
2323
- );
2324
- const hasOutgoing = useStore(
2325
- (s) => s.edges.some(
2326
- (e) => e.source === nodeId && (e.sourceHandle === handleId || isOnlyHandle && (e.sourceHandle === null || e.sourceHandle === void 0))
2327
- )
2328
- );
2329
- if (hasOutgoing || isConnectable === false) return null;
2330
- const containerStyle = anchor === "card" ? {
2331
- left: "100%",
2332
- top: "50%",
2333
- transform: "translateY(-50%)",
2334
- marginLeft: 10
2335
- } : { left: 10, top: 0, transform: "translateY(-50%)" };
2336
- return /* @__PURE__ */ jsxs(
2337
- "div",
2338
- {
2339
- className: "nodrag nopan absolute flex items-center",
2340
- style: { ...containerStyle, pointerEvents: "none", zIndex: 5 },
2341
- children: [
2342
- /* @__PURE__ */ jsx(
2343
- "div",
2344
- {
2345
- style: {
2346
- width: 16,
2347
- borderTop: `1.5px dashed ${color}`,
2348
- opacity: 0.5
2349
- }
2350
- }
2351
- ),
2352
- /* @__PURE__ */ jsx(
2353
- "button",
2354
- {
2355
- type: "button",
2356
- title: tr(
2357
- "fluidgrids.builder.outputHandles.addNextNodeTooltip",
2358
- "Add next node"
2359
- ),
2360
- "aria-label": `Add next node from ${handleId}`,
2361
- "data-testid": `handle-plus-${nodeId}-${handleId}`,
2362
- onClick: (e) => {
2363
- e.stopPropagation();
2364
- startAttach({ nodeId, handleId });
2365
- },
2366
- onDoubleClick: (e) => e.stopPropagation(),
2367
- onMouseDown: (e) => e.stopPropagation(),
2368
- className: "cursor-pointer transition-transform hover:scale-110",
2369
- style: {
2370
- pointerEvents: "all",
2371
- width: 16,
2372
- height: 16,
2373
- borderRadius: "50%",
2374
- display: "flex",
2375
- alignItems: "center",
2376
- justifyContent: "center",
2377
- border: `1.5px solid ${color}`,
2378
- background: isArmedHere ? color : "var(--color-node-bg)",
2379
- color: isArmedHere ? "var(--color-node-bg)" : color,
2380
- boxShadow: isArmedHere ? `0 0 0 3px color-mix(in srgb, ${color} 22%, transparent)` : `0 0 0 2px color-mix(in srgb, ${color} 10%, transparent)`,
2381
- transition: "transform .12s ease, background .12s ease, color .12s ease"
2382
- },
2383
- children: /* @__PURE__ */ jsx(Icon, { icon: "mdi:plus", className: "size-2.5" })
2384
- }
2385
- )
2386
- ]
2387
- }
2388
- );
2389
- }
2390
- function OutputHandles({
2391
- nodeId,
2392
- handles,
2393
- accent,
2394
- isConnectable
2395
- }) {
2396
- const updateNodeInternals = useUpdateNodeInternals();
2397
- const signature = useMemo(
2398
- () => handles.map((h) => h.id).join("|"),
2399
- [handles]
2400
- );
2401
- useEffect(() => {
2402
- updateNodeInternals(nodeId);
2403
- }, [nodeId, signature, updateNodeInternals]);
2404
- if (handles.length === 1) {
2405
- const handle = handles[0];
2406
- const color = handle.tone ? TONE_COLORS[handle.tone] : accent;
2407
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2408
- /* @__PURE__ */ jsx(
2409
- CustomHandle,
2410
- {
2411
- type: "source",
2412
- id: handle.id,
2413
- position: Position.Right,
2414
- isConnectable,
2415
- accent: color
2416
- }
2417
- ),
2418
- /* @__PURE__ */ jsx(
2419
- HandlePlusStub,
2420
- {
2421
- nodeId,
2422
- handleId: handle.id,
2423
- color,
2424
- isConnectable,
2425
- isOnlyHandle: true,
2426
- anchor: "card"
2427
- }
2428
- )
2429
- ] });
2430
- }
2431
- const total = handles.length;
2432
- return /* @__PURE__ */ jsx(Fragment, { children: handles.map((handle, index) => {
2433
- const top = `${(index + 1) / (total + 1) * 100}%`;
2434
- const color = handle.tone ? TONE_COLORS[handle.tone] : accent;
2435
- return /* @__PURE__ */ jsxs(
2436
- "div",
2437
- {
2438
- className: "absolute right-0",
2439
- style: { top, transform: "translateY(-50%)" },
2440
- children: [
2441
- /* @__PURE__ */ jsx(
2442
- CustomHandle,
2443
- {
2444
- type: "source",
2445
- id: handle.id,
2446
- position: Position.Right,
2447
- isConnectable,
2448
- accent: color
2449
- }
2450
- ),
2451
- handle.label && /* @__PURE__ */ jsx(
2452
- "span",
775
+ className: "flex h-full w-full flex-col overflow-hidden rounded-lg border",
776
+ style: {
777
+ backgroundColor: `${accent}1F`,
778
+ // Same border whether selected or not — selection shows only via the
779
+ // floating toolbar (no extra border/outline on click).
780
+ borderColor: `${accent}59`
781
+ },
782
+ onDoubleClick: (e) => {
783
+ e.stopPropagation();
784
+ if (!readOnly) startEditing();
785
+ },
786
+ children: editing ? /* @__PURE__ */ jsx(
787
+ "textarea",
2453
788
  {
2454
- className: "pointer-events-none absolute whitespace-nowrap text-[10px] font-medium leading-none",
2455
- style: { color, left: 12, bottom: 8 },
2456
- title: handle.label,
2457
- children: handle.label
789
+ ref: textareaRef,
790
+ readOnly,
791
+ value: draft,
792
+ onChange: (e) => setDraft(e.target.value),
793
+ onBlur: commitEdit,
794
+ onKeyDown: (e) => {
795
+ e.stopPropagation();
796
+ if (e.key === "Escape") cancelEdit();
797
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) commitEdit();
798
+ },
799
+ placeholder: tr(
800
+ "fluidgrids.builder.stickyNote.editorPlaceholder",
801
+ "# Note\nSupports **markdown** \u2014 headings, lists, links\u2026"
802
+ ),
803
+ className: "nodrag nowheel h-full w-full resize-none bg-transparent p-3 font-mono text-xs leading-relaxed text-foreground outline-none placeholder:text-muted-foreground/60"
2458
804
  }
2459
- ),
2460
- /* @__PURE__ */ jsx(
2461
- HandlePlusStub,
805
+ ) : content.trim() ? /* @__PURE__ */ jsx(
806
+ "div",
2462
807
  {
2463
- nodeId,
2464
- handleId: handle.id,
2465
- color,
2466
- isConnectable,
2467
- isOnlyHandle: false,
2468
- anchor: "point"
808
+ className: cn(
809
+ "nowheel h-full w-full overflow-auto p-3 text-xs leading-relaxed text-foreground",
810
+ // Compact markdown typography scoped to the note body.
811
+ "[&_h1]:mb-1.5 [&_h1]:text-sm [&_h1]:font-bold",
812
+ "[&_h2]:mb-1 [&_h2]:text-[13px] [&_h2]:font-bold",
813
+ "[&_h3]:mb-1 [&_h3]:text-xs [&_h3]:font-semibold",
814
+ "[&_p]:mb-1.5 [&_ul]:mb-1.5 [&_ul]:list-disc [&_ul]:pl-4",
815
+ "[&_ol]:mb-1.5 [&_ol]:list-decimal [&_ol]:pl-4 [&_li]:mb-0.5",
816
+ "[&_a]:underline [&_code]:rounded [&_code]:bg-muted [&_code]:px-1"
817
+ ),
818
+ children: /* @__PURE__ */ jsx(ReactMarkdown, { children: content })
2469
819
  }
2470
- )
2471
- ]
2472
- },
2473
- handle.id
2474
- );
2475
- }) });
2476
- }
2477
- function InputHandles({
2478
- nodeId,
2479
- handles,
2480
- accent,
2481
- isConnectable
2482
- }) {
2483
- const updateNodeInternals = useUpdateNodeInternals();
2484
- const signature = useMemo(
2485
- () => handles.map((h) => h.id).join("|"),
2486
- [handles]
820
+ ) : /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center p-3 text-xs text-muted-foreground/70", children: tr(
821
+ "fluidgrids.builder.stickyNote.emptyStateHint",
822
+ "Double-click to edit"
823
+ ) })
824
+ }
825
+ )
826
+ ] })
2487
827
  );
2488
- useEffect(() => {
2489
- updateNodeInternals(nodeId);
2490
- }, [nodeId, signature, updateNodeInternals]);
2491
- if (handles.length === 1) {
2492
- const handle = handles[0];
2493
- return /* @__PURE__ */ jsx(
2494
- CustomHandle,
2495
- {
2496
- type: "target",
2497
- id: handle.id,
2498
- position: Position.Left,
2499
- isConnectable,
2500
- accent: handle.tone ? TONE_COLORS[handle.tone] : accent
2501
- }
2502
- );
2503
- }
2504
- const total = handles.length;
2505
- return /* @__PURE__ */ jsx(Fragment, { children: handles.map((handle, index) => {
2506
- const top = `${(index + 1) / (total + 1) * 100}%`;
2507
- const color = handle.tone ? TONE_COLORS[handle.tone] : accent;
2508
- return /* @__PURE__ */ jsxs(
2509
- "div",
2510
- {
2511
- className: "absolute left-0",
2512
- style: { top, transform: "translateY(-50%)" },
2513
- children: [
2514
- /* @__PURE__ */ jsx(
2515
- CustomHandle,
2516
- {
2517
- type: "target",
2518
- id: handle.id,
2519
- position: Position.Left,
2520
- isConnectable,
2521
- accent: color
2522
- }
2523
- ),
2524
- handle.label && /* @__PURE__ */ jsx(
2525
- "span",
2526
- {
2527
- className: "pointer-events-none absolute whitespace-nowrap text-[10px] font-medium leading-none",
2528
- style: {
2529
- color,
2530
- right: "100%",
2531
- marginRight: 16,
2532
- top: "50%",
2533
- transform: "translateY(-50%)"
2534
- },
2535
- title: handle.label,
2536
- children: handle.label
2537
- }
2538
- )
2539
- ]
2540
- },
2541
- handle.id
2542
- );
2543
- }) });
2544
828
  }
2545
- function LogicNodeCard({
2546
- title,
2547
- subtitle,
2548
- icon,
2549
- accent,
2550
- selected,
2551
- executionStatus,
2552
- itemCount,
2553
- isTrigger,
2554
- isExecuting,
2555
- nodeId,
2556
- inputHandles,
2557
- outputHandles,
2558
- isConnectable,
2559
- onClick,
2560
- onDoubleClick,
2561
- onSettings,
2562
- onExecute,
2563
- onDelete,
2564
- infoButton,
2565
- disabled = false,
2566
- onToggleDisabled,
2567
- isRenaming = false,
2568
- onStartRename,
2569
- onCommitRename,
2570
- onCancelRename
2571
- }) {
2572
- const minHeight = Math.max(
2573
- 64,
2574
- Math.max(outputHandles.length, inputHandles.length) * 30 + 24
2575
- );
2576
- return /* @__PURE__ */ jsxs(
2577
- NodeCard,
2578
- {
2579
- className: "w-48",
2580
- "data-selected": selected,
2581
- selected,
2582
- onClick,
2583
- onDoubleClick,
2584
- executionStatus,
2585
- itemCount,
2586
- isTrigger,
2587
- accent,
2588
- isExecuting,
2589
- onSettings,
2590
- onExecute,
2591
- onDelete,
2592
- infoButton,
2593
- disabled,
2594
- onToggleDisabled,
2595
- children: [
2596
- /* @__PURE__ */ jsxs(
2597
- "div",
2598
- {
2599
- className: "flex flex-col items-center justify-center gap-1 px-3 py-3 text-center",
2600
- style: { minHeight },
2601
- children: [
2602
- /* @__PURE__ */ jsx("div", { className: "flex size-9 items-center justify-center rounded-lg bg-muted", children: /* @__PURE__ */ jsx(Icon, { icon, className: "size-5", style: { color: accent } }) }),
2603
- onStartRename && onCommitRename && onCancelRename ? /* @__PURE__ */ jsx(
2604
- InlineEditableLabel,
2605
- {
2606
- value: title,
2607
- placeholder: title,
2608
- editing: isRenaming,
2609
- onStartEdit: onStartRename,
2610
- onCommit: onCommitRename,
2611
- onCancel: onCancelRename,
2612
- className: "max-w-full truncate text-sm font-semibold leading-tight",
2613
- inputClassName: "text-sm font-semibold text-center"
2614
- }
2615
- ) : /* @__PURE__ */ jsx("span", { className: "max-w-full truncate text-sm font-semibold leading-tight", children: title }),
2616
- subtitle && /* @__PURE__ */ jsx(
2617
- "span",
2618
- {
2619
- className: "max-w-full truncate font-mono text-[10px] text-muted-foreground",
2620
- title: subtitle,
2621
- children: subtitle
2622
- }
2623
- )
2624
- ]
2625
- }
2626
- ),
2627
- /* @__PURE__ */ jsx(
2628
- InputHandles,
2629
- {
2630
- nodeId,
2631
- handles: inputHandles,
2632
- accent,
2633
- isConnectable
2634
- }
2635
- ),
2636
- /* @__PURE__ */ jsx(
2637
- OutputHandles,
2638
- {
2639
- nodeId,
2640
- handles: outputHandles,
2641
- accent,
2642
- isConnectable
2643
- }
2644
- )
2645
- ]
2646
- }
2647
- );
829
+ var StickyNoteNode = memo(StickyNoteNodeComponent);
830
+ function buildStickyNode(position) {
831
+ return {
832
+ id: nanoid(),
833
+ type: STICKY_NOTE_TYPE,
834
+ position,
835
+ width: STICKY_DEFAULT_SIZE.width,
836
+ height: STICKY_DEFAULT_SIZE.height,
837
+ // Render under regular nodes and edges — notes are a background layer.
838
+ zIndex: -1,
839
+ selected: true,
840
+ data: { content: "", color: DEFAULT_STICKY_COLOR }
841
+ };
2648
842
  }
2649
843
 
2650
- // src/builder/components/code-workflow/code-workflow-state.ts
2651
- var CODE_WORKFLOW_NODE_TYPE = "internal:code-workflow";
2652
- var WAIT_FOR_SIGNAL_NODE_TYPE = "internal:wait-for-signal";
2653
- function isRecord3(value) {
2654
- return typeof value === "object" && value !== null && !Array.isArray(value);
2655
- }
2656
- function str(value) {
2657
- return typeof value === "string" && value.trim() ? value : void 0;
844
+ // src/modules/connections/utils/adapter-selection.ts
845
+ var ANY_PROVIDER = "*";
846
+ function isOptionalAdapter(adapter) {
847
+ return adapter.required === false;
2658
848
  }
2659
- function readCodeWorkflowState(state) {
2660
- const s = isRecord3(state) ? state : {};
2661
- const waitForCompletion = s.waitForCompletion !== false;
2662
- const policy = s.parentClosePolicy;
2663
- return {
2664
- workflowFn: str(s.workflowFn),
2665
- taskQueue: str(s.taskQueue) ?? "managed",
2666
- waitForCompletion,
2667
- parentClosePolicy: policy === "TERMINATE" || policy === "ABANDON" || policy === "REQUEST_CANCEL" ? policy : waitForCompletion ? "TERMINATE" : "ABANDON",
2668
- timeout: typeof s.timeout === "number" && s.timeout > 0 ? s.timeout : 0
2669
- };
849
+ function newConnectionProviderKey(adapters) {
850
+ if (adapters.length !== 1) return void 0;
851
+ const provider = adapters[0]?.provider;
852
+ return provider && provider !== ANY_PROVIDER ? provider : void 0;
2670
853
  }
2671
- function readSignalName(state) {
2672
- return isRecord3(state) ? str(state.signalName) : void 0;
854
+ function matchesAdapterProvider(adapter, providerKey) {
855
+ const normalizedKey = providerKey.trim().toLowerCase();
856
+ if (normalizedKey.length === 0) return false;
857
+ if (adapter.provider.trim() === ANY_PROVIDER) return true;
858
+ return adapter.provider.trim().toLowerCase() === normalizedKey || adapter.name.trim().toLowerCase() === normalizedKey;
2673
859
  }
2674
- function typeLabel(schema) {
2675
- if (!isRecord3(schema)) return "any";
2676
- if (Array.isArray(schema.enum)) return schema.enum.map(String).join(" | ");
2677
- if (Array.isArray(schema.type)) return schema.type.map(String).join(" | ");
2678
- if (schema.type === "array") return `${typeLabel(schema.items)}[]`;
2679
- return typeof schema.type === "string" ? schema.type : "any";
860
+ function connectionsForAdapter(connections, adapterName) {
861
+ return connections.filter(
862
+ (connection) => connection.adapter.toLowerCase() === adapterName.toLowerCase()
863
+ );
2680
864
  }
2681
- function interfaceFields(schema) {
2682
- if (!isRecord3(schema) || !isRecord3(schema.properties)) return [];
2683
- const required = new Set(
2684
- Array.isArray(schema.required) ? schema.required.map(String) : []
865
+ function replaceAdapterConnections(connections, adapterName, connectionIds = []) {
866
+ const otherConnections = connections.filter(
867
+ (connection) => connection.adapter.toLowerCase() !== adapterName.toLowerCase()
2685
868
  );
2686
- return Object.entries(schema.properties).map(([name, property]) => ({
2687
- name,
2688
- type: typeLabel(property),
2689
- required: required.has(name),
2690
- description: isRecord3(property) ? str(property.description) : void 0
2691
- }));
869
+ const uniqueIds = [...new Set(connectionIds.filter(Boolean))];
870
+ return [
871
+ ...otherConnections,
872
+ ...uniqueIds.map((connectionId) => ({
873
+ connectionId,
874
+ adapter: adapterName
875
+ }))
876
+ ];
2692
877
  }
2693
- function interfaceHandlers(iface) {
2694
- const of = (key) => isRecord3(iface) && isRecord3(iface[key]) ? Object.keys(iface[key]).sort() : [];
2695
- return {
2696
- signals: of("signals"),
2697
- queries: of("queries"),
2698
- updates: of("updates")
2699
- };
878
+ function missingRequiredAdapters(adapters, selected) {
879
+ return adapters.filter(
880
+ (adapter) => !isOptionalAdapter(adapter) && !selected.some((connection) => connection.adapter === adapter.name)
881
+ );
2700
882
  }
2701
- function childRunIdOf(nodeStates, nodeId) {
2702
- const states = nodeStates.filter((state) => state.nodeId === nodeId);
2703
- for (let i = states.length - 1; i >= 0; i -= 1) {
2704
- const state = states[i];
2705
- const fromColumn = str(state.childRunId);
2706
- if (fromColumn) return fromColumn;
2707
- if (isRecord3(state.output)) {
2708
- const fromOutput = str(state.output._childRunId);
2709
- if (fromOutput) return fromOutput;
883
+ var NODE_DOCS_URL = gql`
884
+ query NodeDocsUrlLookup($nodeType: String!, $nodeVersion: String!) {
885
+ nodeEndpointConfig(nodeType: $nodeType, nodeVersion: $nodeVersion) {
886
+ docsUrl
2710
887
  }
2711
888
  }
2712
- return void 0;
889
+ `;
890
+ var cache = /* @__PURE__ */ new Map();
891
+ var inFlight = /* @__PURE__ */ new Map();
892
+ function fetchDocsUrl(nodeType, nodeVersion) {
893
+ const key = `${nodeType}@${nodeVersion}`;
894
+ if (cache.has(key)) return Promise.resolve(cache.get(key));
895
+ const existing = inFlight.get(key);
896
+ if (existing) return existing;
897
+ const client = getFluidGridsClientSafe();
898
+ if (!client) {
899
+ cache.set(key, void 0);
900
+ return Promise.resolve(void 0);
901
+ }
902
+ const promise = client.query({
903
+ query: NODE_DOCS_URL,
904
+ variables: { nodeType, nodeVersion },
905
+ fetchPolicy: "cache-first"
906
+ }).then((result) => result.data?.nodeEndpointConfig?.docsUrl ?? void 0).catch(() => void 0).finally(() => inFlight.delete(key));
907
+ inFlight.set(key, promise);
908
+ return promise.then((url) => {
909
+ cache.set(key, url);
910
+ return url;
911
+ });
2713
912
  }
2714
- function sourceLanguageOf(path) {
2715
- if (/\.(ts|tsx|mts|cts)$/.test(path)) return "typescript";
2716
- if (/\.py$/.test(path)) return "python";
2717
- if (/\.json$/.test(path)) return "json";
2718
- return "text";
2719
- }
2720
- function isEditableSourcePath(path) {
2721
- if (path.startsWith(".fluidgrids/")) return false;
2722
- const language = sourceLanguageOf(path);
2723
- return language === "typescript" || language === "json";
2724
- }
2725
- function projectLanguage(files) {
2726
- return files.some(
2727
- (file) => file.path.startsWith("workflows/") && file.path.endsWith(".py")
2728
- ) ? "python" : "typescript";
2729
- }
2730
- function changedSourceFiles(loaded, current) {
2731
- return loaded.filter((file) => {
2732
- const now = current[file.path];
2733
- return now !== void 0 && now !== file.content;
2734
- }).map((file) => ({ path: file.path, content: current[file.path] })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
2735
- }
2736
- function defaultSourcePath(files, entry) {
2737
- if (entry && files.some((file) => file.path === entry)) return entry;
2738
- return files.find(
2739
- (file) => file.path.startsWith("workflows/") && isEditableSourcePath(file.path)
2740
- )?.path ?? files[0]?.path;
2741
- }
2742
- function diagnosticsByPath(diagnostics) {
2743
- const result = /* @__PURE__ */ new Map();
2744
- for (const diagnostic of diagnostics) {
2745
- const path = diagnostic.path?.replace(/^\.\//, "") ?? "";
2746
- const list = result.get(path) ?? [];
2747
- list.push({
2748
- line: diagnostic.line,
2749
- column: diagnostic.column,
2750
- severity: editorSeverity(diagnostic.severity),
2751
- code: diagnostic.code,
2752
- message: diagnostic.message
913
+ function useNodeDocsUrl(nodeType, nodeVersion) {
914
+ const [docsUrl, setDocsUrl] = useState(
915
+ () => nodeType && nodeVersion ? cache.get(`${nodeType}@${nodeVersion}`) : void 0
916
+ );
917
+ useEffect(() => {
918
+ if (!nodeType || !nodeVersion) {
919
+ setDocsUrl(void 0);
920
+ return;
921
+ }
922
+ let cancelled = false;
923
+ fetchDocsUrl(nodeType, nodeVersion).then((url) => {
924
+ if (!cancelled) setDocsUrl(url);
2753
925
  });
2754
- result.set(path, list);
2755
- }
2756
- return result;
926
+ return () => {
927
+ cancelled = true;
928
+ };
929
+ }, [nodeType, nodeVersion]);
930
+ return docsUrl;
2757
931
  }
2758
932
 
2759
933
  // src/modules/executions/utils/signal-targets.ts
2760
- function isRecord4(value) {
934
+ function isRecord(value) {
2761
935
  return typeof value === "object" && value !== null && !Array.isArray(value);
2762
936
  }
2763
- function str2(value) {
937
+ function str(value) {
2764
938
  return typeof value === "string" && value.trim() ? value : void 0;
2765
939
  }
2766
940
  function durableInterfaceOf(definitionJson, workflowFn) {
2767
- if (!isRecord4(definitionJson)) return void 0;
941
+ if (!isRecord(definitionJson)) return void 0;
2768
942
  const code = definitionJson.code;
2769
- if (isRecord4(code) && isRecord4(code.exports) && workflowFn) {
943
+ if (isRecord(code) && isRecord(code.exports) && workflowFn) {
2770
944
  const workflows = code.exports.workflows;
2771
- if (isRecord4(workflows)) {
945
+ if (isRecord(workflows)) {
2772
946
  const entry = workflows[workflowFn];
2773
- if (isRecord4(entry) && isRecord4(entry.interface)) return entry.interface;
947
+ if (isRecord(entry) && isRecord(entry.interface)) return entry.interface;
2774
948
  }
2775
949
  }
2776
- return isRecord4(definitionJson.interface) ? definitionJson.interface : void 0;
950
+ return isRecord(definitionJson.interface) ? definitionJson.interface : void 0;
2777
951
  }
2778
952
  function declaredSignals(iface) {
2779
953
  if (!iface) return { targets: [], openNames: false };
2780
- const signals = isRecord4(iface.signals) ? iface.signals : {};
954
+ const signals = isRecord(iface.signals) ? iface.signals : {};
2781
955
  const targets = Object.keys(signals).sort().map((name) => {
2782
956
  const entry = signals[name];
2783
- const payload = isRecord4(entry) && isRecord4(entry.payload) ? entry.payload : void 0;
957
+ const payload = isRecord(entry) && isRecord(entry.payload) ? entry.payload : void 0;
2784
958
  return payload ? { name, payloadSchema: payload } : { name };
2785
959
  });
2786
- const open = isRecord4(iface.open) && iface.open.signals === true;
960
+ const open = isRecord(iface.open) && iface.open.signals === true;
2787
961
  return { targets, openNames: open };
2788
962
  }
2789
963
  function nodeStateArgs(node) {
2790
- if (!isRecord4(node)) return void 0;
2791
- const root = isRecord4(node.args) ? node.args : void 0;
2792
- if (root && isRecord4(root.state)) return root.state;
2793
- const data = isRecord4(node.data) ? node.data : void 0;
2794
- const mirror = data && isRecord4(data.args) ? data.args : void 0;
2795
- return mirror && isRecord4(mirror.state) ? mirror.state : void 0;
964
+ if (!isRecord(node)) return void 0;
965
+ const root = isRecord(node.args) ? node.args : void 0;
966
+ if (root && isRecord(root.state)) return root.state;
967
+ const data = isRecord(node.data) ? node.data : void 0;
968
+ const mirror = data && isRecord(data.args) ? data.args : void 0;
969
+ return mirror && isRecord(mirror.state) ? mirror.state : void 0;
2796
970
  }
2797
971
  function definitionNodes(definitionJson) {
2798
- if (!isRecord4(definitionJson) || !isRecord4(definitionJson.data)) return [];
972
+ if (!isRecord(definitionJson) || !isRecord(definitionJson.data)) return [];
2799
973
  const nodes = definitionJson.data.nodes;
2800
974
  return Array.isArray(nodes) ? nodes : [];
2801
975
  }
@@ -2804,16 +978,16 @@ function parkedSignalTargets(nodeStates, definitionJson) {
2804
978
  const result = [];
2805
979
  const seen = /* @__PURE__ */ new Set();
2806
980
  for (const state of nodeStates) {
2807
- if (state.status !== "WAITING" || !isRecord4(state.output)) continue;
981
+ if (state.status !== "WAITING" || !isRecord(state.output)) continue;
2808
982
  if (state.output._waiting !== "signal") continue;
2809
- const name = str2(state.output._signalName);
983
+ const name = str(state.output._signalName);
2810
984
  if (!name || seen.has(`${state.nodeId}:${name}`)) continue;
2811
985
  seen.add(`${state.nodeId}:${name}`);
2812
986
  const node = nodes.find(
2813
- (candidate) => isRecord4(candidate) && candidate.id === state.nodeId
987
+ (candidate) => isRecord(candidate) && candidate.id === state.nodeId
2814
988
  );
2815
989
  const args = nodeStateArgs(node);
2816
- const schema = args && isRecord4(args.payloadSchema) ? args.payloadSchema : void 0;
990
+ const schema = args && isRecord(args.payloadSchema) ? args.payloadSchema : void 0;
2817
991
  result.push(
2818
992
  schema ? { name, payloadSchema: schema, nodeId: state.nodeId } : { name, nodeId: state.nodeId }
2819
993
  );
@@ -2836,9 +1010,9 @@ function canSignalRun(targets) {
2836
1010
  return targets.targets.length > 0 || targets.openNames;
2837
1011
  }
2838
1012
  function fieldOf(name, schema, required) {
2839
- if (!isRecord4(schema)) return null;
2840
- const title = str2(schema.title) ?? name;
2841
- const description = str2(schema.description);
1013
+ if (!isRecord(schema)) return null;
1014
+ const title = str(schema.title) ?? name;
1015
+ const description = str(schema.description);
2842
1016
  if (Array.isArray(schema.enum)) {
2843
1017
  const choices = schema.enum.filter(
2844
1018
  (value) => typeof value === "string"
@@ -2853,7 +1027,7 @@ function fieldOf(name, schema, required) {
2853
1027
  return null;
2854
1028
  }
2855
1029
  function formFieldsOf(schema) {
2856
- if (!schema || schema.type !== "object" || !isRecord4(schema.properties))
1030
+ if (!schema || schema.type !== "object" || !isRecord(schema.properties))
2857
1031
  return null;
2858
1032
  const required = new Set(
2859
1033
  Array.isArray(schema.required) ? schema.required.filter(
@@ -3134,20 +1308,20 @@ function listCustomBlocks() {
3134
1308
  // src/builder/components/lib/expression-preview.ts
3135
1309
  var PURE_EXPRESSION_REGEX = /^\{\{(.*)\}\}$/s;
3136
1310
  var PURE_LEGACY_REGEX = /^\$\{(.*)\}$/s;
3137
- function containsExpressionSyntax(str3) {
3138
- return typeof str3 === "string" && str3.includes("{{");
1311
+ function containsExpressionSyntax(str2) {
1312
+ return typeof str2 === "string" && str2.includes("{{");
3139
1313
  }
3140
- function findEmbeddedExpressions(str3) {
1314
+ function findEmbeddedExpressions(str2) {
3141
1315
  const matches2 = [];
3142
1316
  let searchFrom = 0;
3143
- while (searchFrom < str3.length) {
3144
- const start = str3.indexOf("{{", searchFrom);
1317
+ while (searchFrom < str2.length) {
1318
+ const start = str2.indexOf("{{", searchFrom);
3145
1319
  if (start === -1) break;
3146
- const end = str3.indexOf("}}", start + 2);
1320
+ const end = str2.indexOf("}}", start + 2);
3147
1321
  if (end === -1) break;
3148
1322
  matches2.push({
3149
- full: str3.slice(start, end + 2),
3150
- expression: str3.slice(start + 2, end).trim()
1323
+ full: str2.slice(start, end + 2),
1324
+ expression: str2.slice(start + 2, end).trim()
3151
1325
  });
3152
1326
  searchFrom = end + 2;
3153
1327
  }
@@ -3237,9 +1411,9 @@ function stringifyForInterpolation(value) {
3237
1411
  if (typeof value === "object") return JSON.stringify(value);
3238
1412
  return String(value);
3239
1413
  }
3240
- function previewNewSyntax(str3, scope) {
1414
+ function previewNewSyntax(str2, scope) {
3241
1415
  const sandbox = buildExpressionSandbox(scope);
3242
- const pure = PURE_EXPRESSION_REGEX.exec(str3);
1416
+ const pure = PURE_EXPRESSION_REGEX.exec(str2);
3243
1417
  try {
3244
1418
  if (pure) {
3245
1419
  const expression = pure[1]?.trim();
@@ -3248,8 +1422,8 @@ function previewNewSyntax(str3, scope) {
3248
1422
  }
3249
1423
  return { isTemplate: true, value: evaluateBare(expression, sandbox) };
3250
1424
  }
3251
- let result = str3;
3252
- for (const match of findEmbeddedExpressions(str3)) {
1425
+ let result = str2;
1426
+ for (const match of findEmbeddedExpressions(str2)) {
3253
1427
  if (!match.expression) continue;
3254
1428
  const value = evaluateBare(match.expression, sandbox);
3255
1429
  result = result.replace(match.full, stringifyForInterpolation(value));
@@ -3262,7 +1436,7 @@ function previewNewSyntax(str3, scope) {
3262
1436
  };
3263
1437
  }
3264
1438
  }
3265
- function previewLegacySyntax(str3, scope) {
1439
+ function previewLegacySyntax(str2, scope) {
3266
1440
  const sandbox = {
3267
1441
  ...SAFE_GLOBALS,
3268
1442
  output: scope.input,
@@ -3279,19 +1453,19 @@ function previewLegacySyntax(str3, scope) {
3279
1453
  version: scope.version ?? "1.0.0"
3280
1454
  }
3281
1455
  };
3282
- const pure = PURE_LEGACY_REGEX.exec(str3);
1456
+ const pure = PURE_LEGACY_REGEX.exec(str2);
3283
1457
  try {
3284
1458
  if (pure) {
3285
1459
  const expression = pure[1]?.trim();
3286
1460
  if (!expression) return { isTemplate: true, error: "Empty expression" };
3287
1461
  return { isTemplate: true, value: evaluateBare(expression, sandbox) };
3288
1462
  }
3289
- if (!str3.includes("${")) {
1463
+ if (!str2.includes("${")) {
3290
1464
  return { isTemplate: false };
3291
1465
  }
3292
- let result = str3;
1466
+ let result = str2;
3293
1467
  const templateRegex = /\$\{([^}]+)\}/g;
3294
- for (const match of str3.matchAll(templateRegex)) {
1468
+ for (const match of str2.matchAll(templateRegex)) {
3295
1469
  const expression = match[1]?.trim();
3296
1470
  if (!expression) continue;
3297
1471
  const value = evaluateBare(expression, sandbox);
@@ -3305,14 +1479,14 @@ function previewLegacySyntax(str3, scope) {
3305
1479
  };
3306
1480
  }
3307
1481
  }
3308
- function evaluateExpressionPreview(str3, scope) {
3309
- if (typeof str3 !== "string" || str3.trim() === "") {
1482
+ function evaluateExpressionPreview(str2, scope) {
1483
+ if (typeof str2 !== "string" || str2.trim() === "") {
3310
1484
  return { isTemplate: false };
3311
1485
  }
3312
- if (containsExpressionSyntax(str3)) {
3313
- return previewNewSyntax(str3, scope);
1486
+ if (containsExpressionSyntax(str2)) {
1487
+ return previewNewSyntax(str2, scope);
3314
1488
  }
3315
- return previewLegacySyntax(str3, scope);
1489
+ return previewLegacySyntax(str2, scope);
3316
1490
  }
3317
1491
  var EXPRESSION_BUILTINS = [
3318
1492
  { insert: "$json", label: "$json", description: "Current item's data" },
@@ -4361,7 +2535,7 @@ var AdaptiveCardRenderer = forwardRef(function AdaptiveCardRenderer2({ cardData,
4361
2535
  var components_default = AdaptiveCardRenderer;
4362
2536
 
4363
2537
  // src/modules/ui-blocks/parse.ts
4364
- function isRecord5(value) {
2538
+ function isRecord2(value) {
4365
2539
  return typeof value === "object" && value !== null && !Array.isArray(value);
4366
2540
  }
4367
2541
  function humanizeFieldName(name) {
@@ -4372,7 +2546,7 @@ function synthesizeCardFromInputFields(inputFields) {
4372
2546
  if (names.length === 0) return null;
4373
2547
  const body = [];
4374
2548
  for (const name of names) {
4375
- const field = isRecord5(inputFields[name]) ? inputFields[name] : {};
2549
+ const field = isRecord2(inputFields[name]) ? inputFields[name] : {};
4376
2550
  const description = typeof field.description === "string" ? field.description : void 0;
4377
2551
  const required = field.required === true;
4378
2552
  const secret = field.secret === true;
@@ -4403,9 +2577,9 @@ function synthesizeCardFromInputFields(inputFields) {
4403
2577
  }
4404
2578
  function secretFieldNames(inputFields) {
4405
2579
  const names = /* @__PURE__ */ new Set();
4406
- if (!isRecord5(inputFields)) return names;
2580
+ if (!isRecord2(inputFields)) return names;
4407
2581
  for (const [name, field] of Object.entries(inputFields)) {
4408
- if (isRecord5(field) && field.secret === true) names.add(name);
2582
+ if (isRecord2(field) && field.secret === true) names.add(name);
4409
2583
  }
4410
2584
  return names;
4411
2585
  }
@@ -4418,7 +2592,7 @@ function maskSecretInputs(card, secrets) {
4418
2592
  if (secrets.size === 0) return card;
4419
2593
  const visit = (value) => {
4420
2594
  if (Array.isArray(value)) return value.map(visit);
4421
- if (!isRecord5(value)) return value;
2595
+ if (!isRecord2(value)) return value;
4422
2596
  const copy = {};
4423
2597
  for (const [key, nested] of Object.entries(value))
4424
2598
  copy[key] = visit(nested);
@@ -4432,7 +2606,7 @@ function maskSecretInputs(card, secrets) {
4432
2606
  return visit(card);
4433
2607
  }
4434
2608
  function validateUiSpec(raw) {
4435
- if (!isRecord5(raw)) return "ui is not an object";
2609
+ if (!isRecord2(raw)) return "ui is not an object";
4436
2610
  if (raw.version !== 1)
4437
2611
  return `unsupported ui.version: ${String(raw.version)}`;
4438
2612
  if (!Array.isArray(raw.blocks) || raw.blocks.length === 0) {
@@ -4441,7 +2615,7 @@ function validateUiSpec(raw) {
4441
2615
  const seenIds = /* @__PURE__ */ new Set();
4442
2616
  const blocks = [];
4443
2617
  for (const [index, rawBlock] of raw.blocks.entries()) {
4444
- if (!isRecord5(rawBlock)) return `ui.blocks[${index}] is not an object`;
2618
+ if (!isRecord2(rawBlock)) return `ui.blocks[${index}] is not an object`;
4445
2619
  const id = rawBlock.id;
4446
2620
  if (typeof id !== "string" || id.length === 0) {
4447
2621
  return `ui.blocks[${index}] is missing a non-empty string id`;
@@ -4449,7 +2623,7 @@ function validateUiSpec(raw) {
4449
2623
  if (seenIds.has(id)) return `ui.blocks has duplicate id "${id}"`;
4450
2624
  seenIds.add(id);
4451
2625
  if (rawBlock.type === "adaptive_card") {
4452
- if (!isRecord5(rawBlock.card)) {
2626
+ if (!isRecord2(rawBlock.card)) {
4453
2627
  return `ui.blocks[${index}] ("${id}") has no card object`;
4454
2628
  }
4455
2629
  blocks.push({ id, type: "adaptive_card", card: rawBlock.card });
@@ -4461,7 +2635,7 @@ function validateUiSpec(raw) {
4461
2635
  id,
4462
2636
  type: "custom",
4463
2637
  component: rawBlock.component,
4464
- props: isRecord5(rawBlock.props) ? rawBlock.props : void 0
2638
+ props: isRecord2(rawBlock.props) ? rawBlock.props : void 0
4465
2639
  });
4466
2640
  } else {
4467
2641
  return `ui.blocks[${index}] ("${id}") has unknown type "${String(rawBlock.type)}"`;
@@ -4488,7 +2662,7 @@ function resolveUiSpec(operation) {
4488
2662
  `ui-blocks: invalid ui declaration on operation "${operation.name ?? "?"}" (${result}); falling back to adaptive_card`
4489
2663
  );
4490
2664
  }
4491
- if (isRecord5(operation.adaptive_card)) {
2665
+ if (isRecord2(operation.adaptive_card)) {
4492
2666
  return {
4493
2667
  spec: {
4494
2668
  version: 1,
@@ -4503,7 +2677,7 @@ function resolveUiSpec(operation) {
4503
2677
  synthesized: true
4504
2678
  };
4505
2679
  }
4506
- if (isRecord5(operation.input_fields)) {
2680
+ if (isRecord2(operation.input_fields)) {
4507
2681
  const card = synthesizeCardFromInputFields(operation.input_fields);
4508
2682
  if (card) {
4509
2683
  return {
@@ -4518,17 +2692,17 @@ function resolveUiSpec(operation) {
4518
2692
  return null;
4519
2693
  }
4520
2694
  function isSubmitAction(action) {
4521
- return isRecord5(action) && action.type === "Action.Submit";
2695
+ return isRecord2(action) && action.type === "Action.Submit";
4522
2696
  }
4523
2697
  function stripFromActions(actions) {
4524
2698
  return actions.filter((action) => !isSubmitAction(action)).map(
4525
- (action) => isRecord5(action) && isRecord5(action.card) ? { ...action, card: stripSubmitActions(action.card) } : action
2699
+ (action) => isRecord2(action) && isRecord2(action.card) ? { ...action, card: stripSubmitActions(action.card) } : action
4526
2700
  );
4527
2701
  }
4528
2702
  function stripFromElements(elements) {
4529
2703
  const result = [];
4530
2704
  for (const element of elements) {
4531
- if (!isRecord5(element)) {
2705
+ if (!isRecord2(element)) {
4532
2706
  result.push(element);
4533
2707
  continue;
4534
2708
  }
@@ -4543,7 +2717,7 @@ function stripFromElements(elements) {
4543
2717
  }
4544
2718
  if (Array.isArray(el.columns)) {
4545
2719
  el.columns = el.columns.map(
4546
- (column) => isRecord5(column) && Array.isArray(column.items) ? { ...column, items: stripFromElements(column.items) } : column
2720
+ (column) => isRecord2(column) && Array.isArray(column.items) ? { ...column, items: stripFromElements(column.items) } : column
4547
2721
  );
4548
2722
  }
4549
2723
  result.push(el);
@@ -5854,23 +4028,6 @@ function AdapterConnectionSelector({
5854
4028
  ] });
5855
4029
  }
5856
4030
 
5857
- // src/builder/lib/sticky-notes.ts
5858
- function isStickyNode(node) {
5859
- return node?.type === STICKY_NOTE_TYPE;
5860
- }
5861
- function partitionStickyNotes(nodes) {
5862
- return {
5863
- nodes: nodes.filter((n) => !isStickyNode(n)),
5864
- stickyNotes: nodes.filter((n) => isStickyNode(n))
5865
- };
5866
- }
5867
- function mergeStickyNotes(def) {
5868
- const nodes = Array.isArray(def.nodes) ? def.nodes : [];
5869
- const stickies = Array.isArray(def.stickyNotes) ? def.stickyNotes : [];
5870
- const seen = new Set(nodes.map((n) => n.id));
5871
- return [...nodes, ...stickies.filter((s) => s?.id && !seen.has(s.id))];
5872
- }
5873
-
5874
4031
  // src/hooks/workflow-saved-events.ts
5875
4032
  var listeners = /* @__PURE__ */ new Map();
5876
4033
  function subscribeWorkflowSaved(workflowKey, listener) {
@@ -5887,11 +4044,11 @@ function notifyWorkflowSaved(workflowKey) {
5887
4044
  }
5888
4045
 
5889
4046
  // src/builder/lib/display-options.ts
5890
- function isRecord6(value) {
4047
+ function isRecord3(value) {
5891
4048
  return typeof value === "object" && value !== null && !Array.isArray(value);
5892
4049
  }
5893
4050
  function readRuleSet(value) {
5894
- if (!isRecord6(value)) return void 0;
4051
+ if (!isRecord3(value)) return void 0;
5895
4052
  const rules = {};
5896
4053
  for (const [key, allowed] of Object.entries(value)) {
5897
4054
  if (Array.isArray(allowed)) rules[key] = allowed;
@@ -5900,8 +4057,8 @@ function readRuleSet(value) {
5900
4057
  return Object.keys(rules).length > 0 ? rules : void 0;
5901
4058
  }
5902
4059
  function readDisplayOptions(spec) {
5903
- const raw = isRecord6(spec) ? spec.displayOptions : void 0;
5904
- if (!isRecord6(raw)) return void 0;
4060
+ const raw = isRecord3(spec) ? spec.displayOptions : void 0;
4061
+ if (!isRecord3(raw)) return void 0;
5905
4062
  const show = readRuleSet(raw.show);
5906
4063
  const hide = readRuleSet(raw.hide);
5907
4064
  if (!show && !hide) return void 0;
@@ -5914,7 +4071,7 @@ function matches(current, allowed) {
5914
4071
  );
5915
4072
  }
5916
4073
  function isParamVisible(spec, values, defaults = {}) {
5917
- const options = isRecord6(spec) ? readDisplayOptions(spec) ?? (isRecord6(spec.show) || isRecord6(spec.hide) ? readDisplayOptions({ displayOptions: spec }) : void 0) : void 0;
4074
+ const options = isRecord3(spec) ? readDisplayOptions(spec) ?? (isRecord3(spec.show) || isRecord3(spec.hide) ? readDisplayOptions({ displayOptions: spec }) : void 0) : void 0;
5918
4075
  if (!options) return true;
5919
4076
  const valueOf = (key) => values[key] === void 0 ? defaults[key] : values[key];
5920
4077
  if (options.show) {
@@ -5934,7 +4091,7 @@ function isParamVisible(spec, values, defaults = {}) {
5934
4091
  function defaultsOf(fields) {
5935
4092
  const defaults = {};
5936
4093
  for (const [name, spec] of Object.entries(fields)) {
5937
- if (isRecord6(spec) && spec.default !== void 0)
4094
+ if (isRecord3(spec) && spec.default !== void 0)
5938
4095
  defaults[name] = spec.default;
5939
4096
  }
5940
4097
  return defaults;
@@ -5945,17 +4102,17 @@ function dropHiddenParameters(state, fields, mappedFields = /* @__PURE__ */ new
5945
4102
  let next;
5946
4103
  const ensure = () => next ??= { ...state };
5947
4104
  for (const [name, spec] of Object.entries(fields)) {
5948
- if (!isRecord6(spec)) continue;
4105
+ if (!isRecord3(spec)) continue;
5949
4106
  if (!mappedFields.has(name) && Object.hasOwn(state, name) && !isParamVisible(spec, state, defaults)) {
5950
4107
  delete ensure()[name];
5951
4108
  continue;
5952
4109
  }
5953
- const rowFields = isRecord6(spec.fields) ? spec.fields : void 0;
4110
+ const rowFields = isRecord3(spec.fields) ? spec.fields : void 0;
5954
4111
  const rows = state[name];
5955
4112
  if (rowFields && Array.isArray(rows)) {
5956
4113
  let changed = false;
5957
4114
  const cleaned = rows.map((row) => {
5958
- if (!isRecord6(row)) return row;
4115
+ if (!isRecord3(row)) return row;
5959
4116
  const cleanedRow = dropHiddenParameters(row, rowFields);
5960
4117
  if (cleanedRow !== row) changed = true;
5961
4118
  return cleanedRow;
@@ -5967,7 +4124,7 @@ function dropHiddenParameters(state, fields, mappedFields = /* @__PURE__ */ new
5967
4124
  }
5968
4125
 
5969
4126
  // src/builder/lib/field-schema.ts
5970
- function isRecord7(value) {
4127
+ function isRecord4(value) {
5971
4128
  return typeof value === "object" && value !== null && !Array.isArray(value);
5972
4129
  }
5973
4130
  function isIdentifier(key) {
@@ -5986,11 +4143,11 @@ function pathToExpression(path) {
5986
4143
  function mergeFieldSpecs(common, variant) {
5987
4144
  const base = typeof common === "string" ? { type: common } : common;
5988
4145
  const branch = typeof variant === "string" ? { type: variant } : variant;
5989
- if (!isRecord7(base) || !isRecord7(branch)) return branch;
4146
+ if (!isRecord4(base) || !isRecord4(branch)) return branch;
5990
4147
  const merged = { ...base, ...branch };
5991
4148
  const commonProperties = base.properties;
5992
4149
  const branchProperties = branch.properties;
5993
- if (isRecord7(commonProperties) && isRecord7(branchProperties)) {
4150
+ if (isRecord4(commonProperties) && isRecord4(branchProperties)) {
5994
4151
  merged.properties = Object.fromEntries(
5995
4152
  Object.entries({ ...commonProperties, ...branchProperties }).map(
5996
4153
  ([key, value]) => [
@@ -6009,14 +4166,14 @@ function fieldFromSpec(key, path, spec) {
6009
4166
  if (typeof spec === "string") {
6010
4167
  return { key, path, type: spec };
6011
4168
  }
6012
- if (isRecord7(spec)) {
4169
+ if (isRecord4(spec)) {
6013
4170
  const declaredType2 = typeof spec.type === "string" ? spec.type : void 0;
6014
4171
  const description = typeof spec.description === "string" ? spec.description : void 0;
6015
4172
  const required = spec.required === true;
6016
4173
  const alternatives = ["oneOf", "anyOf"].flatMap((keyword) => {
6017
4174
  const variants = spec[keyword];
6018
4175
  return Array.isArray(variants) ? variants.flatMap(
6019
- (variant, index) => isRecord7(variant) || typeof variant === "string" ? [{ keyword, index, variant }] : []
4176
+ (variant, index) => isRecord4(variant) || typeof variant === "string" ? [{ keyword, index, variant }] : []
6020
4177
  ) : [];
6021
4178
  });
6022
4179
  if (alternatives.length > 0) {
@@ -6045,7 +4202,7 @@ function fieldFromSpec(key, path, spec) {
6045
4202
  type: declaredType2 ?? [...new Set(children.map((field) => field.type))].join(" | ")
6046
4203
  };
6047
4204
  }
6048
- if (isRecord7(spec.properties) && (!declaredType2 || declaredType2 === "object")) {
4205
+ if (isRecord4(spec.properties) && (!declaredType2 || declaredType2 === "object")) {
6049
4206
  const children = Object.entries(spec.properties).map(
6050
4207
  ([childKey, childSpec]) => fieldFromSpec(childKey, joinPath(path, childKey), childSpec)
6051
4208
  );
@@ -6067,7 +4224,7 @@ function fieldFromSpec(key, path, spec) {
6067
4224
  return { key, path, type: "any" };
6068
4225
  }
6069
4226
  function fieldsFromIO(io, root = "") {
6070
- if (!isRecord7(io)) return [];
4227
+ if (!isRecord4(io)) return [];
6071
4228
  return Object.entries(io).map(
6072
4229
  ([key, spec]) => fieldFromSpec(key, joinPath(root, key), spec)
6073
4230
  );
@@ -6086,7 +4243,7 @@ function fieldFromValue(key, path, value) {
6086
4243
  const children = value.length > 0 ? [fieldFromValue("0", `${path}[0]`, value[0])] : void 0;
6087
4244
  return { key, path, type: "array", children };
6088
4245
  }
6089
- if (isRecord7(value)) {
4246
+ if (isRecord4(value)) {
6090
4247
  const children = Object.entries(value).map(
6091
4248
  ([k, v]) => fieldFromValue(k, joinPath(path, k), v)
6092
4249
  );
@@ -6095,7 +4252,7 @@ function fieldFromValue(key, path, value) {
6095
4252
  return { key, path, type: typeOfValue(value) };
6096
4253
  }
6097
4254
  function fieldsFromValue(value, root = "") {
6098
- if (!isRecord7(value)) return [];
4255
+ if (!isRecord4(value)) return [];
6099
4256
  return Object.entries(value).map(
6100
4257
  ([key, v]) => fieldFromValue(key, joinPath(root, key), v)
6101
4258
  );
@@ -6111,13 +4268,13 @@ function declaredOutputFields(nodeType, op) {
6111
4268
  }
6112
4269
  function normalizeOperations(raw) {
6113
4270
  if (Array.isArray(raw)) return raw;
6114
- if (isRecord7(raw) && Array.isArray(raw.operations)) {
4271
+ if (isRecord4(raw) && Array.isArray(raw.operations)) {
6115
4272
  return raw.operations;
6116
4273
  }
6117
4274
  return [];
6118
4275
  }
6119
4276
  function resolveNodeOperation(data, fallbackOperations) {
6120
- const nodeData = isRecord7(data?.nodeData) ? data?.nodeData : void 0;
4277
+ const nodeData = isRecord4(data?.nodeData) ? data?.nodeData : void 0;
6121
4278
  let operations = normalizeOperations(nodeData?.operations);
6122
4279
  if (operations.length === 0) {
6123
4280
  operations = normalizeOperations(data?.operations);
@@ -6131,11 +4288,11 @@ function resolveNodeOperation(data, fallbackOperations) {
6131
4288
  }
6132
4289
 
6133
4290
  // src/builder/lib/parameter-values.ts
6134
- function isRecord8(value) {
4291
+ function isRecord5(value) {
6135
4292
  return typeof value === "object" && value !== null && !Array.isArray(value);
6136
4293
  }
6137
4294
  function record(value) {
6138
- return isRecord8(value) ? value : void 0;
4295
+ return isRecord5(value) ? value : void 0;
6139
4296
  }
6140
4297
  function declaredType(schema) {
6141
4298
  const type = typeof schema === "string" ? schema : record(schema)?.type;
@@ -8211,7 +6368,7 @@ function useNodeExecution() {
8211
6368
  executionId: runId,
8212
6369
  trigger: analyticsTrigger,
8213
6370
  errorCode: run.errorType,
8214
- durationMs: run.duration
6371
+ durationMs: runDurationMs(run)
8215
6372
  });
8216
6373
  }
8217
6374
  completeExecution();
@@ -8310,7 +6467,7 @@ function useNodeExecution() {
8310
6467
  } catch (error) {
8311
6468
  console.error("[useNodeExecution] Error executing step:", error);
8312
6469
  const errorMessage = describeOperationError(error);
8313
- const errorRunId = nanoid();
6470
+ const errorRunId = nanoid$1();
8314
6471
  const errorLog = {
8315
6472
  id: `${errorRunId}-${nodeId}`,
8316
6473
  runId: errorRunId,
@@ -8423,7 +6580,7 @@ function useNodeExecution() {
8423
6580
  surface: "builder-run"
8424
6581
  });
8425
6582
  const errorMessage = describeOperationError(error);
8426
- const errorRunId = nanoid();
6583
+ const errorRunId = nanoid$1();
8427
6584
  const errorLog = {
8428
6585
  id: `${errorRunId}-${triggerNode.id}`,
8429
6586
  runId: errorRunId,
@@ -9076,45 +7233,6 @@ function useInsertNode() {
9076
7233
  [screenToFlowPosition, getNodes, addNodes, updateNode]
9077
7234
  );
9078
7235
  }
9079
-
9080
- // src/builder/lib/node-operation-selection.ts
9081
- function resolveOperationName(selectedOperation, fallback) {
9082
- return typeof selectedOperation === "string" && selectedOperation.trim() ? selectedOperation : fallback;
9083
- }
9084
- function canRenderOperationPanel(selectedOperation, supportedOperations) {
9085
- const operationName = resolveOperationName(selectedOperation, "");
9086
- return operationName === "" || supportedOperations.includes(operationName);
9087
- }
9088
- function isRecord9(value) {
9089
- return typeof value === "object" && value !== null && !Array.isArray(value);
9090
- }
9091
- function readRecord3(value) {
9092
- return isRecord9(value) ? value : void 0;
9093
- }
9094
- function readOperations(value) {
9095
- const operations = Array.isArray(value) ? value : readRecord3(value)?.operations;
9096
- return Array.isArray(operations) ? operations : [];
9097
- }
9098
- function synchronizeOperationUpdate(currentData, updates, registeredOperations) {
9099
- const operationName = resolveOperationName(updates.operation_name, "");
9100
- if (!operationName || operationName === currentData.operation_name)
9101
- return updates;
9102
- const manifest = readRecord3(updates.nodeData ?? currentData.nodeData);
9103
- const operations = [
9104
- ...readOperations(manifest?.operations),
9105
- ...readOperations(registeredOperations)
9106
- ];
9107
- const selected = readRecord3(
9108
- operations.find(
9109
- (operation) => readRecord3(operation)?.name === operationName
9110
- )
9111
- );
9112
- return {
9113
- ...updates,
9114
- operation_type: typeof selected?.type === "string" ? selected.type : updates.operation_type,
9115
- nodeData: { ...manifest, operations: selected ? [selected] : [] }
9116
- };
9117
- }
9118
7236
  function isAutoManagedConnectionAdapter(nodeType, adapter) {
9119
7237
  const isBigConsoleDataSinkAutoAdapter = nodeType === "store/bigconsole_datasink" && adapter.name === "bigconsole" && adapter.provider === "bigconsole";
9120
7238
  return isBigConsoleDataSinkAutoAdapter || adapter.mode === "auto" || adapter.autoProvision === true || adapter.autoManaged === true || adapter.hidden === true;
@@ -10109,7 +8227,7 @@ function getNodeDetail(nodeType) {
10109
8227
  }
10110
8228
  function createNodeData(type, data) {
10111
8229
  return {
10112
- id: nanoid(),
8230
+ id: nanoid$1(),
10113
8231
  type,
10114
8232
  data
10115
8233
  };
@@ -10119,4 +8237,4 @@ function createNodeWithDefaultData(type, data) {
10119
8237
  return Object.assign(createNodeData(type, defaultData), data);
10120
8238
  }
10121
8239
 
10122
- export { AUTOSAVE_DEBOUNCE_MS, AUTOSAVE_RETRY_DELAYS_MS, AVAILABLE_NODES, BILLING_PLANS_PATH, CODE_WORKFLOW_NODE_TYPE, ConnectionFormDialog, ConnectionSelector, DURABLE_FUNCTION_NODE_TYPES, EDIT_SESSION_ID, ExpressionEditor, FLUIDGRIDS_FLAGS, FLUIDGRIDS_FLAG_DEFAULTS, FluidGridsContext, FluidGridsProvider, NODES, NODES_METADATA, NODE_CONFIGS, NODE_OPERATION_DRAG_DATA_FORMAT, NODE_TYPES, NODE_TYPE_DRAG_DATA_FORMAT, NodePropertyPanel, NodeTypeIcon, PHASE_C_FLAGS_OFF, RUNS_QUOTA_NAME, RUN_CONTROL_NODE_TYPES, STICKY_NOTE_TYPE, WORKFLOWS_QUOTA_NAME, WORKFLOW_TEST_NODE_TYPES, autosaveAllowedFor, bannerOf, blankWorkflowCreateInput, buildOperationSeed, buildStickyNode, builderPathOf, canAutosave, canRenderOperationPanel, canSignalRun, changedSourceFiles, childRunIdOf, classifyCreateRefusal, clearCodeWorkflowDefinitionCache, codeBuildIdOf, components_default, createConfiguredNode, createThenOpen, declaredOutputFields, declaredSignals, defaultSourcePath, departingSave, diagnosticsByPath, editStateFor, editingLeaseLocked, emitNodeInstalled, emitRunFailed, emitRunLimitHit, emitRunStarted, emitTemplateUsed, emitUpgradeClicked, emitWorkflowCreated, emitWorkflowLimitHit, emitWorkflowPublished, ensureNodeConfigsLoaded, fieldsFromIO, fieldsFromValue, filterPaletteNodes, findQuotaRefusal, findSaveConflict, forgetSessionForReload, formatParameterValue, getNodeDetail, getNodeMetadata, inAppLeaveTarget, interfaceFields, interfaceHandlers, isDurableFunctionNodeType, isEditableSourcePath, isFlowComplete, isImageIcon, isLocked, isNodeConfigsInitialized, isNodeTypeHiddenFromPalette, isParamVisible, isQuotaExhaustedFailure, isRunControlNodeType, isTriggerOperation, leaseCallQueue, leavingLosesChanges, listCustomBlocks, liveInternalOperations, loadDefinitionJson, mergeStickyNotes, nodeAccent, nodesOf, onNodeTypesUpdate, openRunQuotaUpgradePrompt, parseParameterValue, parseRawSignalPayload, pathToExpression, payloadFromCardValues, phaseAfterAcquire, phaseAfterTakeOver, projectLanguage, readCodeWorkflowState, readDisplayOptions, refetchStoreNodes, registerCustomBlock, registerWorkflowNodeTypes, releasesOnClose, rememberOwnStatusChange, rememberRevision, rememberSessionForReload, resolveInputHandles, resolveNodeOperation, resolveOperationName, resolveOutputHandles, resolvePhaseCFlags, resolveUiSpec, runSignalTargets, runTriggerFromMode, saveIndicatorOf, savedEverythingBeforeLeaving, sessionBeforeReload, shouldReleaseForIdle, signalPayloadCard, sourceLanguageOf, stripSubmitActions, structuredParameterType, subscribeWorkflowSaved, synchronizeOperationUpdate, tickAction, touchesSavedFields, useAgentDefinitionList, useAgentDefinitions, useCodeWorkflowInterface, useConnection, useConnections, useConnectionsByProvider, useContinuousOptimisationEnabled, useCreateConnection, useDurableFunctionNodesEnabled, useDurableParityEnabled, useEditLeaseLocked, useEditLeaseStore, useFlowStore, useFlowValidator, useFluidGridsAnalytics, useFluidGridsContext, useFluidGridsFlag, useGuardedNavigate, useInitiateOAuth, useInsertNode, useIsValidConnection, useManualSave, useNodeExecution, useNodeRegistryRevision, usePaletteGates, usePhaseCFlags, useProviders, useRunGuard, useSaveStatusStore, useSaveWorkflow, useSelfHostedWorkersEnabled, useWorkflowTestsEnabled, watchLeavingLosesChanges, workflowFingerprint };
8240
+ export { AUTOSAVE_DEBOUNCE_MS, AUTOSAVE_RETRY_DELAYS_MS, AVAILABLE_NODES, BILLING_PLANS_PATH, ConnectionFormDialog, ConnectionSelector, DURABLE_FUNCTION_NODE_TYPES, EDIT_SESSION_ID, ExpressionEditor, FLUIDGRIDS_FLAGS, FLUIDGRIDS_FLAG_DEFAULTS, FluidGridsContext, FluidGridsProvider, NODES, NODES_METADATA, NODE_CONFIGS, NODE_TYPES, NodePropertyPanel, 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, components_default, createConfiguredNode, createThenOpen, declaredOutputFields, declaredSignals, departingSave, 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, isParamVisible, isQuotaExhaustedFailure, isRunControlNodeType, leaseCallQueue, leavingLosesChanges, listCustomBlocks, liveInternalOperations, loadDefinitionJson, nodeStateDurationMs, nodesOf, onNodeTypesUpdate, openRunQuotaUpgradePrompt, parseParameterValue, parseRawSignalPayload, pathToExpression, payloadFromCardValues, phaseAfterAcquire, phaseAfterTakeOver, readDisplayOptions, refetchStoreNodes, registerCustomBlock, registerWorkflowNodeTypes, releasesOnClose, rememberOwnStatusChange, rememberRevision, rememberSessionForReload, resolveNodeOperation, resolvePhaseCFlags, resolveUiSpec, runDurationMs, runSignalTargets, runTriggerFromMode, saveIndicatorOf, savedEverythingBeforeLeaving, sessionBeforeReload, shouldReleaseForIdle, signalPayloadCard, stripSubmitActions, structuredParameterType, subscribeWorkflowSaved, tickAction, touchesSavedFields, useAgentDefinitionList, useAgentDefinitions, useCodeWorkflowInterface, useConnection, useConnections, useConnectionsByProvider, useContinuousOptimisationEnabled, useCreateConnection, useDurableFunctionNodesEnabled, useDurableParityEnabled, useEditLeaseLocked, useEditLeaseStore, useFlowValidator, useFluidGridsAnalytics, useFluidGridsContext, useFluidGridsFlag, useGuardedNavigate, useInitiateOAuth, useInsertNode, useManualSave, useNodeExecution, useNodeRegistryRevision, usePaletteGates, usePhaseCFlags, useProviders, useRunGuard, useSaveStatusStore, useSaveWorkflow, useSelfHostedWorkersEnabled, useWorkflowTestsEnabled, watchLeavingLosesChanges, workflowFingerprint };