@burdenoff/fluidgrids-fe-sdk 2026.1105.1 → 2026.1105.3

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 (60) hide show
  1. package/README.md +30 -0
  2. package/dist/ExecutionDetailPage-JXGPSLS3.js +30 -0
  3. package/dist/{WorkflowsList-EV725VG5.js → ExecutionsListPage-U3TC5JUI.js} +11 -8
  4. package/dist/{WorkflowBuilder-CRR5WP4V.js → WorkflowBuilder-6DOD6WWH.js} +16 -13
  5. package/dist/{ExecutionsListPage-NQIQXMTY.js → WorkflowsList-XOWRQNST.js} +10 -8
  6. package/dist/{agentTools-PN4W7QZH.js → agentTools-YQBN33WG.js} +7 -5
  7. package/dist/{chunk-VFVN7D3A.js → chunk-22U3TUKZ.js} +3 -93
  8. package/dist/{chunk-XLI75E3D.js → chunk-2WUVF4EQ.js} +2 -2
  9. package/dist/{chunk-GVIYGJQG.js → chunk-3MZOYJ5I.js} +1 -1
  10. package/dist/{chunk-L44BRLW5.js → chunk-3ZYRLGDF.js} +612 -22
  11. package/dist/{chunk-FCT6POFX.js → chunk-4NNF7DIB.js} +1 -1
  12. package/dist/chunk-5DVP2UYH.js +1924 -0
  13. package/dist/{chunk-JDQYGH4Q.js → chunk-EKDUYCTS.js} +4 -4
  14. package/dist/{chunk-6P7DOBVN.js → chunk-EYQ7GJRW.js} +2 -2
  15. package/dist/{chunk-HLAUK5GM.js → chunk-FVWRGL7S.js} +1 -1
  16. package/dist/{chunk-AZKVI4YN.js → chunk-G33OMSW6.js} +8 -6
  17. package/dist/{chunk-D7G5VWSH.js → chunk-G774FI2E.js} +2 -2
  18. package/dist/{chunk-DANOMJ5I.js → chunk-IJ7UIDCW.js} +7 -6
  19. package/dist/{chunk-EC2AN74G.js → chunk-IL343FKX.js} +1 -1
  20. package/dist/chunk-JYYN3CLL.js +32 -0
  21. package/dist/{chunk-O2EB7UT2.js → chunk-KAWRHIJZ.js} +2 -2
  22. package/dist/{chunk-JC4D7RQ6.js → chunk-KN37RIFC.js} +5 -4
  23. package/dist/{chunk-AASHQI77.js → chunk-KZCNJAMK.js} +2 -1
  24. package/dist/{chunk-BHAKNT5M.js → chunk-LDWBMCX4.js} +4 -3
  25. package/dist/{chunk-MSQAULLG.js → chunk-MA6I4KY5.js} +186 -7
  26. package/dist/{chunk-R3Z7W4EX.js → chunk-MP44TAOO.js} +276 -196
  27. package/dist/{chunk-FJSXXO5W.js → chunk-MY7SEUXM.js} +1 -1
  28. package/dist/{chunk-I3QPAEL6.js → chunk-NMQENSPW.js} +15 -14
  29. package/dist/chunk-NZAYYUQ4.js +172 -0
  30. package/dist/{chunk-6EZJ5J3B.js → chunk-NZFPCTIS.js} +4 -4
  31. package/dist/{chunk-MZFJL4X3.js → chunk-OXXXUAWM.js} +1 -1
  32. package/dist/chunk-SDZEAIIE.js +9 -0
  33. package/dist/{chunk-CGR3AEZU.js → chunk-TCIBTMJI.js} +68 -2
  34. package/dist/{chunk-4QB6S7YW.js → chunk-TJAG2DQT.js} +9 -38
  35. package/dist/{chunk-MQN6YDX4.js → chunk-TLHX4DK4.js} +7 -6
  36. package/dist/{chunk-AG3VC7Y2.js → chunk-VESQH55Q.js} +2 -2
  37. package/dist/chunk-WZ3EJVS6.js +93 -0
  38. package/dist/{chunk-OCJTTHTS.js → chunk-Y7FKJJMF.js} +1 -9
  39. package/dist/{chunk-HOTVUVZG.js → chunk-YGOGJDN4.js} +2 -2
  40. package/dist/{chunk-AVHBWJRY.js → chunk-Z3ABLERJ.js} +3 -1
  41. package/dist/{chunk-C26QAWCT.js → chunk-ZBPRNWKY.js} +235 -2146
  42. package/dist/{chunk-2QOAJF7V.js → chunk-ZLBTUCC2.js} +4 -3
  43. package/dist/code-editor-field-WWPDMF4O.js +3 -0
  44. package/dist/executions.js +16 -12
  45. package/dist/history.js +9 -6
  46. package/dist/index.d.ts +77 -1
  47. package/dist/index.js +46 -41
  48. package/dist/{node-detail-view-3VG7XFIZ.js → node-detail-view-U5HOP2WX.js} +35 -9
  49. package/dist/nodes.js +2 -2
  50. package/dist/observability.js +9 -6
  51. package/dist/{quick-config-panel-GB7SDWHD.js → quick-config-panel-XAISBW6Y.js} +10 -7
  52. package/dist/settings.js +9 -6
  53. package/dist/templates.js +15 -12
  54. package/dist/workflow-builder.js +17 -14
  55. package/dist/workflow-preview.d.ts +158 -0
  56. package/dist/workflow-preview.js +721 -0
  57. package/dist/workflows-list.js +10 -8
  58. package/package.json +5 -1
  59. package/dist/ExecutionDetailPage-YBAMVPMB.js +0 -26
  60. 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';
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-5DVP2UYH.js';
3
4
  import { getGlobalStoreClient, createGlobalStoreClient, setGlobalStoreClient, resetGlobalStoreClient } from './chunk-AU7LACOY.js';
4
5
  import { GetMyNodeInstallationsDocument } from './chunk-G77IGC3Y.js';
