otomato-sdk 2.0.64 → 2.0.65
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.
|
@@ -1,60 +1,158 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export const
|
|
4
|
-
export const
|
|
5
|
-
export
|
|
6
|
-
|
|
1
|
+
export const xSpacing = 500;
|
|
2
|
+
export const ySpacing = 120;
|
|
3
|
+
export const ROOT_X = 400;
|
|
4
|
+
export const ROOT_Y = 120;
|
|
5
|
+
export function positionWorkflowNodesAvoidOverlap(workflow) {
|
|
6
|
+
// 1) Lay out nodes using existing logic
|
|
7
|
+
positionWorkflowNodes(workflow);
|
|
8
|
+
// 2) Group nodes by 'level' based on vertical position
|
|
9
|
+
const levels = new Map();
|
|
10
|
+
workflow.nodes.forEach((node) => {
|
|
11
|
+
if (node.position) {
|
|
12
|
+
const level = Math.round(node.position.y / ySpacing);
|
|
13
|
+
if (!levels.has(level)) {
|
|
14
|
+
levels.set(level, []);
|
|
15
|
+
}
|
|
16
|
+
levels.get(level).push(node);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
// 3) Resolve horizontal overlaps among *all* nodes in each level
|
|
20
|
+
levels.forEach((levelNodes) => {
|
|
21
|
+
// Sort the nodes in this level by X
|
|
22
|
+
levelNodes.sort((a, b) => { var _a, _b; return ((_a = a.position.x) !== null && _a !== void 0 ? _a : 0) - ((_b = b.position.x) !== null && _b !== void 0 ? _b : 0); });
|
|
23
|
+
// Walk left-to-right, shifting nodes that would collide with the previous one
|
|
24
|
+
for (let i = 1; i < levelNodes.length; i++) {
|
|
25
|
+
const prev = levelNodes[i - 1];
|
|
26
|
+
const current = levelNodes[i];
|
|
27
|
+
// Compute how close they are
|
|
28
|
+
const dx = current.position.x - prev.position.x;
|
|
29
|
+
// If they are too close, shift the current node (and its children) to the right
|
|
30
|
+
if (dx < xSpacing) {
|
|
31
|
+
const shift = xSpacing - dx;
|
|
32
|
+
moveNodeAndChildren(current, shift, workflow.edges);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// 4) Re-center parents above their children in a bottom-up pass
|
|
37
|
+
centerParentXPositions(workflow);
|
|
38
|
+
}
|
|
7
39
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* This function transforms the workflow into a DAG format for d3-dag,
|
|
11
|
-
* runs the Sugiyama layout algorithm, and updates the positions of workflow nodes.
|
|
40
|
+
* Lays out the workflow nodes in a rough top-down manner,
|
|
41
|
+
* then positions each node relative to its parent.
|
|
12
42
|
*/
|
|
13
43
|
export function positionWorkflowNodes(workflow) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
.
|
|
21
|
-
|
|
22
|
-
|
|
44
|
+
try {
|
|
45
|
+
// Step 1: Find the starting nodes
|
|
46
|
+
const startingNodes = identityStartingNodes(workflow);
|
|
47
|
+
// Step 2: Place the starting nodes
|
|
48
|
+
let xPosition = ROOT_X;
|
|
49
|
+
startingNodes.forEach((startNode) => {
|
|
50
|
+
startNode.setPosition(xPosition, ROOT_Y);
|
|
51
|
+
xPosition += xSpacing;
|
|
52
|
+
});
|
|
53
|
+
// Step 3: For all other nodes, position them based on their parent(s)
|
|
54
|
+
const nodesToPosition = workflow.nodes.filter((node) => !startingNodes.includes(node));
|
|
55
|
+
nodesToPosition.forEach((node) => positionNode(node, workflow.edges, xSpacing, ySpacing, workflow));
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
console.error(e);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function positionNode(node, edges, xSpacing, ySpacing, workflow) {
|
|
62
|
+
const parents = getParents(node, edges);
|
|
63
|
+
if (!parents.length)
|
|
64
|
+
return; // Edge case: no parents?
|
|
65
|
+
// Sort children of the first parent by edge labels (true/false) so "true" is left, "false" is right
|
|
66
|
+
const children = getChildren(parents[0], edges).sort((a, b) => {
|
|
67
|
+
var _a, _b;
|
|
68
|
+
const edgeA = edges.find((edge) => edge.source === parents[0] && edge.target === a);
|
|
69
|
+
const edgeB = edges.find((edge) => edge.source === parents[0] && edge.target === b);
|
|
70
|
+
const labelA = (_a = edgeA === null || edgeA === void 0 ? void 0 : edgeA.label) !== null && _a !== void 0 ? _a : "";
|
|
71
|
+
const labelB = (_b = edgeB === null || edgeB === void 0 ? void 0 : edgeB.label) !== null && _b !== void 0 ? _b : "";
|
|
72
|
+
if (labelA === "true" && labelB !== "true")
|
|
73
|
+
return -1;
|
|
74
|
+
if (labelB === "true" && labelA !== "true")
|
|
75
|
+
return 1;
|
|
76
|
+
if (labelA === "false" && labelB !== "false")
|
|
77
|
+
return 1;
|
|
78
|
+
if (labelB === "false" && labelA !== "false")
|
|
79
|
+
return -1;
|
|
80
|
+
return 0;
|
|
23
81
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
.
|
|
33
|
-
|
|
34
|
-
.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
82
|
+
const parentX = parents.reduce((sum, p) => { var _a, _b; return sum + ((_b = (_a = p.position) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : ROOT_X); }, 0) /
|
|
83
|
+
parents.length;
|
|
84
|
+
const parentY = Math.max(...parents.map((p) => { var _a, _b; return (_b = (_a = p.position) === null || _a === void 0 ? void 0 : _a.y) !== null && _b !== void 0 ? _b : ROOT_Y; }));
|
|
85
|
+
if (children.length <= 1) {
|
|
86
|
+
node.setPosition(parentX, parentY + ySpacing);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// This node’s index among siblings
|
|
90
|
+
const index = children.indexOf(node);
|
|
91
|
+
const offset = index - (children.length - 1) / 2;
|
|
92
|
+
node.setPosition(parentX + offset * xSpacing, parentY + ySpacing);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Repositions each parent node so that its X = average of its children’s X.
|
|
97
|
+
*/
|
|
98
|
+
function centerParentXPositions(workflow) {
|
|
99
|
+
var _a, _b;
|
|
100
|
+
// Identify the leaf nodes to start a bottom-up pass
|
|
101
|
+
const queue = identifyLeafNodes(workflow);
|
|
102
|
+
while (queue.length > 0) {
|
|
103
|
+
const child = queue.shift();
|
|
104
|
+
const parents = getParents(child, workflow.edges);
|
|
105
|
+
for (const parent of parents) {
|
|
106
|
+
const children = getChildren(parent, workflow.edges);
|
|
107
|
+
if (children.length) {
|
|
108
|
+
const sumX = children.reduce((acc, c) => { var _a, _b; return acc + ((_b = (_a = c.position) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 0); }, 0);
|
|
109
|
+
const avgX = sumX / children.length;
|
|
110
|
+
parent.setPosition(avgX, (_b = (_a = parent.position) === null || _a === void 0 ? void 0 : _a.y) !== null && _b !== void 0 ? _b : 0);
|
|
111
|
+
}
|
|
112
|
+
// Push this parent upward in the queue to continue up the chain
|
|
113
|
+
if (!queue.includes(parent)) {
|
|
114
|
+
queue.push(parent);
|
|
115
|
+
}
|
|
43
116
|
}
|
|
44
117
|
}
|
|
45
|
-
;
|
|
46
118
|
}
|
|
47
119
|
/**
|
|
48
|
-
*
|
|
120
|
+
* Recursively shifts a node and all its descendants by `shift` in the x-direction.
|
|
121
|
+
*/
|
|
122
|
+
function moveNodeAndChildren(node, shift, edges) {
|
|
123
|
+
node.setPosition(node.position.x + shift, node.position.y);
|
|
124
|
+
edges
|
|
125
|
+
.filter((edge) => edge.source === node)
|
|
126
|
+
.forEach((edge) => {
|
|
127
|
+
moveNodeAndChildren(edge.target, shift, edges);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A "leaf" node is one that has no children (it’s never the source of an edge).
|
|
49
132
|
*/
|
|
133
|
+
export function identifyLeafNodes(workflow) {
|
|
134
|
+
const sources = new Set(workflow.edges.map((edge) => edge.source.getRef()));
|
|
135
|
+
return workflow.nodes.filter((node) => !sources.has(node.getRef()));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Identify starting nodes (no incoming edges).
|
|
139
|
+
*/
|
|
140
|
+
export function identityStartingNodes(workflow) {
|
|
141
|
+
const childRefs = new Set(workflow.edges.map((edge) => edge.target.getRef()));
|
|
142
|
+
return workflow.nodes.filter((node) => !childRefs.has(node.getRef()));
|
|
143
|
+
}
|
|
50
144
|
export function getChildren(node, edges) {
|
|
51
|
-
return edges
|
|
145
|
+
return edges
|
|
146
|
+
.filter((edge) => edge.source === node)
|
|
147
|
+
.map((edge) => edge.target);
|
|
52
148
|
}
|
|
53
149
|
export function getParents(node, edges) {
|
|
54
|
-
return edges
|
|
150
|
+
return edges
|
|
151
|
+
.filter((edge) => edge.target === node)
|
|
152
|
+
.map((edge) => edge.source);
|
|
55
153
|
}
|
|
56
154
|
export function getEdges(node, edges) {
|
|
57
|
-
return edges.filter(edge => edge.source === node || edge.target === node);
|
|
155
|
+
return edges.filter((edge) => edge.source === node || edge.target === node);
|
|
58
156
|
}
|
|
59
157
|
export function getEndNodePositions(workflow) {
|
|
60
158
|
return workflow.nodes
|
|
@@ -63,15 +161,7 @@ export function getEndNodePositions(workflow) {
|
|
|
63
161
|
var _a, _b, _c, _d;
|
|
64
162
|
return ({
|
|
65
163
|
x: (_b = (_a = node.position) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 0,
|
|
66
|
-
y: ((_d = (_c = node.position) === null || _c === void 0 ? void 0 : _c.y) !== null && _d !== void 0 ? _d : 0) + ySpacing
|
|
164
|
+
y: ((_d = (_c = node.position) === null || _c === void 0 ? void 0 : _c.y) !== null && _d !== void 0 ? _d : 0) + ySpacing,
|
|
67
165
|
});
|
|
68
166
|
});
|
|
69
167
|
}
|
|
70
|
-
export function identifyLeafNodes(workflow) {
|
|
71
|
-
const sources = new Set(workflow.edges.map((edge) => edge.source.getRef()));
|
|
72
|
-
return workflow.nodes.filter((node) => !sources.has(node.getRef()));
|
|
73
|
-
}
|
|
74
|
-
export function identityStartingNodes(workflow) {
|
|
75
|
-
const childRefs = new Set(workflow.edges.map((edge) => edge.target.getRef()));
|
|
76
|
-
return workflow.nodes.filter((node) => !childRefs.has(node.getRef()));
|
|
77
|
-
}
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import { Workflow } from '../models/Workflow.js';
|
|
2
2
|
import { Node } from '../models/Node.js';
|
|
3
3
|
import { Edge } from '../models/Edge.js';
|
|
4
|
-
export declare const xSpacing =
|
|
4
|
+
export declare const xSpacing = 500;
|
|
5
5
|
export declare const ySpacing = 120;
|
|
6
6
|
export declare const ROOT_X = 400;
|
|
7
7
|
export declare const ROOT_Y = 120;
|
|
8
|
+
export declare function positionWorkflowNodesAvoidOverlap(workflow: Workflow): void;
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* This function transforms the workflow into a DAG format for d3-dag,
|
|
12
|
-
* runs the Sugiyama layout algorithm, and updates the positions of workflow nodes.
|
|
10
|
+
* Lays out the workflow nodes in a rough top-down manner,
|
|
11
|
+
* then positions each node relative to its parent.
|
|
13
12
|
*/
|
|
14
13
|
export declare function positionWorkflowNodes(workflow: Workflow): void;
|
|
14
|
+
export declare function positionNode(node: Node, edges: Edge[], xSpacing: number, ySpacing: number, workflow: Workflow): void;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* A "leaf" node is one that has no children (it’s never the source of an edge).
|
|
17
17
|
*/
|
|
18
|
+
export declare function identifyLeafNodes(workflow: Workflow): Node[];
|
|
19
|
+
/**
|
|
20
|
+
* Identify starting nodes (no incoming edges).
|
|
21
|
+
*/
|
|
22
|
+
export declare function identityStartingNodes(workflow: Workflow): Node[];
|
|
18
23
|
export declare function getChildren(node: Node, edges: Edge[]): Node[];
|
|
19
24
|
export declare function getParents(node: Node, edges: Edge[]): Node[];
|
|
20
25
|
export declare function getEdges(node: Node, edges: Edge[]): Edge[];
|
|
@@ -22,5 +27,3 @@ export declare function getEndNodePositions(workflow: Workflow): {
|
|
|
22
27
|
x: number;
|
|
23
28
|
y: number;
|
|
24
29
|
}[];
|
|
25
|
-
export declare function identifyLeafNodes(workflow: Workflow): Node[];
|
|
26
|
-
export declare function identityStartingNodes(workflow: Workflow): Node[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "otomato-sdk",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.65",
|
|
4
4
|
"description": "An SDK for building and managing automations on Otomato",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"axios": "^1.7.2",
|
|
47
|
-
"d3-dag": "^1.1.0",
|
|
48
47
|
"ethers": "^6.13.1",
|
|
49
48
|
"jsonwebtoken": "^9.0.2",
|
|
50
49
|
"mustache": "^4.2.0"
|