@bpmnkit/core 0.0.8
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 +185 -0
- package/dist/bpmn/auto-layout.d.ts +10 -0
- package/dist/bpmn/auto-layout.js +203 -0
- package/dist/bpmn/bpmn-builder.d.ts +381 -0
- package/dist/bpmn/bpmn-builder.js +1223 -0
- package/dist/bpmn/bpmn-model.d.ts +403 -0
- package/dist/bpmn/bpmn-model.js +2 -0
- package/dist/bpmn/bpmn-parser.d.ts +4 -0
- package/dist/bpmn/bpmn-parser.js +642 -0
- package/dist/bpmn/bpmn-serializer.d.ts +4 -0
- package/dist/bpmn/bpmn-serializer.js +515 -0
- package/dist/bpmn/compact.d.ts +91 -0
- package/dist/bpmn/compact.js +349 -0
- package/dist/bpmn/di-color.d.ts +9 -0
- package/dist/bpmn/di-color.js +32 -0
- package/dist/bpmn/index.d.ts +105 -0
- package/dist/bpmn/index.js +176 -0
- package/dist/bpmn/optimize/feel.d.ts +4 -0
- package/dist/bpmn/optimize/feel.js +186 -0
- package/dist/bpmn/optimize/flow.d.ts +4 -0
- package/dist/bpmn/optimize/flow.js +323 -0
- package/dist/bpmn/optimize/index.d.ts +5 -0
- package/dist/bpmn/optimize/index.js +51 -0
- package/dist/bpmn/optimize/naming.d.ts +8 -0
- package/dist/bpmn/optimize/naming.js +145 -0
- package/dist/bpmn/optimize/tasks.d.ts +4 -0
- package/dist/bpmn/optimize/tasks.js +273 -0
- package/dist/bpmn/optimize/types.d.ts +44 -0
- package/dist/bpmn/optimize/types.js +2 -0
- package/dist/bpmn/optimize/utils.d.ts +23 -0
- package/dist/bpmn/optimize/utils.js +129 -0
- package/dist/bpmn/rest-connector.d.ts +48 -0
- package/dist/bpmn/rest-connector.js +69 -0
- package/dist/bpmn/svg.d.ts +35 -0
- package/dist/bpmn/svg.js +557 -0
- package/dist/bpmn/type-guards.d.ts +136 -0
- package/dist/bpmn/type-guards.js +211 -0
- package/dist/bpmn/utils.d.ts +88 -0
- package/dist/bpmn/utils.js +207 -0
- package/dist/bpmn/zeebe-extensions.d.ts +72 -0
- package/dist/bpmn/zeebe-extensions.js +96 -0
- package/dist/dmn/compact.d.ts +44 -0
- package/dist/dmn/compact.js +125 -0
- package/dist/dmn/dmn-builder.d.ts +59 -0
- package/dist/dmn/dmn-builder.js +144 -0
- package/dist/dmn/dmn-layout.d.ts +57 -0
- package/dist/dmn/dmn-layout.js +256 -0
- package/dist/dmn/dmn-model.d.ts +158 -0
- package/dist/dmn/dmn-model.js +2 -0
- package/dist/dmn/dmn-parser.d.ts +4 -0
- package/dist/dmn/dmn-parser.js +275 -0
- package/dist/dmn/dmn-serializer.d.ts +4 -0
- package/dist/dmn/dmn-serializer.js +296 -0
- package/dist/dmn/index.d.ts +28 -0
- package/dist/dmn/index.js +51 -0
- package/dist/errors.d.ts +77 -0
- package/dist/errors.js +66 -0
- package/dist/form/compact.d.ts +20 -0
- package/dist/form/compact.js +146 -0
- package/dist/form/form-builder.d.ts +111 -0
- package/dist/form/form-builder.js +347 -0
- package/dist/form/form-model.d.ts +221 -0
- package/dist/form/form-model.js +2 -0
- package/dist/form/form-parser.d.ts +4 -0
- package/dist/form/form-parser.js +514 -0
- package/dist/form/form-serializer.d.ts +4 -0
- package/dist/form/form-serializer.js +240 -0
- package/dist/form/index.d.ts +27 -0
- package/dist/form/index.js +43 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +19 -0
- package/dist/layout/bench.d.ts +86 -0
- package/dist/layout/bench.js +225 -0
- package/dist/layout/coordinates.d.ts +62 -0
- package/dist/layout/coordinates.js +868 -0
- package/dist/layout/crossing.d.ts +8 -0
- package/dist/layout/crossing.js +60 -0
- package/dist/layout/graph.d.ts +28 -0
- package/dist/layout/graph.js +126 -0
- package/dist/layout/index.d.ts +7 -0
- package/dist/layout/index.js +5 -0
- package/dist/layout/layers.d.ts +13 -0
- package/dist/layout/layers.js +49 -0
- package/dist/layout/layout-engine.d.ts +21 -0
- package/dist/layout/layout-engine.js +191 -0
- package/dist/layout/overlap.d.ts +10 -0
- package/dist/layout/overlap.js +70 -0
- package/dist/layout/routing.d.ts +26 -0
- package/dist/layout/routing.js +439 -0
- package/dist/layout/subprocess.d.ts +14 -0
- package/dist/layout/subprocess.js +77 -0
- package/dist/layout/types.d.ts +72 -0
- package/dist/layout/types.js +37 -0
- package/dist/types/id-generator.d.ts +8 -0
- package/dist/types/id-generator.js +31 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +2 -0
- package/dist/types/xml-element.d.ts +12 -0
- package/dist/types/xml-element.js +2 -0
- package/dist/xml/index.d.ts +2 -0
- package/dist/xml/index.js +2 -0
- package/dist/xml/xml-parser.d.ts +13 -0
- package/dist/xml/xml-parser.js +271 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/bpmn-sdk/monorepo/main/doc/logos/logo-2-gateway.svg" width="72" height="72" alt="BPMN Kit logo">
|
|
3
|
+
<h1>@bpmnkit/core</h1>
|
|
4
|
+
<p>TypeScript-first BPMN 2.0 SDK — parse, build, layout, and optimize diagrams</p>
|
|
5
|
+
|
|
6
|
+
[](https://www.npmjs.com/package/@bpmnkit/core)
|
|
7
|
+
[](https://github.com/bpmnkit/monorepo/blob/main/LICENSE)
|
|
8
|
+
[](https://github.com/bpmnkit/monorepo)
|
|
9
|
+
|
|
10
|
+
[Documentation](https://bpmn-sdk-docs.pages.dev) · [GitHub](https://github.com/bpmnkit/monorepo) · [Changelog](https://github.com/bpmnkit/monorepo/blob/main/packages/core/CHANGELOG.md)
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
`@bpmnkit/core` is the foundation of the BPMN Kit. It gives you everything to work with BPMN 2.0, DMN 1.3, and Camunda Form definitions in pure TypeScript — no XML wrestling, no runtime dependencies.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
Parse → Modify → Validate → Export
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- **BPMN 2.0** — parse, create, and export process diagrams with full Zeebe/Camunda 8 extension support
|
|
26
|
+
- **Fluent Builder API** — construct valid processes programmatically, never touch raw XML
|
|
27
|
+
- **Sugiyama Layout Engine** — auto-position elements with clean orthogonal edge routing
|
|
28
|
+
- **DMN 1.3** — decision tables, including FEEL expression support
|
|
29
|
+
- **Camunda Form Definitions** — type-safe form schema builder
|
|
30
|
+
- **Optimizer** — built-in rule engine to detect and auto-fix anti-patterns
|
|
31
|
+
- **Compact Format** — 70% smaller token-efficient JSON representation for AI/LLM workflows
|
|
32
|
+
- **Zero Dependencies** — runs in browsers, Node.js, Deno, Bun, and edge runtimes
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npm install @bpmnkit/core
|
|
38
|
+
pnpm add @bpmnkit/core
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
### Build a process from code
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { Bpmn } from "@bpmnkit/core"
|
|
47
|
+
|
|
48
|
+
const process = Bpmn.createProcess("order-flow", "Order Flow")
|
|
49
|
+
.startEvent("start", "Order Received")
|
|
50
|
+
.serviceTask("validate", "Validate Order", {
|
|
51
|
+
type: "order-validator",
|
|
52
|
+
inputs: [{ source: "=order", target: "order" }],
|
|
53
|
+
outputs: [{ source: "=valid", target: "isValid" }],
|
|
54
|
+
})
|
|
55
|
+
.exclusiveGateway("check", "Order Valid?")
|
|
56
|
+
.sequenceFlow("check", "fulfill", "=isValid = true")
|
|
57
|
+
.serviceTask("fulfill", "Fulfill Order", { type: "fulfillment-service" })
|
|
58
|
+
.endEvent("end", "Order Complete")
|
|
59
|
+
.sequenceFlow("check", "reject", "=isValid = false")
|
|
60
|
+
.endEvent("reject-end", "Order Rejected")
|
|
61
|
+
.build()
|
|
62
|
+
|
|
63
|
+
const xml = Bpmn.export(process)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Parse and modify existing BPMN
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { Bpmn } from "@bpmnkit/core"
|
|
70
|
+
|
|
71
|
+
const defs = Bpmn.parse(xml)
|
|
72
|
+
const process = defs.processes[0]
|
|
73
|
+
|
|
74
|
+
// Access flow elements
|
|
75
|
+
for (const el of process.flowElements) {
|
|
76
|
+
console.log(el.type, el.id, el.name)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Serialize back to XML
|
|
80
|
+
const updated = Bpmn.export(defs)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Auto-layout a process
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { Bpmn, layoutProcess } from "@bpmnkit/core"
|
|
87
|
+
|
|
88
|
+
const defs = Bpmn.parse(xml)
|
|
89
|
+
const result = layoutProcess(defs.processes[0])
|
|
90
|
+
// result.defs now has updated DI coordinates
|
|
91
|
+
const laid = Bpmn.export(result.defs)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Optimize a diagram
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { Bpmn, optimize } from "@bpmnkit/core"
|
|
98
|
+
|
|
99
|
+
const defs = Bpmn.parse(xml)
|
|
100
|
+
const report = optimize(defs)
|
|
101
|
+
|
|
102
|
+
console.log(`${report.summary.total} findings`)
|
|
103
|
+
|
|
104
|
+
for (const finding of report.findings) {
|
|
105
|
+
console.log(`[${finding.severity}] ${finding.message}`)
|
|
106
|
+
if (finding.applyFix) {
|
|
107
|
+
const { description } = finding.applyFix(defs)
|
|
108
|
+
console.log("Fixed:", description)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Compact format for AI/LLM workflows
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { Bpmn, compactify, expand } from "@bpmnkit/core"
|
|
117
|
+
|
|
118
|
+
// Shrink for AI prompt
|
|
119
|
+
const defs = Bpmn.parse(xml)
|
|
120
|
+
const compact = compactify(defs) // ~70% smaller JSON
|
|
121
|
+
const json = JSON.stringify(compact) // send to LLM
|
|
122
|
+
|
|
123
|
+
// Restore full BPMN from AI response
|
|
124
|
+
const restored = expand(JSON.parse(json))
|
|
125
|
+
const outXml = Bpmn.export(restored)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## API Reference
|
|
129
|
+
|
|
130
|
+
### BPMN
|
|
131
|
+
|
|
132
|
+
| Export | Description |
|
|
133
|
+
|--------|-------------|
|
|
134
|
+
| `Bpmn.parse(xml)` | Parse BPMN XML → `BpmnDefinitions` |
|
|
135
|
+
| `Bpmn.export(defs)` | Serialize `BpmnDefinitions` → XML |
|
|
136
|
+
| `Bpmn.createProcess(id, name?)` | Start a `ProcessBuilder` |
|
|
137
|
+
| `Bpmn.makeEmpty(processId?, name?)` | Minimal BPMN XML with one start event |
|
|
138
|
+
| `Bpmn.SAMPLE_XML` | 3-node sample diagram string |
|
|
139
|
+
|
|
140
|
+
### DMN
|
|
141
|
+
|
|
142
|
+
| Export | Description |
|
|
143
|
+
|--------|-------------|
|
|
144
|
+
| `Dmn.parse(xml)` | Parse DMN XML → `DmnDefinitions` |
|
|
145
|
+
| `Dmn.export(defs)` | Serialize → XML |
|
|
146
|
+
| `Dmn.createDecisionTable(id, name?)` | Start a `DecisionTableBuilder` |
|
|
147
|
+
| `Dmn.makeEmpty()` | Minimal DMN with one empty decision table |
|
|
148
|
+
|
|
149
|
+
### Form
|
|
150
|
+
|
|
151
|
+
| Export | Description |
|
|
152
|
+
|--------|-------------|
|
|
153
|
+
| `Form.create()` | Start a `FormBuilder` |
|
|
154
|
+
| `Form.parse(json)` | Parse a form schema |
|
|
155
|
+
| `Form.export(schema)` | Serialize → JSON string |
|
|
156
|
+
|
|
157
|
+
### Layout & Optimization
|
|
158
|
+
|
|
159
|
+
| Export | Description |
|
|
160
|
+
|--------|-------------|
|
|
161
|
+
| `layoutProcess(process)` | Auto-layout all elements; returns `LayoutResult` |
|
|
162
|
+
| `optimize(defs)` | Run all optimization rules; returns `OptimizeReport` |
|
|
163
|
+
| `compactify(defs)` | Convert to compact `CompactDiagram` |
|
|
164
|
+
| `expand(compact)` | Restore full `BpmnDefinitions` |
|
|
165
|
+
| `generateId(prefix)` | Generate a unique short ID |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Related Packages
|
|
170
|
+
|
|
171
|
+
| Package | Description |
|
|
172
|
+
|---------|-------------|
|
|
173
|
+
| [`@bpmnkit/canvas`](https://www.npmjs.com/package/@bpmnkit/canvas) | Zero-dependency SVG BPMN viewer |
|
|
174
|
+
| [`@bpmnkit/editor`](https://www.npmjs.com/package/@bpmnkit/editor) | Full-featured interactive BPMN editor |
|
|
175
|
+
| [`@bpmnkit/engine`](https://www.npmjs.com/package/@bpmnkit/engine) | Lightweight BPMN process execution engine |
|
|
176
|
+
| [`@bpmnkit/feel`](https://www.npmjs.com/package/@bpmnkit/feel) | FEEL expression language parser & evaluator |
|
|
177
|
+
| [`@bpmnkit/plugins`](https://www.npmjs.com/package/@bpmnkit/plugins) | 22 composable canvas plugins |
|
|
178
|
+
| [`@bpmnkit/api`](https://www.npmjs.com/package/@bpmnkit/api) | Camunda 8 REST API TypeScript client |
|
|
179
|
+
| [`@bpmnkit/ascii`](https://www.npmjs.com/package/@bpmnkit/ascii) | Render BPMN diagrams as Unicode ASCII art |
|
|
180
|
+
| [`@bpmnkit/profiles`](https://www.npmjs.com/package/@bpmnkit/profiles) | Shared auth, profile storage, and client factories for CLI & proxy |
|
|
181
|
+
| [`@bpmnkit/operate`](https://www.npmjs.com/package/@bpmnkit/operate) | Monitoring & operations frontend for Camunda clusters |
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
[MIT](https://github.com/bpmnkit/monorepo/blob/main/LICENSE) © bpmn-sdk
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BpmnDefinitions } from "./bpmn-model.js";
|
|
2
|
+
/**
|
|
3
|
+
* Apply auto-layout to all processes in a BpmnDefinitions, replacing the
|
|
4
|
+
* diagram interchange (BPMNDi) with freshly computed positions.
|
|
5
|
+
*
|
|
6
|
+
* - Handles plain processes (no collaboration) and collaborations with pools.
|
|
7
|
+
* - When pools have lanes, lane shapes are tiled vertically around the process content.
|
|
8
|
+
*/
|
|
9
|
+
export declare function applyAutoLayout(defs: BpmnDefinitions): BpmnDefinitions;
|
|
10
|
+
//# sourceMappingURL=auto-layout.d.ts.map
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { layoutProcess } from "../layout/layout-engine.js";
|
|
2
|
+
const POOL_HEADER = 30;
|
|
3
|
+
const LANE_HEADER = 30;
|
|
4
|
+
const PADDING = 20;
|
|
5
|
+
const POOL_GAP = 30;
|
|
6
|
+
function contentBbox(nodes) {
|
|
7
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
8
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
9
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
10
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
11
|
+
for (const n of nodes) {
|
|
12
|
+
minX = Math.min(minX, n.bounds.x);
|
|
13
|
+
minY = Math.min(minY, n.bounds.y);
|
|
14
|
+
maxX = Math.max(maxX, n.bounds.x + n.bounds.width);
|
|
15
|
+
maxY = Math.max(maxY, n.bounds.y + n.bounds.height);
|
|
16
|
+
if (n.labelBounds) {
|
|
17
|
+
minX = Math.min(minX, n.labelBounds.x);
|
|
18
|
+
minY = Math.min(minY, n.labelBounds.y);
|
|
19
|
+
maxX = Math.max(maxX, n.labelBounds.x + n.labelBounds.width);
|
|
20
|
+
maxY = Math.max(maxY, n.labelBounds.y + n.labelBounds.height);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return { minX, minY, maxX, maxY };
|
|
24
|
+
}
|
|
25
|
+
function nodeToShape(node, dx, dy) {
|
|
26
|
+
const shape = {
|
|
27
|
+
id: `${node.id}_di`,
|
|
28
|
+
bpmnElement: node.id,
|
|
29
|
+
bounds: {
|
|
30
|
+
x: Math.round(node.bounds.x + dx),
|
|
31
|
+
y: Math.round(node.bounds.y + dy),
|
|
32
|
+
width: Math.round(node.bounds.width),
|
|
33
|
+
height: Math.round(node.bounds.height),
|
|
34
|
+
},
|
|
35
|
+
unknownAttributes: {},
|
|
36
|
+
};
|
|
37
|
+
if (node.isExpanded !== undefined)
|
|
38
|
+
shape.isExpanded = node.isExpanded;
|
|
39
|
+
if (node.labelBounds) {
|
|
40
|
+
shape.label = {
|
|
41
|
+
bounds: {
|
|
42
|
+
x: Math.round(node.labelBounds.x + dx),
|
|
43
|
+
y: Math.round(node.labelBounds.y + dy),
|
|
44
|
+
width: Math.round(node.labelBounds.width),
|
|
45
|
+
height: Math.round(node.labelBounds.height),
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return shape;
|
|
50
|
+
}
|
|
51
|
+
function edgeToShape(edge, dx, dy) {
|
|
52
|
+
const diEdge = {
|
|
53
|
+
id: `${edge.id}_di`,
|
|
54
|
+
bpmnElement: edge.id,
|
|
55
|
+
waypoints: edge.waypoints.map((wp) => ({
|
|
56
|
+
x: Math.round(wp.x + dx),
|
|
57
|
+
y: Math.round(wp.y + dy),
|
|
58
|
+
})),
|
|
59
|
+
unknownAttributes: {},
|
|
60
|
+
};
|
|
61
|
+
if (edge.labelBounds) {
|
|
62
|
+
diEdge.label = {
|
|
63
|
+
bounds: {
|
|
64
|
+
x: Math.round(edge.labelBounds.x + dx),
|
|
65
|
+
y: Math.round(edge.labelBounds.y + dy),
|
|
66
|
+
width: Math.round(edge.labelBounds.width),
|
|
67
|
+
height: Math.round(edge.labelBounds.height),
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return diEdge;
|
|
72
|
+
}
|
|
73
|
+
function buildLaneShapes(lanes, nodes, dx, dy, poolY, poolHeaderWidth, laneContentWidth, poolHeight) {
|
|
74
|
+
const elemToLane = new Map();
|
|
75
|
+
for (const lane of lanes) {
|
|
76
|
+
for (const ref of lane.flowNodeRefs) {
|
|
77
|
+
elemToLane.set(ref, lane.id);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Compute per-lane mean Y (after shift) to sort lanes top-to-bottom
|
|
81
|
+
const laneAccum = new Map();
|
|
82
|
+
for (const lane of lanes)
|
|
83
|
+
laneAccum.set(lane.id, { sum: 0, count: 0 });
|
|
84
|
+
for (const node of nodes) {
|
|
85
|
+
const laneId = elemToLane.get(node.id);
|
|
86
|
+
if (!laneId)
|
|
87
|
+
continue;
|
|
88
|
+
const acc = laneAccum.get(laneId);
|
|
89
|
+
if (acc) {
|
|
90
|
+
acc.sum += node.bounds.y + dy;
|
|
91
|
+
acc.count++;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const sortedLanes = [...lanes].sort((a, b) => {
|
|
95
|
+
const accA = laneAccum.get(a.id);
|
|
96
|
+
const accB = laneAccum.get(b.id);
|
|
97
|
+
const mA = accA && accA.count > 0 ? accA.sum / accA.count : Number.POSITIVE_INFINITY;
|
|
98
|
+
const mB = accB && accB.count > 0 ? accB.sum / accB.count : Number.POSITIVE_INFINITY;
|
|
99
|
+
return mA - mB;
|
|
100
|
+
});
|
|
101
|
+
const tileH = Math.round(poolHeight / sortedLanes.length);
|
|
102
|
+
return sortedLanes.map((lane, i) => ({
|
|
103
|
+
id: `${lane.id}_di`,
|
|
104
|
+
bpmnElement: lane.id,
|
|
105
|
+
isHorizontal: true,
|
|
106
|
+
bounds: {
|
|
107
|
+
x: Math.round(poolHeaderWidth),
|
|
108
|
+
y: Math.round(poolY + i * tileH),
|
|
109
|
+
width: Math.round(laneContentWidth),
|
|
110
|
+
height: Math.round(i === sortedLanes.length - 1 ? poolHeight - i * tileH : tileH),
|
|
111
|
+
},
|
|
112
|
+
unknownAttributes: {},
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Apply auto-layout to all processes in a BpmnDefinitions, replacing the
|
|
117
|
+
* diagram interchange (BPMNDi) with freshly computed positions.
|
|
118
|
+
*
|
|
119
|
+
* - Handles plain processes (no collaboration) and collaborations with pools.
|
|
120
|
+
* - When pools have lanes, lane shapes are tiled vertically around the process content.
|
|
121
|
+
*/
|
|
122
|
+
export function applyAutoLayout(defs) {
|
|
123
|
+
if (defs.processes.length === 0)
|
|
124
|
+
return defs;
|
|
125
|
+
const collab = defs.collaborations[0];
|
|
126
|
+
// Build processId → participantId map
|
|
127
|
+
const processToParticipant = new Map();
|
|
128
|
+
if (collab) {
|
|
129
|
+
for (const p of collab.participants) {
|
|
130
|
+
if (p.processRef)
|
|
131
|
+
processToParticipant.set(p.processRef, p.id);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const allShapes = [];
|
|
135
|
+
const allEdges = [];
|
|
136
|
+
let poolY = 0;
|
|
137
|
+
for (const process of defs.processes) {
|
|
138
|
+
const participantId = processToParticipant.get(process.id);
|
|
139
|
+
const lanes = process.laneSet?.lanes ?? [];
|
|
140
|
+
const hasLanes = lanes.length > 0;
|
|
141
|
+
const result = layoutProcess(process);
|
|
142
|
+
if (result.nodes.length === 0)
|
|
143
|
+
continue;
|
|
144
|
+
const { minX, minY, maxX, maxY } = contentBbox(result.nodes);
|
|
145
|
+
const contentW = maxX - minX;
|
|
146
|
+
const contentH = maxY - minY;
|
|
147
|
+
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
|
+
const elemX = POOL_HEADER + (hasLanes ? LANE_HEADER : 0) + PADDING;
|
|
152
|
+
const elemY = poolY + PADDING;
|
|
153
|
+
const dx = elemX - minX;
|
|
154
|
+
const dy = elemY - minY;
|
|
155
|
+
for (const node of result.nodes)
|
|
156
|
+
allShapes.push(nodeToShape(node, dx, dy));
|
|
157
|
+
for (const edge of result.edges)
|
|
158
|
+
allEdges.push(edgeToShape(edge, dx, dy));
|
|
159
|
+
const innerW = (hasLanes ? LANE_HEADER : 0) + contentW + 2 * PADDING;
|
|
160
|
+
const innerH = contentH + 2 * PADDING;
|
|
161
|
+
const poolW = POOL_HEADER + innerW;
|
|
162
|
+
// Pool (participant) shape
|
|
163
|
+
allShapes.push({
|
|
164
|
+
id: `${participantId}_di`,
|
|
165
|
+
bpmnElement: participantId,
|
|
166
|
+
isHorizontal: true,
|
|
167
|
+
bounds: { x: 0, y: poolY, width: poolW, height: innerH },
|
|
168
|
+
unknownAttributes: {},
|
|
169
|
+
});
|
|
170
|
+
if (hasLanes) {
|
|
171
|
+
const laneShapes = buildLaneShapes(lanes, result.nodes, dx, dy, poolY, POOL_HEADER, innerW, innerH);
|
|
172
|
+
allShapes.push(...laneShapes);
|
|
173
|
+
}
|
|
174
|
+
poolY += innerH + POOL_GAP;
|
|
175
|
+
}
|
|
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
|
+
}
|
|
186
|
+
const planeBpmnElement = collab?.id ?? defs.processes[0]?.id ?? "plane";
|
|
187
|
+
const existingDiagram = defs.diagrams[0];
|
|
188
|
+
return {
|
|
189
|
+
...defs,
|
|
190
|
+
diagrams: [
|
|
191
|
+
{
|
|
192
|
+
id: existingDiagram?.id ?? "BPMNDiagram_1",
|
|
193
|
+
plane: {
|
|
194
|
+
id: existingDiagram?.plane.id ?? "BPMNPlane_1",
|
|
195
|
+
bpmnElement: planeBpmnElement,
|
|
196
|
+
shapes: allShapes,
|
|
197
|
+
edges: allEdges,
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=auto-layout.js.map
|