@aranzatech/diagrams-bpmn 0.1.3 → 0.2.1

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +121 -0
  3. package/dist/catalog-OVnBDD8R.d.ts +9 -0
  4. package/dist/catalog-OWfI_yHU.d.cts +9 -0
  5. package/dist/{chunk-W3ROOC6E.js → chunk-33AR3PXF.js} +177 -40
  6. package/dist/chunk-33AR3PXF.js.map +1 -0
  7. package/dist/{chunk-3AFZDIMQ.js → chunk-ECTJRD7Z.js} +3 -3
  8. package/dist/{chunk-3AFZDIMQ.js.map → chunk-ECTJRD7Z.js.map} +1 -1
  9. package/dist/{chunk-NXMUX67A.js → chunk-H3YMTGFG.js} +79 -38
  10. package/dist/chunk-H3YMTGFG.js.map +1 -0
  11. package/dist/chunk-KALSGH4D.js +36 -0
  12. package/dist/chunk-KALSGH4D.js.map +1 -0
  13. package/dist/{chunk-DNR5WBQH.js → chunk-L5Z22RLX.js} +81 -20
  14. package/dist/chunk-L5Z22RLX.js.map +1 -0
  15. package/dist/chunk-OZKTOILD.js +3 -0
  16. package/dist/chunk-OZKTOILD.js.map +1 -0
  17. package/dist/{chunk-4AX573IV.js → chunk-RLAJNRF2.js} +3 -3
  18. package/dist/{chunk-4AX573IV.js.map → chunk-RLAJNRF2.js.map} +1 -1
  19. package/dist/chunk-YQTIODXH.js +532 -0
  20. package/dist/chunk-YQTIODXH.js.map +1 -0
  21. package/dist/chunk-ZFGQVLHB.js +226 -0
  22. package/dist/chunk-ZFGQVLHB.js.map +1 -0
  23. package/dist/edges/index.cjs +1 -1
  24. package/dist/edges/index.cjs.map +1 -1
  25. package/dist/edges/index.js +2 -2
  26. package/dist/elements/index.cjs +81 -17
  27. package/dist/elements/index.cjs.map +1 -1
  28. package/dist/elements/index.d.cts +4 -6
  29. package/dist/elements/index.d.ts +4 -6
  30. package/dist/elements/index.js +3 -2
  31. package/dist/index.cjs +1120 -118
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +10 -63
  34. package/dist/index.d.ts +10 -63
  35. package/dist/index.js +9 -42
  36. package/dist/index.js.map +1 -1
  37. package/dist/modeling/index.cjs +1241 -0
  38. package/dist/modeling/index.cjs.map +1 -0
  39. package/dist/modeling/index.d.cts +146 -0
  40. package/dist/modeling/index.d.ts +146 -0
  41. package/dist/modeling/index.js +5 -0
  42. package/dist/modeling/index.js.map +1 -0
  43. package/dist/nodes/index.cjs +91 -38
  44. package/dist/nodes/index.cjs.map +1 -1
  45. package/dist/nodes/index.js +2 -2
  46. package/dist/types-BxjCV2oX.d.ts +20 -0
  47. package/dist/{types-C78d_Kdh.d.cts → types-DznxZxpV.d.cts} +17 -19
  48. package/dist/{types-C78d_Kdh.d.ts → types-DznxZxpV.d.ts} +17 -19
  49. package/dist/types-vVi5T7qj.d.cts +20 -0
  50. package/dist/validation/index.cjs +848 -0
  51. package/dist/validation/index.cjs.map +1 -0
  52. package/dist/validation/index.d.cts +25 -0
  53. package/dist/validation/index.d.ts +25 -0
  54. package/dist/validation/index.js +5 -0
  55. package/dist/validation/index.js.map +1 -0
  56. package/dist/xml/index.cjs +215 -54
  57. package/dist/xml/index.cjs.map +1 -1
  58. package/dist/xml/index.d.cts +4 -19
  59. package/dist/xml/index.d.ts +4 -19
  60. package/dist/xml/index.js +2 -2
  61. package/package.json +16 -4
  62. package/dist/chunk-23B2IGK5.js +0 -24
  63. package/dist/chunk-23B2IGK5.js.map +0 -1
  64. package/dist/chunk-DNR5WBQH.js.map +0 -1
  65. package/dist/chunk-NXMUX67A.js.map +0 -1
  66. package/dist/chunk-W3ROOC6E.js.map +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ - Expanded BPMN modeling helpers on top of the new `diagrams-core` primitives.
