@elabs-ai/components-process 4.2.0 → 5.0.0

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 (144) hide show
  1. package/README.md +8 -1
  2. package/dist/core/index.d.ts +801 -3
  3. package/dist/core/index.js +1334 -0
  4. package/dist/core/index.js.map +1 -1
  5. package/dist/index.d.ts +1889 -34
  6. package/dist/index.js +5512 -196
  7. package/dist/index.js.map +1 -1
  8. package/dist/test/index.d.ts +223 -5
  9. package/dist/test/index.js +346 -191
  10. package/dist/test/index.js.map +1 -1
  11. package/package.json +14 -13
  12. package/src/__contract__/case-table.contract.test.tsx +49 -0
  13. package/src/__contract__/compare-kpi-strip.contract.test.tsx +49 -0
  14. package/src/__contract__/conformance-overlay.contract.test.tsx +49 -0
  15. package/src/__contract__/happy-path-editor.contract.test.tsx +49 -0
  16. package/src/__contract__/violation-list.contract.test.tsx +49 -0
  17. package/src/abstraction-controls/abstraction-controls-per-type.test.tsx +80 -0
  18. package/src/abstraction-controls/abstraction-controls.stories.tsx +43 -1
  19. package/src/abstraction-controls/abstraction-controls.tsx +198 -5
  20. package/src/case-table/case-table.stories.tsx +89 -0
  21. package/src/case-table/case-table.test.tsx +148 -0
  22. package/src/case-table/case-table.tsx +144 -0
  23. package/src/case-table/columns.ts +116 -0
  24. package/src/case-table/index.ts +11 -0
  25. package/src/case-timeline/case-timeline-model.test.ts +72 -0
  26. package/src/case-timeline/case-timeline-model.ts +112 -0
  27. package/src/case-timeline/case-timeline.stories.tsx +94 -0
  28. package/src/case-timeline/case-timeline.test.tsx +51 -0
  29. package/src/case-timeline/case-timeline.tsx +109 -0
  30. package/src/case-timeline/index.ts +9 -0
  31. package/src/conformance-overlay/conformance-fixture.ts +59 -0
  32. package/src/conformance-overlay/conformance-legend.tsx +109 -0
  33. package/src/conformance-overlay/conformance-overlay.stories.tsx +116 -0
  34. package/src/conformance-overlay/conformance-overlay.test.tsx +88 -0
  35. package/src/conformance-overlay/conformance-overlay.tsx +107 -0
  36. package/src/conformance-overlay/conformance-state.test.ts +79 -0
  37. package/src/conformance-overlay/conformance-state.ts +220 -0
  38. package/src/conformance-overlay/index.ts +4 -0
  39. package/src/core/activity-color-scale.test.ts +107 -0
  40. package/src/core/activity-color-scale.ts +133 -0
  41. package/src/core/adapters/ocel.test.ts +112 -0
  42. package/src/core/adapters/ocel.ts +359 -0
  43. package/src/core/adapters/xes.test.ts +293 -0
  44. package/src/core/adapters/xes.ts +384 -0
  45. package/src/core/cases-from-log.test.ts +72 -0
  46. package/src/core/cases-from-log.ts +85 -0
  47. package/src/core/conformance.test.ts +80 -0
  48. package/src/core/conformance.ts +91 -0
  49. package/src/core/diff-graphs.test.ts +151 -0
  50. package/src/core/diff-graphs.ts +118 -0
  51. package/src/core/discover-object-centric-graph.test.ts +94 -0
  52. package/src/core/discover-object-centric-graph.ts +296 -0
  53. package/src/core/fixtures/ocel-sample.ts +82 -0
  54. package/src/core/fixtures/sample.xes +68 -0
  55. package/src/core/index.ts +113 -0
  56. package/src/core/reference-model.test.ts +43 -0
  57. package/src/core/reference-model.ts +116 -0
  58. package/src/core/replay-timeline.test.ts +161 -0
  59. package/src/core/replay-timeline.ts +260 -0
  60. package/src/core/segments.test.ts +185 -0
  61. package/src/core/segments.ts +153 -0
  62. package/src/core/token-replay.test.ts +218 -0
  63. package/src/core/token-replay.ts +456 -0
  64. package/src/core/types.ts +2 -2
  65. package/src/dotted-chart/compute-dots.test.ts +176 -0
  66. package/src/dotted-chart/compute-dots.ts +241 -0
  67. package/src/dotted-chart/dotted-chart-labels.ts +93 -0
  68. package/src/dotted-chart/dotted-chart.stories.tsx +182 -0
  69. package/src/dotted-chart/dotted-chart.test.tsx +135 -0
  70. package/src/dotted-chart/dotted-chart.tsx +841 -0
  71. package/src/dotted-chart/index.ts +23 -0
  72. package/src/dotted-chart/use-element-size.ts +33 -0
  73. package/src/happy-path-editor/happy-path-editor-context.ts +81 -0
  74. package/src/happy-path-editor/happy-path-editor.stories.tsx +116 -0
  75. package/src/happy-path-editor/happy-path-editor.test.tsx +142 -0
  76. package/src/happy-path-editor/happy-path-editor.tsx +239 -0
  77. package/src/happy-path-editor/happy-path-step-node.tsx +175 -0
  78. package/src/happy-path-editor/index.ts +4 -0
  79. package/src/index.ts +51 -1
  80. package/src/performance-spectrum/aggregate-segments.test.ts +107 -0
  81. package/src/performance-spectrum/aggregate-segments.ts +174 -0
  82. package/src/performance-spectrum/index.ts +25 -0
  83. package/src/performance-spectrum/performance-spectrum-context.tsx +116 -0
  84. package/src/performance-spectrum/performance-spectrum.stories.tsx +128 -0
  85. package/src/performance-spectrum/performance-spectrum.test.tsx +190 -0
  86. package/src/performance-spectrum/performance-spectrum.tsx +870 -0
  87. package/src/process-compare/compare-kpi-strip.stories.tsx +48 -0
  88. package/src/process-compare/compare-kpi-strip.tsx +94 -0
  89. package/src/process-compare/compare-model.ts +83 -0
  90. package/src/process-compare/compare-side.tsx +42 -0
  91. package/src/process-compare/diff-to-graph.ts +104 -0
  92. package/src/process-compare/index.ts +23 -0
  93. package/src/process-compare/process-compare.stories.tsx +184 -0
  94. package/src/process-compare/process-compare.test.tsx +224 -0
  95. package/src/process-compare/process-compare.tsx +251 -0
  96. package/src/process-explorer.stories.tsx +1 -1
  97. package/src/process-filter-bar/index.ts +2 -0
  98. package/src/process-filter-bar/process-filter-bar.stories.tsx +156 -0
  99. package/src/process-filter-bar/process-filter-bar.test.tsx +201 -0
  100. package/src/process-filter-bar/process-filter-bar.tsx +167 -0
  101. package/src/process-kpi-strip/process-kpi-strip.stories.tsx +47 -0
  102. package/src/process-kpi-strip/process-kpi-strip.test.tsx +67 -0
  103. package/src/process-kpi-strip/process-kpi-strip.tsx +148 -8
  104. package/src/process-map/activity-accent.ts +25 -0
  105. package/src/process-map/index.ts +1 -0
  106. package/src/process-map/map-model.test.ts +16 -0
  107. package/src/process-map/map-model.ts +323 -1
  108. package/src/process-map/object-centric-map.test.tsx +132 -0
  109. package/src/process-map/process-activity-node.tsx +152 -14
  110. package/src/process-map/process-map-object-centric.stories.tsx +219 -0
  111. package/src/process-map/process-map.stories.tsx +64 -0
  112. package/src/process-map/process-map.tsx +240 -16
  113. package/src/process-map/process-transition-edge.test.tsx +47 -0
  114. package/src/process-map/process-transition-edge.tsx +134 -7
  115. package/src/process-map/use-process-layout.ts +30 -9
  116. package/src/process-replay/congestion-heat.tsx +107 -0
  117. package/src/process-replay/index.ts +14 -0
  118. package/src/process-replay/process-replay.stories.tsx +168 -0
  119. package/src/process-replay/process-replay.test.tsx +170 -0
  120. package/src/process-replay/process-replay.tsx +285 -0
  121. package/src/process-replay/replay-controls.tsx +147 -0
  122. package/src/process-replay/replay-format.ts +83 -0
  123. package/src/process-replay/replay-tokens-context.ts +30 -0
  124. package/src/process-replay/use-controllable-value.ts +30 -0
  125. package/src/templates-process-explorer.stories.tsx +1304 -0
  126. package/src/test/contract.test.ts +66 -0
  127. package/src/test/contract.ts +107 -6
  128. package/src/test/doubles.test.tsx +87 -1
  129. package/src/test/doubles.tsx +174 -3
  130. package/src/test/index.ts +25 -1
  131. package/src/use-process-explorer/use-process-explorer.test.ts +44 -0
  132. package/src/use-process-explorer/use-process-explorer.ts +34 -2
  133. package/src/variant-explorer/coverage-bar.tsx +36 -0
  134. package/src/variant-explorer/index.ts +16 -0
  135. package/src/variant-explorer/sequence-chips.tsx +103 -0
  136. package/src/variant-explorer/variant-explorer-model.ts +42 -0
  137. package/src/variant-explorer/variant-explorer.stories.tsx +226 -0
  138. package/src/variant-explorer/variant-explorer.test.tsx +302 -0
  139. package/src/variant-explorer/variant-explorer.tsx +567 -0
  140. package/src/variant-explorer/variant-row.tsx +137 -0
  141. package/src/violation-list/index.ts +2 -0
  142. package/src/violation-list/violation-list.stories.tsx +73 -0
  143. package/src/violation-list/violation-list.test.tsx +84 -0
  144. package/src/violation-list/violation-list.tsx +259 -0
