otomato-sdk 2.0.24 → 2.0.26
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/dist/src/constants/version.js +1 -1
- package/dist/src/models/Workflow.js +5 -2
- package/dist/src/services/ApiService.js +6 -0
- package/dist/src/utils/WorkflowNodePositioner.js +50 -103
- package/dist/types/src/constants/version.d.ts +1 -1
- package/dist/types/src/models/Workflow.d.ts +4 -0
- package/dist/types/src/services/ApiService.d.ts +1 -0
- package/dist/types/src/utils/WorkflowNodePositioner.d.ts +4 -8
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@ import { Node } from './Node.js';
|
|
|
11
11
|
import { Edge } from './Edge.js';
|
|
12
12
|
import { apiServices } from '../services/ApiService.js';
|
|
13
13
|
import { Note } from './Note.js';
|
|
14
|
-
import { positionWorkflowNodesAvoidOverlap } from '../utils/WorkflowNodePositioner.js';
|
|
14
|
+
import { getEndNodePositions, positionWorkflowNodesAvoidOverlap } from '../utils/WorkflowNodePositioner.js';
|
|
15
15
|
export class Workflow {
|
|
16
16
|
constructor(name = '', nodes = [], edges = []) {
|
|
17
17
|
this.id = null;
|
|
@@ -257,7 +257,7 @@ export class Workflow {
|
|
|
257
257
|
return __awaiter(this, void 0, void 0, function* () {
|
|
258
258
|
var _a;
|
|
259
259
|
try {
|
|
260
|
-
const response = yield apiServices.
|
|
260
|
+
const response = yield apiServices.put(`/workflows/${this.id}`, this.toJSON());
|
|
261
261
|
if (response.status === 200) {
|
|
262
262
|
this.dateModified = response.data.dateModified;
|
|
263
263
|
// Assign IDs to the nodes based on the response
|
|
@@ -369,4 +369,7 @@ export class Workflow {
|
|
|
369
369
|
return apiServices.getSessionKeyPermissions(this.id);
|
|
370
370
|
});
|
|
371
371
|
}
|
|
372
|
+
getEndNodePositions() {
|
|
373
|
+
return getEndNodePositions(this);
|
|
374
|
+
}
|
|
372
375
|
}
|
|
@@ -40,6 +40,12 @@ class ApiServices {
|
|
|
40
40
|
return yield axiosInstance.patch(url, data, { headers });
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
|
+
put(url, data) {
|
|
44
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
const headers = this.auth ? { 'Authorization': this.auth } : {};
|
|
46
|
+
return yield axiosInstance.put(url, data, { headers });
|
|
47
|
+
});
|
|
48
|
+
}
|
|
43
49
|
get(url) {
|
|
44
50
|
return __awaiter(this, void 0, void 0, function* () {
|
|
45
51
|
const headers = this.auth ? { 'Authorization': this.auth } : {};
|
|
@@ -1,96 +1,4 @@
|
|
|
1
1
|
// workflowNodePositioner.ts
|
|
2
|
-
/**
|
|
3
|
-
* Positions nodes in a BFS manner, assuming exactly one root node.
|
|
4
|
-
* - The root is placed at (400, 120) unless it already has a position.
|
|
5
|
-
* - If a parent has exactly 1 child, that child is placed directly below the parent
|
|
6
|
-
* (same x, with an offset in y).
|
|
7
|
-
* - If a parent has multiple children, they are horizontally spread around the parent's X.
|
|
8
|
-
* - Existing positions are not overwritten.
|
|
9
|
-
*/
|
|
10
|
-
/*export function autoPositionNodes(workflow: Workflow): void {
|
|
11
|
-
// 1. Build adjacency & incoming edge count
|
|
12
|
-
const adjacency = new Map<string, Node[]>();
|
|
13
|
-
const incomingCount = new Map<string, number>();
|
|
14
|
-
|
|
15
|
-
// Constants you can tweak or increase if you see overlap or crossing edges
|
|
16
|
-
const ROOT_X = 400;
|
|
17
|
-
const ROOT_Y = 120;
|
|
18
|
-
const MIN_SPACING_X = 500; // Horizontal spacing between siblings
|
|
19
|
-
const MIN_SPACING_Y = 120; // Vertical spacing from parent to child
|
|
20
|
-
|
|
21
|
-
// Initialize adjacency and incoming counts
|
|
22
|
-
workflow.nodes.forEach((node) => {
|
|
23
|
-
adjacency.set(node.getRef(), []);
|
|
24
|
-
incomingCount.set(node.getRef(), 0);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// Fill adjacency list (source -> array of targets) and count incoming edges
|
|
28
|
-
workflow.edges.forEach((edge: Edge) => {
|
|
29
|
-
const sourceRef = edge.source.getRef();
|
|
30
|
-
const targetRef = edge.target.getRef();
|
|
31
|
-
adjacency.get(sourceRef)?.push(edge.target);
|
|
32
|
-
incomingCount.set(targetRef, (incomingCount.get(targetRef) ?? 0) + 1);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// 2. Find all root nodes (no incoming edges). If multiple, just pick the first.
|
|
36
|
-
const rootNodes = workflow.nodes.filter(
|
|
37
|
-
(n) => (incomingCount.get(n.getRef()) || 0) === 0
|
|
38
|
-
);
|
|
39
|
-
if (rootNodes.length === 0) {
|
|
40
|
-
// No root found; nothing to do
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// We'll assume the first root is "the" root for this layout
|
|
45
|
-
const root = rootNodes[0];
|
|
46
|
-
if (!hasPosition(root)) {
|
|
47
|
-
root.setPosition(ROOT_X, ROOT_Y);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// 3. BFS from that single root
|
|
51
|
-
const queue = [root];
|
|
52
|
-
while (queue.length > 0) {
|
|
53
|
-
const parent = queue.shift()!;
|
|
54
|
-
const parentRef = parent.getRef();
|
|
55
|
-
|
|
56
|
-
// Identify the children
|
|
57
|
-
const children = adjacency.get(parentRef) || [];
|
|
58
|
-
|
|
59
|
-
// Among those children, find any that do NOT already have positions
|
|
60
|
-
const unpositionedKids = children.filter((c) => !hasPosition(c));
|
|
61
|
-
|
|
62
|
-
if (unpositionedKids.length > 0) {
|
|
63
|
-
const parentX = parent.position?.x ?? 0;
|
|
64
|
-
const parentY = parent.position?.y ?? 0;
|
|
65
|
-
|
|
66
|
-
if (unpositionedKids.length === 1) {
|
|
67
|
-
// Single child: place it straight below the parent
|
|
68
|
-
const onlyChild = unpositionedKids[0];
|
|
69
|
-
onlyChild.setPosition(parentX, parentY + MIN_SPACING_Y);
|
|
70
|
-
} else {
|
|
71
|
-
// Multiple children: spread them horizontally around the parent's X
|
|
72
|
-
const count = unpositionedKids.length;
|
|
73
|
-
unpositionedKids.forEach((child, i) => {
|
|
74
|
-
const offset = i - (count - 1) / 2;
|
|
75
|
-
const childX = parentX + offset * MIN_SPACING_X;
|
|
76
|
-
const childY = parentY + MIN_SPACING_Y;
|
|
77
|
-
child.setPosition(childX, childY);
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Decrement incoming edges for each child. Once a child hits 0, enqueue it
|
|
83
|
-
children.forEach((c) => {
|
|
84
|
-
const cRef = c.getRef();
|
|
85
|
-
const oldCount = incomingCount.get(cRef) ?? 0;
|
|
86
|
-
const newCount = oldCount - 1;
|
|
87
|
-
incomingCount.set(cRef, newCount);
|
|
88
|
-
if (newCount === 0) {
|
|
89
|
-
queue.push(c);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}*/
|
|
94
2
|
export const xSpacing = 600;
|
|
95
3
|
export const ySpacing = 120;
|
|
96
4
|
export const ROOT_X = 400;
|
|
@@ -135,7 +43,6 @@ export function positionNode(node, edges, xSpacing, ySpacing, workflow) {
|
|
|
135
43
|
}
|
|
136
44
|
export function positionWorkflowNodesAvoidOverlap(workflow) {
|
|
137
45
|
const levels = new Map();
|
|
138
|
-
// Helper: Add node to its level
|
|
139
46
|
function addToLevel(node) {
|
|
140
47
|
const level = Math.round(node.position.y / ySpacing);
|
|
141
48
|
if (!levels.has(level)) {
|
|
@@ -143,28 +50,57 @@ export function positionWorkflowNodesAvoidOverlap(workflow) {
|
|
|
143
50
|
}
|
|
144
51
|
levels.get(level).push(node);
|
|
145
52
|
}
|
|
146
|
-
//
|
|
53
|
+
// 1) Lay out nodes using existing logic
|
|
147
54
|
positionWorkflowNodes(workflow);
|
|
148
|
-
//
|
|
55
|
+
// 2) Fill the `levels` map
|
|
149
56
|
workflow.nodes.forEach((node) => {
|
|
150
57
|
if (node.position) {
|
|
151
58
|
addToLevel(node);
|
|
152
59
|
}
|
|
153
60
|
});
|
|
154
|
-
//
|
|
61
|
+
// 3) Resolve horizontal overlaps level by level
|
|
155
62
|
levels.forEach((nodes, level) => {
|
|
156
|
-
// Sort
|
|
63
|
+
// Sort by x so we can detect collisions
|
|
157
64
|
nodes.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); });
|
|
158
|
-
//
|
|
65
|
+
// Shift nodes that collide
|
|
159
66
|
for (let i = 1; i < nodes.length; i++) {
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
if (
|
|
163
|
-
const shift = xSpacing - (
|
|
164
|
-
moveNodeAndChildren(
|
|
67
|
+
const prev = nodes[i - 1];
|
|
68
|
+
const current = nodes[i];
|
|
69
|
+
if (current.position.x - prev.position.x < xSpacing) {
|
|
70
|
+
const shift = xSpacing - (current.position.x - prev.position.x);
|
|
71
|
+
moveNodeAndChildren(current, shift, workflow.edges);
|
|
165
72
|
}
|
|
166
73
|
}
|
|
167
74
|
});
|
|
75
|
+
// 4) **Center each parent over its children** (the new step)
|
|
76
|
+
centerParentXPositions(workflow);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Repositions each parent node so that its X is the average of the children’s X.
|
|
80
|
+
* We do a simple bottom-up pass: start with all leaves, then move upward to parents.
|
|
81
|
+
*/
|
|
82
|
+
function centerParentXPositions(workflow) {
|
|
83
|
+
var _a, _b;
|
|
84
|
+
// Identify the “leaf” nodes
|
|
85
|
+
const queue = identifyLeafNodes(workflow);
|
|
86
|
+
while (queue.length > 0) {
|
|
87
|
+
const child = queue.shift();
|
|
88
|
+
const parents = getParents(child, workflow.edges);
|
|
89
|
+
for (const parent of parents) {
|
|
90
|
+
const children = getChildren(parent, workflow.edges);
|
|
91
|
+
if (children.length) {
|
|
92
|
+
// Average x of all children
|
|
93
|
+
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);
|
|
94
|
+
const avgX = sumX / children.length;
|
|
95
|
+
// Move parent to that average, keep same y
|
|
96
|
+
parent.setPosition(avgX, (_b = (_a = parent.position) === null || _a === void 0 ? void 0 : _a.y) !== null && _b !== void 0 ? _b : 0);
|
|
97
|
+
}
|
|
98
|
+
// Add this parent to the queue so we can recurse upward
|
|
99
|
+
if (!queue.includes(parent)) {
|
|
100
|
+
queue.push(parent);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
168
104
|
}
|
|
169
105
|
function moveNodeAndChildren(node, shift, edges) {
|
|
170
106
|
// Move the node
|
|
@@ -200,3 +136,14 @@ export function getParents(node, edges) {
|
|
|
200
136
|
export function getEdges(node, edges) {
|
|
201
137
|
return edges.filter(edge => edge.source === node || edge.target === node);
|
|
202
138
|
}
|
|
139
|
+
export function getEndNodePositions(workflow) {
|
|
140
|
+
return workflow.nodes
|
|
141
|
+
.filter(node => getChildren(node, workflow.edges).length === 0) // node with no children
|
|
142
|
+
.map(node => {
|
|
143
|
+
var _a, _b, _c, _d;
|
|
144
|
+
return ({
|
|
145
|
+
x: (_b = (_a = node.position) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 0,
|
|
146
|
+
y: ((_d = (_c = node.position) === null || _c === void 0 ? void 0 : _c.y) !== null && _d !== void 0 ? _d : 0) + ySpacing,
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "2.0.
|
|
1
|
+
export declare const SDK_VERSION = "2.0.26";
|
|
2
2
|
export declare function compareVersions(v1: string, v2: string): number;
|
|
@@ -4,6 +4,7 @@ declare class ApiServices {
|
|
|
4
4
|
setUrl(baseUrl: string): void;
|
|
5
5
|
post(url: string, data: any): Promise<import("axios").AxiosResponse<any, any>>;
|
|
6
6
|
patch(url: string, data: any): Promise<import("axios").AxiosResponse<any, any>>;
|
|
7
|
+
put(url: string, data: any): Promise<import("axios").AxiosResponse<any, any>>;
|
|
7
8
|
get(url: string): Promise<any>;
|
|
8
9
|
delete(url: string): Promise<import("axios").AxiosResponse<any, any>>;
|
|
9
10
|
generateLoginPayload(address: string, chainId: number, referralCode: string): Promise<any>;
|
|
@@ -1,14 +1,6 @@
|
|
|
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
|
-
/**
|
|
5
|
-
* Positions nodes in a BFS manner, assuming exactly one root node.
|
|
6
|
-
* - The root is placed at (400, 120) unless it already has a position.
|
|
7
|
-
* - If a parent has exactly 1 child, that child is placed directly below the parent
|
|
8
|
-
* (same x, with an offset in y).
|
|
9
|
-
* - If a parent has multiple children, they are horizontally spread around the parent's X.
|
|
10
|
-
* - Existing positions are not overwritten.
|
|
11
|
-
*/
|
|
12
4
|
export declare const xSpacing = 600;
|
|
13
5
|
export declare const ySpacing = 120;
|
|
14
6
|
export declare const ROOT_X = 400;
|
|
@@ -28,3 +20,7 @@ export declare function identityStartingNodes(workflow: Workflow): Node[];
|
|
|
28
20
|
export declare function getChildren(node: Node, edges: Edge[]): Node[];
|
|
29
21
|
export declare function getParents(node: Node, edges: Edge[]): Node[];
|
|
30
22
|
export declare function getEdges(node: Node, edges: Edge[]): Edge[];
|
|
23
|
+
export declare function getEndNodePositions(workflow: Workflow): {
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
}[];
|