@aranzatech/diagrams-bpmn 0.2.10 → 0.2.12

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/index.cjs CHANGED
@@ -2518,7 +2518,11 @@ var ARANZA_DESCRIPTOR = {
2518
2518
  { name: "formKey", isAttr: true, type: "String" },
2519
2519
  // UserTask: Flowable task assignment — comma-separated ids
2520
2520
  { name: "candidateUsers", isAttr: true, type: "String" },
2521
- { name: "candidateGroups", isAttr: true, type: "String" }
2521
+ { name: "candidateGroups", isAttr: true, type: "String" },
2522
+ // UserTask: scheduling + skip expression
2523
+ { name: "dueDate", isAttr: true, type: "String" },
2524
+ { name: "skipExpression", isAttr: true, type: "String" },
2525
+ { name: "businessCalendarName", isAttr: true, type: "String" }
2522
2526
  ]
2523
2527
  }
2524
2528
  ],
@@ -2835,6 +2839,12 @@ function extractAranzaExtensions(el) {
2835
2839
  if (candidateUsers) result.candidateUsers = candidateUsers;
2836
2840
  const candidateGroups = asString(taskConfig.candidateGroups);
2837
2841
  if (candidateGroups) result.candidateGroups = candidateGroups;
2842
+ const dueDate = asString(taskConfig.dueDate);
2843
+ if (dueDate) result.dueDate = dueDate;
2844
+ const skipExpression = asString(taskConfig.skipExpression);
2845
+ if (skipExpression) result.skipExpression = skipExpression;
2846
+ const businessCalendarName = asString(taskConfig.businessCalendarName);
2847
+ if (businessCalendarName) result.businessCalendarName = businessCalendarName;
2838
2848
  return result;
2839
2849
  }
