@elabs-ai/components-process 4.2.0 → 5.1.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
@@ -7,8 +7,8 @@
7
7
  * additive: a required field added here is a breaking change for five items at once.
8
8
  *
9
9
  * NOTHING in this module — or anywhere under `src/core/` — may import React, React Flow,
10
- * visx, d3 or an `@elabs-ai/components-*` package. See `.claude/rules/process-components.md`
11
- * and `pnpm process:reuse:check`.
10
+ * visx, d3 or an `@elabs-ai/components-*` package. See `.claude/rules/data.md` ("Process
11
+ * mining" section) and `pnpm check --rule process-reuse`.
12
12
  */
13
13
  /**
14
14
  * One raw row of an event log, before normalization.
@@ -1026,4 +1026,802 @@ type ProcessWorkerResponse = {
1026
1026
  */
1027
1027
  declare function handleProcessRequest(request: ProcessWorkerRequest): ProcessWorkerResponse;
1028
1028
 
1029
- export { type AbstractedGraph, type AbstractionOptions, type ActivityRework, type ActivityStats, type AnyLog, BPI_2012_ACTIVITIES, BPI_2012_SUBSET_EPOCH, type Bpi2012Activity, type Bpi2012SubsetOptions, type CreateProcessWorkerOptions, type CsvMapping, type CsvOptions, DEFAULT_LIFECYCLE_VALUES, DURATION_SAMPLE_CAP, DURATION_UNIT_MS, type DiscoverGraphOptions, DurationSampler, type DurationStats, type DurationUnit, EDGE_KEY_SEPARATOR, EMPTY_DURATION_STATS, type EventLog, type EventRow, type FilterSpec, type FlatRow, type FlatRowMapping, type FlowTime, type FrequencyMode, type LifecycleValues, type NormalizedCase, type NormalizedEvent, type NormalizedLog, type PerformanceAgg, type PerformanceGraph, type PerformanceLayer, type PerformanceOptions, type ProcessGraph, type ProcessWorkerHandle, type ProcessWorkerLike, type ProcessWorkerRequest, type ProcessWorkerResponse, type ReconciledGraph, type ReworkStats, SYNTHETIC_ACTIVITIES, SYNTHETIC_LOG_EPOCH, type SyntheticActivity, type SyntheticLogOptions, TRIM_FRACTION, type TransitionStats, VARIANT_KEY_SEPARATOR, type Variant, abstractGraph, aggregatePerformance, asNormalizedLog, caseMatchesFilters, clampWidth, createProcessWorker, detectRework, discoverGraph, durationStats, emptyDurationStats, extractVariants, filterLog, filterNormalizedLog, fromCsv, fromFlatRows, generateBpi2012Subset, generateSyntheticLog, handleProcessRequest, isNormalizedLog, minMax, normalizeLifecycle, normalizeLog, parseDelimited, performanceValue, quantile, quantileSorted, reconcileGraph, toEpochMs, variantId, variantKey };
1029
+ /**
1030
+ * Activity colour scale — RM-054.
1031
+ *
1032
+ * One activity, one colour, across every process view. `ProcessMap` paints the colour as
1033
+ * a small accent mark on the activity node; `VariantExplorer` paints it on the sequence
1034
+ * chips. Both read the SAME scale instance built from one graph, so "Create Order" is the
1035
+ * same swatch in the map and in the variant list.
1036
+ *
1037
+ * ## The colour budget
1038
+ *
1039
+ * The chart palette ships twelve series tokens (`--chart-1` … `--chart-12`). The eleven
1040
+ * most frequent activities (ranked by the number of cases they occur in) take
1041
+ * `--chart-1` … `--chart-11`; every remaining activity shares `--chart-12` and is flagged
1042
+ * `pattern: "other"`, which the views render as a hatch so "other" never reads as a
1043
+ * twelfth distinct activity. Colour is never the only channel: every view that paints a
1044
+ * swatch also prints the activity label (or its two-letter {@link ActivityColorScale.codeFor}
1045
+ * code) as text.
1046
+ *
1047
+ * Pure and framework-free: returns token NAMES, never resolved colours, so a theme switch
1048
+ * re-inks every swatch with no recomputation.
1049
+ */
1050
+
1051
+ /** How many activities get a distinct palette slot before the rest share "other". */
1052
+ declare const ACTIVITY_COLOR_SLOTS = 11;
1053
+ /** The token every activity outside the top {@link ACTIVITY_COLOR_SLOTS} shares. */
1054
+ declare const ACTIVITY_OTHER_TOKEN = "--chart-12";
1055
+ /** The colour one activity is painted with. */
1056
+ interface ActivityColor {
1057
+ /** A CSS custom-property NAME, e.g. `"--chart-3"`. Paint with `var(${token})`. */
1058
+ token: string;
1059
+ /** Present for the shared "other" bucket — render the hatch, not a flat swatch. */
1060
+ pattern?: "other";
1061
+ }
1062
+ /** One legend entry, in rank order. */
1063
+ interface ActivityColorLegendEntry extends ActivityColor {
1064
+ activityId: string;
1065
+ label: string;
1066
+ /** A two-character code unique within the scale, for abbreviated ("DNA strip") chips. */
1067
+ code: string;
1068
+ }
1069
+ /** The shared activity→colour mapping. */
1070
+ interface ActivityColorScale {
1071
+ /** The colour for an activity. An id the graph never contained is "other". */
1072
+ colorFor(activityId: string): ActivityColor;
1073
+ /** The two-character code for an activity; derived from the id when it is unknown. */
1074
+ codeFor(activityId: string): string;
1075
+ /** The activity's display label; the id itself when it is unknown. */
1076
+ labelFor(activityId: string): string;
1077
+ /** Every activity in the graph, ranked by case count descending, ties by id. */
1078
+ legend: ActivityColorLegendEntry[];
1079
+ }
1080
+ /**
1081
+ * Build the shared colour scale for a graph.
1082
+ *
1083
+ * Deterministic: the same graph always yields the same assignment, regardless of the
1084
+ * order `graph.activities` arrives in. Build it from the FULL (unfiltered, unabstracted)
1085
+ * graph so colours stay put while a reader filters or abstracts.
1086
+ */
1087
+ declare function activityColorScale(graph: ProcessGraph): ActivityColorScale;
1088
+
1089
+ /**
1090
+ * One case's own summary — its extent, size and path identity, not its trace. The shape
1091
+ * `CaseTable` (case-table/) renders one row per, and `CaseTimeline`'s own model reads a
1092
+ * single case's raw `EventRow[]` separately (a summary row has no per-activity detail to
1093
+ * build a Gantt row from).
1094
+ */
1095
+ interface CaseRow {
1096
+ caseId: string;
1097
+ /** ISO 8601. Empty string when the case has no resolvable extent — see `normalizeLog`. */
1098
+ start: string;
1099
+ end: string;
1100
+ durationMs: number;
1101
+ eventCount: number;
1102
+ /** Identical to the `id` {@link extractVariants} assigns the SAME case over the SAME log. */
1103
+ variantId: string;
1104
+ /**
1105
+ * Never set here: no conformance model lives in `/core` (`ProcessKpiStrip`'s own
1106
+ * `conformance` prop treats it the same way — a fitted value the HOST supplies, never a
1107
+ * number this package invents). A caller with a conformance result attaches it per row.
1108
+ */
1109
+ conformance?: "conforming" | "nonConforming" | "unknown";
1110
+ /** Carried over from `EventLog.caseAttributes`, untouched. */
1111
+ attributes?: Record<string, string | number | boolean | null>;
1112
+ }
1113
+ /**
1114
+ * One summary row per case in `log`, in the same first-appearance order `normalizeLog`
1115
+ * produces. `variantId` is sourced from {@link extractVariants} over the identical log —
1116
+ * see the module docblock for why that, and not a direct `variantId(sequence)` call, is
1117
+ * what keeps a `CaseTable` and a variant explorer over the same log always agreeing on
1118
+ * which cases share a path.
1119
+ *
1120
+ * An empty log answers an empty array.
1121
+ */
1122
+ declare function casesFromLog(log: EventLog): CaseRow[];
1123
+
1124
+ /**
1125
+ * Reference model — RM-061.
1126
+ *
1127
+ * A HAPPY PATH (the prescribed sequence of activities an owner or auditor expects) lifted
1128
+ * to the smallest workflow-net-like structure token replay can run against: one place
1129
+ * between consecutive steps, one visible transition per step, a silent SKIP transition
1130
+ * beside every optional step and a SELF-LOOP transition after every repeatable one.
1131
+ *
1132
+ * Scope boundary (analysis §9 risk 2): this is deliberately NOT a general Petri-net
1133
+ * importer. No BPMN, no parallel gateways, no alignments beyond skip/repeat. A host that
1134
+ * needs full alignment-based conformance brings a backend; this module never grows one.
1135
+ *
1136
+ * Framework-free, deterministic: no React, no `@elabs-ai/components-*` import.
1137
+ */
1138
+ /** One prescribed step of a happy path. */
1139
+ interface HappyPathStep {
1140
+ /** Activity name, matched exactly against `EventRow.activity`. */
1141
+ activity: string;
1142
+ /** The step may be left out without a deviation (a silent skip arc is added). */
1143
+ optional?: boolean;
1144
+ /** The step may run several times in a row without a deviation (a self-loop is added). */
1145
+ repeatable?: boolean;
1146
+ }
1147
+ /** A prescribed process: an ordered list of steps. */
1148
+ interface HappyPath {
1149
+ id: string;
1150
+ label: string;
1151
+ steps: HappyPathStep[];
1152
+ }
1153
+ /**
1154
+ * What a transition stands for. `"step"` fires a step's activity for the first time,
1155
+ * `"repeat"` fires it again from the place after it, `"skip"` is SILENT (no activity is
1156
+ * observed) and moves the token past an optional step.
1157
+ */
1158
+ type ReplayTransitionKind = "step" | "skip" | "repeat";
1159
+ /** One transition of a {@link ReplayModel}. */
1160
+ interface ReplayTransition {
1161
+ id: string;
1162
+ /** The activity this transition fires on. The skipped step's activity for a `"skip"`. */
1163
+ activity: string;
1164
+ kind: ReplayTransitionKind;
1165
+ /** Input places — one token each is consumed when the transition fires. */
1166
+ consumes: string[];
1167
+ /** Output places — one token each is produced when the transition fires. */
1168
+ produces: string[];
1169
+ }
1170
+ /** A workflow-net-like replay structure. Places are listed in path order. */
1171
+ interface ReplayModel {
1172
+ places: string[];
1173
+ initialMarking: string[];
1174
+ finalMarking: string[];
1175
+ transitions: ReplayTransition[];
1176
+ }
1177
+ /**
1178
+ * Lift a happy path to a {@link ReplayModel}.
1179
+ *
1180
+ * For `n` steps the model has places `p0 … pn`, starts with one token in `p0` and ends
1181
+ * with one token in `pn`. Step `i` gets transition `t<i>` (`p<i>` → `p<i+1>`); an optional
1182
+ * step also gets a silent `skip<i>` over the same places; a repeatable step also gets
1183
+ * `repeat<i>` consuming and producing `p<i+1>`. An empty path lifts to a single place
1184
+ * that is both initial and final.
1185
+ */
1186
+ declare function liftHappyPath(path: HappyPath): ReplayModel;
1187
+
1188
+ /**
1189
+ * Conformance model — RM-061.
1190
+ *
1191
+ * The log-level result of replaying an event log against a reference model
1192
+ * ({@link ConformanceResult}, produced by `tokenReplay`) and the fitness-over-time series a
1193
+ * KPI sparkline plots ({@link conformanceRateSeries}).
1194
+ *
1195
+ * Scope boundary (analysis §9 risk 2): token replay against a lifted happy path only — no
1196
+ * alignments, no BPMN import. A host needing either brings a backend.
1197
+ *
1198
+ * Framework-free, deterministic: no React, no `Date.now()`, no `@elabs-ai/components-*`
1199
+ * import.
1200
+ */
1201
+
1202
+ /** Log-level conformance of an event log against a reference model. */
1203
+ interface ConformanceResult {
1204
+ /** Mean trace fitness in `[0, 1]`; `0` for an empty log. */
1205
+ overallFitness: number;
1206
+ /** One result per case, in the log's case order. */
1207
+ traces: TraceReplayResult[];
1208
+ /** Total deviations per type across all cases; every type is present. */
1209
+ deviationCounts: Record<DeviationType, number>;
1210
+ /** Deviations charged to each activity. Sums to the total deviation count. */
1211
+ perActivity: Record<string, {
1212
+ deviations: number;
1213
+ }>;
1214
+ /**
1215
+ * Deviations charged to each OBSERVED directly-follows edge, keyed
1216
+ * `source + EDGE_KEY_SEPARATOR + target` — the same key `discoverGraph` gives the edge.
1217
+ * Sums to at most the total deviation count.
1218
+ */
1219
+ perEdge: Record<string, {
1220
+ deviations: number;
1221
+ }>;
1222
+ }
1223
+ /** Calendar granularity of {@link conformanceRateSeries}. All buckets are UTC. */
1224
+ type ConformanceBucket = "day" | "week" | "month";
1225
+ /** One point of {@link conformanceRateSeries}. */
1226
+ interface ConformanceRatePoint {
1227
+ /** `YYYY-MM-DD` (day; for week, the UTC Monday that starts it) or `YYYY-MM` (month). */
1228
+ bucket: string;
1229
+ /** Mean fitness of the cases that started in this bucket. */
1230
+ fitness: number;
1231
+ caseCount: number;
1232
+ }
1233
+ /**
1234
+ * Mean replay fitness per calendar bucket, keyed by each case's START time, in ascending
1235
+ * bucket order. Empty buckets are not emitted. A case with no resolvable start timestamp
1236
+ * cannot be placed in time and is left out, so `caseCount`s sum to the number of cases
1237
+ * with a valid start.
1238
+ */
1239
+ declare function conformanceRateSeries(log: AnyLog, model: ReplayModel, bucket: ConformanceBucket): ConformanceRatePoint[];
1240
+
1241
+ /**
1242
+ * Token-based replay — RM-061.
1243
+ *
1244
+ * Replays each case of an event log against a {@link ReplayModel} (a lifted happy path)
1245
+ * and scores it with the classic produced / consumed / missing / remaining token counts:
1246
+ *
1247
+ * fitness = ½ · (1 − missing / consumed) + ½ · (1 − remaining / produced)
1248
+ *
1249
+ * The replay procedure — seed the initial marking, fire an enabled transition per event,
1250
+ * enable a disabled one through silent transitions first, force-insert missing tokens
1251
+ * only when that fails, then consume the final marking and count what is left over —
1252
+ * follows the token-based replay algorithm published in pm4js (BSD-3-Clause, credited in
1253
+ * `scripts/attributions.sources.json`). It is re-typed in TypeScript for the restricted
1254
+ * nets `liftHappyPath` builds; no pm4js code is copied, and nothing here derives from the
1255
+ * AGPL Python reference implementation.
1256
+ *
1257
+ * Deviation typing is this module's own layer on top of the counts: every forced token
1258
+ * (and every unreachable final token) is explained by at least one
1259
+ * {@link Deviation}, so a violation list can say WHY a case lost fitness.
1260
+ *
1261
+ * Scope boundary (analysis §9 risk 2): no alignment search. Replay is greedy and
1262
+ * single-pass per trace.
1263
+ *
1264
+ * Framework-free, deterministic: no React, no `@elabs-ai/components-*` import.
1265
+ */
1266
+
1267
+ /**
1268
+ * Why a case lost fitness.
1269
+ *
1270
+ * - `undesired` — the activity is not in the model at all.
1271
+ * - `skipped` — a required modelled activity was jumped over and never observed in the case.
1272
+ * - `wrongOrder` — the activity is modelled but its preceding place was unmarked, and the
1273
+ * gap is not explained by an unobserved step (it ran too early, too late, or again).
1274
+ * - `wrongStart` — the first event of the case is not one the initial marking enables.
1275
+ * - `incomplete` — the case ended before reaching the model's final marking.
1276
+ */
1277
+ type DeviationType = "undesired" | "skipped" | "wrongOrder" | "wrongStart" | "incomplete";
1278
+ /** Every {@link DeviationType}, in a stable display order. */
1279
+ declare const DEVIATION_TYPES: readonly DeviationType[];
1280
+ /** One deviation found while replaying a case. */
1281
+ interface Deviation {
1282
+ type: DeviationType;
1283
+ /**
1284
+ * The activity the deviation is about: the observed event for `undesired`/`wrongOrder`/
1285
+ * `wrongStart`, the missing step for `skipped`, the last observed event for `incomplete`.
1286
+ */
1287
+ activity?: string;
1288
+ /** The activity the model expected next at that point, when one is determinable. */
1289
+ expected?: string;
1290
+ /**
1291
+ * Zero-based index into the case's ordered trace of the event where the deviation was
1292
+ * detected. `incomplete` uses the trace length (the position after the last event).
1293
+ */
1294
+ at: number;
1295
+ }
1296
+ /** Replay result for one case. */
1297
+ interface TraceReplayResult {
1298
+ caseId: string;
1299
+ produced: number;
1300
+ consumed: number;
1301
+ missing: number;
1302
+ remaining: number;
1303
+ /** `½(1 − missing/consumed) + ½(1 − remaining/produced)`, in `[0, 1]`. */
1304
+ fitness: number;
1305
+ deviations: Deviation[];
1306
+ }
1307
+ /**
1308
+ * Replay an already-ordered activity sequence. The shared engine behind
1309
+ * {@link replayTrace}, {@link tokenReplay} and `conformanceRateSeries`.
1310
+ */
1311
+ declare function replayActivities(caseId: string, trace: readonly string[], model: ReplayModel): TraceReplayResult;
1312
+ /**
1313
+ * Replay ONE case's raw rows against `model`.
1314
+ *
1315
+ * Rows are normalized first (ordered in time, lifecycle pairs merged into one instance),
1316
+ * exactly as every other `/core` derivation sees them. The rows are expected to share one
1317
+ * `caseId`; if they do not, every instance is replayed as one trace in start order under
1318
+ * the first row's `caseId`.
1319
+ */
1320
+ declare function replayTrace(events: EventRow[], model: ReplayModel): TraceReplayResult;
1321
+ /** A {@link DeviationType}-keyed tally with every type present at zero. */
1322
+ declare function emptyDeviationCounts(): Record<DeviationType, number>;
1323
+ /**
1324
+ * Replay every case of `log` against `model` and fold the results.
1325
+ *
1326
+ * - `deviationCounts[type]` — total deviations of that type across all cases.
1327
+ * - `perActivity[activity].deviations` — every deviation carries an activity, so these sum
1328
+ * to the total deviation count.
1329
+ * - `perEdge[source + EDGE_KEY_SEPARATOR + target].deviations` — a deviation detected at an
1330
+ * event with a predecessor is charged to the OBSERVED directly-follows edge into that
1331
+ * event (the key `discoverGraph` uses for the same edge). A deviation at the first event
1332
+ * or at the end of the case has no edge, so these sum to at most the total.
1333
+ * - `overallFitness` — mean trace fitness; `0` for an empty log.
1334
+ */
1335
+ declare function tokenReplay(log: AnyLog, model: ReplayModel): ConformanceResult;
1336
+
1337
+ /**
1338
+ * XES adapter — RM-063.
1339
+ *
1340
+ * IEEE 1849-2016's interchange format for event logs: a `<log>` of `<trace>`s of
1341
+ * `<event>`s, each carrying `key`/`value` attributes typed by their own element name
1342
+ * (`string`, `date`, `int`, `float`, `boolean`). It is the format most public benchmark
1343
+ * logs — including the BPI Challenge series RM-049's synthetic fixture is modelled on —
1344
+ * actually ship in.
1345
+ *
1346
+ * Parsing is hand-rolled here on purpose, and NOT behind a `DOMParser` fast path:
1347
+ * `/core` must run identically in Node, in a worker and in a browser main thread, and a
1348
+ * parser this narrow — six element names, attribute-only values, the five standard XML
1349
+ * entities, self-closing tags — does not need a general XML engine. `DOMParser` is
1350
+ * unavailable in Node/workers anyway, and branching on its presence would leave one of
1351
+ * the two code paths permanently untested. Do not reach for an XML dependency or
1352
+ * `DOMParser` here; extend the tokenizer below instead.
1353
+ */
1354
+
1355
+ /** Options for {@link fromXes}. */
1356
+ interface XesParseOptions {
1357
+ /**
1358
+ * Event attribute keys that jointly identify an activity, applied in order and joined
1359
+ * with `"+"` when there is more than one — the same semantics as an XES `<classifier>`
1360
+ * element's `keys` attribute. Defaults to the log's own first `<classifier>`, or
1361
+ * `["concept:name"]` when the log declares none.
1362
+ */
1363
+ classifiers?: string[];
1364
+ /**
1365
+ * How `lifecycle:transition` maps onto {@link EventRow.lifecycle}. `"standard"`
1366
+ * (default) maps the literal value `"start"` to `"start"` and every other XES
1367
+ * lifecycle value (`"complete"`, `"schedule"`, `"suspend"`, …) to `"complete"`;
1368
+ * `"none"` ignores the attribute entirely, so every event stays atomic.
1369
+ */
1370
+ lifecycleModel?: "standard" | "none";
1371
+ }
1372
+ /** One recoverable problem found while reading a XES document. */
1373
+ interface XesParseError {
1374
+ type: "malformed_xml" | "missing_trace" | "missing_concept_name" | "missing_timestamp";
1375
+ message: string;
1376
+ traceIndex?: number;
1377
+ eventIndex?: number;
1378
+ }
1379
+ /**
1380
+ * `fromXes`'s result — mirrors the worker's `{ ok: true, … } | { ok: false, … }` result
1381
+ * convention (`worker/process-worker.ts`), because unlike `fromCsv`/`fromFlatRows` (which
1382
+ * silently skip an incomplete row) a XES document can be genuinely malformed XML, which
1383
+ * has nothing sound left to skip to.
1384
+ */
1385
+ type XesParseResult = {
1386
+ ok: true;
1387
+ log: EventLog;
1388
+ } | {
1389
+ ok: false;
1390
+ errors: XesParseError[];
1391
+ };
1392
+ /**
1393
+ * Read a IEEE 1849 XES document into an {@link EventLog}.
1394
+ *
1395
+ * Collects every recoverable problem (a trace with no events, an event missing its
1396
+ * activity classifier key(s) or `time:timestamp`) rather than stopping at the first one;
1397
+ * only malformed XML itself — which leaves nothing sound to keep reading — short-circuits
1398
+ * with a single `malformed_xml` error. Output feeds directly into `normalizeLog`, exactly
1399
+ * like `fromCsv`/`fromFlatRows` — no XES-specific branching downstream.
1400
+ */
1401
+ declare function fromXes(source: string, options?: XesParseOptions): XesParseResult;
1402
+
1403
+ /**
1404
+ * Segment occurrences — RM-060.
1405
+ *
1406
+ * A performance spectrum (ProM's PSM) draws one line per case through a FIXED, chosen
1407
+ * sequence of segments, where a segment is one directly-follows pair `from → to`. That
1408
+ * needs something `TransitionStats` does not carry: the individual, time-ordered
1409
+ * OCCURRENCES of a pair (which case, when it entered, when it left), not one aggregate
1410
+ * across the whole log. Batching, FIFO violations and queue build-up are visible only
1411
+ * in the occurrences.
1412
+ *
1413
+ * ## What an occurrence measures
1414
+ *
1415
+ * `start` is the moment the `from` event COMPLETES and `end` the moment the `to` event
1416
+ * STARTS — the same idle-time reading `discoverGraph` defaults to, so a spectrum row and
1417
+ * the map's edge median agree about one pair. For atomic events (the common case) start
1418
+ * and completion coincide, so this is simply the two event timestamps. Overlapping
1419
+ * (parallel) events would give `end < start`; `end` is clamped to `start`, so `duration`
1420
+ * is never negative and a line never runs backwards.
1421
+ *
1422
+ * Deterministic and framework-free: no React, no `@elabs-ai/components-*`.
1423
+ */
1424
+
1425
+ /** One row of a spectrum: the directly-follows pair `from → to`. */
1426
+ interface SegmentDefinition {
1427
+ from: string;
1428
+ to: string;
1429
+ /** Display label. Defaults to `"from → to"` in a view. */
1430
+ label?: string;
1431
+ }
1432
+ /** One case passing through one segment. */
1433
+ interface SegmentOccurrence {
1434
+ /** The segment's key — {@link segmentKey}`(from, to)`. */
1435
+ segment: string;
1436
+ caseId: string;
1437
+ /** When the `from` event completed, epoch ms. */
1438
+ start: number;
1439
+ /** When the `to` event started, epoch ms. Never before `start`. */
1440
+ end: number;
1441
+ /** `end - start`, in ms. */
1442
+ duration: number;
1443
+ }
1444
+ /** A duration quartile, `1` = fastest quarter, `4` = slowest. */
1445
+ type DurationQuartile = 1 | 2 | 3 | 4;
1446
+ /** The three cut points (25th, 50th, 75th percentile) a quartile is read against. */
1447
+ type QuartileThresholds = readonly [number, number, number];
1448
+ /**
1449
+ * The key of a segment — the same `source + separator + target` edge key `discoverGraph`
1450
+ * and `ProcessMap` use, so a segment round-trips to a transition selection unchanged.
1451
+ */
1452
+ declare function segmentKey(from: string, to: string): string;
1453
+ /**
1454
+ * Every occurrence of the segments in `order`, walking each case's normalised sequence
1455
+ * once. Pairs not in `order` are skipped — a spectrum shows a chosen sequence, never the
1456
+ * whole graph. Output is in log order: case by case, and within a case in trace order.
1457
+ * Duplicate definitions in `order` are ignored.
1458
+ */
1459
+ declare function segmentsFor(log: AnyLog, order: readonly SegmentDefinition[]): SegmentOccurrence[];
1460
+ /**
1461
+ * The `limit` most frequent directly-follows pairs of `graph`, busiest first. Reads
1462
+ * `graph.transitions` in the order `discoverGraph` already ranks them (count descending,
1463
+ * ties by source then target), so the two can never disagree about "the top N".
1464
+ */
1465
+ declare function segmentOrderByFrequency(graph: ProcessGraph, limit?: number): SegmentDefinition[];
1466
+ /**
1467
+ * The consecutive pairs of `variant.sequence`, in path order. A pair repeated by a loop
1468
+ * (`A, B, A, B`) appears once, at its first position — a spectrum has one row per segment.
1469
+ */
1470
+ declare function segmentOrderForVariant(variant: Variant): SegmentDefinition[];
1471
+ /**
1472
+ * The 25th/50th/75th percentile of `durations` (R-7 interpolation, the same `quantile`
1473
+ * every process view uses). Non-finite samples are dropped; an empty input gives zeros.
1474
+ * Compute this ONCE per segment and read many occurrences against it with
1475
+ * {@link quartileOf} — {@link durationQuartile} sorts on every call.
1476
+ */
1477
+ declare function durationQuartileThresholds(durations: readonly number[]): QuartileThresholds;
1478
+ /** Which quartile `duration` falls in, against precomputed thresholds (upper bounds inclusive). */
1479
+ declare function quartileOf(duration: number, thresholds: QuartileThresholds): DurationQuartile;
1480
+ /**
1481
+ * Buckets `occurrence` against ITS OWN segment's duration distribution — not the whole
1482
+ * log's — which is PSM's colour convention: a slow line is slow for that segment.
1483
+ */
1484
+ declare function durationQuartile(occurrence: SegmentOccurrence, allDurationsForSegment: readonly number[]): DurationQuartile;
1485
+
1486
+ /** Which side(s) of the diff an activity or transition survives in. */
1487
+ type DiffState = "common" | "aOnly" | "bOnly";
1488
+ /** One activity's or transition's diff — its identity, its state, and (when `"common"`) its delta. */
1489
+ interface DiffEntry<Stats> {
1490
+ id: string;
1491
+ state: DiffState;
1492
+ /** Present unless the element is `"bOnly"`. */
1493
+ a?: Stats;
1494
+ /** Present unless the element is `"aOnly"`. */
1495
+ b?: Stats;
1496
+ /** `b`'s reference value minus `a`'s. Only set for a `"common"` entry. */
1497
+ delta?: number;
1498
+ /** `b`'s reference value divided by `a`'s. Only set for a `"common"` entry with `a > 0`. */
1499
+ ratio?: number;
1500
+ }
1501
+ /** The full diff between two graphs. */
1502
+ interface ProcessGraphDiff {
1503
+ activities: DiffEntry<ActivityStats>[];
1504
+ transitions: DiffEntry<TransitionStats>[];
1505
+ totals: {
1506
+ a: ProcessGraph["totals"];
1507
+ b: ProcessGraph["totals"];
1508
+ };
1509
+ }
1510
+ /**
1511
+ * Diff two discovered graphs. Neither input is mutated; the entries reference the original
1512
+ * `ActivityStats`/`TransitionStats` objects, never copies.
1513
+ */
1514
+ declare function diffGraphs(a: ProcessGraph, b: ProcessGraph): ProcessGraphDiff;
1515
+
1516
+ /** One case travelling one edge. Times are relative to {@link ReplayTimeline.origin}. */
1517
+ interface ReplaySegment {
1518
+ caseId: string;
1519
+ /** `source + EDGE_KEY_SEPARATOR + target`. */
1520
+ edgeId: string;
1521
+ source: string;
1522
+ target: string;
1523
+ /** The source activity's completion. */
1524
+ enterAt: number;
1525
+ /** The target activity's start — never before {@link enterAt}. */
1526
+ exitAt: number;
1527
+ }
1528
+ /** One case's token on one edge at a playhead. */
1529
+ interface ReplayFrameToken {
1530
+ caseId: string;
1531
+ edgeId: string;
1532
+ /** 0 at the source end, 1 at the target end. */
1533
+ progress: number;
1534
+ }
1535
+ /** The replay at one playhead. */
1536
+ interface ReplayFrame {
1537
+ /** Playhead, relative to the timeline origin. */
1538
+ t: number;
1539
+ /** One token per in-flight case, in segment order. */
1540
+ tokens: ReplayFrameToken[];
1541
+ /** Edge id → distinct cases on that edge during the bucket starting at `t`. Zero edges are omitted. */
1542
+ congestion: Record<string, number>;
1543
+ }
1544
+ /** Options for {@link replayTimeline}. */
1545
+ interface ReplayTimelineOptions {
1546
+ /** Bucket width in ms. Defaults to {@link defaultReplayBucketMs}. Raised when it would exceed {@link REPLAY_MAX_FRAMES}. */
1547
+ bucketMs?: number;
1548
+ /** Align every case's first activity to `t = 0`. @default false */
1549
+ synchronizedStart?: boolean;
1550
+ }
1551
+ /** A replay-ready timeline. */
1552
+ interface ReplayTimeline {
1553
+ /** Epoch ms of `t = 0` in wall-clock mode; `0` in synchronized-start mode. */
1554
+ origin: number;
1555
+ /** Last relative instant any case reaches. `0` for an empty log. */
1556
+ duration: number;
1557
+ /** The bucket width actually used. */
1558
+ bucketMs: number;
1559
+ synchronizedStart: boolean;
1560
+ /** Every edge move of every case, ordered by `enterAt` then case order. */
1561
+ segments: ReplaySegment[];
1562
+ /** One frame per bucket, `frames[i].t === i * bucketMs`, covering `[0, duration]`. */
1563
+ frames: ReplayFrame[];
1564
+ /** Highest congestion any edge reaches in any bucket. `0` when nothing moves. */
1565
+ peakCongestion: number;
1566
+ }
1567
+ /** How many frames {@link defaultReplayBucketMs} aims for. */
1568
+ declare const REPLAY_TARGET_FRAMES = 300;
1569
+ /** Upper bound on frames; a finer `bucketMs` is widened to respect it. */
1570
+ declare const REPLAY_MAX_FRAMES = 5000;
1571
+ /** The default bucket: the log span over {@link REPLAY_TARGET_FRAMES}, at least 1 ms. */
1572
+ declare function defaultReplayBucketMs(duration: number): number;
1573
+ /**
1574
+ * Bucket a log into replay frames.
1575
+ *
1576
+ * @example
1577
+ * ```ts
1578
+ * const timeline = replayTimeline(log, { synchronizedStart: true });
1579
+ * const frame = replayFrameAt(timeline, timeline.duration / 2);
1580
+ * ```
1581
+ */
1582
+ declare function replayTimeline(log: AnyLog, options?: ReplayTimelineOptions): ReplayTimeline;
1583
+ /**
1584
+ * The replay at any playhead — tokens at exactly `t` (not snapped), congestion from the
1585
+ * bucket containing `t`. `t` is clamped to `[0, duration]`.
1586
+ */
1587
+ declare function replayFrameAt(timeline: ReplayTimeline, t: number): ReplayFrame;
1588
+ /** One transition in {@link rankReplayCongestion}'s list. */
1589
+ interface ReplayCongestionEntry {
1590
+ edgeId: string;
1591
+ source: string;
1592
+ target: string;
1593
+ /** Most distinct cases on the edge in one bucket. */
1594
+ peak: number;
1595
+ /** Relative start of the first bucket reaching {@link peak}. */
1596
+ peakAt: number;
1597
+ /** Mean congestion over every bucket of the timeline. */
1598
+ mean: number;
1599
+ }
1600
+ /** Transitions ranked by peak congestion, then mean, then edge id. Edges that never carry a case are omitted. */
1601
+ declare function rankReplayCongestion(timeline: ReplayTimeline, limit?: number): ReplayCongestionEntry[];
1602
+ /** Smallest and largest token radius, in px. */
1603
+ declare const REPLAY_TOKEN_RADIUS_RANGE: readonly [3, 8];
1604
+ /**
1605
+ * Token radius for an edge's congestion: the radius above the minimum grows with
1606
+ * √(count / peak), so a busier edge's blob grows by area rather than by diameter.
1607
+ */
1608
+ declare function replayTokenRadius(congestion: number, peak: number): number;
1609
+
1610
+ /**
1611
+ * OCEL 2.0 adapter — RM-066.
1612
+ *
1613
+ * The Object-Centric Event Log standard (OCEL 2.0, ocel-standard.org) drops the single
1614
+ * case notion: an event references any number of OBJECTS of any number of object TYPES
1615
+ * (`order`, `item`, `package`, …). This adapter reads the standard's JSON serialization
1616
+ * and FLATTENS it once per object type — the projection every object-centric
1617
+ * directly-follows graph starts from: for object type `T`, every object of type `T` is a
1618
+ * case, and every event that references that object is one row of that case.
1619
+ *
1620
+ * ## JSON only, on purpose
1621
+ *
1622
+ * OCEL 2.0 also ships as XML and SQLite. Only JSON is read here: it needs nothing beyond
1623
+ * `JSON.parse`, which runs identically in Node, a worker and a browser main thread, and
1624
+ * `/core` takes no parser dependency (the same format-choice precedent `fromXes` set in
1625
+ * RM-063). SQLite would need a WASM engine; XML would need a second hand-rolled tokenizer
1626
+ * for a format whose producers all also emit JSON. Convert an XML/SQLite log to JSON with
1627
+ * the producing tool first.
1628
+ *
1629
+ * ## What survives the flattening
1630
+ *
1631
+ * Each emitted {@link EventRow} carries two reserved attributes so the object-centric
1632
+ * discovery step can re-merge the projections:
1633
+ *
1634
+ * - `__ocelEventId` — the OCEL event's own id, so a shared activity is counted once per
1635
+ * EVENT rather than once per referenced object;
1636
+ * - `__objectRefs` — the event's full object map (`{ order: ["o1"], item: ["i1", "i2"] }`),
1637
+ * JSON-encoded because `EventRow.attributes` values are scalars by contract (RM-049's
1638
+ * frozen types). Read it back with {@link readObjectRefs}.
1639
+ */
1640
+
1641
+ /** One `{ name, … }` type declaration (`objectTypes` / `eventTypes`). */
1642
+ interface OcelTypeDeclaration {
1643
+ name: string;
1644
+ attributes?: {
1645
+ name: string;
1646
+ type?: string;
1647
+ }[];
1648
+ }
1649
+ /** An event or object attribute value. Object attributes carry a `time` (they can change). */
1650
+ interface OcelAttribute {
1651
+ name: string;
1652
+ value: string | number | boolean | null;
1653
+ time?: string;
1654
+ }
1655
+ /** A qualified reference from an event (or object) to an object. */
1656
+ interface OcelRelationship {
1657
+ objectId: string;
1658
+ qualifier?: string;
1659
+ }
1660
+ /** One OCEL 2.0 event. `type` is the activity; `time` is an ISO-8601 timestamp. */
1661
+ interface OcelEvent {
1662
+ id: string;
1663
+ type: string;
1664
+ time: string;
1665
+ attributes?: OcelAttribute[];
1666
+ relationships?: OcelRelationship[];
1667
+ }
1668
+ /** One OCEL 2.0 object. */
1669
+ interface OcelObject {
1670
+ id: string;
1671
+ type: string;
1672
+ attributes?: OcelAttribute[];
1673
+ relationships?: OcelRelationship[];
1674
+ }
1675
+ /** An OCEL 2.0 JSON document (OCEL 2.0 specification, JSON serialization). */
1676
+ interface OcelJson {
1677
+ objectTypes: OcelTypeDeclaration[];
1678
+ eventTypes?: OcelTypeDeclaration[];
1679
+ objects: OcelObject[];
1680
+ events: OcelEvent[];
1681
+ }
1682
+ /** Options for {@link fromOcel}. */
1683
+ interface OcelParseOptions {
1684
+ /**
1685
+ * Which object types to project a log for, in output order. Defaults to every declared
1686
+ * object type, in declaration order. Naming an undeclared type is an error.
1687
+ */
1688
+ objectTypes?: string[];
1689
+ }
1690
+ /** One problem found while reading an OCEL 2.0 document. */
1691
+ interface OcelParseError {
1692
+ type: "malformed_json" | "invalid_document" | "missing_event_id" | "missing_event_type" | "missing_timestamp" | "unknown_object" | "unknown_object_type";
1693
+ message: string;
1694
+ eventIndex?: number;
1695
+ objectIndex?: number;
1696
+ }
1697
+ /**
1698
+ * `fromOcel`'s result — the same `{ ok: true, … } | { ok: false, errors }` shape as
1699
+ * `fromXes`. `logs` is keyed by object type; `activities` lists every event type that
1700
+ * occurs, in first-appearance order; `objectTypes` is the projected types, in order.
1701
+ */
1702
+ type OcelParseResult = {
1703
+ ok: true;
1704
+ logs: Record<string, EventLog>;
1705
+ activities: string[];
1706
+ objectTypes: string[];
1707
+ } | {
1708
+ ok: false;
1709
+ errors: OcelParseError[];
1710
+ };
1711
+ /** The reserved attribute holding an emitted row's OCEL event id. */
1712
+ declare const OCEL_EVENT_ID_ATTRIBUTE = "__ocelEventId";
1713
+ /** The reserved attribute holding an emitted row's JSON-encoded object map. */
1714
+ declare const OCEL_OBJECT_REFS_ATTRIBUTE = "__objectRefs";
1715
+ /**
1716
+ * Read an OCEL 2.0 JSON document (a string, or an already-parsed object) into one
1717
+ * {@link EventLog} per object type.
1718
+ *
1719
+ * Collects every per-event problem (a missing id, type or parsable `time`; a relationship
1720
+ * to an object the document never declares) rather than stopping at the first; only a
1721
+ * document that is not JSON, or not shaped like OCEL at all, short-circuits. Any error
1722
+ * fails the whole read — a silently-dropped event would change every count downstream.
1723
+ */
1724
+ declare function fromOcel(input: string | OcelJson, options?: OcelParseOptions): OcelParseResult;
1725
+ /**
1726
+ * The object map an emitted row was flattened from (`{ order: ["o1"], item: ["i1"] }`), or
1727
+ * `undefined` for a row that did not come from {@link fromOcel}.
1728
+ */
1729
+ declare function readObjectRefs(row: EventRow): Record<string, string[]> | undefined;
1730
+
1731
+ /**
1732
+ * Object-centric directly-follows graph (OC-DFG) discovery — RM-066.
1733
+ *
1734
+ * An OC-DFG is one directly-follows graph PER OBJECT TYPE, drawn on one canvas: activities
1735
+ * shared between types merge into a single node that keeps a per-type breakdown, while
1736
+ * edges stay per type — an `order` following and an `item` following are different
1737
+ * relations, so they are never summed into one arrow. The merge semantics (per-type
1738
+ * projection, shared-activity join, per-type edges) follow pm4js's documented OC-DFG
1739
+ * construction; see `ATTRIBUTION.md`. No pm4js code is copied, and nothing here derives
1740
+ * from the AGPL Python reference implementation.
1741
+ *
1742
+ * Built entirely on `discoverGraph` and `abstractGraph`: every per-type number is exactly
1743
+ * what the single-case pipeline would print for that type's projection.
1744
+ */
1745
+
1746
+ /** One object type's share of a merged activity. */
1747
+ interface ObjectTypeActivityCounts {
1748
+ /** Occurrences in that type's projection (one per event × referenced object). */
1749
+ instances: number;
1750
+ /** Distinct objects of that type the activity touches. */
1751
+ cases: number;
1752
+ }
1753
+ /** A merged activity: shared across object types, with a per-type breakdown. */
1754
+ interface ObjectCentricActivityStats extends Omit<ActivityStats, "instances" | "cases"> {
1755
+ /**
1756
+ * Distinct OCEL events of this activity across every type — an event that references an
1757
+ * order and two items counts once. Falls back to the largest per-type `instances` when
1758
+ * the logs carry no `__ocelEventId` (hand-built logs).
1759
+ */
1760
+ events: number;
1761
+ /** Object type → counts, only for the types this activity occurs in. */
1762
+ perType: Record<string, ObjectTypeActivityCounts>;
1763
+ }
1764
+ /** An object-centric directly-follows graph. */
1765
+ interface ObjectCentricGraph {
1766
+ /** Merged activities, busiest (by `events`) first, ties by id. */
1767
+ activities: ObjectCentricActivityStats[];
1768
+ /** Object type → that type's own directly-follows edges. Never merged across types. */
1769
+ transitionsByType: Record<string, TransitionStats[]>;
1770
+ /** The object types, in the caller's order. */
1771
+ objectTypes: string[];
1772
+ /** Object type → the full per-type graph the merge was built from. */
1773
+ graphsByType: Record<string, ProcessGraph>;
1774
+ }
1775
+ /** {@link abstractObjectCentricGraph}'s result — what each type's abstraction hid. */
1776
+ interface AbstractedObjectCentricGraph extends ObjectCentricGraph {
1777
+ hiddenByType: Record<string, {
1778
+ activities: number;
1779
+ paths: number;
1780
+ }>;
1781
+ }
1782
+ /**
1783
+ * Merge per-type graphs into one {@link ObjectCentricGraph}.
1784
+ *
1785
+ * `eventCounts` supplies each activity's distinct-event count; an activity it does not
1786
+ * name falls back to its largest per-type `instances`. A merged activity's `duration` is
1787
+ * the duration of the type with the most instances of it (ties: earlier type), because
1788
+ * durations of the same events seen through two projections are not additive.
1789
+ */
1790
+ declare function mergeObjectCentricGraphs(graphsByType: Record<string, ProcessGraph>, objectTypes?: readonly string[], eventCounts?: ReadonlyMap<string, number>): ObjectCentricGraph;
1791
+ /**
1792
+ * Discover an {@link ObjectCentricGraph} from per-object-type logs (typically
1793
+ * `fromOcel(…).logs`). Runs `discoverGraph` once per type, then merges.
1794
+ */
1795
+ declare function discoverObjectCentricGraph(logsByType: Record<string, EventLog>, options?: DiscoverGraphOptions & {
1796
+ objectTypes?: string[];
1797
+ }): ObjectCentricGraph;
1798
+ /**
1799
+ * Abstract each object type independently, then re-merge.
1800
+ *
1801
+ * `perType[type]` wins for a type it names; `fallback` applies to every other type; a type
1802
+ * with neither is kept whole. Activity `events` counts are carried over from the input,
1803
+ * so abstraction hides nodes and edges without restating a statistic.
1804
+ */
1805
+ declare function abstractObjectCentricGraph(graph: ObjectCentricGraph, perType?: Readonly<Record<string, AbstractionOptions>>, fallback?: AbstractionOptions): AbstractedObjectCentricGraph;
1806
+ /**
1807
+ * Flatten an {@link ObjectCentricGraph} into one {@link ProcessGraph} — the shape the
1808
+ * process map lays out and reads its node metrics from.
1809
+ *
1810
+ * - an activity's `instances` is its distinct-event count and its `cases` is the number of
1811
+ * objects (of any type) it touches;
1812
+ * - a pair of activities joined in several types becomes ONE transition whose `count` and
1813
+ * `caseCount` sum the types' — used for layout and selection only; the map still draws
1814
+ * the per-type edges from `transitionsByType`;
1815
+ * - `totals.cases` counts objects, `totals.events` distinct events.
1816
+ */
1817
+ declare function objectCentricProcessGraph(graph: ObjectCentricGraph): ProcessGraph;
1818
+ /**
1819
+ * The object-type colour scale — RM-054's `activityColorScale`, reused rather than
1820
+ * reimplemented: object types are ranked by how many objects each has (ties by name) and
1821
+ * take `--chart-1` … `--chart-11` in that order, beyond which they share the hatched
1822
+ * "other" slot. `codeFor(type)` is the two-character text code painted beside every
1823
+ * swatch, so a type is never identified by colour alone.
1824
+ */
1825
+ declare function objectTypeColorScale(graph: ObjectCentricGraph): ActivityColorScale;
1826
+
1827
+ export { ACTIVITY_COLOR_SLOTS, ACTIVITY_OTHER_TOKEN, type AbstractedGraph, type AbstractedObjectCentricGraph, type AbstractionOptions, type ActivityColor, type ActivityColorLegendEntry, type ActivityColorScale, type ActivityRework, type ActivityStats, type AnyLog, BPI_2012_ACTIVITIES, BPI_2012_SUBSET_EPOCH, type Bpi2012Activity, type Bpi2012SubsetOptions, type CaseRow, type ConformanceBucket, type ConformanceRatePoint, type ConformanceResult, type CreateProcessWorkerOptions, type CsvMapping, type CsvOptions, DEFAULT_LIFECYCLE_VALUES, DEVIATION_TYPES, DURATION_SAMPLE_CAP, DURATION_UNIT_MS, type Deviation, type DeviationType, type DiffEntry, type DiffState, type DiscoverGraphOptions, type DurationQuartile, DurationSampler, type DurationStats, type DurationUnit, EDGE_KEY_SEPARATOR, EMPTY_DURATION_STATS, type EventLog, type EventRow, type FilterSpec, type FlatRow, type FlatRowMapping, type FlowTime, type FrequencyMode, type HappyPath, type HappyPathStep, type LifecycleValues, type NormalizedCase, type NormalizedEvent, type NormalizedLog, OCEL_EVENT_ID_ATTRIBUTE, OCEL_OBJECT_REFS_ATTRIBUTE, type ObjectCentricActivityStats, type ObjectCentricGraph, type ObjectTypeActivityCounts, type OcelAttribute, type OcelEvent, type OcelJson, type OcelObject, type OcelParseError, type OcelParseOptions, type OcelParseResult, type OcelRelationship, type OcelTypeDeclaration, type PerformanceAgg, type PerformanceGraph, type PerformanceLayer, type PerformanceOptions, type ProcessGraph, type ProcessGraphDiff, type ProcessWorkerHandle, type ProcessWorkerLike, type ProcessWorkerRequest, type ProcessWorkerResponse, type QuartileThresholds, REPLAY_MAX_FRAMES, REPLAY_TARGET_FRAMES, REPLAY_TOKEN_RADIUS_RANGE, type ReconciledGraph, type ReplayCongestionEntry, type ReplayFrame, type ReplayFrameToken, type ReplayModel, type ReplaySegment, type ReplayTimeline, type ReplayTimelineOptions, type ReplayTransition, type ReplayTransitionKind, type ReworkStats, SYNTHETIC_ACTIVITIES, SYNTHETIC_LOG_EPOCH, type SegmentDefinition, type SegmentOccurrence, type SyntheticActivity, type SyntheticLogOptions, TRIM_FRACTION, type TraceReplayResult, type TransitionStats, VARIANT_KEY_SEPARATOR, type Variant, type XesParseError, type XesParseOptions, type XesParseResult, abstractGraph, abstractObjectCentricGraph, activityColorScale, aggregatePerformance, asNormalizedLog, caseMatchesFilters, casesFromLog, clampWidth, conformanceRateSeries, createProcessWorker, defaultReplayBucketMs, detectRework, diffGraphs, discoverGraph, discoverObjectCentricGraph, durationQuartile, durationQuartileThresholds, durationStats, emptyDeviationCounts, emptyDurationStats, extractVariants, filterLog, filterNormalizedLog, fromCsv, fromFlatRows, fromOcel, fromXes, generateBpi2012Subset, generateSyntheticLog, handleProcessRequest, isNormalizedLog, liftHappyPath, mergeObjectCentricGraphs, minMax, normalizeLifecycle, normalizeLog, objectCentricProcessGraph, objectTypeColorScale, parseDelimited, performanceValue, quantile, quantileSorted, quartileOf, rankReplayCongestion, readObjectRefs, reconcileGraph, replayActivities, replayFrameAt, replayTimeline, replayTokenRadius, replayTrace, segmentKey, segmentOrderByFrequency, segmentOrderForVariant, segmentsFor, toEpochMs, tokenReplay, variantId, variantKey };