5
- import { fetchAllNodeEndpointConfigs, NodeManifestDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowDefinitionDocument, WorkflowRunDocument } from './chunk-AVHBWJRY.js';
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
8
  import { waitForFluidGridsClient, getFluidGridsClient, getFluidGridsClientSafe, createFluidGridsClient, setFluidGridsClient, resetFluidGridsClient } from './chunk-3IXU7WUC.js';
8
9
  import { throwIfResultError, emitFluidGridsEvent, emitOperationErrorEvent, describeOperationError, emitRunCompletedOnce } from './chunk-US5QSLPV.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';
@@ -510,901 +509,6 @@ function openRunQuotaUpgradePrompt(error, tr = ENGLISH_FALLBACK_TRANSLATOR, anal
510
509
  });
511
510
  return true;
512
511
  }
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
512
  function useDeleteNode() {
1409
513
  const { getNode, getEdges, deleteElements } = useReactFlow();
1410
514
  const deleteNodes = useFlowStore((state) => state.actions.nodes.deleteNodes);
@@ -1420,11 +524,6 @@ function useDeleteNode() {
1420
524
  [deleteElements, getEdges, getNode, deleteNodes]
1421
525
  );
1422
526
  }
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
527
  var STICKY_COLORS = {
1429
528
  yellow: "var(--color-chart-4)",
1430
529
  orange: "var(--color-status-warning-bg)",
@@ -1520,7 +619,7 @@ function StickyNoteNodeComponent({
1520
619
  if (!source) return;
1521
620
  const clone = {
1522
621
  ...source,
1523
- id: nanoid$1(),
622
+ id: nanoid(),
1524
623
  position: {
1525
624
  x: source.position.x + 24,
1526
625
  y: source.position.y + 24
@@ -1644,1158 +743,204 @@ function StickyNoteNodeComponent({
1644
743
  /* @__PURE__ */ jsx(
1645
744
  "div",
1646
745
  {
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",
746
+ className: "flex h-full w-full flex-col overflow-hidden rounded-lg border",
747
+ style: {
748
+ backgroundColor: `${accent}1F`,
749
+ // Same border whether selected or not — selection shows only via the
750
+ // floating toolbar (no extra border/outline on click).
751
+ borderColor: `${accent}59`
752
+ },
753
+ onDoubleClick: (e) => {
754
+ e.stopPropagation();
755
+ if (!readOnly) startEditing();
756
+ },
757
+ children: editing ? /* @__PURE__ */ jsx(
758
+ "textarea",
2453
759
  {
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
760
+ ref: textareaRef,
761
+ readOnly,
762
+ value: draft,
763
+ onChange: (e) => setDraft(e.target.value),
764
+ onBlur: commitEdit,
765
+ onKeyDown: (e) => {
766
+ e.stopPropagation();
767
+ if (e.key === "Escape") cancelEdit();
768
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) commitEdit();
769
+ },
770
+ placeholder: tr(
771
+ "fluidgrids.builder.stickyNote.editorPlaceholder",
772
+ "# Note\nSupports **markdown** \u2014 headings, lists, links\u2026"
773
+ ),
774
+ 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
775
  }
2459
- ),
2460
- /* @__PURE__ */ jsx(
2461
- HandlePlusStub,
776
+ ) : content.trim() ? /* @__PURE__ */ jsx(
777
+ "div",
2462
778
  {
2463
- nodeId,
2464
- handleId: handle.id,
2465
- color,
2466
- isConnectable,
2467
- isOnlyHandle: false,
2468
- anchor: "point"
779
+ className: cn(
780
+ "nowheel h-full w-full overflow-auto p-3 text-xs leading-relaxed text-foreground",
781
+ // Compact markdown typography scoped to the note body.
782
+ "[&_h1]:mb-1.5 [&_h1]:text-sm [&_h1]:font-bold",
783
+ "[&_h2]:mb-1 [&_h2]:text-[13px] [&_h2]:font-bold",
784
+ "[&_h3]:mb-1 [&_h3]:text-xs [&_h3]:font-semibold",
785
+ "[&_p]:mb-1.5 [&_ul]:mb-1.5 [&_ul]:list-disc [&_ul]:pl-4",
786
+ "[&_ol]:mb-1.5 [&_ol]:list-decimal [&_ol]:pl-4 [&_li]:mb-0.5",
787
+ "[&_a]:underline [&_code]:rounded [&_code]:bg-muted [&_code]:px-1"
788
+ ),
789
+ children: /* @__PURE__ */ jsx(ReactMarkdown, { children: content })
2469
790
  }
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]
791
+ ) : /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center p-3 text-xs text-muted-foreground/70", children: tr(
792
+ "fluidgrids.builder.stickyNote.emptyStateHint",
793
+ "Double-click to edit"
794
+ ) })
795
+ }
796
+ )
797
+ ] })
2487
798
  );
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
799
  }
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
- );
800
+ var StickyNoteNode = memo(StickyNoteNodeComponent);
801
+ function buildStickyNode(position) {
802
+ return {
803
+ id: nanoid(),
804
+ type: STICKY_NOTE_TYPE,
805
+ position,
806
+ width: STICKY_DEFAULT_SIZE.width,
807
+ height: STICKY_DEFAULT_SIZE.height,
808
+ // Render under regular nodes and edges — notes are a background layer.
809
+ zIndex: -1,
810
+ selected: true,
811
+ data: { content: "", color: DEFAULT_STICKY_COLOR }
812
+ };
2648
813
  }
2649
814
 
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;
815
+ // src/modules/connections/utils/adapter-selection.ts
816
+ var ANY_PROVIDER = "*";
817
+ function isOptionalAdapter(adapter) {
818
+ return adapter.required === false;
2658
819
  }
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
- };
820
+ function newConnectionProviderKey(adapters) {
821
+ if (adapters.length !== 1) return void 0;
822
+ const provider = adapters[0]?.provider;
823
+ return provider && provider !== ANY_PROVIDER ? provider : void 0;
2670
824
  }
