@aranzatech/diagrams-bpmn 0.2.10 → 0.2.11
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/CHANGELOG.md +25 -0
- package/dist/index.cjs +125 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +99 -1
- package/dist/index.d.ts +99 -1
- package/dist/index.js +127 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.3.0
|
|
6
|
+
|
|
7
|
+
### Batch process simulation
|
|
8
|
+
|
|
9
|
+
New public API for running synthetic batch simulations directly on a BPMN diagram
|
|
10
|
+
without an external engine. Enables bottleneck detection, heatmap overlays, and
|
|
11
|
+
operational capacity projections from inside the modeler.
|
|
12
|
+
|
|
13
|
+
**New exports (root `@aranzatech/diagrams-bpmn`)**
|
|
14
|
+
|
|
15
|
+
- `runBatchSimulation(diagram, config)` — runs `N` process instances synchronously
|
|
16
|
+
and aggregates per-node statistics. Designed to be called from a Web Worker for
|
|
17
|
+
large `N` to keep the main thread unblocked.
|
|
18
|
+
|
|
19
|
+
**New types**
|
|
20
|
+
|
|
21
|
+
- `BatchSimConfig` — `instances`, `gatewayWeights`, `nodeDurations`, `maxSteps`
|
|
22
|
+
- `BatchSimResult` — `total`, `completed`, `deadlocked`, `avgSteps`, `nodeStats`
|
|
23
|
+
- `NodeSimStats` — `visits`, `totalDurationMs`, `peakConcurrency` per node
|
|
24
|
+
- `GatewayWeights` — edge-id → relative weight map for exclusive / inclusive gateways
|
|
25
|
+
|
|
26
|
+
Gateway weights are normalised internally; edges absent from the map default to
|
|
27
|
+
weight `1`. `nodeDurations` drive `totalDurationMs` (visits × duration), enabling
|
|
28
|
+
duration-based heatmaps and daily workload projections.
|
|
29
|
+
|
|
5
30
|
## 0.2.7
|
|
6
31
|
|
|
7
32
|
### XML round-trip improvements
|
package/dist/index.cjs
CHANGED
|
@@ -4054,6 +4054,130 @@ function isCompleted(state) {
|
|
|
4054
4054
|
function setVariable(state, key, value) {
|
|
4055
4055
|
return { ...state, variables: { ...state.variables, [key]: value } };
|
|
4056
4056
|
}
|
|
4057
|
+
|
|
4058
|
+
// src/simulation/batch.ts
|
|
4059
|
+
function weightedChoice(edgeIds, weights) {
|
|
4060
|
+
if (edgeIds.length === 0) return "";
|
|
4061
|
+
const w = edgeIds.map((id) => Math.max(0, weights[id] ?? 1));
|
|
4062
|
+
const total = w.reduce((a, b) => a + b, 0);
|
|
4063
|
+
if (total === 0) return edgeIds[0];
|
|
4064
|
+
let r = Math.random() * total;
|
|
4065
|
+
for (let i = 0; i < edgeIds.length; i++) {
|
|
4066
|
+
r -= w[i];
|
|
4067
|
+
if (r <= 0) return edgeIds[i];
|
|
4068
|
+
}
|
|
4069
|
+
return edgeIds[edgeIds.length - 1];
|
|
4070
|
+
}
|
|
4071
|
+
function buildBatchDiagram(diagram, gatewayWeights, gwVarMap) {
|
|
4072
|
+
const patchedEdges = diagram.edges.map((edge) => {
|
|
4073
|
+
const gwId = edge.source;
|
|
4074
|
+
if (!(gwId in gatewayWeights) || edge.type !== "sequenceFlow") return edge;
|
|
4075
|
+
const varName = gwVarMap[gwId];
|
|
4076
|
+
const outEdges = diagram.edges.filter(
|
|
4077
|
+
(e) => e.source === gwId && e.type === "sequenceFlow"
|
|
4078
|
+
);
|
|
4079
|
+
const idx = outEdges.findIndex((e) => e.id === edge.id);
|
|
4080
|
+
if (idx === -1) return edge;
|
|
4081
|
+
return {
|
|
4082
|
+
...edge,
|
|
4083
|
+
conditionExpression: `\${${varName} == ${idx}}`,
|
|
4084
|
+
isDefault: false
|
|
4085
|
+
};
|
|
4086
|
+
});
|
|
4087
|
+
return { nodes: diagram.nodes, edges: patchedEdges };
|
|
4088
|
+
}
|
|
4089
|
+
function runOneInstance(diagram, initialVars, maxSteps) {
|
|
4090
|
+
let state = createSimulation(diagram, initialVars);
|
|
4091
|
+
state = tick(diagram, state);
|
|
4092
|
+
const visitedNodes = /* @__PURE__ */ new Set();
|
|
4093
|
+
const peakPerNode = {};
|
|
4094
|
+
function snapshot() {
|
|
4095
|
+
const counts = {};
|
|
4096
|
+
for (const token of state.tokens) {
|
|
4097
|
+
visitedNodes.add(token.elementId);
|
|
4098
|
+
counts[token.elementId] = (counts[token.elementId] ?? 0) + 1;
|
|
4099
|
+
}
|
|
4100
|
+
for (const [nodeId, c] of Object.entries(counts)) {
|
|
4101
|
+
if (c > (peakPerNode[nodeId] ?? 0)) peakPerNode[nodeId] = c;
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
snapshot();
|
|
4105
|
+
let steps = 0;
|
|
4106
|
+
while (state.status === "running" && steps < maxSteps) {
|
|
4107
|
+
const fireable = getFireable(diagram, state);
|
|
4108
|
+
if (fireable.length === 0) break;
|
|
4109
|
+
for (const id of fireable) {
|
|
4110
|
+
if (state.status !== "running") break;
|
|
4111
|
+
state = fire(diagram, state, id);
|
|
4112
|
+
snapshot();
|
|
4113
|
+
}
|
|
4114
|
+
steps++;
|
|
4115
|
+
}
|
|
4116
|
+
for (const entry of state.log) {
|
|
4117
|
+
visitedNodes.add(entry.elementId);
|
|
4118
|
+
}
|
|
4119
|
+
return {
|
|
4120
|
+
visitedNodes,
|
|
4121
|
+
peakPerNode,
|
|
4122
|
+
steps: state.step,
|
|
4123
|
+
completed: state.status === "completed"
|
|
4124
|
+
};
|
|
4125
|
+
}
|
|
4126
|
+
function runBatchSimulation(diagram, config = {}) {
|
|
4127
|
+
const n = Math.max(1, config.instances ?? 100);
|
|
4128
|
+
const maxSteps = Math.max(1, config.maxSteps ?? 500);
|
|
4129
|
+
const gwWeights = config.gatewayWeights ?? {};
|
|
4130
|
+
const nodeDurations = config.nodeDurations ?? {};
|
|
4131
|
+
const gwVarMap = {};
|
|
4132
|
+
Object.keys(gwWeights).forEach((gwId, i) => {
|
|
4133
|
+
gwVarMap[gwId] = `__g${i}`;
|
|
4134
|
+
});
|
|
4135
|
+
const batchDiagram = Object.keys(gwWeights).length > 0 ? buildBatchDiagram(diagram, gwWeights, gwVarMap) : diagram;
|
|
4136
|
+
const nodeStats = {};
|
|
4137
|
+
function node(id) {
|
|
4138
|
+
if (!nodeStats[id]) {
|
|
4139
|
+
nodeStats[id] = { visits: 0, totalDurationMs: 0, peakConcurrency: 0 };
|
|
4140
|
+
}
|
|
4141
|
+
return nodeStats[id];
|
|
4142
|
+
}
|
|
4143
|
+
let completed = 0;
|
|
4144
|
+
let deadlocked = 0;
|
|
4145
|
+
let totalSteps = 0;
|
|
4146
|
+
for (let i = 0; i < n; i++) {
|
|
4147
|
+
const instanceVars = {};
|
|
4148
|
+
for (const [gwId, varName] of Object.entries(gwVarMap)) {
|
|
4149
|
+
const outEdges = diagram.edges.filter(
|
|
4150
|
+
(e) => e.source === gwId && e.type === "sequenceFlow"
|
|
4151
|
+
);
|
|
4152
|
+
const edgeIds = outEdges.map((e) => e.id);
|
|
4153
|
+
const chosen = weightedChoice(edgeIds, gwWeights[gwId]);
|
|
4154
|
+
instanceVars[varName] = outEdges.findIndex((e) => e.id === chosen);
|
|
4155
|
+
}
|
|
4156
|
+
const result = runOneInstance(batchDiagram, instanceVars, maxSteps);
|
|
4157
|
+
if (result.completed) {
|
|
4158
|
+
completed++;
|
|
4159
|
+
totalSteps += result.steps;
|
|
4160
|
+
} else {
|
|
4161
|
+
deadlocked++;
|
|
4162
|
+
}
|
|
4163
|
+
for (const nodeId of result.visitedNodes) {
|
|
4164
|
+
const s = node(nodeId);
|
|
4165
|
+
s.visits++;
|
|
4166
|
+
s.totalDurationMs += nodeDurations[nodeId] ?? 0;
|
|
4167
|
+
}
|
|
4168
|
+
for (const [nodeId, peak] of Object.entries(result.peakPerNode)) {
|
|
4169
|
+
const s = node(nodeId);
|
|
4170
|
+
if (peak > s.peakConcurrency) s.peakConcurrency = peak;
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
return {
|
|
4174
|
+
nodeStats,
|
|
4175
|
+
completed,
|
|
4176
|
+
deadlocked,
|
|
4177
|
+
total: n,
|
|
4178
|
+
avgSteps: completed > 0 ? Math.round(totalSteps / completed) : 0
|
|
4179
|
+
};
|
|
4180
|
+
}
|
|
4057
4181
|
function createBpmnNode(options) {
|
|
4058
4182
|
const size = getBpmnElementSize(options.elementType);
|
|
4059
4183
|
const meta = BPMN_ELEMENT_CATALOG[options.elementType];
|
|
@@ -4997,6 +5121,7 @@ exports.resizeBpmnNodeByHandleCommand = resizeBpmnNodeByHandleCommand;
|
|
|
4997
5121
|
exports.resizeBpmnNodeCommand = resizeBpmnNodeCommand;
|
|
4998
5122
|
exports.restoreBpmnHistory = restoreBpmnHistory;
|
|
4999
5123
|
exports.routeBpmnEdgeCommand = routeBpmnEdgeCommand;
|
|
5124
|
+
exports.runBatchSimulation = runBatchSimulation;
|
|
5000
5125
|
exports.runBpmnCommand = runBpmnCommand;
|
|
5001
5126
|
exports.runBpmnCommands = runBpmnCommands;
|
|
5002
5127
|
exports.selectBpmnElementsCommand = selectBpmnElementsCommand;
|