6
+ - Added BPMN node factory, edge-type inference and BPMN document serialization helpers.
7
+ - Added replace, resize, reparent, select, copy/paste and batch-command helpers.
8
+ - Improved BPMN connection rules to allow subprocesses as flow nodes while blocking pools/lanes/artifacts without handles.
9
+ - Added sequence-flow cardinality checks based on the BPMN element catalog.
10
+ - Boundary attachment now updates both `parentId` and BPMN `attachedToRef` data.
11
+
12
+ ## 0.2.1
13
+
14
+ - Added BPMN modeling helpers built on `@aranzatech/diagrams-core` commands and rules.
15
+ - Added public `@aranzatech/diagrams-bpmn/modeling` subpath.
16
+ - Improved XML roundtrip for default sequence flows, boundary event attachment,
17
+ non-interrupting boundary events, documentation and parent-relative coordinates.
18
+ - Removed Flowable adapter exports from the root public API; Flowable integration
19
+ is planned as a separate wrapper concern.
20
+ - Added README coverage matrix and stable-scope notes.
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # @aranzatech/diagrams-bpmn
2
+
3
+ BPMN 2.0 building blocks for the Aranza diagram ecosystem. This package owns
4
+ BPMN-specific visual components, element metadata, BPMN modeling helpers, XML
5
+ import/export and preview token simulation.
6
+
7
+ It is intentionally not responsible for execution-engine communication. Flowable
8
+ integration belongs in a future wrapper package or in the consuming app backend.
9
+
10
+ ## Public Subpaths
11
+
12
+ ```ts
13
+ import { BPMN_NODE_TYPES } from "@aranzatech/diagrams-bpmn/nodes";
14
+ import { BPMN_EDGE_TYPES } from "@aranzatech/diagrams-bpmn/edges";
15
+ import { BPMN_ELEMENT_CATALOG } from "@aranzatech/diagrams-bpmn/elements";
16
+ import { parseBpmnXml, serializeBpmnXml } from "@aranzatech/diagrams-bpmn/xml";
17
+ import {
18
+ createBpmnNodeCommand,
19
+ replaceBpmnNodeCommand,
20
+ serializeBpmnDiagram,
21
+ } from "@aranzatech/diagrams-bpmn/modeling";
22
+ import { createSimulation } from "@aranzatech/diagrams-bpmn/simulation";
23
+ ```
24
+
25
+ ## Scope
26
+
27
+ - BPMN visual node and edge renderers for ReactFlow.
28
+ - BPMN element catalog and guards.
29
+ - BPMN XML import/export through `bpmn-moddle`.
30
+ - BPMN modeling commands and interaction rules on top of `diagrams-core`.
31
+ - Client-side preview token simulation.
32
+
33
+ ## Out Of Scope
34
+
35
+ - Flowable REST/backend communication.
36
+ - BPMN business validation rules. Those belong in `@aranzatech/flowslint`.
37
+ - Full execution semantics.
38
+
39
+ ## Coverage Matrix
40
+
41
+ | Area | Status | Notes |
42
+ |---|---:|---|
43
+ | Events | Partial | Start, end, intermediate catch/throw, boundary; common triggers rendered/imported. |
44
+ | Tasks | Partial | Task variants and call activity rendered; advanced task semantics still need richer XML mapping. |
45
+ | Gateways | Good | Exclusive, inclusive, parallel, event-based and complex rendered. |
46
+ | Containers | Partial | Pool, lane, subprocess, transaction, event subprocess and ad-hoc subprocess rendered; lane resizing/modeling still needs depth. |
47
+ | Artifacts | Partial | Text annotation and group rendered; association semantics are basic. |
48
+ | Data | Partial | Data object/input/output/store and refs rendered; full item definitions/data states pending. |
49
+ | Conversation | Partial | Conversation nodes and links rendered; full conversation model pending. |
50
+ | Choreography | Partial | Choreography nodes rendered; participant semantics are lightweight. |
51
+ | XML import/export | Partial | Basic BPMN + DI, default flows, boundary attachment, docs and relative child coordinates; full lossless roundtrip pending. |
52
+ | Modeling | Growing | Commands and rules for create/connect/attach/delete/replace/resize/reparent/copy/paste; palette/context-pad/auto-place can build on this. |
53
+ | Simulation | Preview | Useful design-time token simulation, not an execution engine. |
54
+
55
+ ## Modeling Example
56
+
57
+ ```ts
58
+ import { createCommandStackState } from "@aranzatech/diagrams-core/commands";
59
+ import {
60
+ createBpmnNodeCommand,
61
+ connectBpmnCommand,
62
+ replaceBpmnNodeCommand,
63
+ runBpmnCommands,
64
+ runBpmnCommand,
65
+ } from "@aranzatech/diagrams-bpmn/modeling";
66
+
67
+ let stack = createCommandStackState({ nodes: [], edges: [] });
68
+
69
+ stack = runBpmnCommand(
70
+ stack,
71
+ createBpmnNodeCommand({
72
+ id: "task_1",
73
+ elementType: "Task",
74
+ position: { x: 100, y: 100 },
75
+ label: "Review request",
76
+ }),
77
+ );
78
+
79
+ stack = runBpmnCommand(
80
+ stack,
81
+ connectBpmnCommand({
82
+ source: "start_1",
83
+ target: "task_1",
84
+ }),
85
+ );
86
+
87
+ stack = runBpmnCommands(
88
+ stack,
89
+ [
90
+ replaceBpmnNodeCommand({
91
+ id: "task_1",
92
+ elementType: "UserTask",
93
+ label: "Human review",
94
+ }),
95
+ ],
96
+ { id: "review-as-user-task" },
97
+ );
98
+ ```
99
+
100
+ ## BPMN Diagram Document Example
101
+
102
+ ```ts
103
+ import {
104
+ deserializeBpmnDiagram,
105
+ serializeBpmnDiagram,
106
+ } from "@aranzatech/diagrams-bpmn/modeling";
107
+
108
+ const json = serializeBpmnDiagram(stack.current, {
109
+ metadata: { source: "designer" },
110
+ });
111
+
112
+ const restored = deserializeBpmnDiagram(json);
113
+ ```
114
+
115
+ ## Road To Stable
116
+
117
+ - Expand XML to preserve more BPMN semantics losslessly.
118
+ - Add real-world XML fixtures from bpmn.io/Camunda/Flowable modelers.
119
+ - Add palette/context-pad and auto-place commands on top of the current modeling API.
120
+ - Add robust lane/pool resize and reparent behavior.
121
+ - Move engine-specific extensions to a dedicated package.
@@ -0,0 +1,9 @@
1
+ import { e as BpmnElementType, c as BpmnElementMeta, d as BpmnElementSize } from './types-DznxZxpV.js';
2
+
3
+ declare const BPMN_ELEMENT_CATALOG: Record<BpmnElementType, BpmnElementMeta>;
4
+ declare function getElementMeta(type: BpmnElementType): BpmnElementMeta;
5
+ declare const BPMN_RESIZABLE_ELEMENT_TYPES: ("Task" | "UserTask" | "ServiceTask" | "ScriptTask" | "ManualTask" | "BusinessRuleTask" | "ReceiveTask" | "SendTask" | "CallActivity" | "SubProcess" | "Transaction" | "AdHocSubProcess" | "EventSubProcess" | "Pool" | "Lane" | "Annotation" | "Group" | "SubConversation" | "ChoreographyTask" | "SubChoreography" | "CallChoreography")[];
6
+ declare function isBpmnElementResizable(type: BpmnElementType): boolean;
7
+ declare function getBpmnElementSize(type: BpmnElementType): BpmnElementSize;
8
+
9
+ export { BPMN_ELEMENT_CATALOG as B, BPMN_RESIZABLE_ELEMENT_TYPES as a, getElementMeta as b, getBpmnElementSize as g, isBpmnElementResizable as i };
@@ -0,0 +1,9 @@
1
+ import { e as BpmnElementType, c as BpmnElementMeta, d as BpmnElementSize } from './types-DznxZxpV.cjs';
2
+
3
+ declare const BPMN_ELEMENT_CATALOG: Record<BpmnElementType, BpmnElementMeta>;
4
+ declare function getElementMeta(type: BpmnElementType): BpmnElementMeta;
5
+ declare const BPMN_RESIZABLE_ELEMENT_TYPES: ("Task" | "UserTask" | "ServiceTask" | "ScriptTask" | "ManualTask" | "BusinessRuleTask" | "ReceiveTask" | "SendTask" | "CallActivity" | "SubProcess" | "Transaction" | "AdHocSubProcess" | "EventSubProcess" | "Pool" | "Lane" | "Annotation" | "Group" | "SubConversation" | "ChoreographyTask" | "SubChoreography" | "CallChoreography")[];
6
+ declare function isBpmnElementResizable(type: BpmnElementType): boolean;
7
+ declare function getBpmnElementSize(type: BpmnElementType): BpmnElementSize;
8
+
9
+ export { BPMN_ELEMENT_CATALOG as B, BPMN_RESIZABLE_ELEMENT_TYPES as a, getElementMeta as b, getBpmnElementSize as g, isBpmnElementResizable as i };
@@ -1,4 +1,4 @@
1
- import { BPMN_ELEMENT_CATALOG } from './chunk-DNR5WBQH.js';
1
+ import { BPMN_ELEMENT_CATALOG } from './chunk-L5Z22RLX.js';
2
2
  import { BpmnModdle } from 'bpmn-moddle';