2671
- function readSignalName(state) {
2672
- return isRecord3(state) ? str(state.signalName) : void 0;
825
+ function matchesAdapterProvider(adapter, providerKey) {
826
+ const normalizedKey = providerKey.trim().toLowerCase();
827
+ if (normalizedKey.length === 0) return false;
828
+ if (adapter.provider.trim() === ANY_PROVIDER) return true;
829
+ return adapter.provider.trim().toLowerCase() === normalizedKey || adapter.name.trim().toLowerCase() === normalizedKey;
2673
830
  }
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";
831
+ function connectionsForAdapter(connections, adapterName) {
832
+ return connections.filter(
833
+ (connection) => connection.adapter.toLowerCase() === adapterName.toLowerCase()
834
+ );
2680
835
  }
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) : []
836
+ function replaceAdapterConnections(connections, adapterName, connectionIds = []) {
837
+ const otherConnections = connections.filter(
838
+ (connection) => connection.adapter.toLowerCase() !== adapterName.toLowerCase()
2685
839
  );
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
- }));
840
+ const uniqueIds = [...new Set(connectionIds.filter(Boolean))];
841
+ return [
842
+ ...otherConnections,
843
+ ...uniqueIds.map((connectionId) => ({
844
+ connectionId,
845
+ adapter: adapterName
846
+ }))
847
+ ];
2692
848
  }
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
- };
849
+ function missingRequiredAdapters(adapters, selected) {
850
+ return adapters.filter(
851
+ (adapter) => !isOptionalAdapter(adapter) && !selected.some((connection) => connection.adapter === adapter.name)
852
+ );
2700
853
  }
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;
854
+ var NODE_DOCS_URL = gql`
855
+ query NodeDocsUrlLookup($nodeType: String!, $nodeVersion: String!) {
856
+ nodeEndpointConfig(nodeType: $nodeType, nodeVersion: $nodeVersion) {
857
+ docsUrl
2710
858
  }
2711
859
  }
2712
- return void 0;
860
+ `;
861
+ var cache = /* @__PURE__ */ new Map();
862
+ var inFlight = /* @__PURE__ */ new Map();
863
+ function fetchDocsUrl(nodeType, nodeVersion) {
864
+ const key = `${nodeType}@${nodeVersion}`;
865
+ if (cache.has(key)) return Promise.resolve(cache.get(key));
866
+ const existing = inFlight.get(key);
867
+ if (existing) return existing;
868
+ const client = getFluidGridsClientSafe();
869
+ if (!client) {
870
+ cache.set(key, void 0);
871
+ return Promise.resolve(void 0);
872
+ }
873
+ const promise = client.query({
874
+ query: NODE_DOCS_URL,
875
+ variables: { nodeType, nodeVersion },
876
+ fetchPolicy: "cache-first"
877
+ }).then((result) => result.data?.nodeEndpointConfig?.docsUrl ?? void 0).catch(() => void 0).finally(() => inFlight.delete(key));
878
+ inFlight.set(key, promise);
879
+ return promise.then((url) => {
880
+ cache.set(key, url);
881
+ return url;
882
+ });
2713
883
  }
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
884
+ function useNodeDocsUrl(nodeType, nodeVersion) {
885
+ const [docsUrl, setDocsUrl] = useState(
886
+ () => nodeType && nodeVersion ? cache.get(`${nodeType}@${nodeVersion}`) : void 0
887
+ );
888
+ useEffect(() => {
889
+ if (!nodeType || !nodeVersion) {
890
+ setDocsUrl(void 0);
891
+ return;
892
+ }
893
+ let cancelled = false;
894
+ fetchDocsUrl(nodeType, nodeVersion).then((url) => {
895
+ if (!cancelled) setDocsUrl(url);
2753
896
  });
2754
- result.set(path, list);
2755
- }
2756
- return result;
897
+ return () => {
898
+ cancelled = true;
899
+ };
900
+ }, [nodeType, nodeVersion]);
901
+ return docsUrl;
2757
902
  }
2758
903
 
2759
904
  // src/modules/executions/utils/signal-targets.ts
