@flowuent-org/diagramming-core 1.4.1 → 1.4.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.
- package/apps/diagramming/src/AutomationDiagramData.ts +110 -4
- package/package.json +1 -1
- package/packages/diagrams/src/lib/components/automation/AutomationApiNode.tsx +3 -13
- package/packages/diagrams/src/lib/components/automation/AutomationDecisionNode.tsx +0 -1
- package/packages/diagrams/src/lib/components/automation/AutomationEndNode.tsx +3 -13
- package/packages/diagrams/src/lib/components/automation/AutomationFormattingNode.tsx +3 -13
- package/packages/diagrams/src/lib/components/automation/AutomationInteractionNode.tsx +3 -13
- package/packages/diagrams/src/lib/components/automation/AutomationLoopNode.tsx +105 -0
- package/packages/diagrams/src/lib/components/automation/AutomationNavigationNode.tsx +3 -13
- package/packages/diagrams/src/lib/components/automation/AutomationNodeCard.tsx +32 -11
- package/packages/diagrams/src/lib/components/automation/AutomationNodeHeader.tsx +45 -0
- package/packages/diagrams/src/lib/components/automation/AutomationSlackNode.tsx +3 -13
- package/packages/diagrams/src/lib/components/automation/AutomationSmsNode.tsx +0 -1
- package/packages/diagrams/src/lib/components/automation/AutomationStartNode.tsx +3 -13
- package/packages/diagrams/src/lib/components/automation/AutomationTelegramNode.tsx +3 -13
- package/packages/diagrams/src/lib/components/automation/AutomationVoiceCallNode.tsx +0 -1
- package/packages/diagrams/src/lib/components/automation/AutomationWorkflowNode.tsx +0 -1
- package/packages/diagrams/src/lib/components/automation/index.ts +11 -0
- package/packages/diagrams/src/lib/icons/icons.tsx +10 -0
- package/packages/diagrams/src/lib/icons/index.ts +1 -0
- package/packages/diagrams/src/lib/types/automation-node-data-types.ts +14 -1
- package/packages/diagrams/src/lib/types/node-types.ts +2 -0
- package/packages/diagrams/src/lib/utils/AutomationExecutionEngine.ts +268 -41
- package/packages/diagrams/src/lib/utils/automation-flow-processor.ts +70 -0
- package/packages/diagrams/src/lib/utils/validateAutomationLoopNode.ts +104 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Edge, Node } from '@xyflow/react';
|
|
2
|
+
import { AutomationLoopNodeForm } from '../types/automation-node-data-types';
|
|
3
|
+
|
|
4
|
+
export interface LoopNodeValidationResult {
|
|
5
|
+
valid: boolean;
|
|
6
|
+
errors: string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function getFormData(node: Node): Partial<AutomationLoopNodeForm> {
|
|
10
|
+
return (node.data?.formData ?? node.data ?? {}) as Partial<AutomationLoopNodeForm>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getOutgoingEdges(nodeId: string, edges: Edge[], sourceHandle?: string): Edge[] {
|
|
14
|
+
return edges.filter(
|
|
15
|
+
(edge) =>
|
|
16
|
+
edge.source === nodeId &&
|
|
17
|
+
(sourceHandle === undefined || edge.sourceHandle === sourceHandle),
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getReachableBodyNodeIds(
|
|
22
|
+
startNodeId: string,
|
|
23
|
+
loopNodeId: string,
|
|
24
|
+
edges: Edge[],
|
|
25
|
+
): Set<string> {
|
|
26
|
+
const reachable = new Set<string>();
|
|
27
|
+
const queue = [startNodeId];
|
|
28
|
+
|
|
29
|
+
while (queue.length > 0) {
|
|
30
|
+
const currentId = queue.shift()!;
|
|
31
|
+
if (currentId === loopNodeId || reachable.has(currentId)) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
reachable.add(currentId);
|
|
35
|
+
|
|
36
|
+
const outgoing = edges.filter((edge) => edge.source === currentId);
|
|
37
|
+
for (const edge of outgoing) {
|
|
38
|
+
if (edge.target !== loopNodeId && !reachable.has(edge.target)) {
|
|
39
|
+
queue.push(edge.target);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return reachable;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function validateAutomationLoopNode(
|
|
48
|
+
node: Node,
|
|
49
|
+
edges: Edge[],
|
|
50
|
+
): LoopNodeValidationResult {
|
|
51
|
+
const errors: string[] = [];
|
|
52
|
+
const formData = getFormData(node);
|
|
53
|
+
const nodeId = node.id;
|
|
54
|
+
|
|
55
|
+
if (!formData.arraySource?.type) {
|
|
56
|
+
errors.push('Array data source is not configured');
|
|
57
|
+
} else if (
|
|
58
|
+
formData.arraySource.type === 'variable' &&
|
|
59
|
+
!formData.arraySource.variableName?.trim()
|
|
60
|
+
) {
|
|
61
|
+
errors.push('Variable name is required when array source type is "variable"');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const loopEdges = getOutgoingEdges(nodeId, edges, 'loop');
|
|
65
|
+
const doneEdges = getOutgoingEdges(nodeId, edges, 'done');
|
|
66
|
+
|
|
67
|
+
if (loopEdges.length === 0) {
|
|
68
|
+
errors.push('Loop node must have exactly one outgoing edge on the "loop" handle');
|
|
69
|
+
} else if (loopEdges.length > 1) {
|
|
70
|
+
errors.push('Loop node must have only one outgoing edge on the "loop" handle');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (doneEdges.length === 0) {
|
|
74
|
+
errors.push('Loop node must have exactly one outgoing edge on the "done" handle');
|
|
75
|
+
} else if (doneEdges.length > 1) {
|
|
76
|
+
errors.push('Loop node must have only one outgoing edge on the "done" handle');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (loopEdges.length === 1) {
|
|
80
|
+
const bodyStartId = loopEdges[0].target;
|
|
81
|
+
const bodyNodeIds = getReachableBodyNodeIds(bodyStartId, nodeId, edges);
|
|
82
|
+
const hasBackEdge = edges.some(
|
|
83
|
+
(edge) => edge.target === nodeId && bodyNodeIds.has(edge.source),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (!hasBackEdge) {
|
|
87
|
+
errors.push(
|
|
88
|
+
'Loop body must connect back to the Loop node to advance iterations',
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
valid: errors.length === 0,
|
|
95
|
+
errors,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function assertAutomationLoopNodeValid(node: Node, edges: Edge[]): void {
|
|
100
|
+
const result = validateAutomationLoopNode(node, edges);
|
|
101
|
+
if (!result.valid) {
|
|
102
|
+
throw new Error(result.errors.join('; '));
|
|
103
|
+
}
|
|
104
|
+
}
|