@bpmnkit/core 0.0.21 → 0.0.23
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/README.md +1 -0
- package/dist/bpmn/auto-layout.js +320 -21
- package/dist/bpmn/bpmn-builder.js +1 -0
- package/dist/bpmn/bpmn-model.d.ts +6 -0
- package/dist/bpmn/bpmn-parser.js +10 -0
- package/dist/bpmn/bpmn-serializer.js +10 -1
- package/dist/bpmn/compact.js +1 -0
- package/dist/bpmn/optimize/tasks.js +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/layout/astar.d.ts +15 -0
- package/dist/layout/astar.js +191 -0
- package/dist/layout/block-builder.d.ts +37 -0
- package/dist/layout/block-builder.js +154 -0
- package/dist/layout/block-layout.d.ts +9 -0
- package/dist/layout/block-layout.js +163 -0
- package/dist/layout/coordinates.d.ts +23 -0
- package/dist/layout/coordinates.js +612 -88
- package/dist/layout/index.d.ts +5 -0
- package/dist/layout/index.js +4 -0
- package/dist/layout/layout-engine.js +79 -28
- package/dist/layout/routing.d.ts +10 -3
- package/dist/layout/routing.js +254 -71
- package/dist/layout/types.d.ts +6 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -186,6 +186,7 @@ const outXml = Bpmn.export(restored)
|
|
|
186
186
|
| [`@bpmnkit/cli`](https://www.npmjs.com/package/@bpmnkit/cli) | Camunda 8 command-line interface (casen) |
|
|
187
187
|
| [`@bpmnkit/proxy`](https://www.npmjs.com/package/@bpmnkit/proxy) | Local AI bridge and Camunda API proxy server |
|
|
188
188
|
| [`@bpmnkit/patterns`](https://www.npmjs.com/package/@bpmnkit/patterns) | Domain process patterns for BPMNKit AIKit |
|
|
189
|
+
| [`@bpmnkit/reebe-wasm`](https://www.npmjs.com/package/@bpmnkit/reebe-wasm) | WebAssembly BPMN engine for browser simulation |
|
|
189
190
|
| [`@bpmnkit/worker-client`](https://www.npmjs.com/package/@bpmnkit/worker-client) | Thin Zeebe REST client for standalone workers |
|
|
190
191
|
| [`@bpmnkit/cli-sdk`](https://www.npmjs.com/package/@bpmnkit/cli-sdk) | Plugin authoring SDK for the casen CLI |
|
|
191
192
|
| [`@bpmnkit/create-casen-plugin`](https://www.npmjs.com/package/@bpmnkit/create-casen-plugin) | Scaffold a new casen CLI plugin in seconds |
|
package/dist/bpmn/auto-layout.js
CHANGED
|
@@ -1,9 +1,140 @@
|
|
|
1
1
|
import { layoutProcess } from "../layout/layout-engine.js";
|
|
2
|
+
import { resolveEdgeCrossings } from "../layout/routing.js";
|
|
2
3
|
const POOL_HEADER = 30;
|
|
3
4
|
const LANE_HEADER = 30;
|
|
4
5
|
const PADDING = 20;
|
|
5
6
|
const POOL_GAP = 30;
|
|
6
|
-
|
|
7
|
+
const CHAIN_GAP = 30;
|
|
8
|
+
const CHAIN_V_GAP = 20;
|
|
9
|
+
const ANN_H = 50;
|
|
10
|
+
const ANN_GAP = 60;
|
|
11
|
+
const ANN_PADDING = 20;
|
|
12
|
+
/**
|
|
13
|
+
* Reposition boundary events to the bottom edge of their host task, then walk
|
|
14
|
+
* each boundary event's exclusive downstream chain and place those nodes
|
|
15
|
+
* horizontally to the right of the host task. Re-routes all affected edges.
|
|
16
|
+
*/
|
|
17
|
+
function repositionBoundaryEvents(flowElements, result) {
|
|
18
|
+
// Collect boundary events grouped by host task id
|
|
19
|
+
const boundaryMap = new Map();
|
|
20
|
+
for (const el of flowElements) {
|
|
21
|
+
if (el.type !== "boundaryEvent")
|
|
22
|
+
continue;
|
|
23
|
+
const list = boundaryMap.get(el.attachedToRef) ?? [];
|
|
24
|
+
list.push(el.id);
|
|
25
|
+
boundaryMap.set(el.attachedToRef, list);
|
|
26
|
+
}
|
|
27
|
+
if (boundaryMap.size === 0)
|
|
28
|
+
return;
|
|
29
|
+
const nodeById = new Map(result.nodes.map((n) => [n.id, n]));
|
|
30
|
+
// Build successor / predecessor maps from edges for chain walking
|
|
31
|
+
const succIds = new Map();
|
|
32
|
+
const predIds = new Map();
|
|
33
|
+
for (const edge of result.edges) {
|
|
34
|
+
const se = succIds.get(edge.sourceRef) ?? [];
|
|
35
|
+
se.push(edge.targetRef);
|
|
36
|
+
succIds.set(edge.sourceRef, se);
|
|
37
|
+
const ps = predIds.get(edge.targetRef) ?? new Set();
|
|
38
|
+
ps.add(edge.sourceRef);
|
|
39
|
+
predIds.set(edge.targetRef, ps);
|
|
40
|
+
}
|
|
41
|
+
for (const [hostId, beIds] of boundaryMap) {
|
|
42
|
+
const hostNode = nodeById.get(hostId);
|
|
43
|
+
if (!hostNode)
|
|
44
|
+
continue;
|
|
45
|
+
for (let i = 0; i < beIds.length; i++) {
|
|
46
|
+
const beId = beIds[i];
|
|
47
|
+
if (!beId)
|
|
48
|
+
continue;
|
|
49
|
+
const beNode = nodeById.get(beId);
|
|
50
|
+
if (!beNode)
|
|
51
|
+
continue;
|
|
52
|
+
const bW = beNode.bounds.width;
|
|
53
|
+
const bH = beNode.bounds.height;
|
|
54
|
+
// Place boundary event at bottom-right of host task, stacking leftward
|
|
55
|
+
const rightEdge = hostNode.bounds.x + hostNode.bounds.width;
|
|
56
|
+
beNode.bounds.x = Math.round(rightEdge - bW / 2 - i * (bW + 4));
|
|
57
|
+
beNode.bounds.y = Math.round(hostNode.bounds.y + hostNode.bounds.height - bH / 2);
|
|
58
|
+
if (beNode.labelBounds) {
|
|
59
|
+
beNode.labelBounds.x = beNode.bounds.x + Math.round(bW / 2 - beNode.labelBounds.width / 2);
|
|
60
|
+
beNode.labelBounds.y = beNode.bounds.y + bH + 4;
|
|
61
|
+
}
|
|
62
|
+
// Collect nodes exclusively reachable from this boundary event (in BFS order)
|
|
63
|
+
const chainSet = new Set([beId]);
|
|
64
|
+
const chainOrder = [];
|
|
65
|
+
const queue = [...(succIds.get(beId) ?? [])];
|
|
66
|
+
while (queue.length > 0) {
|
|
67
|
+
const id = queue.shift();
|
|
68
|
+
if (!id || chainSet.has(id))
|
|
69
|
+
continue;
|
|
70
|
+
// Include only if every predecessor is already in the chain
|
|
71
|
+
const preds = predIds.get(id) ?? new Set();
|
|
72
|
+
if ([...preds].every((p) => chainSet.has(p))) {
|
|
73
|
+
chainSet.add(id);
|
|
74
|
+
chainOrder.push(id);
|
|
75
|
+
queue.push(...(succIds.get(id) ?? []));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Find tallest chain element to compute center Y below boundary event.
|
|
79
|
+
// Each boundary event's chain gets its own vertical lane to avoid overlaps.
|
|
80
|
+
let maxChainH = 0;
|
|
81
|
+
for (const id of chainOrder) {
|
|
82
|
+
const n = nodeById.get(id);
|
|
83
|
+
if (n)
|
|
84
|
+
maxChainH = Math.max(maxChainH, n.bounds.height);
|
|
85
|
+
}
|
|
86
|
+
const laneOffset = i * (maxChainH + CHAIN_V_GAP + 10);
|
|
87
|
+
const chainCenterY = Math.round(beNode.bounds.y + bH + CHAIN_V_GAP + maxChainH / 2 + laneOffset);
|
|
88
|
+
const chainStartX = Math.max(Math.round(beNode.bounds.x + bW / 2) + CHAIN_GAP, hostNode.bounds.x + hostNode.bounds.width + CHAIN_GAP);
|
|
89
|
+
let curX = chainStartX;
|
|
90
|
+
for (const id of chainOrder) {
|
|
91
|
+
const n = nodeById.get(id);
|
|
92
|
+
if (!n)
|
|
93
|
+
continue;
|
|
94
|
+
n.bounds.x = curX;
|
|
95
|
+
n.bounds.y = chainCenterY - Math.round(n.bounds.height / 2);
|
|
96
|
+
if (n.labelBounds) {
|
|
97
|
+
n.labelBounds.x = n.bounds.x + Math.round(n.bounds.width / 2 - n.labelBounds.width / 2);
|
|
98
|
+
n.labelBounds.y = n.bounds.y + n.bounds.height + 4;
|
|
99
|
+
}
|
|
100
|
+
curX += n.bounds.width + CHAIN_GAP;
|
|
101
|
+
}
|
|
102
|
+
// Re-route all edges touching the boundary event or its chain
|
|
103
|
+
for (const edge of result.edges) {
|
|
104
|
+
if (!chainSet.has(edge.sourceRef))
|
|
105
|
+
continue;
|
|
106
|
+
const src = nodeById.get(edge.sourceRef);
|
|
107
|
+
const tgt = nodeById.get(edge.targetRef);
|
|
108
|
+
if (!src || !tgt)
|
|
109
|
+
continue;
|
|
110
|
+
if (edge.sourceRef === beId) {
|
|
111
|
+
// Boundary event → first chain node: route down then right
|
|
112
|
+
const srcX = Math.round(src.bounds.x + bW / 2);
|
|
113
|
+
const srcY = Math.round(src.bounds.y + bH);
|
|
114
|
+
const tgtX = Math.round(tgt.bounds.x);
|
|
115
|
+
const tgtY = Math.round(tgt.bounds.y + tgt.bounds.height / 2);
|
|
116
|
+
edge.waypoints = [
|
|
117
|
+
{ x: srcX, y: srcY },
|
|
118
|
+
{ x: srcX, y: tgtY },
|
|
119
|
+
{ x: tgtX, y: tgtY },
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Within chain: straight horizontal edge
|
|
124
|
+
const srcX = Math.round(src.bounds.x + src.bounds.width);
|
|
125
|
+
const srcY = Math.round(src.bounds.y + src.bounds.height / 2);
|
|
126
|
+
const tgtX = Math.round(tgt.bounds.x);
|
|
127
|
+
const tgtY = Math.round(tgt.bounds.y + tgt.bounds.height / 2);
|
|
128
|
+
edge.waypoints = [
|
|
129
|
+
{ x: srcX, y: srcY },
|
|
130
|
+
{ x: tgtX, y: tgtY },
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function contentBbox(nodes, extra) {
|
|
7
138
|
let minX = Number.POSITIVE_INFINITY;
|
|
8
139
|
let minY = Number.POSITIVE_INFINITY;
|
|
9
140
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -20,8 +151,107 @@ function contentBbox(nodes) {
|
|
|
20
151
|
maxY = Math.max(maxY, n.labelBounds.y + n.labelBounds.height);
|
|
21
152
|
}
|
|
22
153
|
}
|
|
154
|
+
if (extra) {
|
|
155
|
+
for (const b of extra) {
|
|
156
|
+
minX = Math.min(minX, b.x);
|
|
157
|
+
minY = Math.min(minY, b.y);
|
|
158
|
+
maxX = Math.max(maxX, b.x + b.width);
|
|
159
|
+
maxY = Math.max(maxY, b.y + b.height);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
23
162
|
return { minX, minY, maxX, maxY };
|
|
24
163
|
}
|
|
164
|
+
/** Pre-compute annotation positions in layout space (before dx/dy shift). */
|
|
165
|
+
function computeAnnotationLocalBounds(process, layoutNodes) {
|
|
166
|
+
const nodeById = new Map(layoutNodes.map((n) => [n.id, n]));
|
|
167
|
+
const placements = new Map();
|
|
168
|
+
// Occupied regions for overlap checks (node bounds + label bounds)
|
|
169
|
+
const occupied = [];
|
|
170
|
+
for (const n of layoutNodes) {
|
|
171
|
+
occupied.push({ ...n.bounds });
|
|
172
|
+
if (n.labelBounds)
|
|
173
|
+
occupied.push({ ...n.labelBounds });
|
|
174
|
+
}
|
|
175
|
+
// Static obstacles for crossing detection (nodes + labels only, not annotations)
|
|
176
|
+
const obstacles = [...occupied];
|
|
177
|
+
for (const ta of process.textAnnotations) {
|
|
178
|
+
const assoc = process.associations.find((a) => a.sourceRef === ta.id || a.targetRef === ta.id);
|
|
179
|
+
const connId = assoc
|
|
180
|
+
? assoc.sourceRef === ta.id
|
|
181
|
+
? assoc.targetRef
|
|
182
|
+
: assoc.sourceRef
|
|
183
|
+
: undefined;
|
|
184
|
+
const connNode = connId ? nodeById.get(connId) : undefined;
|
|
185
|
+
const annW = Math.min(200, Math.max(100, (ta.text?.length ?? 10) * 5));
|
|
186
|
+
if (!connNode) {
|
|
187
|
+
const candidate = { x: 0, y: 0, width: annW, height: ANN_H };
|
|
188
|
+
occupied.push({ ...candidate });
|
|
189
|
+
placements.set(ta.id, candidate);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const localX = connNode.bounds.x + connNode.bounds.width / 2 - annW / 2;
|
|
193
|
+
const anchorX = connNode.bounds.x + connNode.bounds.width / 2;
|
|
194
|
+
const pushStep = ANN_H + ANN_PADDING * 2 + 10;
|
|
195
|
+
// Try below: start below connected element, push down for overlaps
|
|
196
|
+
const belowY = connNode.bounds.y + connNode.bounds.height + ANN_GAP;
|
|
197
|
+
const below = { x: localX, y: belowY, width: annW, height: ANN_H };
|
|
198
|
+
for (let i = 0; i < 30 && hasOverlapPadded(below, occupied, ANN_PADDING); i++)
|
|
199
|
+
below.y += pushStep;
|
|
200
|
+
// Try above: gap scales with text length so longer annotations have more breathing room
|
|
201
|
+
const aboveGap = ANN_GAP + Math.round(annW * 0.2);
|
|
202
|
+
const aboveY = connNode.bounds.y - aboveGap - ANN_H;
|
|
203
|
+
const above = { x: localX, y: aboveY, width: annW, height: ANN_H };
|
|
204
|
+
for (let i = 0; i < 30 && hasOverlapPadded(above, occupied, ANN_PADDING); i++)
|
|
205
|
+
above.y -= pushStep;
|
|
206
|
+
// Count how many obstacles the association line would cross for each candidate
|
|
207
|
+
const belowCrossings = countLineCrossings(anchorX, connNode.bounds, below, obstacles);
|
|
208
|
+
const aboveCrossings = countLineCrossings(anchorX, connNode.bounds, above, obstacles);
|
|
209
|
+
const candidate = belowCrossings <= aboveCrossings ? below : above;
|
|
210
|
+
occupied.push({ ...candidate });
|
|
211
|
+
obstacles.push({ ...candidate });
|
|
212
|
+
placements.set(ta.id, candidate);
|
|
213
|
+
}
|
|
214
|
+
return placements;
|
|
215
|
+
}
|
|
216
|
+
/** Count how many obstacles the vertical association line from connNode to annotation crosses. */
|
|
217
|
+
function countLineCrossings(lineX, connBounds, annBounds, obstacles) {
|
|
218
|
+
const annCY = annBounds.y + annBounds.height / 2;
|
|
219
|
+
const connCY = connBounds.y + connBounds.height / 2;
|
|
220
|
+
const top = Math.min(annCY, connCY);
|
|
221
|
+
const bottom = Math.max(annCY, connCY);
|
|
222
|
+
const tolerance = 20;
|
|
223
|
+
let crossings = 0;
|
|
224
|
+
for (const b of obstacles) {
|
|
225
|
+
// Skip the connected element itself
|
|
226
|
+
if (b.x === connBounds.x && b.y === connBounds.y && b.width === connBounds.width)
|
|
227
|
+
continue;
|
|
228
|
+
// Obstacle must overlap with the line's X corridor
|
|
229
|
+
if (b.x + b.width < lineX - tolerance || b.x > lineX + tolerance)
|
|
230
|
+
continue;
|
|
231
|
+
// Obstacle must be between connNode and annotation vertically
|
|
232
|
+
if (b.y + b.height <= top || b.y >= bottom)
|
|
233
|
+
continue;
|
|
234
|
+
crossings++;
|
|
235
|
+
}
|
|
236
|
+
return crossings;
|
|
237
|
+
}
|
|
238
|
+
function hasOverlap(a, others) {
|
|
239
|
+
for (const b of others) {
|
|
240
|
+
if (a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y)
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
function hasOverlapPadded(a, others, padding) {
|
|
246
|
+
for (const b of others) {
|
|
247
|
+
if (a.x - padding < b.x + b.width &&
|
|
248
|
+
a.x + a.width + padding > b.x &&
|
|
249
|
+
a.y - padding < b.y + b.height &&
|
|
250
|
+
a.y + a.height + padding > b.y)
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
25
255
|
function nodeToShape(node, dx, dy) {
|
|
26
256
|
const shape = {
|
|
27
257
|
id: `${node.id}_di`,
|
|
@@ -112,6 +342,70 @@ function buildLaneShapes(lanes, nodes, dx, dy, poolY, poolHeaderWidth, laneConte
|
|
|
112
342
|
unknownAttributes: {},
|
|
113
343
|
}));
|
|
114
344
|
}
|
|
345
|
+
function addAnnotationShapes(process, layoutNodes, annLocalBounds, allShapes, allEdges, dx, dy) {
|
|
346
|
+
if (process.textAnnotations.length === 0 && process.associations.length === 0)
|
|
347
|
+
return;
|
|
348
|
+
const nodeById = new Map(layoutNodes.map((n) => [n.id, n]));
|
|
349
|
+
for (const ta of process.textAnnotations) {
|
|
350
|
+
const b = annLocalBounds.get(ta.id);
|
|
351
|
+
if (!b)
|
|
352
|
+
continue;
|
|
353
|
+
allShapes.push({
|
|
354
|
+
id: `${ta.id}_di`,
|
|
355
|
+
bpmnElement: ta.id,
|
|
356
|
+
bounds: {
|
|
357
|
+
x: Math.round(b.x + dx),
|
|
358
|
+
y: Math.round(b.y + dy),
|
|
359
|
+
width: b.width,
|
|
360
|
+
height: b.height,
|
|
361
|
+
},
|
|
362
|
+
unknownAttributes: {},
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
for (const assoc of process.associations) {
|
|
366
|
+
const annId = annLocalBounds.has(assoc.sourceRef)
|
|
367
|
+
? assoc.sourceRef
|
|
368
|
+
: annLocalBounds.has(assoc.targetRef)
|
|
369
|
+
? assoc.targetRef
|
|
370
|
+
: undefined;
|
|
371
|
+
const elId = annId === assoc.sourceRef ? assoc.targetRef : assoc.sourceRef;
|
|
372
|
+
const annB = annId ? annLocalBounds.get(annId) : undefined;
|
|
373
|
+
const elNode = nodeById.get(elId);
|
|
374
|
+
if (!annB || !elNode)
|
|
375
|
+
continue;
|
|
376
|
+
const elB = elNode.bounds;
|
|
377
|
+
const annCx = Math.round(annB.x + annB.width / 2 + dx);
|
|
378
|
+
const elCx = Math.round(elB.x + elB.width / 2 + dx);
|
|
379
|
+
let waypoints;
|
|
380
|
+
if (annB.y >= elB.y + elB.height) {
|
|
381
|
+
// annotation below: element bottom-center → annotation top-center
|
|
382
|
+
waypoints = [
|
|
383
|
+
{ x: elCx, y: Math.round(elB.y + elB.height + dy) },
|
|
384
|
+
{ x: annCx, y: Math.round(annB.y + dy) },
|
|
385
|
+
];
|
|
386
|
+
}
|
|
387
|
+
else if (annB.y + annB.height <= elB.y) {
|
|
388
|
+
// annotation above: element top-center → annotation bottom-center
|
|
389
|
+
waypoints = [
|
|
390
|
+
{ x: elCx, y: Math.round(elB.y + dy) },
|
|
391
|
+
{ x: annCx, y: Math.round(annB.y + annB.height + dy) },
|
|
392
|
+
];
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
// side-by-side: center-to-center
|
|
396
|
+
waypoints = [
|
|
397
|
+
{ x: Math.round(elB.x + elB.width / 2 + dx), y: Math.round(elB.y + elB.height / 2 + dy) },
|
|
398
|
+
{ x: annCx, y: Math.round(annB.y + annB.height / 2 + dy) },
|
|
399
|
+
];
|
|
400
|
+
}
|
|
401
|
+
allEdges.push({
|
|
402
|
+
id: `${assoc.id}_di`,
|
|
403
|
+
bpmnElement: assoc.id,
|
|
404
|
+
waypoints,
|
|
405
|
+
unknownAttributes: {},
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
115
409
|
/**
|
|
116
410
|
* Apply auto-layout to all processes in a BpmnDefinitions, replacing the
|
|
117
411
|
* diagram interchange (BPMNDi) with freshly computed positions.
|
|
@@ -139,27 +433,41 @@ export function applyAutoLayout(defs) {
|
|
|
139
433
|
const lanes = process.laneSet?.lanes ?? [];
|
|
140
434
|
const hasLanes = lanes.length > 0;
|
|
141
435
|
const result = layoutProcess(process);
|
|
436
|
+
// Post-process boundary events: reposition each boundary event to the bottom
|
|
437
|
+
// of its host task, then walk its exclusive downstream chain and reposition
|
|
438
|
+
// those nodes horizontally to the right of the host task.
|
|
439
|
+
repositionBoundaryEvents(process.flowElements, result);
|
|
440
|
+
// Re-resolve edge crossings after boundary events moved shapes
|
|
441
|
+
const nodeMap = new Map(result.nodes.map((n) => [n.id, n]));
|
|
442
|
+
resolveEdgeCrossings(result.edges, nodeMap);
|
|
142
443
|
if (result.nodes.length === 0)
|
|
143
444
|
continue;
|
|
144
|
-
|
|
445
|
+
// Pre-compute annotation positions in layout space so they're included in the bbox
|
|
446
|
+
const annBounds = computeAnnotationLocalBounds(process, result.nodes);
|
|
447
|
+
const { minX, minY, maxX, maxY } = contentBbox(result.nodes, annBounds.values());
|
|
145
448
|
const contentW = maxX - minX;
|
|
146
449
|
const contentH = maxY - minY;
|
|
450
|
+
let dx;
|
|
451
|
+
let dy;
|
|
147
452
|
if (participantId) {
|
|
148
|
-
// Elements sit inside pool content area:
|
|
149
|
-
// x starts at: POOL_HEADER + optional LANE_HEADER + PADDING
|
|
150
|
-
// y starts at: poolY + PADDING
|
|
151
453
|
const elemX = POOL_HEADER + (hasLanes ? LANE_HEADER : 0) + PADDING;
|
|
152
454
|
const elemY = poolY + PADDING;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
455
|
+
dx = elemX - minX;
|
|
456
|
+
dy = elemY - minY;
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
dx = PADDING - minX;
|
|
460
|
+
dy = PADDING - minY;
|
|
461
|
+
}
|
|
462
|
+
for (const node of result.nodes)
|
|
463
|
+
allShapes.push(nodeToShape(node, dx, dy));
|
|
464
|
+
for (const edge of result.edges)
|
|
465
|
+
allEdges.push(edgeToShape(edge, dx, dy));
|
|
466
|
+
addAnnotationShapes(process, result.nodes, annBounds, allShapes, allEdges, dx, dy);
|
|
467
|
+
if (participantId) {
|
|
159
468
|
const innerW = (hasLanes ? LANE_HEADER : 0) + contentW + 2 * PADDING;
|
|
160
469
|
const innerH = contentH + 2 * PADDING;
|
|
161
470
|
const poolW = POOL_HEADER + innerW;
|
|
162
|
-
// Pool (participant) shape
|
|
163
471
|
allShapes.push({
|
|
164
472
|
id: `${participantId}_di`,
|
|
165
473
|
bpmnElement: participantId,
|
|
@@ -173,15 +481,6 @@ export function applyAutoLayout(defs) {
|
|
|
173
481
|
}
|
|
174
482
|
poolY += innerH + POOL_GAP;
|
|
175
483
|
}
|
|
176
|
-
else {
|
|
177
|
-
// No collaboration — layout at (PADDING, PADDING)
|
|
178
|
-
const dx = PADDING - minX;
|
|
179
|
-
const dy = PADDING - minY;
|
|
180
|
-
for (const node of result.nodes)
|
|
181
|
-
allShapes.push(nodeToShape(node, dx, dy));
|
|
182
|
-
for (const edge of result.edges)
|
|
183
|
-
allEdges.push(edgeToShape(edge, dx, dy));
|
|
184
|
-
}
|
|
185
484
|
}
|
|
186
485
|
const planeBpmnElement = collab?.id ?? defs.processes[0]?.id ?? "plane";
|
|
187
486
|
const existingDiagram = defs.diagrams[0];
|
|
@@ -1073,6 +1073,7 @@ export class ProcessBuilder {
|
|
|
1073
1073
|
errors: this.rootErrors,
|
|
1074
1074
|
escalations: [],
|
|
1075
1075
|
messages: this.rootMessages,
|
|
1076
|
+
signals: [],
|
|
1076
1077
|
collaborations: [],
|
|
1077
1078
|
processes: [process],
|
|
1078
1079
|
diagrams: this._autoLayout ? [this.buildDiagram(process)] : [],
|
|
@@ -336,6 +336,11 @@ export interface BpmnMessage {
|
|
|
336
336
|
name?: string;
|
|
337
337
|
unknownAttributes: Record<string, string>;
|
|
338
338
|
}
|
|
339
|
+
/** A root-level BPMN signal definition referenced by signal catch/throw events. */
|
|
340
|
+
export interface BpmnSignal {
|
|
341
|
+
id: string;
|
|
342
|
+
name?: string;
|
|
343
|
+
}
|
|
339
344
|
/** Optional label positioning information for a BPMNDi shape or edge. */
|
|
340
345
|
export interface BpmnDiLabel {
|
|
341
346
|
bounds?: BpmnBounds;
|
|
@@ -397,6 +402,7 @@ export interface BpmnDefinitions {
|
|
|
397
402
|
errors: BpmnError[];
|
|
398
403
|
escalations: BpmnEscalation[];
|
|
399
404
|
messages: BpmnMessage[];
|
|
405
|
+
signals: BpmnSignal[];
|
|
400
406
|
collaborations: BpmnCollaboration[];
|
|
401
407
|
processes: BpmnProcess[];
|
|
402
408
|
diagrams: BpmnDiagram[];
|
package/dist/bpmn/bpmn-parser.js
CHANGED
|
@@ -505,6 +505,12 @@ function parseMessage(element) {
|
|
|
505
505
|
unknownAttributes: unknownAttrs(element),
|
|
506
506
|
};
|
|
507
507
|
}
|
|
508
|
+
function parseSignal(element) {
|
|
509
|
+
return {
|
|
510
|
+
id: requiredAttr(element, "id"),
|
|
511
|
+
name: attr(element, "name"),
|
|
512
|
+
};
|
|
513
|
+
}
|
|
508
514
|
// ---------------------------------------------------------------------------
|
|
509
515
|
// Diagram interchange
|
|
510
516
|
// ---------------------------------------------------------------------------
|
|
@@ -607,6 +613,7 @@ export function parseBpmn(xml) {
|
|
|
607
613
|
const errors = [];
|
|
608
614
|
const escalations = [];
|
|
609
615
|
const messages = [];
|
|
616
|
+
const signals = [];
|
|
610
617
|
const collaborations = [];
|
|
611
618
|
const processes = [];
|
|
612
619
|
const diagrams = [];
|
|
@@ -618,6 +625,8 @@ export function parseBpmn(xml) {
|
|
|
618
625
|
escalations.push(parseEscalation(child));
|
|
619
626
|
else if (ln === "message")
|
|
620
627
|
messages.push(parseMessage(child));
|
|
628
|
+
else if (ln === "signal")
|
|
629
|
+
signals.push(parseSignal(child));
|
|
621
630
|
else if (ln === "collaboration")
|
|
622
631
|
collaborations.push(parseCollaboration(child));
|
|
623
632
|
else if (ln === "process")
|
|
@@ -635,6 +644,7 @@ export function parseBpmn(xml) {
|
|
|
635
644
|
errors,
|
|
636
645
|
escalations,
|
|
637
646
|
messages,
|
|
647
|
+
signals,
|
|
638
648
|
collaborations,
|
|
639
649
|
processes,
|
|
640
650
|
diagrams,
|
|
@@ -387,6 +387,12 @@ function serializeMessage(m, bp) {
|
|
|
387
387
|
attrs.name = m.name;
|
|
388
388
|
return el(`${bp}:message`, attrs, []);
|
|
389
389
|
}
|
|
390
|
+
function serializeSignal(s, bp) {
|
|
391
|
+
const attrs = { id: s.id };
|
|
392
|
+
if (s.name !== undefined)
|
|
393
|
+
attrs.name = s.name;
|
|
394
|
+
return el(`${bp}:signal`, attrs, []);
|
|
395
|
+
}
|
|
390
396
|
// ---------------------------------------------------------------------------
|
|
391
397
|
// Diagram interchange
|
|
392
398
|
// ---------------------------------------------------------------------------
|
|
@@ -490,7 +496,7 @@ export function serializeBpmn(definitions) {
|
|
|
490
496
|
attrs[key] = value;
|
|
491
497
|
}
|
|
492
498
|
const children = [];
|
|
493
|
-
// Root elements: errors, escalations, messages first
|
|
499
|
+
// Root elements: errors, escalations, messages, signals first
|
|
494
500
|
for (const e of definitions.escalations) {
|
|
495
501
|
children.push(serializeEscalation(e, bp));
|
|
496
502
|
}
|
|
@@ -500,6 +506,9 @@ export function serializeBpmn(definitions) {
|
|
|
500
506
|
for (const m of definitions.messages) {
|
|
501
507
|
children.push(serializeMessage(m, bp));
|
|
502
508
|
}
|
|
509
|
+
for (const s of definitions.signals ?? []) {
|
|
510
|
+
children.push(serializeSignal(s, bp));
|
|
511
|
+
}
|
|
503
512
|
// Collaborations
|
|
504
513
|
for (const c of definitions.collaborations) {
|
|
505
514
|
children.push(serializeCollaboration(c, ns));
|
package/dist/bpmn/compact.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { findElement, findElementInProcess, findProcess, findSequenceFlow, getAl
|
|
|
5
5
|
export { Bpmn, SAMPLE_BPMN_XML } from "./bpmn/index.js";
|
|
6
6
|
export { applyAutoLayout } from "./bpmn/auto-layout.js";
|
|
7
7
|
export type { ProcessBuilder, BranchBuilder, SubProcessContentBuilder, ServiceTaskOptions, ScriptTaskOptions, UserTaskOptions, CallActivityOptions, BusinessRuleTaskOptions, ElementOptions, GatewayOptions, MultiInstanceOptions, SubProcessOptions, StartEventOptions, IntermediateCatchEventOptions, IntermediateThrowEventOptions, BoundaryEventOptions, AdHocSubProcessOptions, } from "./bpmn/bpmn-builder.js";
|
|
8
|
-
export type { BpmnDefinitions, BpmnProcess, BpmnFlowNode, BpmnFlowElement, BpmnSequenceFlow, BpmnBoundaryEvent, BpmnElementType, BpmnStartEvent, BpmnEndEvent, BpmnIntermediateCatchEvent, BpmnIntermediateThrowEvent, BpmnTask, BpmnServiceTask, BpmnScriptTask, BpmnUserTask, BpmnSendTask, BpmnReceiveTask, BpmnBusinessRuleTask, BpmnManualTask, BpmnCallActivity, BpmnSubProcess, BpmnAdHocSubProcess, BpmnEventSubProcess, BpmnTransaction, BpmnExclusiveGateway, BpmnParallelGateway, BpmnInclusiveGateway, BpmnEventBasedGateway, BpmnComplexGateway, BpmnCollaboration, BpmnParticipant, BpmnMessageFlow, BpmnLane, BpmnLaneSet, BpmnError, BpmnEscalation, BpmnMessage, BpmnTextAnnotation, BpmnAssociation, BpmnConditionExpression, BpmnEventDefinition, BpmnTimerEventDefinition, BpmnErrorEventDefinition, BpmnEscalationEventDefinition, BpmnMessageEventDefinition, BpmnSignalEventDefinition, BpmnConditionalEventDefinition, BpmnLinkEventDefinition, BpmnCancelEventDefinition, BpmnTerminateEventDefinition, BpmnCompensateEventDefinition, BpmnMultiInstanceLoopCharacteristics, BpmnDiagram, BpmnDiPlane, BpmnDiShape, BpmnDiEdge, BpmnDiLabel, BpmnBounds, BpmnWaypoint, } from "./bpmn/bpmn-model.js";
|
|
8
|
+
export type { BpmnDefinitions, BpmnProcess, BpmnFlowNode, BpmnFlowElement, BpmnSequenceFlow, BpmnBoundaryEvent, BpmnElementType, BpmnStartEvent, BpmnEndEvent, BpmnIntermediateCatchEvent, BpmnIntermediateThrowEvent, BpmnTask, BpmnServiceTask, BpmnScriptTask, BpmnUserTask, BpmnSendTask, BpmnReceiveTask, BpmnBusinessRuleTask, BpmnManualTask, BpmnCallActivity, BpmnSubProcess, BpmnAdHocSubProcess, BpmnEventSubProcess, BpmnTransaction, BpmnExclusiveGateway, BpmnParallelGateway, BpmnInclusiveGateway, BpmnEventBasedGateway, BpmnComplexGateway, BpmnCollaboration, BpmnParticipant, BpmnMessageFlow, BpmnLane, BpmnLaneSet, BpmnError, BpmnEscalation, BpmnMessage, BpmnSignal, BpmnTextAnnotation, BpmnAssociation, BpmnConditionExpression, BpmnEventDefinition, BpmnTimerEventDefinition, BpmnErrorEventDefinition, BpmnEscalationEventDefinition, BpmnMessageEventDefinition, BpmnSignalEventDefinition, BpmnConditionalEventDefinition, BpmnLinkEventDefinition, BpmnCancelEventDefinition, BpmnTerminateEventDefinition, BpmnCompensateEventDefinition, BpmnMultiInstanceLoopCharacteristics, BpmnDiagram, BpmnDiPlane, BpmnDiShape, BpmnDiEdge, BpmnDiLabel, BpmnBounds, BpmnWaypoint, } from "./bpmn/bpmn-model.js";
|
|
9
9
|
export type { RestConnectorConfig, RestAuthentication, HttpMethod, } from "./bpmn/rest-connector.js";
|
|
10
10
|
export type { ZeebeExtensions, ZeebeTaskDefinition, ZeebeIoMapping, ZeebeIoMappingEntry, ZeebeTaskHeaders, ZeebeTaskHeaderEntry, ZeebeProperties, ZeebePropertyEntry, ZeebeFormDefinition, ZeebeCalledDecision, } from "./bpmn/zeebe-extensions.js";
|
|
11
11
|
export { zeebeExtensionsToXmlElements } from "./bpmn/zeebe-extensions.js";
|
|
@@ -32,7 +32,7 @@ export { layoutProcess, layoutFlowNodes } from "./layout/index.js";
|
|
|
32
32
|
export { benchmarkLayout, compareLayouts, formatBenchmarkResult, generateAutoLayout, parseReferenceLayout, } from "./layout/index.js";
|
|
33
33
|
export type { BenchmarkResult, BoundingBox, ElementComparison, ElementPosition, FlowOrderViolation, } from "./layout/index.js";
|
|
34
34
|
export type { Bounds, LayoutEdge, LayoutNode, LayoutResult, Waypoint } from "./layout/index.js";
|
|
35
|
-
export { ELEMENT_SIZES, GRID_CELL_HEIGHT } from "./layout/index.js";
|
|
35
|
+
export { ELEMENT_SIZES, GRID_CELL_HEIGHT, GRID_CELL_WIDTH } from "./layout/index.js";
|
|
36
36
|
export { compactify, expand } from "./bpmn/compact.js";
|
|
37
37
|
export { applyOperations } from "./bpmn/operations.js";
|
|
38
38
|
export type { BpmnOperation } from "./bpmn/operations.js";
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ export { renderStoryHtml } from "./bpmn/story.js";
|
|
|
15
15
|
export { analyzeVariableFlow, extractFeelIdentifiers } from "./bpmn/optimize/variable-flow.js";
|
|
16
16
|
export { layoutProcess, layoutFlowNodes } from "./layout/index.js";
|
|
17
17
|
export { benchmarkLayout, compareLayouts, formatBenchmarkResult, generateAutoLayout, parseReferenceLayout, } from "./layout/index.js";
|
|
18
|
-
export { ELEMENT_SIZES, GRID_CELL_HEIGHT } from "./layout/index.js";
|
|
18
|
+
export { ELEMENT_SIZES, GRID_CELL_HEIGHT, GRID_CELL_WIDTH } from "./layout/index.js";
|
|
19
19
|
export { compactify, expand } from "./bpmn/compact.js";
|
|
20
20
|
export { applyOperations } from "./bpmn/operations.js";
|
|
21
21
|
export { buildValidationDmn, findValidationStructure, getValidationInputNames, insertValidationStructure, removeValidationStructure, validationDecisionId, } from "./bpmn/input-validation.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Bounds, Waypoint } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Route a single edge using A* on a 10px grid.
|
|
4
|
+
* source/target are center points of source/target nodes.
|
|
5
|
+
* obstacles are node bounding boxes to avoid (inflated by 6px margin).
|
|
6
|
+
* Returns simplified orthogonal waypoints.
|
|
7
|
+
*/
|
|
8
|
+
export declare function routeEdgeAstar(source: {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
}, target: {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}, obstacles: Bounds[], canvasWidth: number, canvasHeight: number): Waypoint[];
|
|
15
|
+
//# sourceMappingURL=astar.d.ts.map
|