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
- // Import necessary functions from d3-dag
2
- import { graphStratify, sugiyama, layeringLongestPath, decrossTwoLayer, coordCenter } from 'd3-dag';
3
- export const xSpacing = 400; // used for horizontal spacing between nodes
4
- export const ySpacing = 120; // used for vertical spacing between layers
5
- export const ROOT_X = 400; // x-offset for the entire layout
6
- export const ROOT_Y = 120; // y-offset for the entire layout
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
- * d3-dag based layout for the top-down pass.
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
- const nodeMap = new Map();
15
- // 1) Transform workflow into data for stratification
16
- const data = workflow.nodes.map((node) => {
17
- const id = node.getRef();
18
- nodeMap.set(id, node);
19
- const parents = workflow.edges
20
- .filter(edge => edge.target === node)
21
- .map(edge => edge.source.getRef());
22
- return { id, parentIds: parents };
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
- // 2) Build the DAG using graphStratify
25
- // Cast to 'any' so we can access .each(...) etc. at runtime
26
- const dag = graphStratify()(data);
27
- // 3) Create and configure the Sugiyama layout
28
- // Note the calls to the operator factories: layeringLongestPath(), decrossTwoLayer(), coordCenter()
29
- const layout = sugiyama()
30
- .layering(layeringLongestPath())
31
- .decross(decrossTwoLayer())
32
- .coord(coordCenter())
33
- // Instead of .size, we use .nodeSize for spacing between nodes
34
- .nodeSize([xSpacing, ySpacing]);
35
- // 4) Run layout
36
- layout(dag);
37
- // 5) Update workflow node positions
38
- for (const node of dag.nodes()) {
39
- const id = node.data.id;
40
- const wfNode = nodeMap.get(id);
41
- if (wfNode && typeof node.x === 'number' && typeof node.y === 'number') {
42
- wfNode.setPosition(node.x + ROOT_X, node.y + ROOT_Y);
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
- * Helper functions (unchanged).
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.filter(edge => edge.source === node).map(edge => edge.target);
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.filter(edge => edge.target === node).map(edge => edge.source);
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 * 2,
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 = 400;
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
- * d3-dag based layout for the top-down pass.
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
- * Helper functions (unchanged).
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.64",
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"