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.
@@ -1,4 +1,4 @@
1
- export const SDK_VERSION = '2.0.24';
1
+ export const SDK_VERSION = '2.0.26';
2
2
  export function compareVersions(v1, v2) {
3
3
  // Split the version strings into parts
4
4
  const v1Parts = v1.split('.').map(Number);
@@ -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.patch(`/workflows/${this.id}`, this.toJSON());
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
- // Step 1: Position nodes using the existing logic
53
+ // 1) Lay out nodes using existing logic
147
54
  positionWorkflowNodes(workflow);
148
- // Step 2: Populate levels
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
- // Step 3: Resolve overlaps for each level
61
+ // 3) Resolve horizontal overlaps level by level
155
62
  levels.forEach((nodes, level) => {
156
- // Sort nodes by X position
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
- // Adjust overlapping nodes
65
+ // Shift nodes that collide
159
66
  for (let i = 1; i < nodes.length; i++) {
160
- const prevNode = nodes[i - 1];
161
- const currentNode = nodes[i];
162
- if (currentNode.position.x - prevNode.position.x < xSpacing) {
163
- const shift = xSpacing - (currentNode.position.x - prevNode.position.x);
164
- moveNodeAndChildren(currentNode, shift, workflow.edges);
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.24";
1
+ export declare const SDK_VERSION = "2.0.26";
2
2
  export declare function compareVersions(v1: string, v2: string): number;
@@ -86,4 +86,8 @@ export declare class Workflow {
86
86
  error?: string;
87
87
  }>;
88
88
  getSessionKeyPermissions(): Promise<SessionKeyPermission>;
89
+ getEndNodePositions(): {
90
+ x: number;
91
+ y: number;
92
+ }[];
89
93
  }
@@ -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
+ }[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "otomato-sdk",
3
- "version": "2.0.24",
3
+ "version": "2.0.26",
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",