2760
- function isRecord4(value) {
905
+ function isRecord(value) {
2761
906
  return typeof value === "object" && value !== null && !Array.isArray(value);
2762
907
  }
2763
- function str2(value) {
908
+ function str(value) {
2764
909
  return typeof value === "string" && value.trim() ? value : void 0;
2765
910
  }
2766
911
  function durableInterfaceOf(definitionJson, workflowFn) {
2767
- if (!isRecord4(definitionJson)) return void 0;
912
+ if (!isRecord(definitionJson)) return void 0;
2768
913
  const code = definitionJson.code;
2769
- if (isRecord4(code) && isRecord4(code.exports) && workflowFn) {
914
+ if (isRecord(code) && isRecord(code.exports) && workflowFn) {
2770
915
  const workflows = code.exports.workflows;
2771
- if (isRecord4(workflows)) {
916
+ if (isRecord(workflows)) {
2772
917
  const entry = workflows[workflowFn];
2773
- if (isRecord4(entry) && isRecord4(entry.interface)) return entry.interface;
918
+ if (isRecord(entry) && isRecord(entry.interface)) return entry.interface;
2774
919
  }
2775
920
  }
2776
- return isRecord4(definitionJson.interface) ? definitionJson.interface : void 0;
921
+ return isRecord(definitionJson.interface) ? definitionJson.interface : void 0;
2777
922
  }
2778
923
  function declaredSignals(iface) {
2779
924
  if (!iface) return { targets: [], openNames: false };
2780
- const signals = isRecord4(iface.signals) ? iface.signals : {};
925
+ const signals = isRecord(iface.signals) ? iface.signals : {};
2781
926
  const targets = Object.keys(signals).sort().map((name) => {
2782
927
  const entry = signals[name];
2783
- const payload = isRecord4(entry) && isRecord4(entry.payload) ? entry.payload : void 0;
928
+ const payload = isRecord(entry) && isRecord(entry.payload) ? entry.payload : void 0;
2784
929
  return payload ? { name, payloadSchema: payload } : { name };
2785
930
  });
2786
- const open = isRecord4(iface.open) && iface.open.signals === true;
931
+ const open = isRecord(iface.open) && iface.open.signals === true;
2787
932
  return { targets, openNames: open };
2788
933
  }
2789
934
  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;
935
+ if (!isRecord(node)) return void 0;
936
+ const root = isRecord(node.args) ? node.args : void 0;
937
+ if (root && isRecord(root.state)) return root.state;
938
+ const data = isRecord(node.data) ? node.data : void 0;
939
+ const mirror = data && isRecord(data.args) ? data.args : void 0;
940
+ return mirror && isRecord(mirror.state) ? mirror.state : void 0;
2796
941
  }
2797
942
  function definitionNodes(definitionJson) {
2798
- if (!isRecord4(definitionJson) || !isRecord4(definitionJson.data)) return [];
943
+ if (!isRecord(definitionJson) || !isRecord(definitionJson.data)) return [];
2799
944
  const nodes = definitionJson.data.nodes;
2800
945
  return Array.isArray(nodes) ? nodes : [];
2801
946
  }
@@ -2804,16 +949,16 @@ function parkedSignalTargets(nodeStates, definitionJson) {
2804
949
  const result = [];
2805
950
  const seen = /* @__PURE__ */ new Set();
2806
951
  for (const state of nodeStates) {
2807
- if (state.status !== "WAITING" || !isRecord4(state.output)) continue;
952
+ if (state.status !== "WAITING" || !isRecord(state.output)) continue;
2808
953
  if (state.output._waiting !== "signal") continue;
2809
- const name = str2(state.output._signalName);
954
+ const name = str(state.output._signalName);
2810
955
  if (!name || seen.has(`${state.nodeId}:${name}`)) continue;
2811
956
  seen.add(`${state.nodeId}:${name}`);
2812
957
  const node = nodes.find(
2813
- (candidate) => isRecord4(candidate) && candidate.id === state.nodeId
958
+ (candidate) => isRecord(candidate) && candidate.id === state.nodeId
2814
959
  );
2815
960
  const args = nodeStateArgs(node);
2816
- const schema = args && isRecord4(args.payloadSchema) ? args.payloadSchema : void 0;
961
+ const schema = args && isRecord(args.payloadSchema) ? args.payloadSchema : void 0;
2817
962
  result.push(
2818
963
  schema ? { name, payloadSchema: schema, nodeId: state.nodeId } : { name, nodeId: state.nodeId }
2819
964
  );
@@ -2836,9 +981,9 @@ function canSignalRun(targets) {
2836
981
  return targets.targets.length > 0 || targets.openNames;
2837
982
  }
2838
983
  function fieldOf(name, schema, required) {
2839
- if (!isRecord4(schema)) return null;
2840
- const title = str2(schema.title) ?? name;
2841
- const description = str2(schema.description);
984
+ if (!isRecord(schema)) return null;
985
+ const title = str(schema.title) ?? name;
986
+ const description = str(schema.description);
2842
987
  if (Array.isArray(schema.enum)) {
2843
988
  const choices = schema.enum.filter(
2844
989
  (value) => typeof value === "string"
@@ -2853,7 +998,7 @@ function fieldOf(name, schema, required) {
2853
998
  return null;
2854
999
  }
2855
1000
  function formFieldsOf(schema) {
2856
- if (!schema || schema.type !== "object" || !isRecord4(schema.properties))
1001
+ if (!schema || schema.type !== "object" || !isRecord(schema.properties))
2857
1002
  return null;
2858
1003
  const required = new Set(
2859
1004
  Array.isArray(schema.required) ? schema.required.filter(
@@ -3134,20 +1279,20 @@ function listCustomBlocks() {
3134
1279
  // src/builder/components/lib/expression-preview.ts
3135
1280
  var PURE_EXPRESSION_REGEX = /^\{\{(.*)\}\}$/s;
3136
1281
  var PURE_LEGACY_REGEX = /^\$\{(.*)\}$/s;
3137
- function containsExpressionSyntax(str3) {
3138
- return typeof str3 === "string" && str3.includes("{{");
1282
+ function containsExpressionSyntax(str2) {
1283
+ return typeof str2 === "string" && str2.includes("{{");
3139
1284
  }
3140
- function findEmbeddedExpressions(str3) {
1285
+ function findEmbeddedExpressions(str2) {
3141
1286
  const matches2 = [];
3142
1287
  let searchFrom = 0;
3143
- while (searchFrom < str3.length) {
3144
- const start = str3.indexOf("{{", searchFrom);
1288
+ while (searchFrom < str2.length) {
1289
+ const start = str2.indexOf("{{", searchFrom);
3145
1290
  if (start === -1) break;
3146
- const end = str3.indexOf("}}", start + 2);
1291
+ const end = str2.indexOf("}}", start + 2);
3147
1292
  if (end === -1) break;
3148
1293
  matches2.push({
3149
- full: str3.slice(start, end + 2),
3150
- expression: str3.slice(start + 2, end).trim()
1294
+ full: str2.slice(start, end + 2),
1295
+ expression: str2.slice(start + 2, end).trim()
3151
1296
  });
3152
1297
  searchFrom = end + 2;
3153
1298
  }
@@ -3237,9 +1382,9 @@ function stringifyForInterpolation(value) {
3237
1382
  if (typeof value === "object") return JSON.stringify(value);
3238
1383
  return String(value);
3239
1384
  }
3240
- function previewNewSyntax(str3, scope) {
1385
+ function previewNewSyntax(str2, scope) {
3241
1386
  const sandbox = buildExpressionSandbox(scope);
3242
- const pure = PURE_EXPRESSION_REGEX.exec(str3);
1387
+ const pure = PURE_EXPRESSION_REGEX.exec(str2);
3243
1388
  try {
3244
1389
  if (pure) {
3245
1390
  const expression = pure[1]?.trim();
@@ -3248,8 +1393,8 @@ function previewNewSyntax(str3, scope) {
3248
1393
  }
3249
1394
  return { isTemplate: true, value: evaluateBare(expression, sandbox) };
3250
1395
  }
3251
- let result = str3;
3252
- for (const match of findEmbeddedExpressions(str3)) {
1396
+ let result = str2;
1397
+ for (const match of findEmbeddedExpressions(str2)) {
3253
1398
  if (!match.expression) continue;
3254
1399
  const value = evaluateBare(match.expression, sandbox);
3255
1400
  result = result.replace(match.full, stringifyForInterpolation(value));
@@ -3262,7 +1407,7 @@ function previewNewSyntax(str3, scope) {
3262
1407
  };
3263
1408
  }
3264
1409
  }
3265
- function previewLegacySyntax(str3, scope) {
1410
+ function previewLegacySyntax(str2, scope) {
3266
1411
  const sandbox = {
3267
1412
  ...SAFE_GLOBALS,
3268
1413
  output: scope.input,
@@ -3279,19 +1424,19 @@ function previewLegacySyntax(str3, scope) {
3279
1424
  version: scope.version ?? "1.0.0"
3280
1425
  }
3281
1426
  };
3282
- const pure = PURE_LEGACY_REGEX.exec(str3);
1427
+ const pure = PURE_LEGACY_REGEX.exec(str2);
3283
1428
  try {
3284
1429
  if (pure) {
3285
1430
  const expression = pure[1]?.trim();
3286
1431
  if (!expression) return { isTemplate: true, error: "Empty expression" };
3287
1432
  return { isTemplate: true, value: evaluateBare(expression, sandbox) };
3288
1433
  }
3289
- if (!str3.includes("${")) {
1434
+ if (!str2.includes("${")) {
3290
1435
  return { isTemplate: false };
3291
1436
  }
3292
- let result = str3;
1437
+ let result = str2;
3293
1438
  const templateRegex = /\$\{([^}]+)\}/g;
3294
- for (const match of str3.matchAll(templateRegex)) {
1439
+ for (const match of str2.matchAll(templateRegex)) {
3295
1440
  const expression = match[1]?.trim();
3296
1441
  if (!expression) continue;
3297
1442
  const value = evaluateBare(expression, sandbox);
@@ -3305,14 +1450,14 @@ function previewLegacySyntax(str3, scope) {
3305
1450
  };
3306
1451
  }
3307
1452
  }
3308
- function evaluateExpressionPreview(str3, scope) {
3309
- if (typeof str3 !== "string" || str3.trim() === "") {
1453
+ function evaluateExpressionPreview(str2, scope) {
1454
+ if (typeof str2 !== "string" || str2.trim() === "") {
3310
1455
  return { isTemplate: false };
3311
1456
  }
3312
- if (containsExpressionSyntax(str3)) {
3313
- return previewNewSyntax(str3, scope);
1457
+ if (containsExpressionSyntax(str2)) {
1458
+ return previewNewSyntax(str2, scope);
3314
1459
  }
3315
- return previewLegacySyntax(str3, scope);
1460
+ return previewLegacySyntax(str2, scope);
3316
1461
  }
3317
1462
  var EXPRESSION_BUILTINS = [
3318
1463
  { insert: "$json", label: "$json", description: "Current item's data" },
@@ -4361,7 +2506,7 @@ var AdaptiveCardRenderer = forwardRef(function AdaptiveCardRenderer2({ cardData,
4361
2506
  var components_default = AdaptiveCardRenderer;
4362
2507
 
4363
2508
  // src/modules/ui-blocks/parse.ts
4364
- function isRecord5(value) {
2509
+ function isRecord2(value) {
4365
2510
  return typeof value === "object" && value !== null && !Array.isArray(value);
4366
2511
  }
4367
2512
  function humanizeFieldName(name) {
@@ -4372,7 +2517,7 @@ function synthesizeCardFromInputFields(inputFields) {
4372
2517
  if (names.length === 0) return null;
4373
2518
  const body = [];
4374
2519
  for (const name of names) {
4375
- const field = isRecord5(inputFields[name]) ? inputFields[name] : {};
2520
+ const field = isRecord2(inputFields[name]) ? inputFields[name] : {};
4376
2521
  const description = typeof field.description === "string" ? field.description : void 0;
4377
2522
  const required = field.required === true;
4378
2523
  const secret = field.secret === true;
@@ -4403,9 +2548,9 @@ function synthesizeCardFromInputFields(inputFields) {
4403
2548
  }
4404
2549
  function secretFieldNames(inputFields) {
4405
2550
  const names = /* @__PURE__ */ new Set();
4406
- if (!isRecord5(inputFields)) return names;
2551
+ if (!isRecord2(inputFields)) return names;
4407
2552
  for (const [name, field] of Object.entries(inputFields)) {
4408
- if (isRecord5(field) && field.secret === true) names.add(name);
2553
+ if (isRecord2(field) && field.secret === true) names.add(name);
4409
2554
  }
4410
2555
  return names;
4411
2556
  }
@@ -4418,7 +2563,7 @@ function maskSecretInputs(card, secrets) {
4418
2563
  if (secrets.size === 0) return card;
4419
2564
  const visit = (value) => {
4420
2565
  if (Array.isArray(value)) return value.map(visit);
4421
- if (!isRecord5(value)) return value;
2566
+ if (!isRecord2(value)) return value;
4422
2567
  const copy = {};
4423
2568
  for (const [key, nested] of Object.entries(value))
4424
2569
  copy[key] = visit(nested);
@@ -4432,7 +2577,7 @@ function maskSecretInputs(card, secrets) {
4432
2577
  return visit(card);
4433
2578
  }
4434
2579
  function validateUiSpec(raw) {
4435
- if (!isRecord5(raw)) return "ui is not an object";
2580
+ if (!isRecord2(raw)) return "ui is not an object";
4436
2581
  if (raw.version !== 1)
4437
2582
  return `unsupported ui.version: ${String(raw.version)}`;
4438
2583
  if (!Array.isArray(raw.blocks) || raw.blocks.length === 0) {
@@ -4441,7 +2586,7 @@ function validateUiSpec(raw) {
4441
2586
  const seenIds = /* @__PURE__ */ new Set();
4442
2587
  const blocks = [];
4443
2588
  for (const [index, rawBlock] of raw.blocks.entries()) {
4444
- if (!isRecord5(rawBlock)) return `ui.blocks[${index}] is not an object`;
2589
+ if (!isRecord2(rawBlock)) return `ui.blocks[${index}] is not an object`;
4445
2590
  const id = rawBlock.id;
4446
2591
  if (typeof id !== "string" || id.length === 0) {
4447
2592
  return `ui.blocks[${index}] is missing a non-empty string id`;
@@ -4449,7 +2594,7 @@ function validateUiSpec(raw) {
4449
2594
  if (seenIds.has(id)) return `ui.blocks has duplicate id "${id}"`;
4450
2595
  seenIds.add(id);
4451
2596
  if (rawBlock.type === "adaptive_card") {
4452
- if (!isRecord5(rawBlock.card)) {
2597
+ if (!isRecord2(rawBlock.card)) {
4453
2598
  return `ui.blocks[${index}] ("${id}") has no card object`;
4454
2599
  }
4455
2600
  blocks.push({ id, type: "adaptive_card", card: rawBlock.card });
@@ -4461,7 +2606,7 @@ function validateUiSpec(raw) {
4461
2606
  id,
4462
2607
  type: "custom",
4463
2608
  component: rawBlock.component,
4464
- props: isRecord5(rawBlock.props) ? rawBlock.props : void 0
2609
+ props: isRecord2(rawBlock.props) ? rawBlock.props : void 0
4465
2610
  });
4466
2611
  } else {
4467
2612
  return `ui.blocks[${index}] ("${id}") has unknown type "${String(rawBlock.type)}"`;
@@ -4488,7 +2633,7 @@ function resolveUiSpec(operation) {
4488
2633
  `ui-blocks: invalid ui declaration on operation "${operation.name ?? "?"}" (${result}); falling back to adaptive_card`
4489
2634
  );
4490
2635
  }
4491
- if (isRecord5(operation.adaptive_card)) {
2636
+ if (isRecord2(operation.adaptive_card)) {
4492
2637
  return {
4493
2638
  spec: {
4494
2639
  version: 1,
@@ -4503,7 +2648,7 @@ function resolveUiSpec(operation) {
4503
2648
  synthesized: true
4504
2649
  };
4505
2650
  }
4506
- if (isRecord5(operation.input_fields)) {
2651
+ if (isRecord2(operation.input_fields)) {
4507
2652
  const card = synthesizeCardFromInputFields(operation.input_fields);
4508
2653
  if (card) {
4509
2654
  return {
@@ -4518,17 +2663,17 @@ function resolveUiSpec(operation) {
4518
2663
  return null;
4519
2664
  }
4520
2665
  function isSubmitAction(action) {
4521
- return isRecord5(action) && action.type === "Action.Submit";
2666
+ return isRecord2(action) && action.type === "Action.Submit";
4522
2667
  }
4523
2668
  function stripFromActions(actions) {
4524
2669
  return actions.filter((action) => !isSubmitAction(action)).map(
4525
- (action) => isRecord5(action) && isRecord5(action.card) ? { ...action, card: stripSubmitActions(action.card) } : action
2670
+ (action) => isRecord2(action) && isRecord2(action.card) ? { ...action, card: stripSubmitActions(action.card) } : action
4526
2671
  );
4527
2672
  }
4528
2673
  function stripFromElements(elements) {
4529
2674
  const result = [];
4530
2675
  for (const element of elements) {
4531
- if (!isRecord5(element)) {
2676
+ if (!isRecord2(element)) {
4532
2677
  result.push(element);
4533
2678
  continue;
4534
2679
  }
@@ -4543,7 +2688,7 @@ function stripFromElements(elements) {
4543
2688
  }
4544
2689
  if (Array.isArray(el.columns)) {
4545
2690
  el.columns = el.columns.map(
4546
- (column) => isRecord5(column) && Array.isArray(column.items) ? { ...column, items: stripFromElements(column.items) } : column
2691
+ (column) => isRecord2(column) && Array.isArray(column.items) ? { ...column, items: stripFromElements(column.items) } : column
4547
2692
  );
4548
2693
  }
4549
2694
  result.push(el);
@@ -5854,23 +3999,6 @@ function AdapterConnectionSelector({
5854
3999
  ] });
5855
4000
  }
5856
4001
 
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
4002
  // src/hooks/workflow-saved-events.ts
5875
4003
  var listeners = /* @__PURE__ */ new Map();
5876
4004
  function subscribeWorkflowSaved(workflowKey, listener) {
@@ -5887,11 +4015,11 @@ function notifyWorkflowSaved(workflowKey) {
5887
4015
  }
5888
4016
 
5889
4017
  // src/builder/lib/display-options.ts
5890
- function isRecord6(value) {
4018
+ function isRecord3(value) {
5891
4019
  return typeof value === "object" && value !== null && !Array.isArray(value);
5892
4020
  }
5893
4021
  function readRuleSet(value) {
5894
- if (!isRecord6(value)) return void 0;
4022
+ if (!isRecord3(value)) return void 0;
5895
4023
  const rules = {};
5896
4024
  for (const [key, allowed] of Object.entries(value)) {
5897
4025
  if (Array.isArray(allowed)) rules[key] = allowed;
@@ -5900,8 +4028,8 @@ function readRuleSet(value) {
5900
4028
  return Object.keys(rules).length > 0 ? rules : void 0;
5901
4029
  }
5902
4030
  function readDisplayOptions(spec) {
5903
- const raw = isRecord6(spec) ? spec.displayOptions : void 0;
5904
- if (!isRecord6(raw)) return void 0;
4031
+ const raw = isRecord3(spec) ? spec.displayOptions : void 0;
4032
+ if (!isRecord3(raw)) return void 0;
5905
4033
  const show = readRuleSet(raw.show);
5906
4034
  const hide = readRuleSet(raw.hide);
5907
4035
  if (!show && !hide) return void 0;
@@ -5914,7 +4042,7 @@ function matches(current, allowed) {
5914
4042
  );
5915
4043
  }
5916
4044
  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;
4045
+ const options = isRecord3(spec) ? readDisplayOptions(spec) ?? (isRecord3(spec.show) || isRecord3(spec.hide) ? readDisplayOptions({ displayOptions: spec }) : void 0) : void 0;
5918
4046
  if (!options) return true;
5919
4047
  const valueOf = (key) => values[key] === void 0 ? defaults[key] : values[key];
5920
4048
  if (options.show) {
@@ -5934,7 +4062,7 @@ function isParamVisible(spec, values, defaults = {}) {
5934
4062
  function defaultsOf(fields) {
5935
4063
  const defaults = {};
5936
4064
  for (const [name, spec] of Object.entries(fields)) {
5937
- if (isRecord6(spec) && spec.default !== void 0)
4065
+ if (isRecord3(spec) && spec.default !== void 0)
5938
4066
  defaults[name] = spec.default;
5939
4067
  }
5940
4068
  return defaults;
@@ -5945,17 +4073,17 @@ function dropHiddenParameters(state, fields, mappedFields = /* @__PURE__ */ new
5945
4073
  let next;
5946
4074
  const ensure = () => next ??= { ...state };
5947
4075
  for (const [name, spec] of Object.entries(fields)) {
5948
- if (!isRecord6(spec)) continue;
4076
+ if (!isRecord3(spec)) continue;
5949
4077
  if (!mappedFields.has(name) && Object.hasOwn(state, name) && !isParamVisible(spec, state, defaults)) {
5950
4078
  delete ensure()[name];
5951
4079
  continue;
5952
4080
  }
5953
- const rowFields = isRecord6(spec.fields) ? spec.fields : void 0;
4081
+ const rowFields = isRecord3(spec.fields) ? spec.fields : void 0;
5954
4082
  const rows = state[name];
5955
4083
  if (rowFields && Array.isArray(rows)) {
5956
4084
  let changed = false;
5957
4085
  const cleaned = rows.map((row) => {
5958
- if (!isRecord6(row)) return row;
4086
+ if (!isRecord3(row)) return row;
5959
4087
  const cleanedRow = dropHiddenParameters(row, rowFields);
5960
4088
  if (cleanedRow !== row) changed = true;
5961
4089
  return cleanedRow;
@@ -5967,7 +4095,7 @@ function dropHiddenParameters(state, fields, mappedFields = /* @__PURE__ */ new
5967
4095
  }
5968
4096
 
5969
4097
  // src/builder/lib/field-schema.ts
5970
- function isRecord7(value) {
4098
+ function isRecord4(value) {
5971
4099
  return typeof value === "object" && value !== null && !Array.isArray(value);
5972
4100
  }
5973
4101
  function isIdentifier(key) {
@@ -5986,11 +4114,11 @@ function pathToExpression(path) {
5986
4114
  function mergeFieldSpecs(common, variant) {
5987
4115
  const base = typeof common === "string" ? { type: common } : common;
5988
4116
  const branch = typeof variant === "string" ? { type: variant } : variant;
5989
- if (!isRecord7(base) || !isRecord7(branch)) return branch;
4117
+ if (!isRecord4(base) || !isRecord4(branch)) return branch;
5990
4118
  const merged = { ...base, ...branch };
5991
4119
  const commonProperties = base.properties;
5992
4120
  const branchProperties = branch.properties;
5993
- if (isRecord7(commonProperties) && isRecord7(branchProperties)) {
4121
+ if (isRecord4(commonProperties) && isRecord4(branchProperties)) {
5994
4122
  merged.properties = Object.fromEntries(
5995
4123
  Object.entries({ ...commonProperties, ...branchProperties }).map(
5996
4124
  ([key, value]) => [
@@ -6009,14 +4137,14 @@ function fieldFromSpec(key, path, spec) {
6009
4137
  if (typeof spec === "string") {
6010
4138
  return { key, path, type: spec };
6011
4139
  }
6012
- if (isRecord7(spec)) {
4140
+ if (isRecord4(spec)) {
6013
4141
  const declaredType2 = typeof spec.type === "string" ? spec.type : void 0;
6014
4142
  const description = typeof spec.description === "string" ? spec.description : void 0;
6015
4143
  const required = spec.required === true;
6016
4144
  const alternatives = ["oneOf", "anyOf"].flatMap((keyword) => {
6017
4145
  const variants = spec[keyword];
6018
4146
  return Array.isArray(variants) ? variants.flatMap(
6019
- (variant, index) => isRecord7(variant) || typeof variant === "string" ? [{ keyword, index, variant }] : []
4147
+ (variant, index) => isRecord4(variant) || typeof variant === "string" ? [{ keyword, index, variant }] : []
6020
4148
  ) : [];
6021
4149
  });
6022
4150
  if (alternatives.length > 0) {
@@ -6045,7 +4173,7 @@ function fieldFromSpec(key, path, spec) {
6045
4173
  type: declaredType2 ?? [...new Set(children.map((field) => field.type))].join(" | ")
6046
4174
  };
6047
4175
  }
6048
- if (isRecord7(spec.properties) && (!declaredType2 || declaredType2 === "object")) {
4176
+ if (isRecord4(spec.properties) && (!declaredType2 || declaredType2 === "object")) {
6049
4177
  const children = Object.entries(spec.properties).map(
6050
4178
  ([childKey, childSpec]) => fieldFromSpec(childKey, joinPath(path, childKey), childSpec)
6051
4179
  );
@@ -6067,7 +4195,7 @@ function fieldFromSpec(key, path, spec) {
6067
4195
  return { key, path, type: "any" };
6068
4196
  }
6069
4197
  function fieldsFromIO(io, root = "") {
6070
- if (!isRecord7(io)) return [];
4198
+ if (!isRecord4(io)) return [];
6071
4199
  return Object.entries(io).map(
6072
4200
  ([key, spec]) => fieldFromSpec(key, joinPath(root, key), spec)
6073
4201
  );
@@ -6086,7 +4214,7 @@ function fieldFromValue(key, path, value) {
6086
4214
  const children = value.length > 0 ? [fieldFromValue("0", `${path}[0]`, value[0])] : void 0;
6087
4215
  return { key, path, type: "array", children };
6088
4216
  }
6089
- if (isRecord7(value)) {
4217
+ if (isRecord4(value)) {
6090
4218
  const children = Object.entries(value).map(
6091
4219
  ([k, v]) => fieldFromValue(k, joinPath(path, k), v)
6092
4220
  );
@@ -6095,7 +4223,7 @@ function fieldFromValue(key, path, value) {
6095
4223
  return { key, path, type: typeOfValue(value) };
6096
4224
  }
6097
4225
  function fieldsFromValue(value, root = "") {
6098
- if (!isRecord7(value)) return [];
4226
+ if (!isRecord4(value)) return [];
6099
4227
  return Object.entries(value).map(
6100
4228
  ([key, v]) => fieldFromValue(key, joinPath(root, key), v)
6101
4229
  );
@@ -6111,13 +4239,13 @@ function declaredOutputFields(nodeType, op) {
6111
4239
  }
6112
4240
  function normalizeOperations(raw) {
6113
4241
  if (Array.isArray(raw)) return raw;
6114
- if (isRecord7(raw) && Array.isArray(raw.operations)) {
4242
+ if (isRecord4(raw) && Array.isArray(raw.operations)) {
6115
4243
  return raw.operations;
6116
4244
  }
6117
4245
  return [];
6118
4246
  }
6119
4247
  function resolveNodeOperation(data, fallbackOperations) {
6120
- const nodeData = isRecord7(data?.nodeData) ? data?.nodeData : void 0;
4248
+ const nodeData = isRecord4(data?.nodeData) ? data?.nodeData : void 0;
6121
4249
  let operations = normalizeOperations(nodeData?.operations);
6122
4250
  if (operations.length === 0) {
6123
4251
  operations = normalizeOperations(data?.operations);
@@ -6131,11 +4259,11 @@ function resolveNodeOperation(data, fallbackOperations) {
6131
4259
  }
6132
4260
 
6133
4261
  // src/builder/lib/parameter-values.ts
6134
- function isRecord8(value) {
4262
+ function isRecord5(value) {
6135
4263
  return typeof value === "object" && value !== null && !Array.isArray(value);
6136
4264
  }
6137
4265
  function record(value) {
6138
- return isRecord8(value) ? value : void 0;
4266
+ return isRecord5(value) ? value : void 0;
6139
4267
  }
6140
4268
  function declaredType(schema) {
6141
4269
  const type = typeof schema === "string" ? schema : record(schema)?.type;
@@ -8310,7 +6438,7 @@ function useNodeExecution() {
8310
6438
  } catch (error) {
8311
6439
  console.error("[useNodeExecution] Error executing step:", error);
8312
6440
  const errorMessage = describeOperationError(error);
8313
- const errorRunId = nanoid();
6441
+ const errorRunId = nanoid$1();
8314
6442
  const errorLog = {
8315
6443
  id: `${errorRunId}-${nodeId}`,
8316
6444
  runId: errorRunId,
@@ -8423,7 +6551,7 @@ function useNodeExecution() {
8423
6551
  surface: "builder-run"
8424
6552
  });
8425
6553
  const errorMessage = describeOperationError(error);
8426
- const errorRunId = nanoid();
6554
+ const errorRunId = nanoid$1();
8427
6555
  const errorLog = {
8428
6556
  id: `${errorRunId}-${triggerNode.id}`,
8429
6557
  runId: errorRunId,
@@ -9076,45 +7204,6 @@ function useInsertNode() {
9076
7204
  [screenToFlowPosition, getNodes, addNodes, updateNode]
9077
7205
  );
9078
7206
  }
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
7207
  function isAutoManagedConnectionAdapter(nodeType, adapter) {
9119
7208
  const isBigConsoleDataSinkAutoAdapter = nodeType === "store/bigconsole_datasink" && adapter.name === "bigconsole" && adapter.provider === "bigconsole";
9120
7209
  return isBigConsoleDataSinkAutoAdapter || adapter.mode === "auto" || adapter.autoProvision === true || adapter.autoManaged === true || adapter.hidden === true;
@@ -10109,7 +8198,7 @@ function getNodeDetail(nodeType) {
10109
8198
  }
10110
8199
  function createNodeData(type, data) {
10111
8200
  return {
10112
- id: nanoid(),
8201
+ id: nanoid$1(),
10113
8202
  type,
10114
8203
  data
10115
8204
  };
@@ -10119,4 +8208,4 @@ function createNodeWithDefaultData(type, data) {
10119
8208
  return Object.assign(createNodeData(type, defaultData), data);
10120
8209
  }
10121
8210
 
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 };
8211
+ 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, formatParameterValue, getNodeDetail, getNodeMetadata, inAppLeaveTarget, isDurableFunctionNodeType, isFlowComplete, isLocked, isNodeConfigsInitialized, isNodeTypeHiddenFromPalette, isParamVisible, isQuotaExhaustedFailure, isRunControlNodeType, leaseCallQueue, leavingLosesChanges, listCustomBlocks, liveInternalOperations, loadDefinitionJson, nodesOf, onNodeTypesUpdate, openRunQuotaUpgradePrompt, parseParameterValue, parseRawSignalPayload, pathToExpression, payloadFromCardValues, phaseAfterAcquire, phaseAfterTakeOver, readDisplayOptions, refetchStoreNodes, registerCustomBlock, registerWorkflowNodeTypes, releasesOnClose, rememberOwnStatusChange, rememberRevision, rememberSessionForReload, resolveNodeOperation, resolvePhaseCFlags, resolveUiSpec, 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 };