@bpmnkit/core 0.0.27 → 0.1.0
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/bpmn/auto-layout.js +39 -126
- package/dist/bpmn/bpmn-builder.d.ts +30 -0
- package/dist/bpmn/bpmn-builder.js +343 -31
- package/dist/bpmn/di-check.d.ts +14 -0
- package/dist/bpmn/di-check.js +51 -0
- package/dist/bpmn/svg.js +14 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/layout/annotations.d.ts +27 -0
- package/dist/layout/annotations.js +251 -0
- package/dist/layout/grid/edge-labels.d.ts +8 -0
- package/dist/layout/grid/edge-labels.js +126 -0
- package/dist/layout/grid/flow-graph.d.ts +25 -0
- package/dist/layout/grid/flow-graph.js +99 -0
- package/dist/layout/grid/grid-engine.d.ts +4 -0
- package/dist/layout/grid/grid-engine.js +214 -0
- package/dist/layout/grid/grid-router.d.ts +36 -0
- package/dist/layout/grid/grid-router.js +190 -0
- package/dist/layout/grid/grid.d.ts +43 -0
- package/dist/layout/grid/grid.js +174 -0
- package/dist/layout/grid/walker.d.ts +11 -0
- package/dist/layout/grid/walker.js +126 -0
- package/dist/layout/index.d.ts +2 -5
- package/dist/layout/index.js +1 -4
- package/dist/layout/layout-engine.d.ts +5 -15
- package/dist/layout/layout-engine.js +8 -486
- package/dist/layout/types.js +4 -0
- package/dist/xml/xml-parser.js +5 -0
- package/package.json +1 -1
- package/dist/layout/astar.d.ts +0 -15
- package/dist/layout/astar.js +0 -191
- package/dist/layout/block-builder.d.ts +0 -37
- package/dist/layout/block-builder.js +0 -154
- package/dist/layout/block-layout.d.ts +0 -9
- package/dist/layout/block-layout.js +0 -163
- package/dist/layout/coordinates.d.ts +0 -85
- package/dist/layout/coordinates.js +0 -1392
- package/dist/layout/crossing.d.ts +0 -8
- package/dist/layout/crossing.js +0 -60
- package/dist/layout/graph.d.ts +0 -28
- package/dist/layout/graph.js +0 -126
- package/dist/layout/layers.d.ts +0 -13
- package/dist/layout/layers.js +0 -49
- package/dist/layout/routing.d.ts +0 -33
- package/dist/layout/routing.js +0 -622
- package/dist/layout/subprocess.d.ts +0 -14
- package/dist/layout/subprocess.js +0 -115
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { DirectedGraph } from "./graph.js";
|
|
2
|
-
/**
|
|
3
|
-
* Minimize edge crossings using the barycenter heuristic.
|
|
4
|
-
* For each layer, order nodes by the average position of their neighbors
|
|
5
|
-
* in the adjacent layer. Iterates a fixed number of passes.
|
|
6
|
-
*/
|
|
7
|
-
export declare function minimizeCrossings(layerGroups: string[][], graph: DirectedGraph, iterations?: number): string[][];
|
|
8
|
-
//# sourceMappingURL=crossing.d.ts.map
|
package/dist/layout/crossing.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimize edge crossings using the barycenter heuristic.
|
|
3
|
-
* For each layer, order nodes by the average position of their neighbors
|
|
4
|
-
* in the adjacent layer. Iterates a fixed number of passes.
|
|
5
|
-
*/
|
|
6
|
-
export function minimizeCrossings(layerGroups, graph, iterations = 4) {
|
|
7
|
-
const result = layerGroups.map((layer) => [...layer]);
|
|
8
|
-
for (let iter = 0; iter < iterations; iter++) {
|
|
9
|
-
// Forward sweep (left to right): order by predecessor positions
|
|
10
|
-
for (let i = 1; i < result.length; i++) {
|
|
11
|
-
orderByBarycenter(result, i, graph, "predecessors");
|
|
12
|
-
}
|
|
13
|
-
// Backward sweep (right to left): order by successor positions
|
|
14
|
-
for (let i = result.length - 2; i >= 0; i--) {
|
|
15
|
-
orderByBarycenter(result, i, graph, "successors");
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return result;
|
|
19
|
-
}
|
|
20
|
-
function orderByBarycenter(layers, layerIndex, graph, direction) {
|
|
21
|
-
const layer = layers[layerIndex];
|
|
22
|
-
if (!layer)
|
|
23
|
-
return;
|
|
24
|
-
const adjacentLayer = direction === "predecessors" ? layers[layerIndex - 1] : layers[layerIndex + 1];
|
|
25
|
-
if (!adjacentLayer || adjacentLayer.length === 0)
|
|
26
|
-
return;
|
|
27
|
-
// Build position index for the adjacent layer
|
|
28
|
-
const posIndex = new Map();
|
|
29
|
-
for (let i = 0; i < adjacentLayer.length; i++) {
|
|
30
|
-
const adjNode = adjacentLayer[i];
|
|
31
|
-
if (!adjNode)
|
|
32
|
-
continue;
|
|
33
|
-
posIndex.set(adjNode, i);
|
|
34
|
-
}
|
|
35
|
-
// Compute barycenter for each node in current layer
|
|
36
|
-
const barycenters = new Map();
|
|
37
|
-
for (const nodeId of layer) {
|
|
38
|
-
const neighbors = direction === "predecessors"
|
|
39
|
-
? (graph.predecessors.get(nodeId) ?? [])
|
|
40
|
-
: (graph.successors.get(nodeId) ?? []);
|
|
41
|
-
const adjacentPositions = [];
|
|
42
|
-
for (const neighbor of neighbors) {
|
|
43
|
-
const pos = posIndex.get(neighbor);
|
|
44
|
-
if (pos !== undefined) {
|
|
45
|
-
adjacentPositions.push(pos);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (adjacentPositions.length > 0) {
|
|
49
|
-
const sum = adjacentPositions.reduce((a, b) => a + b, 0);
|
|
50
|
-
barycenters.set(nodeId, sum / adjacentPositions.length);
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
// Keep original position for nodes without connections
|
|
54
|
-
barycenters.set(nodeId, layer.indexOf(nodeId));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// Sort by barycenter, preserving relative order for equal values
|
|
58
|
-
layer.sort((a, b) => (barycenters.get(a) ?? 0) - (barycenters.get(b) ?? 0));
|
|
59
|
-
}
|
|
60
|
-
//# sourceMappingURL=crossing.js.map
|
package/dist/layout/graph.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { BpmnFlowElement, BpmnSequenceFlow } from "../bpmn/bpmn-model.js";
|
|
2
|
-
/** Adjacency list representation of a directed graph. */
|
|
3
|
-
export interface DirectedGraph {
|
|
4
|
-
/** Node IDs. */
|
|
5
|
-
nodes: string[];
|
|
6
|
-
/** Adjacency: nodeId → list of successor nodeIds. */
|
|
7
|
-
successors: Map<string, string[]>;
|
|
8
|
-
/** Reverse adjacency: nodeId → list of predecessor nodeIds. */
|
|
9
|
-
predecessors: Map<string, string[]>;
|
|
10
|
-
}
|
|
11
|
-
/** Back-edge detected during DFS. */
|
|
12
|
-
export interface BackEdge {
|
|
13
|
-
flowId: string;
|
|
14
|
-
sourceRef: string;
|
|
15
|
-
targetRef: string;
|
|
16
|
-
}
|
|
17
|
-
/** Build a directed graph from BPMN flow nodes and sequence flows. */
|
|
18
|
-
export declare function buildGraph(flowNodes: BpmnFlowElement[], sequenceFlows: BpmnSequenceFlow[]): DirectedGraph;
|
|
19
|
-
/**
|
|
20
|
-
* Detect back-edges via DFS.
|
|
21
|
-
* A back-edge is an edge where the target is an ancestor of the source in the DFS tree.
|
|
22
|
-
*/
|
|
23
|
-
export declare function detectBackEdges(graph: DirectedGraph, sequenceFlows: BpmnSequenceFlow[]): BackEdge[];
|
|
24
|
-
/** Reverse back-edges in the graph to make it a DAG. Returns the modified graph. */
|
|
25
|
-
export declare function reverseBackEdges(graph: DirectedGraph, backEdges: BackEdge[]): DirectedGraph;
|
|
26
|
-
/** Topological sort of a DAG. Throws if cycles remain. */
|
|
27
|
-
export declare function topologicalSort(graph: DirectedGraph): string[];
|
|
28
|
-
//# sourceMappingURL=graph.d.ts.map
|
package/dist/layout/graph.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/** Build a directed graph from BPMN flow nodes and sequence flows. */
|
|
2
|
-
export function buildGraph(flowNodes, sequenceFlows) {
|
|
3
|
-
const nodes = flowNodes.map((n) => n.id);
|
|
4
|
-
const successors = new Map();
|
|
5
|
-
const predecessors = new Map();
|
|
6
|
-
for (const id of nodes) {
|
|
7
|
-
successors.set(id, []);
|
|
8
|
-
predecessors.set(id, []);
|
|
9
|
-
}
|
|
10
|
-
for (const flow of sequenceFlows) {
|
|
11
|
-
const succs = successors.get(flow.sourceRef);
|
|
12
|
-
if (succs)
|
|
13
|
-
succs.push(flow.targetRef);
|
|
14
|
-
const preds = predecessors.get(flow.targetRef);
|
|
15
|
-
if (preds)
|
|
16
|
-
preds.push(flow.sourceRef);
|
|
17
|
-
}
|
|
18
|
-
return { nodes, successors, predecessors };
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Detect back-edges via DFS.
|
|
22
|
-
* A back-edge is an edge where the target is an ancestor of the source in the DFS tree.
|
|
23
|
-
*/
|
|
24
|
-
export function detectBackEdges(graph, sequenceFlows) {
|
|
25
|
-
const WHITE = 0;
|
|
26
|
-
const GRAY = 1;
|
|
27
|
-
const BLACK = 2;
|
|
28
|
-
const color = new Map();
|
|
29
|
-
for (const id of graph.nodes) {
|
|
30
|
-
color.set(id, WHITE);
|
|
31
|
-
}
|
|
32
|
-
const backEdges = [];
|
|
33
|
-
const flowIndex = new Map();
|
|
34
|
-
for (const flow of sequenceFlows) {
|
|
35
|
-
flowIndex.set(`${flow.sourceRef}->${flow.targetRef}`, flow);
|
|
36
|
-
}
|
|
37
|
-
function dfs(nodeId) {
|
|
38
|
-
color.set(nodeId, GRAY);
|
|
39
|
-
const succs = graph.successors.get(nodeId) ?? [];
|
|
40
|
-
for (const succ of succs) {
|
|
41
|
-
const c = color.get(succ);
|
|
42
|
-
if (c === GRAY) {
|
|
43
|
-
const flow = flowIndex.get(`${nodeId}->${succ}`);
|
|
44
|
-
if (flow) {
|
|
45
|
-
backEdges.push({
|
|
46
|
-
flowId: flow.id,
|
|
47
|
-
sourceRef: nodeId,
|
|
48
|
-
targetRef: succ,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else if (c === WHITE) {
|
|
53
|
-
dfs(succ);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
color.set(nodeId, BLACK);
|
|
57
|
-
}
|
|
58
|
-
for (const id of graph.nodes) {
|
|
59
|
-
if (color.get(id) === WHITE) {
|
|
60
|
-
dfs(id);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return backEdges;
|
|
64
|
-
}
|
|
65
|
-
/** Reverse back-edges in the graph to make it a DAG. Returns the modified graph. */
|
|
66
|
-
export function reverseBackEdges(graph, backEdges) {
|
|
67
|
-
const successors = new Map();
|
|
68
|
-
const predecessors = new Map();
|
|
69
|
-
for (const id of graph.nodes) {
|
|
70
|
-
successors.set(id, [...(graph.successors.get(id) ?? [])]);
|
|
71
|
-
predecessors.set(id, [...(graph.predecessors.get(id) ?? [])]);
|
|
72
|
-
}
|
|
73
|
-
for (const be of backEdges) {
|
|
74
|
-
// Remove forward direction
|
|
75
|
-
const succs = successors.get(be.sourceRef);
|
|
76
|
-
if (succs) {
|
|
77
|
-
const idx = succs.indexOf(be.targetRef);
|
|
78
|
-
if (idx >= 0)
|
|
79
|
-
succs.splice(idx, 1);
|
|
80
|
-
}
|
|
81
|
-
const preds = predecessors.get(be.targetRef);
|
|
82
|
-
if (preds) {
|
|
83
|
-
const idx = preds.indexOf(be.sourceRef);
|
|
84
|
-
if (idx >= 0)
|
|
85
|
-
preds.splice(idx, 1);
|
|
86
|
-
}
|
|
87
|
-
// Add reversed direction
|
|
88
|
-
const revSuccs = successors.get(be.targetRef);
|
|
89
|
-
if (revSuccs)
|
|
90
|
-
revSuccs.push(be.sourceRef);
|
|
91
|
-
const revPreds = predecessors.get(be.sourceRef);
|
|
92
|
-
if (revPreds)
|
|
93
|
-
revPreds.push(be.targetRef);
|
|
94
|
-
}
|
|
95
|
-
return { nodes: [...graph.nodes], successors, predecessors };
|
|
96
|
-
}
|
|
97
|
-
/** Topological sort of a DAG. Throws if cycles remain. */
|
|
98
|
-
export function topologicalSort(graph) {
|
|
99
|
-
const inDegree = new Map();
|
|
100
|
-
for (const id of graph.nodes) {
|
|
101
|
-
inDegree.set(id, (graph.predecessors.get(id) ?? []).length);
|
|
102
|
-
}
|
|
103
|
-
const queue = [];
|
|
104
|
-
for (const [id, deg] of inDegree) {
|
|
105
|
-
if (deg === 0)
|
|
106
|
-
queue.push(id);
|
|
107
|
-
}
|
|
108
|
-
const sorted = [];
|
|
109
|
-
while (queue.length > 0) {
|
|
110
|
-
const node = queue.shift();
|
|
111
|
-
if (!node)
|
|
112
|
-
break;
|
|
113
|
-
sorted.push(node);
|
|
114
|
-
for (const succ of graph.successors.get(node) ?? []) {
|
|
115
|
-
const newDeg = (inDegree.get(succ) ?? 1) - 1;
|
|
116
|
-
inDegree.set(succ, newDeg);
|
|
117
|
-
if (newDeg === 0)
|
|
118
|
-
queue.push(succ);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (sorted.length !== graph.nodes.length) {
|
|
122
|
-
throw new Error(`Layout error: graph contains cycles after back-edge reversal (sorted ${sorted.length} of ${graph.nodes.length} nodes)`);
|
|
123
|
-
}
|
|
124
|
-
return sorted;
|
|
125
|
-
}
|
|
126
|
-
//# sourceMappingURL=graph.js.map
|
package/dist/layout/layers.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { DirectedGraph } from "./graph.js";
|
|
2
|
-
/**
|
|
3
|
-
* Assign layers using longest-path algorithm.
|
|
4
|
-
* Each node gets a layer equal to the longest path from any source to it.
|
|
5
|
-
* This produces a left-to-right layout where layers represent columns.
|
|
6
|
-
*/
|
|
7
|
-
export declare function assignLayers(graph: DirectedGraph): Map<string, number>;
|
|
8
|
-
/**
|
|
9
|
-
* Group nodes by their assigned layer.
|
|
10
|
-
* Returns an array of arrays, where index = layer number.
|
|
11
|
-
*/
|
|
12
|
-
export declare function groupByLayer(layers: Map<string, number>): string[][];
|
|
13
|
-
//# sourceMappingURL=layers.d.ts.map
|
package/dist/layout/layers.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { topologicalSort } from "./graph.js";
|
|
2
|
-
/**
|
|
3
|
-
* Assign layers using longest-path algorithm.
|
|
4
|
-
* Each node gets a layer equal to the longest path from any source to it.
|
|
5
|
-
* This produces a left-to-right layout where layers represent columns.
|
|
6
|
-
*/
|
|
7
|
-
export function assignLayers(graph) {
|
|
8
|
-
const sorted = topologicalSort(graph);
|
|
9
|
-
const layers = new Map();
|
|
10
|
-
// Initialize all nodes at layer 0
|
|
11
|
-
for (const id of sorted) {
|
|
12
|
-
layers.set(id, 0);
|
|
13
|
-
}
|
|
14
|
-
// Forward pass: each node's layer = max(predecessor layers) + 1
|
|
15
|
-
for (const id of sorted) {
|
|
16
|
-
const preds = graph.predecessors.get(id) ?? [];
|
|
17
|
-
if (preds.length > 0) {
|
|
18
|
-
let maxPredLayer = 0;
|
|
19
|
-
for (const pred of preds) {
|
|
20
|
-
const predLayer = layers.get(pred) ?? 0;
|
|
21
|
-
if (predLayer >= maxPredLayer) {
|
|
22
|
-
maxPredLayer = predLayer + 1;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
layers.set(id, maxPredLayer);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return layers;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Group nodes by their assigned layer.
|
|
32
|
-
* Returns an array of arrays, where index = layer number.
|
|
33
|
-
*/
|
|
34
|
-
export function groupByLayer(layers) {
|
|
35
|
-
let maxLayer = 0;
|
|
36
|
-
for (const layer of layers.values()) {
|
|
37
|
-
if (layer > maxLayer)
|
|
38
|
-
maxLayer = layer;
|
|
39
|
-
}
|
|
40
|
-
const groups = [];
|
|
41
|
-
for (let i = 0; i <= maxLayer; i++) {
|
|
42
|
-
groups.push([]);
|
|
43
|
-
}
|
|
44
|
-
for (const [id, layer] of layers) {
|
|
45
|
-
groups[layer]?.push(id);
|
|
46
|
-
}
|
|
47
|
-
return groups;
|
|
48
|
-
}
|
|
49
|
-
//# sourceMappingURL=layers.js.map
|
package/dist/layout/routing.d.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { BpmnSequenceFlow } from "../bpmn/bpmn-model.js";
|
|
2
|
-
import type { BackEdge } from "./graph.js";
|
|
3
|
-
import type { LayoutEdge, LayoutNode } from "./types.js";
|
|
4
|
-
/** Port side for gateway edge connection. */
|
|
5
|
-
export type PortSide = "right" | "top" | "bottom";
|
|
6
|
-
/**
|
|
7
|
-
* Determine which side of the target a forward edge should connect to.
|
|
8
|
-
* Non-gateway targets always receive edges from the left side.
|
|
9
|
-
* Split gateways (starting): incoming always from the left.
|
|
10
|
-
* Join gateways (closing): incoming based on relative position (top/bottom/left).
|
|
11
|
-
*
|
|
12
|
-
* Uses gridRow (integer row index) when available for exact comparison;
|
|
13
|
-
* falls back to pixel-Y with PORT_SAME_Y_TOLERANCE for nodes without gridRow.
|
|
14
|
-
*/
|
|
15
|
-
export declare function resolveTargetPort(source: LayoutNode, target: LayoutNode, joinGateways: ReadonlySet<string>): "left" | "top" | "bottom";
|
|
16
|
-
/**
|
|
17
|
-
* Assign source ports for outgoing edges of a gateway.
|
|
18
|
-
* Uses absolute direction: target above → top, below → bottom, same level → right.
|
|
19
|
-
* Single output always exits from the right port.
|
|
20
|
-
*/
|
|
21
|
-
export declare function assignGatewayPorts(outgoingFlows: BpmnSequenceFlow[], nodeMap: Map<string, LayoutNode>): Map<string, PortSide>;
|
|
22
|
-
/**
|
|
23
|
-
* Route edges with orthogonal (horizontal + vertical) segments.
|
|
24
|
-
* Forward edges go left-to-right; back-edges route above or below.
|
|
25
|
-
* Gateway sources use port-based routing (top/right/bottom).
|
|
26
|
-
*/
|
|
27
|
-
export declare function routeEdges(sequenceFlows: BpmnSequenceFlow[], nodeMap: Map<string, LayoutNode>, backEdges: BackEdge[]): LayoutEdge[];
|
|
28
|
-
/**
|
|
29
|
-
* Post-process routed edges to avoid crossing through intermediate shapes.
|
|
30
|
-
* For each segment that passes through a shape, adds detour waypoints around it.
|
|
31
|
-
*/
|
|
32
|
-
export declare function resolveEdgeCrossings(edges: LayoutEdge[], nodeMap: Map<string, LayoutNode>): void;
|
|
33
|
-
//# sourceMappingURL=routing.d.ts.map
|