2840
2850
  function extractCompletionCondition(el) {
@@ -2843,6 +2853,25 @@ function extractCompletionCondition(el) {
2843
2853
  if (typeof cc === "string") return cc.trim() || void 0;
2844
2854
  return asString(cc.body);
2845
2855
  }
2856
+ function extractLoopCharacteristics(el) {
2857
+ const lc = el.loopCharacteristics;
2858
+ if (!lc) return {};
2859
+ if (lc.$type === "bpmn:StandardLoopCharacteristics") {
2860
+ const loopCondition = asString(lc.loopCondition?.body);
2861
+ return { loopType: "loop", ...loopCondition ? { loopCondition } : {} };
2862
+ }
2863
+ if (lc.$type === "bpmn:MultiInstanceLoopCharacteristics") {
2864
+ const loopType = lc.isSequential === true ? "sequentialMultiple" : "parallelMultiple";
2865
+ const loopCardinality = asString(lc.loopCardinality?.body);
2866
+ const loopCompletionCondition = asString(lc.completionCondition?.body);
2867
+ return {
2868
+ loopType,
2869
+ ...loopCardinality ? { loopCardinality } : {},
2870
+ ...loopCompletionCondition ? { loopCompletionCondition } : {}
2871
+ };
2872
+ }
2873
+ return {};
2874
+ }
2846
2875
  function walkFlowElements(elements, parentId, ctx, nodes, edges) {
2847
2876
  for (const el of elements) {
2848
2877
  const { $type, id } = el;
@@ -2860,7 +2889,7 @@ function walkFlowElements(elements, parentId, ctx, nodes, edges) {
2860
2889
  }
2861
2890
  buildNode(el, elementType, parentId, ctx, nodes);
2862
2891
  if ($type === "bpmn:SubProcess" || $type === "bpmn:Transaction" || $type === "bpmn:AdHocSubProcess") {
2863
- const children = asElements(el.flowElements);
2892
+ const children = [...asElements(el.flowElements), ...asElements(el.artifacts)];
2864
2893
  const laneMembership = extractLaneMembership(el);
2865
2894
  walkFlowElements(children, id, { ...ctx, laneMembership }, nodes, edges);
2866
2895
  }
@@ -2873,7 +2902,7 @@ function buildNode(el, elementType, parentId, ctx, nodes) {
2873
2902
  const x = shape?.x ?? ctx.autoX.value;
2874
2903
  const y = shape?.y ?? 100;
2875
2904
  if (!shape) ctx.autoX.value += (meta?.defaultWidth ?? 120) + 20;
2876
- const label = asString(el.name);
2905
+ const label = elementType === "Annotation" ? asString(el.text) ?? asString(el.name) : asString(el.name);
2877
2906
  const documentation = extractDocumentation(el);
2878
2907
  const trigger = extractTrigger(el);
2879
2908
  const eventDefinition = extractEventDefinition(el);
@@ -2884,6 +2913,8 @@ function buildNode(el, elementType, parentId, ctx, nodes) {
2884
2913
  const scriptFormat = elementType === "ScriptTask" ? asString(el.scriptFormat) : void 0;
2885
2914
  const script = elementType === "ScriptTask" ? asString(el.script) : void 0;
2886
2915
  const completionCondition = elementType === "AdHocSubProcess" ? extractCompletionCondition(el) : void 0;
2916
+ const loopChars = extractLoopCharacteristics(el);
2917
+ const dataObjectRef = elementType === "DataObjectReference" ? asString(el.dataObjectRef?.id) : void 0;
2887
2918
  const data = {
2888
2919
  elementType,
2889
2920
  ...label ? { label } : {},
@@ -2904,6 +2935,8 @@ function buildNode(el, elementType, parentId, ctx, nodes) {
2904
2935
  ...scriptFormat ? { scriptFormat } : {},
2905
2936
  ...script ? { script } : {},
2906
2937
  ...completionCondition ? { completionCondition } : {},
2938
+ ...dataObjectRef ? { dataObjectRef } : {},
2939
+ ...loopChars,
2907
2940
  ...aranzaExt
2908
2941
  };
2909
2942
  if (elementType === "SubProcess" || elementType === "Transaction" || elementType === "EventSubProcess" || elementType === "AdHocSubProcess") {
@@ -2936,11 +2969,14 @@ function buildEdge(el, edgeType, ctx, edges) {
2936
2969
  const label = asString(el.name);
2937
2970
  const documentation = extractDocumentation(el);
2938
2971
  const condExpr = el.conditionExpression;
2972
+ const rawDir = asString(el.associationDirection);
2973
+ const associationDirection = edgeType === "association" && rawDir ? rawDir.toLowerCase() : void 0;
2939
2974
  const data = {
2940
2975
  edgeType,
2941
2976
  ...label ? { label } : {},
2942
2977
  ...documentation ? { documentation } : {},
2943
- ...condExpr?.body ? { conditionExpression: condExpr.body } : {}
2978
+ ...condExpr?.body ? { conditionExpression: condExpr.body } : {},
2979
+ ...associationDirection ? { associationDirection } : {}
2944
2980
  };
2945
2981
  const waypoints = ctx.waypoints.get(id);
2946
2982
  if (waypoints?.length) data.routingPoints = waypoints;
@@ -2965,7 +3001,7 @@ function handleCollaboration(collaboration, ctx, nodes, edges) {
2965
3001
  if (processRef) {
2966
3002
  const laneMembership = extractLaneMembership(processRef);
2967
3003
  walkFlowElements(
2968
- asElements(processRef.flowElements),
3004
+ [...asElements(processRef.flowElements), ...asElements(processRef.artifacts)],
2969
3005
  id,
2970
3006
  { ...ctx, laneMembership },
2971
3007
  nodes,
@@ -2997,7 +3033,7 @@ function addLaneNodes(laneSet, poolId, ctx, nodes) {
2997
3033
  data: { elementType: "Lane", ...label ? { label } : {} },
2998
3034
  width: shape?.width ?? 570,
2999
3035
  height: shape?.height ?? 120,
3000
- parentId: poolId
3036
+ ...poolId ? { parentId: poolId } : {}
3001
3037
  });
3002
3038
  const child = lane.childLaneSet;
3003
3039
  if (child) addLaneNodes(child, poolId, ctx, nodes);
@@ -3031,9 +3067,9 @@ async function parseBpmnXml(xml) {
3031
3067
  } else if (rootEl.$type === "bpmn:Process") {
3032
3068
  const laneMembership = extractLaneMembership(rootEl);
3033
3069
  const laneSet = rootEl.laneSet;
3034
- if (laneSet) addLaneNodes(laneSet, "", ctx, nodes);
3070
+ if (laneSet) addLaneNodes(laneSet, void 0, ctx, nodes);
3035
3071
  walkFlowElements(
3036
- asElements(rootEl.flowElements),
3072
+ [...asElements(rootEl.flowElements), ...asElements(rootEl.artifacts)],
3037
3073
  void 0,
3038
3074
  { ...ctx, laneMembership },
3039
3075
  nodes,
@@ -3206,7 +3242,10 @@ function buildAranzaExtensionElements(moddle, node) {
3206
3242
  const formKey = typeof node.data.formKey === "string" ? node.data.formKey : void 0;
3207
3243
  const candidateUsers = typeof node.data.candidateUsers === "string" ? node.data.candidateUsers : void 0;
3208
3244
  const candidateGroups = typeof node.data.candidateGroups === "string" ? node.data.candidateGroups : void 0;
3209
- if (!priority && !owner && !sla && !connector && !action && !flowableType && !flowableDelegateExpression && !decisionRef && !formKey && !candidateUsers && !candidateGroups) {
3245
+ const dueDate = typeof node.data.dueDate === "string" ? node.data.dueDate : void 0;
3246
+ const skipExpression = typeof node.data.skipExpression === "string" ? node.data.skipExpression : void 0;
3247
+ const businessCalendarName = typeof node.data.businessCalendarName === "string" ? node.data.businessCalendarName : void 0;
3248
+ if (!priority && !owner && !sla && !connector && !action && !flowableType && !flowableDelegateExpression && !decisionRef && !formKey && !candidateUsers && !candidateGroups && !dueDate && !skipExpression && !businessCalendarName) {
3210
3249
  return null;
3211
3250
  }
3212
3251
  const configAttrs = {};
@@ -3221,9 +3260,13 @@ function buildAranzaExtensionElements(moddle, node) {
3221
3260
  if (formKey) configAttrs.formKey = formKey;
3222
3261
  if (candidateUsers) configAttrs.candidateUsers = candidateUsers;
3223
3262
  if (candidateGroups) configAttrs.candidateGroups = candidateGroups;
3263
+ if (dueDate) configAttrs.dueDate = dueDate;
3264
+ if (skipExpression) configAttrs.skipExpression = skipExpression;
3265
+ if (businessCalendarName) configAttrs.businessCalendarName = businessCalendarName;
3224
3266
  const taskConfig = moddle.create("aranza:TaskConfig", configAttrs);
3225
3267
  return moddle.create("bpmn:ExtensionElements", { values: [taskConfig] });
3226
3268
  }
3269
+ var ARTIFACT_ELEMENT_TYPES = /* @__PURE__ */ new Set(["Annotation", "Group"]);
3227
3270
  function buildSemanticModel(moddle, nodes, edges, opts) {
3228
3271
  const defId = opts.id ?? "Definitions_1";
3229
3272
  const defName = opts.name;
@@ -3319,10 +3362,31 @@ function buildProcess(moddle, allNodes, allEdges, poolId, laneNodes, processId,
3319
3362
  };
3320
3363
  const myNodes = (poolId ? allNodes.filter((n) => belongsToPool(n)) : allNodes.filter((n) => n.data.elementType !== "Pool" && n.data.elementType !== "Lane")).filter((n) => !isInsideSubProcess(n));
3321
3364
  const myLanes = laneNodes.filter((l) => poolId ? l.parentId === poolId : true);
3365
+ const presentDataObjectIds = new Set(
3366
+ myNodes.filter((n) => n.data.elementType === "DataObject").map((n) => n.id)
3367
+ );
3322
3368
  const flowElements = [];
3369
+ const artifacts = [];
3370
+ for (const node of myNodes) {
3371
+ if (node.data.elementType === "DataObjectReference") {
3372
+ const explicitRef = typeof node.data.dataObjectRef === "string" ? node.data.dataObjectRef : null;
3373
+ if (!explicitRef || !presentDataObjectIds.has(explicitRef)) {
3374
+ const syntheticId = `DataObject_${node.id}`;
3375
+ if (!presentDataObjectIds.has(syntheticId)) {
3376
+ flowElements.push(moddle.create("bpmn:DataObject", { id: syntheticId }));
3377
+ presentDataObjectIds.add(syntheticId);
3378
+ }
3379
+ }
3380
+ }
3381
+ }
3323
3382
  for (const node of myNodes) {
3324
3383
  const el = buildFlowElement(moddle, node, allNodes, allEdges);
3325
- if (el) flowElements.push(el);
3384
+ if (!el) continue;
3385
+ if (ARTIFACT_ELEMENT_TYPES.has(node.data.elementType)) {
3386
+ artifacts.push(el);
3387
+ } else {
3388
+ flowElements.push(el);
3389
+ }
3326
3390
  }
3327
3391
  const myNodeIds = new Set(myNodes.map((n) => n.id));
3328
3392
  const myEdges = allEdges.filter(
@@ -3330,7 +3394,12 @@ function buildProcess(moddle, allNodes, allEdges, poolId, laneNodes, processId,
3330
3394
  );
3331
3395
  for (const edge of myEdges) {
3332
3396
  const edgeMeta = buildEdgeElement(moddle, edge);
3333
- if (edgeMeta) flowElements.push(edgeMeta);
3397
+ if (!edgeMeta) continue;
3398
+ if (edge.data?.edgeType === "association") {
3399
+ artifacts.push(edgeMeta);
3400
+ } else {
3401
+ flowElements.push(edgeMeta);
3402
+ }
3334
3403
  }
3335
3404
  const elementById = new Map(
3336
3405
  flowElements.filter((element) => typeof element.id === "string").map((element) => [element.id, element])
@@ -3346,7 +3415,8 @@ function buildProcess(moddle, allNodes, allEdges, poolId, laneNodes, processId,
3346
3415
  const process = moddle.create("bpmn:Process", {
3347
3416
  id: processId,
3348
3417
  isExecutable: opts.process?.executable ?? true,
3349
- flowElements
3418
+ flowElements,
3419
+ ...artifacts.length > 0 ? { artifacts } : {}
3350
3420
  });
3351
3421
  if (opts.process?.documentation) {
3352
3422
  process.documentation = [
@@ -3378,6 +3448,10 @@ function buildFlowElement(moddle, node, allNodes, allEdges) {
3378
3448
  id: node.id,
3379
3449
  name: label ?? ""
3380
3450
  };
3451
+ if (elementType === "Annotation") {
3452
+ delete attrs.name;
3453
+ attrs.text = label ?? "";
3454
+ }
3381
3455
  if (node.data.documentation) {
3382
3456
  attrs.documentation = [
3383
3457
  moddle.create("bpmn:Documentation", { text: node.data.documentation })
@@ -3396,22 +3470,16 @@ function buildFlowElement(moddle, node, allNodes, allEdges) {
3396
3470
  if (elementType === "CallActivity" && node.data.calledElement) {
3397
3471
  attrs.calledElement = node.data.calledElement;
3398
3472
  }
3473
+ if (elementType === "DataObjectReference") {
3474
+ const refId = typeof node.data.dataObjectRef === "string" ? node.data.dataObjectRef : `DataObject_${node.id}`;
3475
+ attrs.dataObjectRef = { id: refId };
3476
+ }
3399
3477
  if (elementType === "ScriptTask") {
3400
3478
  const scriptFormat = typeof node.data.scriptFormat === "string" ? node.data.scriptFormat : void 0;
3401
3479
  const script = typeof node.data.script === "string" ? node.data.script : void 0;
3402
3480
  if (scriptFormat) attrs.scriptFormat = scriptFormat;
3403
3481
  if (script) attrs.script = script;
3404
3482
  }
3405
- if (elementType === "UserTask") {
3406
- const flowableAttrs = {};
3407
- const fk = typeof node.data.formKey === "string" ? node.data.formKey : void 0;
3408
- const cu = typeof node.data.candidateUsers === "string" ? node.data.candidateUsers : void 0;
3409
- const cg = typeof node.data.candidateGroups === "string" ? node.data.candidateGroups : void 0;
3410
- if (fk) flowableAttrs["flowable:formKey"] = fk;
3411
- if (cu) flowableAttrs["flowable:candidateUsers"] = cu;
3412
- if (cg) flowableAttrs["flowable:candidateGroups"] = cg;
3413
- if (Object.keys(flowableAttrs).length > 0) attrs.$attrs = flowableAttrs;
3414
- }
3415
3483
  if (elementType === "ServiceTask") {
3416
3484
  const flowableAttrs = {};
3417
3485
  const flowableType = typeof node.data.flowableType === "string" ? node.data.flowableType : void 0;
@@ -3428,7 +3496,9 @@ function buildFlowElement(moddle, node, allNodes, allEdges) {
3428
3496
  if (aranzaConfig) attrs.extensionElements = aranzaConfig;
3429
3497
  const isSubProcess2 = elementType === "SubProcess" || elementType === "Transaction" || elementType === "EventSubProcess" || elementType === "AdHocSubProcess";
3430
3498
  if (isSubProcess2) {
3431
- attrs.flowElements = buildNestedFlowElements(moddle, node, allNodes, allEdges);
3499
+ const nested = buildNestedFlowElements(moddle, node, allNodes, allEdges);
3500
+ attrs.flowElements = nested.flowElements;
3501
+ if (nested.artifacts.length > 0) attrs.artifacts = nested.artifacts;
3432
3502
  if (elementType === "EventSubProcess" || node.data.subProcessVariant === "event") {
3433
3503
  attrs.triggeredByEvent = true;
3434
3504
  }
@@ -3438,6 +3508,29 @@ function buildFlowElement(moddle, node, allNodes, allEdges) {
3438
3508
  });
3439
3509
  }
3440
3510
  }
3511
+ const loopType = typeof node.data.loopType === "string" ? node.data.loopType : "none";
3512
+ if (loopType && loopType !== "none") {
3513
+ if (loopType === "loop") {
3514
+ const lcAttrs = { id: uid("LoopChar", node.id) };
3515
+ const loopCondition = typeof node.data.loopCondition === "string" ? node.data.loopCondition : void 0;
3516
+ if (loopCondition) {
3517
+ lcAttrs.loopCondition = moddle.create("bpmn:FormalExpression", { body: loopCondition });
3518
+ }
3519
+ attrs.loopCharacteristics = moddle.create("bpmn:StandardLoopCharacteristics", lcAttrs);
3520
+ } else {
3521
+ const isSequential = loopType === "sequentialMultiple";
3522
+ const lcAttrs = { id: uid("LoopChar", node.id), isSequential };
3523
+ const loopCardinality = typeof node.data.loopCardinality === "string" ? node.data.loopCardinality : void 0;
3524
+ const loopCompletionCondition = typeof node.data.loopCompletionCondition === "string" ? node.data.loopCompletionCondition : void 0;
3525
+ if (loopCardinality) {
3526
+ lcAttrs.loopCardinality = moddle.create("bpmn:FormalExpression", { body: loopCardinality });
3527
+ }
3528
+ if (loopCompletionCondition) {
3529
+ lcAttrs.completionCondition = moddle.create("bpmn:FormalExpression", { body: loopCompletionCondition });
3530
+ }
3531
+ attrs.loopCharacteristics = moddle.create("bpmn:MultiInstanceLoopCharacteristics", lcAttrs);
3532
+ }
3533
+ }
3441
3534
  const element = moddle.create(moddleType, attrs);
3442
3535
  return element;
3443
3536
  }
@@ -3445,16 +3538,42 @@ function buildNestedFlowElements(moddle, parent, allNodes, allEdges) {
3445
3538
  const childNodes = allNodes.filter((node) => node.parentId === parent.id);
3446
3539
  const childNodeIds = new Set(childNodes.map((node) => node.id));
3447
3540
  const flowElements = [];
3541
+ const artifacts = [];
3542
+ const presentDataObjectIds = new Set(
3543
+ childNodes.filter((n) => n.data.elementType === "DataObject").map((n) => n.id)
3544
+ );
3545
+ for (const node of childNodes) {
3546
+ if (node.data.elementType === "DataObjectReference") {
3547
+ const explicitRef = typeof node.data.dataObjectRef === "string" ? node.data.dataObjectRef : null;
3548
+ if (!explicitRef || !presentDataObjectIds.has(explicitRef)) {
3549
+ const syntheticId = `DataObject_${node.id}`;
3550
+ if (!presentDataObjectIds.has(syntheticId)) {
3551
+ flowElements.push(moddle.create("bpmn:DataObject", { id: syntheticId }));
3552
+ presentDataObjectIds.add(syntheticId);
3553
+ }
3554
+ }
3555
+ }
3556
+ }
3448
3557
  for (const child of childNodes) {
3449
3558
  const element = buildFlowElement(moddle, child, allNodes, allEdges);
3450
- if (element) flowElements.push(element);
3559
+ if (!element) continue;
3560
+ if (ARTIFACT_ELEMENT_TYPES.has(child.data.elementType)) {
3561
+ artifacts.push(element);
3562
+ } else {
3563
+ flowElements.push(element);
3564
+ }
3451
3565
  }
3452
3566
  for (const edge of allEdges) {
3453
3567
  if (!childNodeIds.has(edge.source) || !childNodeIds.has(edge.target)) continue;
3454
3568
  const element = buildEdgeElement(moddle, edge);
3455
- if (element) flowElements.push(element);
3569
+ if (!element) continue;
3570
+ if (edge.data?.edgeType === "association") {
3571
+ artifacts.push(element);
3572
+ } else {
3573
+ flowElements.push(element);
3574
+ }
3456
3575
  }
3457
- return flowElements;
3576
+ return { flowElements, artifacts };
3458
3577
  }
3459
3578
  function buildEdgeElement(moddle, edge) {
3460
3579
  if (!edge.data) return null;
@@ -3476,6 +3595,9 @@ function buildEdgeElement(moddle, edge) {
3476
3595
  body: edge.data.conditionExpression
3477
3596
  });
3478
3597
  }
3598
+ if (edge.data.edgeType === "association" && edge.data.associationDirection && edge.data.associationDirection !== "none") {
3599
+ attrs.associationDirection = edge.data.associationDirection === "both" ? "Both" : "One";
3600
+ }
3479
3601
  return moddle.create(moddleType, attrs);
3480
3602
  }
3481
3603
  function buildBpmnDI(moddle, definitions, nodes, edges) {
@@ -4054,6 +4176,130 @@ function isCompleted(state) {
4054
4176
  function setVariable(state, key, value) {
4055
4177
  return { ...state, variables: { ...state.variables, [key]: value } };
4056
4178
  }
4179
+
4180
+ // src/simulation/batch.ts
4181
+ function weightedChoice(edgeIds, weights) {
4182
+ if (edgeIds.length === 0) return "";
4183
+ const w = edgeIds.map((id) => Math.max(0, weights[id] ?? 1));
4184
+ const total = w.reduce((a, b) => a + b, 0);
4185
+ if (total === 0) return edgeIds[0];
4186
+ let r = Math.random() * total;
4187
+ for (let i = 0; i < edgeIds.length; i++) {
4188
+ r -= w[i];
4189
+ if (r <= 0) return edgeIds[i];
4190
+ }
4191
+ return edgeIds[edgeIds.length - 1];
4192
+ }
4193
+ function buildBatchDiagram(diagram, gatewayWeights, gwVarMap) {
4194
+ const patchedEdges = diagram.edges.map((edge) => {
4195
+ const gwId = edge.source;
4196
+ if (!(gwId in gatewayWeights) || edge.type !== "sequenceFlow") return edge;
4197
+ const varName = gwVarMap[gwId];
4198
+ const outEdges = diagram.edges.filter(
4199
+ (e) => e.source === gwId && e.type === "sequenceFlow"
4200
+ );
4201
+ const idx = outEdges.findIndex((e) => e.id === edge.id);
4202
+ if (idx === -1) return edge;
4203
+ return {
4204
+ ...edge,
4205
+ conditionExpression: `\${${varName} == ${idx}}`,
4206
+ isDefault: false
4207
+ };
4208
+ });
4209
+ return { nodes: diagram.nodes, edges: patchedEdges };
4210
+ }
4211
+ function runOneInstance(diagram, initialVars, maxSteps) {
4212
+ let state = createSimulation(diagram, initialVars);
4213
+ state = tick(diagram, state);
4214
+ const visitedNodes = /* @__PURE__ */ new Set();
4215
+ const peakPerNode = {};
4216
+ function snapshot() {
4217
+ const counts = {};
4218
+ for (const token of state.tokens) {
4219
+ visitedNodes.add(token.elementId);
4220
+ counts[token.elementId] = (counts[token.elementId] ?? 0) + 1;
4221
+ }
4222
+ for (const [nodeId, c] of Object.entries(counts)) {
4223
+ if (c > (peakPerNode[nodeId] ?? 0)) peakPerNode[nodeId] = c;
4224
+ }
4225
+ }
4226
+ snapshot();
4227
+ let steps = 0;
4228
+ while (state.status === "running" && steps < maxSteps) {
4229
+ const fireable = getFireable(diagram, state);
4230
+ if (fireable.length === 0) break;
4231
+ for (const id of fireable) {
4232
+ if (state.status !== "running") break;
4233
+ state = fire(diagram, state, id);
4234
+ snapshot();
4235
+ }
4236
+ steps++;
4237
+ }
4238
+ for (const entry of state.log) {
4239
+ visitedNodes.add(entry.elementId);
4240
+ }
4241
+ return {
4242
+ visitedNodes,
4243
+ peakPerNode,
4244
+ steps: state.step,
4245
+ completed: state.status === "completed"
4246
+ };
4247
+ }
4248
+ function runBatchSimulation(diagram, config = {}) {
4249
+ const n = Math.max(1, config.instances ?? 100);
4250
+ const maxSteps = Math.max(1, config.maxSteps ?? 500);
4251
+ const gwWeights = config.gatewayWeights ?? {};
4252
+ const nodeDurations = config.nodeDurations ?? {};
4253
+ const gwVarMap = {};
4254
+ Object.keys(gwWeights).forEach((gwId, i) => {
4255
+ gwVarMap[gwId] = `__g${i}`;
4256
+ });
4257
+ const batchDiagram = Object.keys(gwWeights).length > 0 ? buildBatchDiagram(diagram, gwWeights, gwVarMap) : diagram;
4258
+ const nodeStats = {};
4259
+ function node(id) {
4260
+ if (!nodeStats[id]) {
4261
+ nodeStats[id] = { visits: 0, totalDurationMs: 0, peakConcurrency: 0 };
4262
+ }
4263
+ return nodeStats[id];
4264
+ }
4265
+ let completed = 0;
4266
+ let deadlocked = 0;
4267
+ let totalSteps = 0;
4268
+ for (let i = 0; i < n; i++) {
4269
+ const instanceVars = {};
4270
+ for (const [gwId, varName] of Object.entries(gwVarMap)) {
4271
+ const outEdges = diagram.edges.filter(
4272
+ (e) => e.source === gwId && e.type === "sequenceFlow"
4273
+ );
4274
+ const edgeIds = outEdges.map((e) => e.id);
4275
+ const chosen = weightedChoice(edgeIds, gwWeights[gwId]);
4276
+ instanceVars[varName] = outEdges.findIndex((e) => e.id === chosen);
4277
+ }
4278
+ const result = runOneInstance(batchDiagram, instanceVars, maxSteps);
4279
+ if (result.completed) {
4280
+ completed++;
4281
+ totalSteps += result.steps;
4282
+ } else {
4283
+ deadlocked++;
4284
+ }
4285
+ for (const nodeId of result.visitedNodes) {
4286
+ const s = node(nodeId);
4287
+ s.visits++;
4288
+ s.totalDurationMs += nodeDurations[nodeId] ?? 0;
4289
+ }
4290
+ for (const [nodeId, peak] of Object.entries(result.peakPerNode)) {
4291
+ const s = node(nodeId);
4292
+ if (peak > s.peakConcurrency) s.peakConcurrency = peak;
4293
+ }
4294
+ }
4295
+ return {
4296
+ nodeStats,
4297
+ completed,
4298
+ deadlocked,
4299
+ total: n,
4300
+ avgSteps: completed > 0 ? Math.round(totalSteps / completed) : 0
4301
+ };
4302
+ }
4057
4303
  function createBpmnNode(options) {
4058
4304
  const size = getBpmnElementSize(options.elementType);
4059
4305
  const meta = BPMN_ELEMENT_CATALOG[options.elementType];
@@ -4997,6 +5243,7 @@ exports.resizeBpmnNodeByHandleCommand = resizeBpmnNodeByHandleCommand;
4997
5243
  exports.resizeBpmnNodeCommand = resizeBpmnNodeCommand;
4998
5244
  exports.restoreBpmnHistory = restoreBpmnHistory;
4999
5245
  exports.routeBpmnEdgeCommand = routeBpmnEdgeCommand;
5246
+ exports.runBatchSimulation = runBatchSimulation;
5000
5247
  exports.runBpmnCommand = runBpmnCommand;
5001
5248
  exports.runBpmnCommands = runBpmnCommands;
5002
5249
  exports.selectBpmnElementsCommand = selectBpmnElementsCommand;