3
3
 
4
4
  // src/xml/mapper.ts
@@ -23,8 +23,8 @@ var MODDLE_TO_ELEMENT_TYPE = {
23
23
  "bpmn:EventBasedGateway": "EventBasedGateway",
24
24
  "bpmn:ComplexGateway": "ComplexGateway",
25
25
  "bpmn:SubProcess": "SubProcess",
26
- "bpmn:AdHocSubProcess": "SubProcess",
27
- "bpmn:Transaction": "SubProcess",
26
+ "bpmn:AdHocSubProcess": "AdHocSubProcess",
27
+ "bpmn:Transaction": "Transaction",
28
28
  "bpmn:TextAnnotation": "Annotation",
29
29
  "bpmn:Group": "Group",
30
30
  "bpmn:DataObject": "DataObject",
@@ -62,6 +62,9 @@ var ELEMENT_TYPE_TO_MODDLE = {
62
62
  EventBasedGateway: "bpmn:EventBasedGateway",
63
63
  ComplexGateway: "bpmn:ComplexGateway",
64
64
  SubProcess: "bpmn:SubProcess",
65
+ Transaction: "bpmn:Transaction",
66
+ EventSubProcess: "bpmn:SubProcess",
67
+ AdHocSubProcess: "bpmn:AdHocSubProcess",
65
68
  Pool: "bpmn:Participant",
66
69
  Lane: "bpmn:Lane",
67
70
  Annotation: "bpmn:TextAnnotation",
@@ -126,6 +129,10 @@ function asElements(v) {
126
129
  function asString(v) {
127
130
  return typeof v === "string" && v.trim() ? v.trim() : void 0;
128
131
  }
132
+ function extractDocumentation(el) {
133
+ const [doc] = asElements(el.documentation);
134
+ return asString(doc?.text);
135
+ }
129
136
  function extractDiagramInfo(definitions) {
130
137
  const shapes = /* @__PURE__ */ new Map();
131
138
  const waypoints = /* @__PURE__ */ new Map();
@@ -181,6 +188,7 @@ function extractTrigger(el) {
181
188
  function extractSubProcessVariant(el) {
182
189
  if (el.$type === "bpmn:Transaction") return "transaction";
183
190
  if (el.$type === "bpmn:AdHocSubProcess") return "adhoc";
191
+ if (el.$type === "bpmn:SubProcess" && el.triggeredByEvent === true) return "event";
184
192
  if (el.triggeredByEvent === true) return "event";
185
193
  return "embedded";
186
194
  }
@@ -194,7 +202,7 @@ function walkFlowElements(elements, parentId, ctx, nodes, edges) {
194
202
  continue;
195
203
  }
196
204
  if ($type === "bpmn:LaneSet") continue;
197
- const elementType = MODDLE_TO_ELEMENT_TYPE[$type];
205
+ const elementType = $type === "bpmn:SubProcess" && el.triggeredByEvent === true ? "EventSubProcess" : MODDLE_TO_ELEMENT_TYPE[$type];
198
206
  if (!elementType) {
199
207
  ctx.warnings.push(`Unknown element type "${$type}" (id=${id}) \u2014 skipped.`);
200
208
  continue;
@@ -215,22 +223,26 @@ function buildNode(el, elementType, parentId, ctx, nodes) {
215
223
  const y = shape?.y ?? 100;
216
224
  if (!shape) ctx.autoX.value += (meta?.defaultWidth ?? 120) + 20;
217
225
  const label = asString(el.name);
226
+ const documentation = extractDocumentation(el);
218
227
  const trigger = extractTrigger(el);
219
228
  const isNonInterrupting = el.cancelActivity === false;
229
+ const attachedToRef = el.attachedToRef?.id;
220
230
  const data = {
221
231
  elementType,
222
232
  ...label ? { label } : {},
233
+ ...documentation ? { documentation } : {},
223
234
  ...trigger ? { trigger } : {},
224
- ...isNonInterrupting ? { isNonInterrupting: true } : {}
235
+ ...isNonInterrupting ? { isNonInterrupting: true } : {},
236
+ ...attachedToRef ? { attachedToRef } : {}
225
237
  };
226
- if (elementType === "SubProcess") {
238
+ if (elementType === "SubProcess" || elementType === "Transaction" || elementType === "EventSubProcess" || elementType === "AdHocSubProcess") {
227
239
  const variant = extractSubProcessVariant(el);
228
240
  if (variant) data.subProcessVariant = variant;
229
241
  const isExpanded = shape?.isExpanded ?? true;
230
242
  data.isExpanded = isExpanded;
231
243
  }
232
244
  const laneId = ctx.laneMembership.get(id);
233
- const effectiveParentId = laneId ?? parentId;
245
+ const effectiveParentId = attachedToRef ?? laneId ?? parentId;
234
246
  const node = {
235
247
  id,
236
248
  type: elementType,
@@ -251,10 +263,12 @@ function buildEdge(el, edgeType, ctx, edges) {
251
263
  return;
252
264
  }
253
265
  const label = asString(el.name);
266
+ const documentation = extractDocumentation(el);
254
267
  const condExpr = el.conditionExpression;
255
268
  const data = {
256
269
  edgeType,
257
270
  ...label ? { label } : {},
271
+ ...documentation ? { documentation } : {},
258
272
  ...condExpr?.body ? { conditionExpression: condExpr.body } : {}
259
273
  };
260
274
  const waypoints = ctx.waypoints.get(id);
@@ -355,7 +369,46 @@ async function parseBpmnXml(xml) {
355
369
  );
356
370
  }
357
371
  }
358
- return { nodes, edges, warnings };
372
+ const defaultFlowById = /* @__PURE__ */ new Set();
373
+ const nodeIds = new Set(nodes.map((node) => node.id));
374
+ for (const rootEl of asElements(rootElement.rootElements)) {
375
+ collectDefaultFlows(rootEl, defaultFlowById);
376
+ }
377
+ const normalizedEdges = edges.map((edge) => {
378
+ if (!defaultFlowById.has(edge.id)) return edge;
379
+ const data = {
380
+ edgeType: edge.data?.edgeType ?? edge.type,
381
+ ...edge.data ?? {},
382
+ isDefault: true
383
+ };
384
+ return { ...edge, data };
385
+ });
386
+ return {
387
+ nodes: normalizeChildPositions(nodes, nodeIds),
388
+ edges: normalizedEdges,
389
+ warnings
390
+ };
391
+ }
392
+ function collectDefaultFlows(el, defaultFlowById) {
393
+ const defaultRef = el.default;
394
+ if (defaultRef?.id) defaultFlowById.add(defaultRef.id);
395
+ for (const child of asElements(el.rootElements)) collectDefaultFlows(child, defaultFlowById);
396
+ for (const child of asElements(el.flowElements)) collectDefaultFlows(child, defaultFlowById);
397
+ }
398
+ function normalizeChildPositions(nodes, nodeIds) {
399
+ const absoluteById = new Map(nodes.map((node) => [node.id, node.position]));
400
+ return nodes.map((node) => {
401
+ if (!node.parentId || !nodeIds.has(node.parentId)) return node;
402
+ const parentPosition = absoluteById.get(node.parentId);
403
+ if (!parentPosition) return node;
404
+ return {
405
+ ...node,
406
+ position: {
407
+ x: node.position.x - parentPosition.x,
408
+ y: node.position.y - parentPosition.y
409
+ }
410
+ };
411
+ });
359
412
  }
360
413
  function uid(prefix, id) {
361
414
  return `${prefix}_${id}`;
@@ -363,19 +416,6 @@ function uid(prefix, id) {
363
416
  function asNodes(nodes, types) {
364
417
  return nodes.filter((n) => types.includes(n.data.elementType));
365
418
  }
366
- function buildFlowableAttrs(data) {
367
- const attrs = {};
368
- if (data.flowableAssignee) attrs["flowable:assignee"] = data.flowableAssignee;
369
- if (data.flowableCandidateGroups) attrs["flowable:candidateGroups"] = data.flowableCandidateGroups;
370
- if (data.flowableCandidateUsers) attrs["flowable:candidateUsers"] = data.flowableCandidateUsers;
371
- if (data.flowableFormKey) attrs["flowable:formKey"] = data.flowableFormKey;
372
- if (data.flowableDueDate) attrs["flowable:dueDate"] = data.flowableDueDate;
373
- if (data.flowableType) attrs["flowable:type"] = data.flowableType;
374
- if (data.flowableExpression) attrs["flowable:expression"] = data.flowableExpression;
375
- if (data.flowableClass) attrs["flowable:class"] = data.flowableClass;
376
- if (data.flowableDelegateExpression) attrs["flowable:delegateExpression"] = data.flowableDelegateExpression;
377
- return attrs;
378
- }
379
419
  function buildSemanticModel(moddle, nodes, edges, opts) {
380
420
  const defId = opts.id ?? "Definitions_1";
381
421
  const defName = opts.name;
@@ -434,13 +474,32 @@ function buildSemanticModel(moddle, nodes, edges, opts) {
434
474
  return definitions;
435
475
  }
436
476
  function buildProcess(moddle, allNodes, allEdges, poolId, laneNodes, processId) {
437
- const myNodes = poolId ? allNodes.filter(
438
- (n) => n.parentId === poolId || laneNodes.some((l) => l.id === n.parentId && l.parentId === poolId)
439
- ) : allNodes.filter((n) => n.data.elementType !== "Pool" && n.data.elementType !== "Lane");
477
+ const subProcessIds = new Set(
478
+ allNodes.filter(
479
+ (n) => n.data.elementType === "SubProcess" || n.data.elementType === "Transaction" || n.data.elementType === "EventSubProcess" || n.data.elementType === "AdHocSubProcess"
480
+ ).map((n) => n.id)
481
+ );
482
+ const isInsideSubProcess = (node) => {
483
+ let parentId = node.parentId;
484
+ while (parentId) {
485
+ if (subProcessIds.has(parentId)) return true;
486
+ parentId = allNodes.find((candidate) => candidate.id === parentId)?.parentId;
487
+ }
488
+ return false;
489
+ };
490
+ const belongsToPool = (node) => {
491
+ let parentId = node.parentId;
492
+ while (parentId) {
493
+ if (parentId === poolId) return true;
494
+ parentId = allNodes.find((candidate) => candidate.id === parentId)?.parentId;
495
+ }
496
+ return false;
497
+ };
498
+ const myNodes = (poolId ? allNodes.filter((n) => belongsToPool(n)) : allNodes.filter((n) => n.data.elementType !== "Pool" && n.data.elementType !== "Lane")).filter((n) => !isInsideSubProcess(n));
440
499
  const myLanes = laneNodes.filter((l) => poolId ? l.parentId === poolId : true);
441
500
  const flowElements = [];
442
501
  for (const node of myNodes) {
443
- const el = buildFlowElement(moddle, node);
502
+ const el = buildFlowElement(moddle, node, allNodes, allEdges);
444
503
  if (el) flowElements.push(el);
445
504
  }
446
505
  const myNodeIds = new Set(myNodes.map((n) => n.id));
@@ -451,6 +510,17 @@ function buildProcess(moddle, allNodes, allEdges, poolId, laneNodes, processId)
451
510
  const edgeMeta = buildEdgeElement(moddle, edge);
452
511
  if (edgeMeta) flowElements.push(edgeMeta);
453
512
  }
513
+ const elementById = new Map(
514
+ flowElements.filter((element) => typeof element.id === "string").map((element) => [element.id, element])
515
+ );
516
+ for (const edge of myEdges) {
517
+ if (!edge.data?.isDefault) continue;
518
+ const sourceElement = elementById.get(edge.source);
519
+ const edgeElement = elementById.get(edge.id);
520
+ if (sourceElement && edgeElement) {
521
+ sourceElement.default = edgeElement;
522
+ }
523
+ }
454
524
  const process = moddle.create("bpmn:Process", {
455
525
  id: processId,
456
526
  isExecutable: true,
@@ -471,7 +541,7 @@ function buildProcess(moddle, allNodes, allEdges, poolId, laneNodes, processId)
471
541
  }
472
542
  return process;
473
543
  }
474
- function buildFlowElement(moddle, node, _allNodes, _allEdges) {
544
+ function buildFlowElement(moddle, node, allNodes, allEdges) {
475
545
  const { elementType, label, trigger } = node.data;
476
546
  if (elementType === "Pool" || elementType === "Lane") return null;
477
547
  const moddleType = ELEMENT_TYPE_TO_MODDLE[elementType];
@@ -480,25 +550,46 @@ function buildFlowElement(moddle, node, _allNodes, _allEdges) {
480
550
  id: node.id,
481
551
  name: label ?? ""
482
552
  };
553
+ if (node.data.documentation) {
554
+ attrs.documentation = [
555
+ moddle.create("bpmn:Documentation", { text: node.data.documentation })
556
+ ];
557
+ }
483
558
  if (trigger && trigger !== "none") {
484
559
  const defType = TRIGGER_TO_EVENT_DEF[trigger];
485
560
  if (defType) {
486
561
  attrs.eventDefinitions = [moddle.create(defType, { id: uid("EventDef", node.id) })];
487
562
  }
488
563
  }
489
- if (elementType === "SubProcess" && node.data.subProcessVariant === "transaction") {
490
- return moddle.create("bpmn:Transaction", attrs);
564
+ if (elementType === "BoundaryEvent") {
565
+ attrs.attachedToRef = { id: node.data.attachedToRef ?? node.parentId };
566
+ attrs.cancelActivity = node.data.isNonInterrupting ? false : true;
491
567
  }
492
- if (elementType === "SubProcess" && node.data.subProcessVariant === "adhoc") {
493
- return moddle.create("bpmn:AdHocSubProcess", attrs);
568
+ const isSubProcess = elementType === "SubProcess" || elementType === "Transaction" || elementType === "EventSubProcess" || elementType === "AdHocSubProcess";
569
+ if (isSubProcess) {
570
+ attrs.flowElements = buildNestedFlowElements(moddle, node, allNodes, allEdges);
571
+ if (elementType === "EventSubProcess" || node.data.subProcessVariant === "event") {
572
+ attrs.triggeredByEvent = true;
573
+ }
494
574
  }
495
575
  const element = moddle.create(moddleType, attrs);
496
- const flowableAttrs = buildFlowableAttrs(node.data);
497
- if (Object.keys(flowableAttrs).length > 0) {
498
- element.$attrs = flowableAttrs;
499
- }
500
576
  return element;
501
577
  }
578
+ function buildNestedFlowElements(moddle, parent, allNodes, allEdges) {
579
+ const childNodes = allNodes.filter((node) => node.parentId === parent.id);
580
+ const childNodeIds = new Set(childNodes.map((node) => node.id));
581
+ const flowElements = [];
582
+ for (const child of childNodes) {
583
+ const element = buildFlowElement(moddle, child, allNodes, allEdges);
584
+ if (element) flowElements.push(element);
585
+ }
586
+ for (const edge of allEdges) {
587
+ if (!childNodeIds.has(edge.source) || !childNodeIds.has(edge.target)) continue;
588
+ const element = buildEdgeElement(moddle, edge);
589
+ if (element) flowElements.push(element);
590
+ }
591
+ return flowElements;
592
+ }
502
593
  function buildEdgeElement(moddle, edge) {
503
594
  if (!edge.data) return null;
504
595
  const moddleType = EDGE_TYPE_TO_MODDLE[edge.data.edgeType];
@@ -509,6 +600,11 @@ function buildEdgeElement(moddle, edge) {
509
600
  sourceRef: { id: edge.source },
510
601
  targetRef: { id: edge.target }
511
602
  };
603
+ if (edge.data.documentation) {
604
+ attrs.documentation = [
605
+ moddle.create("bpmn:Documentation", { text: edge.data.documentation })
606
+ ];
607
+ }
512
608
  if (edge.data.conditionExpression) {
513
609
  attrs.conditionExpression = moddle.create("bpmn:FormalExpression", {
514
610
  body: edge.data.conditionExpression
@@ -519,13 +615,14 @@ function buildEdgeElement(moddle, edge) {
519
615
  function buildBpmnDI(moddle, definitions, nodes, edges) {
520
616
  const shapes = [];
521
617
  const edgeShapes = [];
618
+ const absolutePositionById = buildAbsolutePositionMap(nodes);
522
619
  for (const node of nodes) {
523
620
  const meta = BPMN_ELEMENT_CATALOG[node.data.elementType];
524
621
  const w = node.width ?? meta?.defaultWidth ?? 120;
525
622
  const h = node.height ?? meta?.defaultHeight ?? 60;
526
623
  const bounds = moddle.create("dc:Bounds", {
527
- x: node.position.x,
528
- y: node.position.y,
624
+ x: absolutePositionById.get(node.id)?.x ?? node.position.x,
625
+ y: absolutePositionById.get(node.id)?.y ?? node.position.y,
529
626
  width: w,
530
627
  height: h
531
628
  });
@@ -540,9 +637,9 @@ function buildBpmnDI(moddle, definitions, nodes, edges) {
540
637
  shapes.push(shape);
541
638
  }
542
639
  for (const edge of edges) {
543
- const waypoints = edge.data?.routingPoints?.map(
640
+ const waypoints = resolveEdgeWaypoints(edge, nodes, absolutePositionById).map(
544
641
  (p) => moddle.create("dc:Point", { x: p.x, y: p.y })
545
- ) ?? [];
642
+ );
546
643
  edgeShapes.push(
547
644
  moddle.create("bpmndi:BPMNEdge", {
548
645
  id: uid("BPMNEdge", edge.id),
@@ -564,6 +661,46 @@ function buildBpmnDI(moddle, definitions, nodes, edges) {
564
661
  });
565
662
  definitions.diagrams = [diagram];
566
663
  }
664
+ function resolveEdgeWaypoints(edge, nodes, absolutePositionById) {
665
+ if (edge.data?.routingPoints && edge.data.routingPoints.length >= 2) {
666
+ return edge.data.routingPoints;
667
+ }
668
+ const source = nodes.find((node) => node.id === edge.source);
669
+ const target = nodes.find((node) => node.id === edge.target);
670
+ if (!source || !target) return [];
671
+ const sourceMeta = BPMN_ELEMENT_CATALOG[source.data.elementType];
672
+ const targetMeta = BPMN_ELEMENT_CATALOG[target.data.elementType];
673
+ const sourcePosition = absolutePositionById.get(source.id) ?? source.position;
674
+ const targetPosition = absolutePositionById.get(target.id) ?? target.position;
675
+ return [
676
+ {
677
+ x: sourcePosition.x + (source.width ?? sourceMeta.defaultWidth) / 2,
678
+ y: sourcePosition.y + (source.height ?? sourceMeta.defaultHeight) / 2
679
+ },
680
+ {
681
+ x: targetPosition.x + (target.width ?? targetMeta.defaultWidth) / 2,
682
+ y: targetPosition.y + (target.height ?? targetMeta.defaultHeight) / 2
683
+ }
684
+ ];
685
+ }
686
+ function buildAbsolutePositionMap(nodes) {
687
+ const nodeById = new Map(nodes.map((node) => [node.id, node]));
688
+ const absoluteById = /* @__PURE__ */ new Map();
689
+ function resolve(node) {
690
+ const cached = absoluteById.get(node.id);
691
+ if (cached) return cached;
692
+ const parent = node.parentId ? nodeById.get(node.parentId) : void 0;
693
+ const parentPosition = parent ? resolve(parent) : { x: 0, y: 0 };
694
+ const absolute = {
695
+ x: parentPosition.x + node.position.x,
696
+ y: parentPosition.y + node.position.y
697
+ };
698
+ absoluteById.set(node.id, absolute);
699
+ return absolute;
700
+ }
701
+ for (const node of nodes) resolve(node);
702
+ return absoluteById;
703
+ }
567
704
  async function serializeBpmnXml(nodes, edges, opts = {}) {
568
705
  const moddle = new BpmnModdle();
569
706
  const definitions = buildSemanticModel(moddle, nodes, edges, opts);
@@ -575,5 +712,5 @@ async function serializeBpmnXml(nodes, edges, opts = {}) {
575
712
  }
576
713
 
577
714
  export { parseBpmnXml, serializeBpmnXml };
578
- //# sourceMappingURL=chunk-W3ROOC6E.js.map
579
- //# sourceMappingURL=chunk-W3ROOC6E.js.map
715
+ //# sourceMappingURL=chunk-33AR3PXF.js.map
716
+ //# sourceMappingURL=chunk-33AR3PXF.js.map