@@ -1506,47 +1506,1381 @@ function createProcessWorker(options = {}) {
1506
1506
  }
1507
1507
  };
1508
1508
  }
1509
+
1510
+ // src/core/activity-color-scale.ts
1511
+ var ACTIVITY_COLOR_SLOTS = 11;
1512
+ var ACTIVITY_OTHER_TOKEN = "--chart-12";
1513
+ var OTHER = Object.freeze({ token: ACTIVITY_OTHER_TOKEN, pattern: "other" });
1514
+ function alphanumerics(label) {
1515
+ return label.replace(/[^\p{L}\p{N}]/gu, "").toUpperCase();
1516
+ }
1517
+ function codeCandidates(label) {
1518
+ const out = [];
1519
+ const words = label.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
1520
+ const first = words[0];
1521
+ const second = words[1];
1522
+ if (first && second) out.push(`${first[0]}${second[0]}`.toUpperCase());
1523
+ const letters = alphanumerics(label);
1524
+ const head = letters[0] ?? "?";
1525
+ if (letters.length >= 2) out.push(letters.slice(0, 2));
1526
+ for (let i = 2; i < letters.length; i += 1) out.push(`${head}${letters[i]}`);
1527
+ for (let digit = 1; digit <= 9; digit += 1) out.push(`${head}${digit}`);
1528
+ return out;
1529
+ }
1530
+ function uniqueCode(label, taken) {
1531
+ for (const candidate of codeCandidates(label)) {
1532
+ if (!taken.has(candidate)) return candidate;
1533
+ }
1534
+ for (let n = 0; n < 36 * 36; n += 1) {
1535
+ const candidate = n.toString(36).toUpperCase().padStart(2, "0");
1536
+ if (!taken.has(candidate)) return candidate;
1537
+ }
1538
+ return (alphanumerics(label).slice(0, 2) || "??").padEnd(2, "?");
1539
+ }
1540
+ function activityColorScale(graph) {
1541
+ const ranked = [...graph.activities].sort(
1542
+ (a, b) => b.cases - a.cases || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
1543
+ );
1544
+ const colors = /* @__PURE__ */ new Map();
1545
+ const codes = /* @__PURE__ */ new Map();
1546
+ const labels = /* @__PURE__ */ new Map();
1547
+ const taken = /* @__PURE__ */ new Set();
1548
+ const legend = ranked.map((activity, index2) => {
1549
+ const color = index2 < ACTIVITY_COLOR_SLOTS ? { token: `--chart-${index2 + 1}` } : OTHER;
1550
+ const label = activity.label || activity.id;
1551
+ const code = uniqueCode(label, taken);
1552
+ taken.add(code);
1553
+ colors.set(activity.id, color);
1554
+ codes.set(activity.id, code);
1555
+ labels.set(activity.id, label);
1556
+ return { activityId: activity.id, label, code, ...color };
1557
+ });
1558
+ return {
1559
+ colorFor: (activityId) => colors.get(activityId) ?? OTHER,
1560
+ codeFor: (activityId) => codes.get(activityId) ?? (alphanumerics(activityId).slice(0, 2) || "??").padEnd(2, "?"),
1561
+ labelFor: (activityId) => labels.get(activityId) ?? activityId,
1562
+ legend
1563
+ };
1564
+ }
1565
+
1566
+ // src/core/cases-from-log.ts
1567
+ function toIso(ms) {
1568
+ return Number.isFinite(ms) ? new Date(ms).toISOString() : "";
1569
+ }
1570
+ function toCaseRow(kase, variantId2) {
1571
+ const row = {
1572
+ caseId: kase.caseId,
1573
+ start: toIso(kase.start),
1574
+ end: toIso(kase.end),
1575
+ durationMs: Number.isFinite(kase.duration) ? kase.duration : 0,
1576
+ eventCount: kase.events.length,
1577
+ variantId: variantId2
1578
+ };
1579
+ if (kase.attributes !== void 0) {
1580
+ row.attributes = kase.attributes;
1581
+ }
1582
+ return row;
1583
+ }
1584
+ function casesFromLog(log) {
1585
+ const normalized = asNormalizedLog(log);
1586
+ if (normalized.cases.length === 0) return [];
1587
+ const variantIdByCase = /* @__PURE__ */ new Map();
1588
+ for (const variant of extractVariants(normalized)) {
1589
+ for (const caseId of variant.caseIds) variantIdByCase.set(caseId, variant.id);
1590
+ }
1591
+ return normalized.cases.map((kase) => toCaseRow(kase, variantIdByCase.get(kase.caseId) ?? ""));
1592
+ }
1593
+
1594
+ // src/core/reference-model.ts
1595
+ function placeName(index2) {
1596
+ return `p${index2}`;
1597
+ }
1598
+ function liftHappyPath(path) {
1599
+ const steps = path.steps;
1600
+ const places = [];
1601
+ for (let i = 0; i <= steps.length; i += 1) places.push(placeName(i));
1602
+ const transitions = [];
1603
+ steps.forEach((step, i) => {
1604
+ const before = placeName(i);
1605
+ const after = placeName(i + 1);
1606
+ transitions.push({
1607
+ id: `t${i}`,
1608
+ activity: step.activity,
1609
+ kind: "step",
1610
+ consumes: [before],
1611
+ produces: [after]
1612
+ });
1613
+ if (step.optional) {
1614
+ transitions.push({
1615
+ id: `skip${i}`,
1616
+ activity: step.activity,
1617
+ kind: "skip",
1618
+ consumes: [before],
1619
+ produces: [after]
1620
+ });
1621
+ }
1622
+ if (step.repeatable) {
1623
+ transitions.push({
1624
+ id: `repeat${i}`,
1625
+ activity: step.activity,
1626
+ kind: "repeat",
1627
+ consumes: [after],
1628
+ produces: [after]
1629
+ });
1630
+ }
1631
+ });
1632
+ return {
1633
+ places,
1634
+ initialMarking: [placeName(0)],
1635
+ finalMarking: [placeName(steps.length)],
1636
+ transitions
1637
+ };
1638
+ }
1639
+
1640
+ // src/core/token-replay.ts
1641
+ var DEVIATION_TYPES = [
1642
+ "undesired",
1643
+ "skipped",
1644
+ "wrongOrder",
1645
+ "wrongStart",
1646
+ "incomplete"
1647
+ ];
1648
+ var compiled = /* @__PURE__ */ new WeakMap();
1649
+ function pushTo(map, key, value) {
1650
+ const list = map.get(key);
1651
+ if (list === void 0) map.set(key, [value]);
1652
+ else list.push(value);
1653
+ }
1654
+ function compile(model) {
1655
+ const cached = compiled.get(model);
1656
+ if (cached !== void 0) return cached;
1657
+ const byActivity = /* @__PURE__ */ new Map();
1658
+ const outgoing = /* @__PURE__ */ new Map();
1659
+ const silentOutgoing = /* @__PURE__ */ new Map();
1660
+ const steps = [];
1661
+ const silent = model.transitions.filter((t) => t.kind === "skip");
1662
+ const visible = model.transitions.filter((t) => t.kind !== "skip");
1663
+ for (const t of [...visible].sort((a, b) => rank(a) - rank(b))) {
1664
+ pushTo(byActivity, t.activity, t);
1665
+ }
1666
+ for (const t of visible) if (t.kind === "step") steps.push(t);
1667
+ for (const t of [...silent, ...visible]) {
1668
+ if (t.consumes.length !== 1 || t.kind === "repeat") continue;
1669
+ const from = t.consumes[0];
1670
+ pushTo(outgoing, from, t);
1671
+ if (t.kind === "skip") pushTo(silentOutgoing, from, t);
1672
+ }
1673
+ const result = { model, byActivity, outgoing, silentOutgoing, steps };
1674
+ compiled.set(model, result);
1675
+ return result;
1676
+ }
1677
+ function rank(t) {
1678
+ return t.kind === "step" ? 0 : 1;
1679
+ }
1680
+ function tokens(marking, place) {
1681
+ return marking.get(place) ?? 0;
1682
+ }
1683
+ function isEnabled(marking, t) {
1684
+ const need = /* @__PURE__ */ new Map();
1685
+ for (const place of t.consumes) need.set(place, (need.get(place) ?? 0) + 1);
1686
+ for (const [place, count] of need) if (tokens(marking, place) < count) return false;
1687
+ return true;
1688
+ }
1689
+ function fire(marking, t) {
1690
+ for (const place of t.consumes) marking.set(place, tokens(marking, place) - 1);
1691
+ for (const place of t.produces) marking.set(place, tokens(marking, place) + 1);
1692
+ }
1693
+ function shortestChain(marking, target, edges) {
1694
+ if (tokens(marking, target) > 0) return [];
1695
+ const parent = /* @__PURE__ */ new Map();
1696
+ const queue = [];
1697
+ for (const [place, count] of marking) {
1698
+ if (count > 0 && !parent.has(place)) {
1699
+ parent.set(place, null);
1700
+ queue.push(place);
1701
+ }
1702
+ }
1703
+ for (let head = 0; head < queue.length; head += 1) {
1704
+ const place = queue[head];
1705
+ for (const t of edges.get(place) ?? []) {
1706
+ for (const next of t.produces) {
1707
+ if (parent.has(next)) continue;
1708
+ parent.set(next, t);
1709
+ if (next === target) return unwind(parent, target);
1710
+ queue.push(next);
1711
+ }
1712
+ }
1713
+ }
1714
+ return void 0;
1715
+ }
1716
+ function unwind(parent, target) {
1717
+ const chain = [];
1718
+ let at = parent.get(target);
1719
+ while (at !== null && at !== void 0) {
1720
+ chain.unshift(at);
1721
+ at = parent.get(at.consumes[0]);
1722
+ }
1723
+ return chain;
1724
+ }
1725
+ function enableSilently(cm, marking, places, counters) {
1726
+ const trial = new Map(marking);
1727
+ let produced = 0;
1728
+ let consumed = 0;
1729
+ for (const place of places) {
1730
+ if (tokens(trial, place) > 0) continue;
1731
+ const chain = shortestChain(trial, place, cm.silentOutgoing);
1732
+ if (chain === void 0) return false;
1733
+ for (const t of chain) {
1734
+ if (!isEnabled(trial, t)) return false;
1735
+ fire(trial, t);
1736
+ consumed += t.consumes.length;
1737
+ produced += t.produces.length;
1738
+ }
1739
+ }
1740
+ for (const [place, count] of trial) marking.set(place, count);
1741
+ counters.produced += produced;
1742
+ counters.consumed += consumed;
1743
+ return true;
1744
+ }
1745
+ function expectedNext(cm, marking) {
1746
+ const reachable = /* @__PURE__ */ new Set();
1747
+ const queue = [];
1748
+ for (const [place, count] of marking) {
1749
+ if (count > 0) {
1750
+ reachable.add(place);
1751
+ queue.push(place);
1752
+ }
1753
+ }
1754
+ for (let head = 0; head < queue.length; head += 1) {
1755
+ for (const t of cm.silentOutgoing.get(queue[head]) ?? []) {
1756
+ for (const next of t.produces) {
1757
+ if (reachable.has(next)) continue;
1758
+ reachable.add(next);
1759
+ queue.push(next);
1760
+ }
1761
+ }
1762
+ }
1763
+ for (const t of cm.steps) {
1764
+ if (t.consumes.every((place) => reachable.has(place))) return t.activity;
1765
+ }
1766
+ return void 0;
1767
+ }
1768
+ function fitnessOf(c) {
1769
+ const missingTerm = c.consumed > 0 ? 1 - c.missing / c.consumed : 1;
1770
+ const remainingTerm = c.produced > 0 ? 1 - c.remaining / c.produced : 1;
1771
+ return Math.min(1, Math.max(0, 0.5 * missingTerm + 0.5 * remainingTerm));
1772
+ }
1773
+ function replayActivities(caseId, trace, model) {
1774
+ const cm = compile(model);
1775
+ const marking = /* @__PURE__ */ new Map();
1776
+ const counters = { produced: 0, consumed: 0, missing: 0 };
1777
+ const deviations = [];
1778
+ const observed = new Set(trace);
1779
+ for (const place of model.initialMarking) {
1780
+ marking.set(place, tokens(marking, place) + 1);
1781
+ counters.produced += 1;
1782
+ }
1783
+ trace.forEach((activity, at) => {
1784
+ const candidates = cm.byActivity.get(activity);
1785
+ if (candidates === void 0 || candidates.length === 0) {
1786
+ deviations.push({ type: "undesired", activity, expected: expectedNext(cm, marking), at });
1787
+ counters.missing += 1;
1788
+ counters.consumed += 1;
1789
+ return;
1790
+ }
1791
+ const enabled = candidates.find((t) => isEnabled(marking, t)) ?? candidates.find((t) => enableSilently(cm, marking, t.consumes, counters));
1792
+ if (enabled !== void 0) {
1793
+ fire(marking, enabled);
1794
+ counters.consumed += enabled.consumes.length;
1795
+ counters.produced += enabled.produces.length;
1796
+ return;
1797
+ }
1798
+ const expected2 = expectedNext(cm, marking);
1799
+ let chosen = candidates[0];
1800
+ let chain;
1801
+ for (const t of candidates) {
1802
+ const gap = t.consumes.find((place) => tokens(marking, place) === 0);
1803
+ if (gap === void 0) continue;
1804
+ const found = shortestChain(marking, gap, cm.outgoing);
1805
+ if (found !== void 0 && (chain === void 0 || found.length < chain.length)) {
1806
+ chain = found;
1807
+ chosen = t;
1808
+ }
1809
+ }
1810
+ if (at === 0) {
1811
+ deviations.push({ type: "wrongStart", activity, expected: expected2, at });
1812
+ } else {
1813
+ const unobserved = (chain ?? []).filter(
1814
+ (t) => t.kind === "step" && !observed.has(t.activity)
1815
+ );
1816
+ if (unobserved.length > 0) {
1817
+ const seen = /* @__PURE__ */ new Set();
1818
+ for (const t of unobserved) {
1819
+ if (seen.has(t.activity)) continue;
1820
+ seen.add(t.activity);
1821
+ deviations.push({ type: "skipped", activity: t.activity, expected: t.activity, at });
1822
+ }
1823
+ } else {
1824
+ deviations.push({ type: "wrongOrder", activity, expected: expected2, at });
1825
+ }
1826
+ }
1827
+ for (const place of chosen.consumes) {
1828
+ if (tokens(marking, place) === 0) {
1829
+ marking.set(place, 1);
1830
+ counters.missing += 1;
1831
+ }
1832
+ }
1833
+ fire(marking, chosen);
1834
+ counters.consumed += chosen.consumes.length;
1835
+ counters.produced += chosen.produces.length;
1836
+ });
1837
+ enableSilently(cm, marking, model.finalMarking, counters);
1838
+ const expected = expectedNext(cm, marking);
1839
+ let reachedEnd = true;
1840
+ for (const place of model.finalMarking) {
1841
+ if (tokens(marking, place) > 0) marking.set(place, tokens(marking, place) - 1);
1842
+ else {
1843
+ counters.missing += 1;
1844
+ reachedEnd = false;
1845
+ }
1846
+ counters.consumed += 1;
1847
+ }
1848
+ if (!reachedEnd) {
1849
+ deviations.push({
1850
+ type: "incomplete",
1851
+ activity: trace.length > 0 ? trace[trace.length - 1] : void 0,
1852
+ expected,
1853
+ at: trace.length
1854
+ });
1855
+ }
1856
+ let remaining = 0;
1857
+ for (const count of marking.values()) remaining += count;
1858
+ const result = { caseId, ...counters, remaining };
1859
+ return { ...result, fitness: fitnessOf(result), deviations };
1860
+ }
1861
+ function activitiesOf(events) {
1862
+ return events.map((event) => event.activity);
1863
+ }
1864
+ function replayTrace(events, model) {
1865
+ const normalized = normalizeLog({ events });
1866
+ const instances = normalized.cases.flatMap((kase) => kase.events);
1867
+ if (normalized.cases.length > 1) instances.sort((a, b) => a.start - b.start);
1868
+ const caseId = normalized.cases[0]?.caseId ?? events[0]?.caseId ?? "";
1869
+ return replayActivities(caseId, activitiesOf(instances), model);
1870
+ }
1871
+ function emptyDeviationCounts() {
1872
+ return { undesired: 0, skipped: 0, wrongOrder: 0, wrongStart: 0, incomplete: 0 };
1873
+ }
1874
+ function tokenReplay(log, model) {
1875
+ const normalized = asNormalizedLog(log);
1876
+ const traces = [];
1877
+ const deviationCounts = emptyDeviationCounts();
1878
+ const perActivity = /* @__PURE__ */ Object.create(null);
1879
+ const perEdge = /* @__PURE__ */ Object.create(null);
1880
+ let fitnessSum = 0;
1881
+ for (const kase of normalized.cases) {
1882
+ const trace = activitiesOf(kase.events);
1883
+ const result = replayActivities(kase.caseId, trace, model);
1884
+ traces.push(result);
1885
+ fitnessSum += result.fitness;
1886
+ for (const deviation of result.deviations) {
1887
+ deviationCounts[deviation.type] += 1;
1888
+ if (deviation.activity !== void 0) {
1889
+ const entry = perActivity[deviation.activity] ??= { deviations: 0 };
1890
+ entry.deviations += 1;
1891
+ }
1892
+ if (deviation.at > 0 && deviation.at < trace.length) {
1893
+ const key = `${trace[deviation.at - 1]}${EDGE_KEY_SEPARATOR}${trace[deviation.at]}`;
1894
+ const entry = perEdge[key] ??= { deviations: 0 };
1895
+ entry.deviations += 1;
1896
+ }
1897
+ }
1898
+ }
1899
+ return {
1900
+ overallFitness: traces.length > 0 ? fitnessSum / traces.length : 0,
1901
+ traces,
1902
+ deviationCounts,
1903
+ perActivity,
1904
+ perEdge
1905
+ };
1906
+ }
1907
+
1908
+ // src/core/conformance.ts
1909
+ var DAY_MS = 864e5;
1910
+ function bucketOf(ms, bucket) {
1911
+ const date = new Date(ms);
1912
+ if (bucket === "week") {
1913
+ const offset = (date.getUTCDay() + 6) % 7;
1914
+ const monday = new Date(
1915
+ Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()) - offset * DAY_MS
1916
+ );
1917
+ return monday.toISOString().slice(0, 10);
1918
+ }
1919
+ const iso = date.toISOString();
1920
+ return bucket === "month" ? iso.slice(0, 7) : iso.slice(0, 10);
1921
+ }
1922
+ function conformanceRateSeries(log, model, bucket) {
1923
+ const totals = /* @__PURE__ */ new Map();
1924
+ for (const kase of asNormalizedLog(log).cases) {
1925
+ if (!Number.isFinite(kase.start)) continue;
1926
+ const key = bucketOf(kase.start, bucket);
1927
+ const trace = kase.events.map((event) => event.activity);
1928
+ const { fitness } = replayActivities(kase.caseId, trace, model);
1929
+ const entry = totals.get(key);
1930
+ if (entry === void 0) totals.set(key, { sum: fitness, count: 1 });
1931
+ else {
1932
+ entry.sum += fitness;
1933
+ entry.count += 1;
1934
+ }
1935
+ }
1936
+ return [...totals.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, { sum, count }]) => ({ bucket: key, fitness: sum / count, caseCount: count }));
1937
+ }
1938
+
1939
+ // src/core/adapters/xes.ts
1940
+ var XmlSyntaxError = class extends Error {
1941
+ };
1942
+ var NAME_STOP = /[\s/>=]/;
1943
+ var ENTITY_NAMES = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
1944
+ function decodeXmlEntities(text) {
1945
+ return text.replace(/&(#x[0-9a-fA-F]+|#\d+|[a-zA-Z]+);/g, (match, body) => {
1946
+ if (body[0] === "#") {
1947
+ const isHex = body[1] === "x" || body[1] === "X";
1948
+ const codePoint = Number.parseInt(body.slice(isHex ? 2 : 1), isHex ? 16 : 10);
1949
+ return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match;
1950
+ }
1951
+ return ENTITY_NAMES[body] ?? match;
1952
+ });
1953
+ }
1954
+ function parseXmlDocument(text) {
1955
+ let i = 0;
1956
+ const n = text.length;
1957
+ const skipWhitespace = () => {
1958
+ while (i < n && /\s/.test(text[i])) i += 1;
1959
+ };
1960
+ const skipUntil = (marker, what) => {
1961
+ const at = text.indexOf(marker, i);
1962
+ if (at === -1) throw new XmlSyntaxError(`unterminated ${what}`);
1963
+ i = at + marker.length;
1964
+ };
1965
+ const readName = () => {
1966
+ const start = i;
1967
+ while (i < n && !NAME_STOP.test(text[i])) i += 1;
1968
+ if (i === start) throw new XmlSyntaxError(`expected a name at offset ${start}`);
1969
+ return text.slice(start, i);
1970
+ };
1971
+ const readAttrs = () => {
1972
+ const attrs = {};
1973
+ for (; ; ) {
1974
+ skipWhitespace();
1975
+ const c = text[i];
1976
+ if (c === void 0) throw new XmlSyntaxError("unexpected end of input in a start tag");
1977
+ if (c === "/" || c === ">") return attrs;
1978
+ const name = readName();
1979
+ skipWhitespace();
1980
+ if (text[i] !== "=") throw new XmlSyntaxError(`expected "=" after attribute "${name}"`);
1981
+ i += 1;
1982
+ skipWhitespace();
1983
+ const quote = text[i];
1984
+ if (quote !== '"' && quote !== "'") {
1985
+ throw new XmlSyntaxError(`expected a quoted value for attribute "${name}"`);
1986
+ }
1987
+ i += 1;
1988
+ const start = i;
1989
+ while (i < n && text[i] !== quote) i += 1;
1990
+ if (i >= n) throw new XmlSyntaxError(`unterminated value for attribute "${name}"`);
1991
+ attrs[name] = decodeXmlEntities(text.slice(start, i));
1992
+ i += 1;
1993
+ }
1994
+ };
1995
+ const readElement = () => {
1996
+ i += 1;
1997
+ const tag = readName();
1998
+ const attrs = readAttrs();
1999
+ skipWhitespace();
2000
+ if (text[i] === "/" && text[i + 1] === ">") {
2001
+ i += 2;
2002
+ return { tag, attrs, children: [] };
2003
+ }
2004
+ if (text[i] !== ">") throw new XmlSyntaxError(`expected ">" closing "<${tag}>"`);
2005
+ i += 1;
2006
+ const children = [];
2007
+ for (; ; ) {
2008
+ const lt = text.indexOf("<", i);
2009
+ if (lt === -1) throw new XmlSyntaxError(`unterminated element "<${tag}>"`);
2010
+ i = lt;
2011
+ if (text.startsWith("<!--", i)) {
2012
+ skipUntil("-->", "comment");
2013
+ continue;
2014
+ }
2015
+ if (text.startsWith("<![CDATA[", i)) {
2016
+ skipUntil("]]>", "CDATA section");
2017
+ continue;
2018
+ }
2019
+ if (text.startsWith("</", i)) {
2020
+ i += 2;
2021
+ const closeName = readName();
2022
+ skipWhitespace();
2023
+ if (text[i] !== ">") throw new XmlSyntaxError(`expected ">" closing "</${closeName}>"`);
2024
+ i += 1;
2025
+ if (closeName !== tag) {
2026
+ throw new XmlSyntaxError(
2027
+ `mismatched closing tag: expected "</${tag}>", found "</${closeName}>"`
2028
+ );
2029
+ }
2030
+ return { tag, attrs, children };
2031
+ }
2032
+ children.push(readElement());
2033
+ }
2034
+ };
2035
+ for (; ; ) {
2036
+ skipWhitespace();
2037
+ if (i >= n) throw new XmlSyntaxError("empty document");
2038
+ if (text.startsWith("<?", i)) {
2039
+ skipUntil("?>", "processing instruction");
2040
+ continue;
2041
+ }
2042
+ if (text.startsWith("<!--", i)) {
2043
+ skipUntil("-->", "comment");
2044
+ continue;
2045
+ }
2046
+ if (text.startsWith("<!", i)) {
2047
+ skipUntil(">", "declaration");
2048
+ continue;
2049
+ }
2050
+ break;
2051
+ }
2052
+ if (text[i] !== "<") throw new XmlSyntaxError("expected a root element");
2053
+ return readElement();
2054
+ }
2055
+ var XES_VALUE_TAGS = /* @__PURE__ */ new Set(["string", "date", "int", "float", "boolean"]);
2056
+ function readXesValue(element) {
2057
+ const raw = element.attrs.value;
2058
+ if (raw === void 0) return null;
2059
+ if (element.tag === "int") {
2060
+ const value = Number.parseInt(raw, 10);
2061
+ return Number.isFinite(value) ? value : raw;
2062
+ }
2063
+ if (element.tag === "float") {
2064
+ const value = Number.parseFloat(raw);
2065
+ return Number.isFinite(value) ? value : raw;
2066
+ }
2067
+ if (element.tag === "boolean") return raw.trim().toLowerCase() === "true";
2068
+ return raw;
2069
+ }
2070
+ function readAttributeChildren(children) {
2071
+ const result = {};
2072
+ for (const child of children) {
2073
+ if (!XES_VALUE_TAGS.has(child.tag)) continue;
2074
+ const key = child.attrs.key;
2075
+ if (key === void 0) continue;
2076
+ result[key] = readXesValue(child);
2077
+ }
2078
+ return result;
2079
+ }
2080
+ function withDefaults(own, defaults) {
2081
+ return defaults === void 0 ? own : { ...defaults, ...own };
2082
+ }
2083
+ function collectGlobalDefaults(root, scope) {
2084
+ let defaults;
2085
+ for (const child of root.children) {
2086
+ if (child.tag !== "global" || child.attrs.scope !== scope) continue;
2087
+ defaults = withDefaults(readAttributeChildren(child.children), defaults);
2088
+ }
2089
+ return defaults;
2090
+ }
2091
+ function resolveClassifierKeys(root, options) {
2092
+ if (options?.classifiers !== void 0 && options.classifiers.length > 0) {
2093
+ return options.classifiers;
2094
+ }
2095
+ const classifier = root.children.find((child) => child.tag === "classifier");
2096
+ const keys = classifier?.attrs.keys?.trim();
2097
+ if (keys !== void 0 && keys !== "") return keys.split(/\s+/);
2098
+ return ["concept:name"];
2099
+ }
2100
+ function mapLifecycle(raw, model) {
2101
+ if (typeof raw !== "string" || model === "none") return void 0;
2102
+ const value = raw.trim().toLowerCase();
2103
+ if (value === "") return void 0;
2104
+ return value === "start" ? "start" : "complete";
2105
+ }
2106
+ function fromXes(source, options) {
2107
+ let root;
2108
+ try {
2109
+ root = parseXmlDocument(source);
2110
+ } catch (error) {
2111
+ const message = error instanceof Error ? error.message : String(error);
2112
+ return { ok: false, errors: [{ type: "malformed_xml", message }] };
2113
+ }
2114
+ const lifecycleModel = options?.lifecycleModel ?? "standard";
2115
+ const classifierKeys = resolveClassifierKeys(root, options);
2116
+ const consumedKeys = /* @__PURE__ */ new Set([
2117
+ ...classifierKeys,
2118
+ "time:timestamp",
2119
+ "org:resource",
2120
+ "lifecycle:transition"
2121
+ ]);
2122
+ const eventDefaults = collectGlobalDefaults(root, "event");
2123
+ const traceDefaults = collectGlobalDefaults(root, "trace");
2124
+ const errors = [];
2125
+ const events = [];
2126
+ let caseAttributes;
2127
+ const traces = root.children.filter((child) => child.tag === "trace");
2128
+ traces.forEach((trace, traceIndex) => {
2129
+ const traceEvents = trace.children.filter((child) => child.tag === "event");
2130
+ if (traceEvents.length === 0) {
2131
+ errors.push({ type: "missing_trace", message: "trace has no events", traceIndex });
2132
+ return;
2133
+ }
2134
+ const { "concept:name": rawCaseId, ...traceValues } = withDefaults(
2135
+ readAttributeChildren(trace.children),
2136
+ traceDefaults
2137
+ );
2138
+ const caseId = typeof rawCaseId === "string" && rawCaseId !== "" ? rawCaseId : `trace-${traceIndex}`;
2139
+ if (Object.keys(traceValues).length > 0) {
2140
+ caseAttributes ??= {};
2141
+ caseAttributes[caseId] = traceValues;
2142
+ }
2143
+ traceEvents.forEach((eventElement, eventIndex) => {
2144
+ const values = withDefaults(readAttributeChildren(eventElement.children), eventDefaults);
2145
+ const activityParts = [];
2146
+ let missingActivityKey = false;
2147
+ for (const key of classifierKeys) {
2148
+ const value = values[key];
2149
+ if (value === void 0 || value === null) {
2150
+ missingActivityKey = true;
2151
+ break;
2152
+ }
2153
+ activityParts.push(String(value));
2154
+ }
2155
+ if (missingActivityKey) {
2156
+ errors.push({
2157
+ type: "missing_concept_name",
2158
+ message: "event is missing its classifier key(s)",
2159
+ traceIndex,
2160
+ eventIndex
2161
+ });
2162
+ return;
2163
+ }
2164
+ const rawTimestamp = values["time:timestamp"];
2165
+ if (rawTimestamp === void 0 || rawTimestamp === null) {
2166
+ errors.push({
2167
+ type: "missing_timestamp",
2168
+ message: "event is missing time:timestamp",
2169
+ traceIndex,
2170
+ eventIndex
2171
+ });
2172
+ return;
2173
+ }
2174
+ const row = {
2175
+ caseId,
2176
+ activity: activityParts.join("+"),
2177
+ timestamp: String(rawTimestamp)
2178
+ };
2179
+ const resource = values["org:resource"];
2180
+ if (typeof resource === "string" && resource !== "") row.resource = resource;
2181
+ const lifecycle = mapLifecycle(values["lifecycle:transition"], lifecycleModel);
2182
+ if (lifecycle !== void 0) row.lifecycle = lifecycle;
2183
+ const attributes = {};
2184
+ let anyAttribute = false;
2185
+ for (const [key, value] of Object.entries(values)) {
2186
+ if (consumedKeys.has(key)) continue;
2187
+ attributes[key] = value;
2188
+ anyAttribute = true;
2189
+ }
2190
+ if (anyAttribute) row.attributes = attributes;
2191
+ events.push(row);
2192
+ });
2193
+ });
2194
+ if (errors.length > 0) return { ok: false, errors };
2195
+ const log = { events };
2196
+ if (caseAttributes !== void 0) log.caseAttributes = caseAttributes;
2197
+ return { ok: true, log };
2198
+ }
2199
+
2200
+ // src/core/segments.ts
2201
+ function segmentKey(from, to) {
2202
+ return `${from}${EDGE_KEY_SEPARATOR}${to}`;
2203
+ }
2204
+ function segmentsFor(log, order) {
2205
+ const wanted = /* @__PURE__ */ new Set();
2206
+ for (const def of order) wanted.add(segmentKey(def.from, def.to));
2207
+ const out = [];
2208
+ if (wanted.size === 0) return out;
2209
+ for (const kase of asNormalizedLog(log).cases) {
2210
+ const trace = kase.events;
2211
+ for (let i = 1; i < trace.length; i += 1) {
2212
+ const previous = trace[i - 1];
2213
+ const event = trace[i];
2214
+ const key = segmentKey(previous.activity, event.activity);
2215
+ if (!wanted.has(key)) continue;
2216
+ const start = previous.end;
2217
+ if (!Number.isFinite(start) || !Number.isFinite(event.start)) continue;
2218
+ const end = Math.max(start, event.start);
2219
+ out.push({ segment: key, caseId: kase.caseId, start, end, duration: end - start });
2220
+ }
2221
+ }
2222
+ return out;
2223
+ }
2224
+ function segmentOrderByFrequency(graph, limit = Number.POSITIVE_INFINITY) {
2225
+ const n = Math.max(0, Math.floor(limit));
2226
+ return graph.transitions.slice(0, n).map((t) => ({ from: t.source, to: t.target }));
2227
+ }
2228
+ function segmentOrderForVariant(variant) {
2229
+ const seen = /* @__PURE__ */ new Set();
2230
+ const out = [];
2231
+ for (let i = 1; i < variant.sequence.length; i += 1) {
2232
+ const from = variant.sequence[i - 1];
2233
+ const to = variant.sequence[i];
2234
+ const key = segmentKey(from, to);
2235
+ if (seen.has(key)) continue;
2236
+ seen.add(key);
2237
+ out.push({ from, to });
2238
+ }
2239
+ return out;
2240
+ }
2241
+ function durationQuartileThresholds(durations) {
2242
+ const sorted = [];
2243
+ for (const d of durations) if (Number.isFinite(d)) sorted.push(d);
2244
+ sorted.sort(ascending);
2245
+ if (sorted.length === 0) return [0, 0, 0];
2246
+ return [quantileSorted(sorted, 0.25), quantileSorted(sorted, 0.5), quantileSorted(sorted, 0.75)];
2247
+ }
2248
+ function quartileOf(duration, thresholds) {
2249
+ if (duration <= thresholds[0]) return 1;
2250
+ if (duration <= thresholds[1]) return 2;
2251
+ if (duration <= thresholds[2]) return 3;
2252
+ return 4;
2253
+ }
2254
+ function durationQuartile(occurrence, allDurationsForSegment) {
2255
+ return quartileOf(occurrence.duration, durationQuartileThresholds(allDurationsForSegment));
2256
+ }
2257
+
2258
+ // src/core/diff-graphs.ts
2259
+ function transitionKey2(transition) {
2260
+ return `${transition.source}${EDGE_KEY_SEPARATOR}${transition.target}`;
2261
+ }
2262
+ function unionIds(first, second) {
2263
+ const seen = /* @__PURE__ */ new Set();
2264
+ const out = [];
2265
+ for (const id of first) {
2266
+ if (seen.has(id)) continue;
2267
+ seen.add(id);
2268
+ out.push(id);
2269
+ }
2270
+ for (const id of second) {
2271
+ if (seen.has(id)) continue;
2272
+ seen.add(id);
2273
+ out.push(id);
2274
+ }
2275
+ return out;
2276
+ }
2277
+ function diffEntries(aById, bById, ids, referenceValue) {
2278
+ return ids.map((id) => {
2279
+ const a = aById.get(id);
2280
+ const b = bById.get(id);
2281
+ const state = a !== void 0 && b !== void 0 ? "common" : a ? "aOnly" : "bOnly";
2282
+ const entry = { id, state };
2283
+ if (a !== void 0) entry.a = a;
2284
+ if (b !== void 0) entry.b = b;
2285
+ if (a !== void 0 && b !== void 0) {
2286
+ const aValue = referenceValue(a);
2287
+ const bValue = referenceValue(b);
2288
+ entry.delta = bValue - aValue;
2289
+ if (aValue > 0) entry.ratio = bValue / aValue;
2290
+ }
2291
+ return entry;
2292
+ });
2293
+ }
2294
+ function diffGraphs(a, b) {
2295
+ const aActivities = new Map(a.activities.map((activity) => [activity.id, activity]));
2296
+ const bActivities = new Map(b.activities.map((activity) => [activity.id, activity]));
2297
+ const activityIds = unionIds(
2298
+ a.activities.map((activity) => activity.id),
2299
+ b.activities.map((activity) => activity.id)
2300
+ );
2301
+ const aTransitions = new Map(a.transitions.map((t) => [transitionKey2(t), t]));
2302
+ const bTransitions = new Map(b.transitions.map((t) => [transitionKey2(t), t]));
2303
+ const transitionIds = unionIds(
2304
+ a.transitions.map((t) => transitionKey2(t)),
2305
+ b.transitions.map((t) => transitionKey2(t))
2306
+ );
2307
+ return {
2308
+ activities: diffEntries(aActivities, bActivities, activityIds, (stats) => stats.instances),
2309
+ transitions: diffEntries(aTransitions, bTransitions, transitionIds, (stats) => stats.count),
2310
+ totals: { a: a.totals, b: b.totals }
2311
+ };
2312
+ }
2313
+
2314
+ // src/core/replay-timeline.ts
2315
+ var REPLAY_TARGET_FRAMES = 300;
2316
+ var REPLAY_MAX_FRAMES = 5e3;
2317
+ function defaultReplayBucketMs(duration) {
2318
+ if (!Number.isFinite(duration) || duration <= 0) return 1;
2319
+ return Math.max(1, duration / REPLAY_TARGET_FRAMES);
2320
+ }
2321
+ function buildSegments(log, synchronizedStart) {
2322
+ const { cases } = asNormalizedLog(log);
2323
+ if (cases.length === 0) return { origin: 0, duration: 0, segments: [] };
2324
+ const origin = synchronizedStart ? 0 : Math.min(...cases.map((c) => c.start));
2325
+ const segments = [];
2326
+ let duration = 0;
2327
+ for (const trace of cases) {
2328
+ const base = synchronizedStart ? trace.start : origin;
2329
+ duration = Math.max(duration, trace.end - base);
2330
+ for (let i = 1; i < trace.events.length; i++) {
2331
+ const from = trace.events[i - 1];
2332
+ const to = trace.events[i];
2333
+ const enterAt = from.end - base;
2334
+ segments.push({
2335
+ caseId: trace.caseId,
2336
+ edgeId: `${from.activity}${EDGE_KEY_SEPARATOR}${to.activity}`,
2337
+ source: from.activity,
2338
+ target: to.activity,
2339
+ enterAt,
2340
+ exitAt: Math.max(enterAt, to.start - base)
2341
+ });
2342
+ }
2343
+ }
2344
+ segments.sort((a, b) => a.enterAt - b.enterAt);
2345
+ return { origin, duration, segments };
2346
+ }
2347
+ function tokensAt(segments, t) {
2348
+ const tokens2 = [];
2349
+ for (const segment of segments) {
2350
+ if (segment.enterAt > t) break;
2351
+ if (t >= segment.exitAt) continue;
2352
+ tokens2.push({
2353
+ caseId: segment.caseId,
2354
+ edgeId: segment.edgeId,
2355
+ progress: (t - segment.enterAt) / (segment.exitAt - segment.enterAt)
2356
+ });
2357
+ }
2358
+ return tokens2;
2359
+ }
2360
+ function congestionIn(segments, start, end) {
2361
+ const casesByEdge = /* @__PURE__ */ new Map();
2362
+ for (const segment of segments) {
2363
+ if (segment.enterAt >= end) break;
2364
+ const overlaps = segment.exitAt > start || segment.enterAt >= start;
2365
+ if (!overlaps) continue;
2366
+ let cases = casesByEdge.get(segment.edgeId);
2367
+ if (!cases) casesByEdge.set(segment.edgeId, cases = /* @__PURE__ */ new Set());
2368
+ cases.add(segment.caseId);
2369
+ }
2370
+ const congestion = {};
2371
+ for (const [edgeId, cases] of casesByEdge) congestion[edgeId] = cases.size;
2372
+ return congestion;
2373
+ }
2374
+ function replayTimeline(log, options = {}) {
2375
+ const synchronizedStart = options.synchronizedStart ?? false;
2376
+ const { origin, duration, segments } = buildSegments(log, synchronizedStart);
2377
+ const requested = options.bucketMs !== void 0 && Number.isFinite(options.bucketMs) && options.bucketMs > 0 ? options.bucketMs : defaultReplayBucketMs(duration);
2378
+ const bucketMs = Math.max(requested, duration / (REPLAY_MAX_FRAMES - 1));
2379
+ const frameCount = Math.floor(duration / bucketMs) + 1;
2380
+ const frames = [];
2381
+ let peakCongestion = 0;
2382
+ for (let i = 0; i < frameCount; i++) {
2383
+ const t = i * bucketMs;
2384
+ const congestion = congestionIn(segments, t, t + bucketMs);
2385
+ for (const count of Object.values(congestion)) peakCongestion = Math.max(peakCongestion, count);
2386
+ frames.push({ t, tokens: tokensAt(segments, t), congestion });
2387
+ }
2388
+ return { origin, duration, bucketMs, synchronizedStart, segments, frames, peakCongestion };
2389
+ }
2390
+ function replayFrameAt(timeline, t) {
2391
+ const clamped = Math.min(Math.max(Number.isFinite(t) ? t : 0, 0), timeline.duration);
2392
+ const bucket = Math.min(Math.floor(clamped / timeline.bucketMs), timeline.frames.length - 1);
2393
+ return {
2394
+ t: clamped,
2395
+ tokens: tokensAt(timeline.segments, clamped),
2396
+ congestion: timeline.frames[Math.max(bucket, 0)]?.congestion ?? {}
2397
+ };
2398
+ }
2399
+ function rankReplayCongestion(timeline, limit = Number.POSITIVE_INFINITY) {
2400
+ const ends = /* @__PURE__ */ new Map();
2401
+ for (const s of timeline.segments) ends.set(s.edgeId, { source: s.source, target: s.target });
2402
+ const byEdge = /* @__PURE__ */ new Map();
2403
+ for (const frame of timeline.frames) {
2404
+ for (const [edgeId, count] of Object.entries(frame.congestion)) {
2405
+ let entry = byEdge.get(edgeId);
2406
+ if (!entry) {
2407
+ const { source, target } = ends.get(edgeId);
2408
+ byEdge.set(edgeId, entry = { edgeId, source, target, peak: 0, peakAt: 0, mean: 0 });
2409
+ }
2410
+ if (count > entry.peak) {
2411
+ entry.peak = count;
2412
+ entry.peakAt = frame.t;
2413
+ }
2414
+ entry.mean += count;
2415
+ }
2416
+ }
2417
+ const frameCount = Math.max(timeline.frames.length, 1);
2418
+ return [...byEdge.values()].map((entry) => ({ ...entry, mean: entry.mean / frameCount })).sort(
2419
+ (a, b) => b.peak - a.peak || b.mean - a.mean || (a.edgeId < b.edgeId ? -1 : a.edgeId > b.edgeId ? 1 : 0)
2420
+ ).slice(0, limit);
2421
+ }
2422
+ var REPLAY_TOKEN_RADIUS_RANGE = Object.freeze([3, 8]);
2423
+ function replayTokenRadius(congestion, peak) {
2424
+ const [min, max] = REPLAY_TOKEN_RADIUS_RANGE;
2425
+ if (!(peak > 0) || !(congestion > 0)) return min;
2426
+ const share = Math.min(congestion / peak, 1);
2427
+ return min + (max - min) * Math.sqrt(share);
2428
+ }
2429
+
2430
+ // src/core/adapters/ocel.ts
2431
+ var OCEL_EVENT_ID_ATTRIBUTE = "__ocelEventId";
2432
+ var OCEL_OBJECT_REFS_ATTRIBUTE = "__objectRefs";
2433
+ function isRecord(value) {
2434
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2435
+ }
2436
+ function isScalar(value) {
2437
+ return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
2438
+ }
2439
+ function nonEmptyString(value) {
2440
+ return typeof value === "string" && value !== "";
2441
+ }
2442
+ function objectAttributes(object) {
2443
+ if (!Array.isArray(object.attributes)) return void 0;
2444
+ const latest = /* @__PURE__ */ new Map();
2445
+ for (const raw of object.attributes) {
2446
+ if (!isRecord(raw) || !nonEmptyString(raw.name) || !isScalar(raw.value)) continue;
2447
+ const parsed = typeof raw.time === "string" ? Date.parse(raw.time) : Number.NEGATIVE_INFINITY;
2448
+ const time = Number.isNaN(parsed) ? Number.NEGATIVE_INFINITY : parsed;
2449
+ const previous = latest.get(raw.name);
2450
+ if (previous === void 0 || time >= previous.time) {
2451
+ latest.set(raw.name, { time, value: raw.value });
2452
+ }
2453
+ }
2454
+ if (latest.size === 0) return void 0;
2455
+ const out = {};
2456
+ for (const [name, entry] of latest) out[name] = entry.value;
2457
+ return out;
2458
+ }
2459
+ function fromOcel(input, options) {
2460
+ let document = input;
2461
+ if (typeof input === "string") {
2462
+ try {
2463
+ document = JSON.parse(input);
2464
+ } catch (error) {
2465
+ const message = error instanceof Error ? error.message : String(error);
2466
+ return { ok: false, errors: [{ type: "malformed_json", message }] };
2467
+ }
2468
+ }
2469
+ if (!isRecord(document) || !Array.isArray(document.objectTypes) || !Array.isArray(document.objects) || !Array.isArray(document.events)) {
2470
+ return {
2471
+ ok: false,
2472
+ errors: [
2473
+ {
2474
+ type: "invalid_document",
2475
+ message: "expected an object with objectTypes, objects and events arrays"
2476
+ }
2477
+ ]
2478
+ };
2479
+ }
2480
+ const errors = [];
2481
+ const declaredTypes = [];
2482
+ for (const declaration of document.objectTypes) {
2483
+ if (isRecord(declaration) && nonEmptyString(declaration.name)) {
2484
+ if (!declaredTypes.includes(declaration.name)) declaredTypes.push(declaration.name);
2485
+ }
2486
+ }
2487
+ const projected = options?.objectTypes ?? declaredTypes;
2488
+ for (const type of projected) {
2489
+ if (!declaredTypes.includes(type)) {
2490
+ errors.push({
2491
+ type: "unknown_object_type",
2492
+ message: `object type "${type}" is not declared in objectTypes`
2493
+ });
2494
+ }
2495
+ }
2496
+ const projectedSet = new Set(projected);
2497
+ const typeOfObject = /* @__PURE__ */ new Map();
2498
+ const attributesOfObject = /* @__PURE__ */ new Map();
2499
+ document.objects.forEach((object, objectIndex) => {
2500
+ if (!isRecord(object) || !nonEmptyString(object.id) || !nonEmptyString(object.type)) {
2501
+ errors.push({
2502
+ type: "invalid_document",
2503
+ message: "object is missing its id or type",
2504
+ objectIndex
2505
+ });
2506
+ return;
2507
+ }
2508
+ if (!declaredTypes.includes(object.type)) {
2509
+ errors.push({
2510
+ type: "unknown_object_type",
2511
+ message: `object "${object.id}" has undeclared type "${object.type}"`,
2512
+ objectIndex
2513
+ });
2514
+ return;
2515
+ }
2516
+ typeOfObject.set(object.id, object.type);
2517
+ const attributes = objectAttributes(object);
2518
+ if (attributes !== void 0) attributesOfObject.set(object.id, attributes);
2519
+ });
2520
+ const rowsByType = new Map(projected.map((type) => [type, []]));
2521
+ const activities = [];
2522
+ const seenActivities = /* @__PURE__ */ new Set();
2523
+ document.events.forEach((event, eventIndex) => {
2524
+ if (!isRecord(event)) {
2525
+ errors.push({ type: "invalid_document", message: "event is not an object", eventIndex });
2526
+ return;
2527
+ }
2528
+ if (!nonEmptyString(event.id)) {
2529
+ errors.push({ type: "missing_event_id", message: "event is missing its id", eventIndex });
2530
+ return;
2531
+ }
2532
+ if (!nonEmptyString(event.type)) {
2533
+ errors.push({
2534
+ type: "missing_event_type",
2535
+ message: `event "${event.id}" is missing its type`,
2536
+ eventIndex
2537
+ });
2538
+ return;
2539
+ }
2540
+ if (!nonEmptyString(event.time) || Number.isNaN(Date.parse(event.time))) {
2541
+ errors.push({
2542
+ type: "missing_timestamp",
2543
+ message: `event "${event.id}" has no parsable time`,
2544
+ eventIndex
2545
+ });
2546
+ return;
2547
+ }
2548
+ const refs = {};
2549
+ let broken = false;
2550
+ const relationships = Array.isArray(event.relationships) ? event.relationships : [];
2551
+ for (const relationship of relationships) {
2552
+ if (!isRecord(relationship) || !nonEmptyString(relationship.objectId)) continue;
2553
+ const type = typeOfObject.get(relationship.objectId);
2554
+ if (type === void 0) {
2555
+ errors.push({
2556
+ type: "unknown_object",
2557
+ message: `event "${event.id}" references undeclared object "${relationship.objectId}"`,
2558
+ eventIndex
2559
+ });
2560
+ broken = true;
2561
+ continue;
2562
+ }
2563
+ const ids = refs[type] ??= [];
2564
+ if (!ids.includes(relationship.objectId)) ids.push(relationship.objectId);
2565
+ }
2566
+ if (broken) return;
2567
+ if (!seenActivities.has(event.type)) {
2568
+ seenActivities.add(event.type);
2569
+ activities.push(event.type);
2570
+ }
2571
+ let attributes;
2572
+ if (Array.isArray(event.attributes)) {
2573
+ for (const attribute of event.attributes) {
2574
+ if (!isRecord(attribute) || !nonEmptyString(attribute.name)) continue;
2575
+ if (!isScalar(attribute.value)) continue;
2576
+ attributes ??= {};
2577
+ attributes[attribute.name] = attribute.value;
2578
+ }
2579
+ }
2580
+ const encodedRefs = JSON.stringify(refs);
2581
+ for (const [type, objectIds] of Object.entries(refs)) {
2582
+ if (!projectedSet.has(type)) continue;
2583
+ const rows = rowsByType.get(type);
2584
+ for (const objectId of objectIds) {
2585
+ rows.push({
2586
+ caseId: objectId,
2587
+ activity: event.type,
2588
+ timestamp: event.time,
2589
+ attributes: {
2590
+ ...attributes,
2591
+ [OCEL_EVENT_ID_ATTRIBUTE]: event.id,
2592
+ [OCEL_OBJECT_REFS_ATTRIBUTE]: encodedRefs
2593
+ }
2594
+ });
2595
+ }
2596
+ }
2597
+ });
2598
+ if (errors.length > 0) return { ok: false, errors };
2599
+ const logs = {};
2600
+ for (const type of projected) {
2601
+ const events = rowsByType.get(type);
2602
+ const log = { events };
2603
+ let caseAttributes;
2604
+ for (const row of events) {
2605
+ const attributes = attributesOfObject.get(row.caseId);
2606
+ if (attributes === void 0 || caseAttributes?.[row.caseId] !== void 0) continue;
2607
+ caseAttributes ??= {};
2608
+ caseAttributes[row.caseId] = attributes;
2609
+ }
2610
+ if (caseAttributes !== void 0) log.caseAttributes = caseAttributes;
2611
+ logs[type] = log;
2612
+ }
2613
+ return { ok: true, logs, activities, objectTypes: [...projected] };
2614
+ }
2615
+ function readObjectRefs(row) {
2616
+ const raw = row.attributes?.[OCEL_OBJECT_REFS_ATTRIBUTE];
2617
+ if (typeof raw !== "string") return void 0;
2618
+ try {
2619
+ const parsed = JSON.parse(raw);
2620
+ if (!isRecord(parsed)) return void 0;
2621
+ const out = {};
2622
+ for (const [type, ids] of Object.entries(parsed)) {
2623
+ if (Array.isArray(ids)) out[type] = ids.filter((id) => typeof id === "string");
2624
+ }
2625
+ return out;
2626
+ } catch {
2627
+ return void 0;
2628
+ }
2629
+ }
2630
+
2631
+ // src/core/discover-object-centric-graph.ts
2632
+ function compareStrings3(a, b) {
2633
+ return a < b ? -1 : a > b ? 1 : 0;
2634
+ }
2635
+ function countDistinctEvents(logs) {
2636
+ const ids = /* @__PURE__ */ new Map();
2637
+ let any = false;
2638
+ for (const log of logs) {
2639
+ for (const row of log.events) {
2640
+ const id = row.attributes?.[OCEL_EVENT_ID_ATTRIBUTE];
2641
+ if (typeof id !== "string") continue;
2642
+ any = true;
2643
+ let set = ids.get(row.activity);
2644
+ if (set === void 0) {
2645
+ set = /* @__PURE__ */ new Set();
2646
+ ids.set(row.activity, set);
2647
+ }
2648
+ set.add(id);
2649
+ }
2650
+ }
2651
+ if (!any) return void 0;
2652
+ const out = /* @__PURE__ */ new Map();
2653
+ for (const [activity, set] of ids) out.set(activity, set.size);
2654
+ return out;
2655
+ }
2656
+ function mergeObjectCentricGraphs(graphsByType, objectTypes = Object.keys(graphsByType), eventCounts) {
2657
+ const merged = /* @__PURE__ */ new Map();
2658
+ const bestInstances = /* @__PURE__ */ new Map();
2659
+ const transitionsByType = {};
2660
+ const types = objectTypes.filter((type) => graphsByType[type] !== void 0);
2661
+ for (const type of types) {
2662
+ const graph = graphsByType[type];
2663
+ transitionsByType[type] = graph.transitions;
2664
+ for (const activity of graph.activities) {
2665
+ let entry = merged.get(activity.id);
2666
+ if (entry === void 0) {
2667
+ entry = {
2668
+ id: activity.id,
2669
+ label: activity.label,
2670
+ isStart: false,
2671
+ isEnd: false,
2672
+ duration: activity.duration,
2673
+ events: 0,
2674
+ perType: {}
2675
+ };
2676
+ merged.set(activity.id, entry);
2677
+ }
2678
+ entry.perType[type] = { instances: activity.instances, cases: activity.cases };
2679
+ entry.isStart ||= activity.isStart;
2680
+ entry.isEnd ||= activity.isEnd;
2681
+ if (activity.instances > (bestInstances.get(activity.id) ?? -1)) {
2682
+ bestInstances.set(activity.id, activity.instances);
2683
+ entry.duration = activity.duration;
2684
+ }
2685
+ }
2686
+ }
2687
+ const activities = [...merged.values()];
2688
+ for (const activity of activities) {
2689
+ activity.events = eventCounts?.get(activity.id) ?? bestInstances.get(activity.id) ?? 0;
2690
+ }
2691
+ activities.sort((a, b) => b.events - a.events || compareStrings3(a.id, b.id));
2692
+ const graphs = {};
2693
+ for (const type of types) graphs[type] = graphsByType[type];
2694
+ return { activities, transitionsByType, objectTypes: types, graphsByType: graphs };
2695
+ }
2696
+ function discoverObjectCentricGraph(logsByType, options = {}) {
2697
+ const { objectTypes = Object.keys(logsByType), ...discoverOptions } = options;
2698
+ const types = objectTypes.filter((type) => logsByType[type] !== void 0);
2699
+ const graphsByType = {};
2700
+ for (const type of types) {
2701
+ graphsByType[type] = discoverGraph(logsByType[type], discoverOptions);
2702
+ }
2703
+ const eventCounts = countDistinctEvents(types.map((type) => logsByType[type]));
2704
+ return mergeObjectCentricGraphs(graphsByType, types, eventCounts);
2705
+ }
2706
+ function abstractObjectCentricGraph(graph, perType = {}, fallback) {
2707
+ const graphsByType = {};
2708
+ const hiddenByType = {};
2709
+ for (const type of graph.objectTypes) {
2710
+ const source = graph.graphsByType[type];
2711
+ const options = perType[type] ?? fallback;
2712
+ if (options === void 0) {
2713
+ graphsByType[type] = source;
2714
+ hiddenByType[type] = { activities: 0, paths: 0 };
2715
+ continue;
2716
+ }
2717
+ const { hidden, ...abstracted } = abstractGraph(source, options);
2718
+ graphsByType[type] = abstracted;
2719
+ hiddenByType[type] = hidden;
2720
+ }
2721
+ const eventCounts = new Map(graph.activities.map((activity) => [activity.id, activity.events]));
2722
+ return {
2723
+ ...mergeObjectCentricGraphs(graphsByType, graph.objectTypes, eventCounts),
2724
+ hiddenByType
2725
+ };
2726
+ }
2727
+ function objectCentricProcessGraph(graph) {
2728
+ const activities = graph.activities.map((activity) => {
2729
+ let cases = 0;
2730
+ for (const counts of Object.values(activity.perType)) cases += counts.cases;
2731
+ return {
2732
+ id: activity.id,
2733
+ label: activity.label,
2734
+ instances: activity.events,
2735
+ cases,
2736
+ isStart: activity.isStart,
2737
+ isEnd: activity.isEnd,
2738
+ duration: activity.duration
2739
+ };
2740
+ });
2741
+ const transitions = /* @__PURE__ */ new Map();
2742
+ const startActivities = {};
2743
+ const endActivities = {};
2744
+ const totals = { cases: 0, events: 0, variants: 0 };
2745
+ for (const activity of graph.activities) totals.events += activity.events;
2746
+ for (const type of graph.objectTypes) {
2747
+ const typeGraph = graph.graphsByType[type];
2748
+ totals.cases += typeGraph.totals.cases;
2749
+ totals.variants += typeGraph.totals.variants;
2750
+ for (const [id, count] of Object.entries(typeGraph.startActivities)) {
2751
+ startActivities[id] = (startActivities[id] ?? 0) + count;
2752
+ }
2753
+ for (const [id, count] of Object.entries(typeGraph.endActivities)) {
2754
+ endActivities[id] = (endActivities[id] ?? 0) + count;
2755
+ }
2756
+ for (const transition of graph.transitionsByType[type] ?? []) {
2757
+ const key = `${transition.source}${EDGE_KEY_SEPARATOR}${transition.target}`;
2758
+ const existing = transitions.get(key);
2759
+ if (existing === void 0) {
2760
+ transitions.set(key, { ...transition });
2761
+ continue;
2762
+ }
2763
+ if (transition.count > existing.count) existing.duration = transition.duration;
2764
+ existing.count += transition.count;
2765
+ existing.caseCount += transition.caseCount;
2766
+ }
2767
+ }
2768
+ const transitionList = [...transitions.values()].sort(
2769
+ (a, b) => b.count - a.count || compareStrings3(a.source, b.source) || compareStrings3(a.target, b.target)
2770
+ );
2771
+ const sorted = (record) => {
2772
+ const out = {};
2773
+ for (const key of Object.keys(record).sort()) out[key] = record[key];
2774
+ return out;
2775
+ };
2776
+ return {
2777
+ activities,
2778
+ transitions: transitionList,
2779
+ startActivities: sorted(startActivities),
2780
+ endActivities: sorted(endActivities),
2781
+ totals
2782
+ };
2783
+ }
2784
+ function objectTypeColorScale(graph) {
2785
+ const zero = { min: 0, max: 0, mean: 0, median: 0, p90: 0, sum: 0, trimmedMean: 0 };
2786
+ const activities = graph.objectTypes.map((type) => {
2787
+ const typeGraph = graph.graphsByType[type];
2788
+ return {
2789
+ id: type,
2790
+ label: type,
2791
+ instances: typeGraph.totals.events,
2792
+ cases: typeGraph.totals.cases,
2793
+ isStart: false,
2794
+ isEnd: false,
2795
+ duration: zero
2796
+ };
2797
+ });
2798
+ return activityColorScale({
2799
+ activities,
2800
+ transitions: [],
2801
+ startActivities: {},
2802
+ endActivities: {},
2803
+ totals: { cases: 0, events: 0, variants: 0 }
2804
+ });
2805
+ }
1509
2806
  export {
2807
+ ACTIVITY_COLOR_SLOTS,
2808
+ ACTIVITY_OTHER_TOKEN,
1510
2809
  BPI_2012_ACTIVITIES,
1511
2810
  BPI_2012_SUBSET_EPOCH,
1512
2811
  DEFAULT_LIFECYCLE_VALUES,
2812
+ DEVIATION_TYPES,
1513
2813
  DURATION_SAMPLE_CAP,
1514
2814
  DURATION_UNIT_MS,
1515
2815
  DurationSampler,
1516
2816
  EDGE_KEY_SEPARATOR,
1517
2817
  EMPTY_DURATION_STATS,
2818
+ OCEL_EVENT_ID_ATTRIBUTE,
2819
+ OCEL_OBJECT_REFS_ATTRIBUTE,
2820
+ REPLAY_MAX_FRAMES,
2821
+ REPLAY_TARGET_FRAMES,
2822
+ REPLAY_TOKEN_RADIUS_RANGE,
1518
2823
  SYNTHETIC_ACTIVITIES,
1519
2824
  SYNTHETIC_LOG_EPOCH,
1520
2825
  TRIM_FRACTION,
1521
2826
  VARIANT_KEY_SEPARATOR,
1522
2827
  abstractGraph,
2828
+ abstractObjectCentricGraph,
2829
+ activityColorScale,
1523
2830
  aggregatePerformance,
1524
2831
  asNormalizedLog,
1525
2832
  caseMatchesFilters,
2833
+ casesFromLog,
1526
2834
  clampWidth,
2835
+ conformanceRateSeries,
1527
2836
  createProcessWorker,
2837
+ defaultReplayBucketMs,
1528
2838
  detectRework,
2839
+ diffGraphs,
1529
2840
  discoverGraph,
2841
+ discoverObjectCentricGraph,
2842
+ durationQuartile,
2843
+ durationQuartileThresholds,
1530
2844
  durationStats,
2845
+ emptyDeviationCounts,
1531
2846
  emptyDurationStats,
1532
2847
  extractVariants,
1533
2848
  filterLog,
1534
2849
  filterNormalizedLog,
1535
2850
  fromCsv,
1536
2851
  fromFlatRows,
2852
+ fromOcel,
2853
+ fromXes,
1537
2854
  generateBpi2012Subset,
1538
2855
  generateSyntheticLog,
1539
2856
  handleProcessRequest,
1540
2857
  isNormalizedLog,
2858
+ liftHappyPath,
2859
+ mergeObjectCentricGraphs,
1541
2860
  minMax,
1542
2861
  normalizeLifecycle,
1543
2862
  normalizeLog,
2863
+ objectCentricProcessGraph,
2864
+ objectTypeColorScale,
1544
2865
  parseDelimited,
1545
2866
  performanceValue,
1546
2867
  quantile,
1547
2868
  quantileSorted,
2869
+ quartileOf,
2870
+ rankReplayCongestion,
2871
+ readObjectRefs,
1548
2872
  reconcileGraph,
2873
+ replayActivities,
2874
+ replayFrameAt,
2875
+ replayTimeline,
2876
+ replayTokenRadius,
2877
+ replayTrace,
2878
+ segmentKey,
2879
+ segmentOrderByFrequency,
2880
+ segmentOrderForVariant,
2881
+ segmentsFor,
1549
2882
  toEpochMs,
2883
+ tokenReplay,
1550
2884
  variantId,
1551
2885
  variantKey
1552
2886
  };