@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.
- package/README.md +8 -1
- package/dist/core/index.d.ts +801 -3
- package/dist/core/index.js +1334 -0
- package/dist/core/index.js.map +1 -1
- package/dist/index.d.ts +1889 -34
- package/dist/index.js +5512 -196
- package/dist/index.js.map +1 -1
- package/dist/test/index.d.ts +223 -5
- package/dist/test/index.js +346 -191
- package/dist/test/index.js.map +1 -1
- package/package.json +14 -13
- package/src/__contract__/case-table.contract.test.tsx +49 -0
- package/src/__contract__/compare-kpi-strip.contract.test.tsx +49 -0
- package/src/__contract__/conformance-overlay.contract.test.tsx +49 -0
- package/src/__contract__/happy-path-editor.contract.test.tsx +49 -0
- package/src/__contract__/violation-list.contract.test.tsx +49 -0
- package/src/abstraction-controls/abstraction-controls-per-type.test.tsx +80 -0
- package/src/abstraction-controls/abstraction-controls.stories.tsx +43 -1
- package/src/abstraction-controls/abstraction-controls.tsx +198 -5
- package/src/case-table/case-table.stories.tsx +89 -0
- package/src/case-table/case-table.test.tsx +148 -0
- package/src/case-table/case-table.tsx +144 -0
- package/src/case-table/columns.ts +116 -0
- package/src/case-table/index.ts +11 -0
- package/src/case-timeline/case-timeline-model.test.ts +72 -0
- package/src/case-timeline/case-timeline-model.ts +112 -0
- package/src/case-timeline/case-timeline.stories.tsx +94 -0
- package/src/case-timeline/case-timeline.test.tsx +51 -0
- package/src/case-timeline/case-timeline.tsx +109 -0
- package/src/case-timeline/index.ts +9 -0
- package/src/conformance-overlay/conformance-fixture.ts +59 -0
- package/src/conformance-overlay/conformance-legend.tsx +109 -0
- package/src/conformance-overlay/conformance-overlay.stories.tsx +116 -0
- package/src/conformance-overlay/conformance-overlay.test.tsx +88 -0
- package/src/conformance-overlay/conformance-overlay.tsx +107 -0
- package/src/conformance-overlay/conformance-state.test.ts +79 -0
- package/src/conformance-overlay/conformance-state.ts +220 -0
- package/src/conformance-overlay/index.ts +4 -0
- package/src/core/activity-color-scale.test.ts +107 -0
- package/src/core/activity-color-scale.ts +133 -0
- package/src/core/adapters/ocel.test.ts +112 -0
- package/src/core/adapters/ocel.ts +359 -0
- package/src/core/adapters/xes.test.ts +293 -0
- package/src/core/adapters/xes.ts +384 -0
- package/src/core/cases-from-log.test.ts +72 -0
- package/src/core/cases-from-log.ts +85 -0
- package/src/core/conformance.test.ts +80 -0
- package/src/core/conformance.ts +91 -0
- package/src/core/diff-graphs.test.ts +151 -0
- package/src/core/diff-graphs.ts +118 -0
- package/src/core/discover-object-centric-graph.test.ts +94 -0
- package/src/core/discover-object-centric-graph.ts +296 -0
- package/src/core/fixtures/ocel-sample.ts +82 -0
- package/src/core/fixtures/sample.xes +68 -0
- package/src/core/index.ts +113 -0
- package/src/core/reference-model.test.ts +43 -0
- package/src/core/reference-model.ts +116 -0
- package/src/core/replay-timeline.test.ts +161 -0
- package/src/core/replay-timeline.ts +260 -0
- package/src/core/segments.test.ts +185 -0
- package/src/core/segments.ts +153 -0
- package/src/core/token-replay.test.ts +218 -0
- package/src/core/token-replay.ts +456 -0
- package/src/core/types.ts +2 -2
- package/src/dotted-chart/compute-dots.test.ts +176 -0
- package/src/dotted-chart/compute-dots.ts +241 -0
- package/src/dotted-chart/dotted-chart-labels.ts +93 -0
- package/src/dotted-chart/dotted-chart.stories.tsx +182 -0
- package/src/dotted-chart/dotted-chart.test.tsx +135 -0
- package/src/dotted-chart/dotted-chart.tsx +841 -0
- package/src/dotted-chart/index.ts +23 -0
- package/src/dotted-chart/use-element-size.ts +33 -0
- package/src/happy-path-editor/happy-path-editor-context.ts +81 -0
- package/src/happy-path-editor/happy-path-editor.stories.tsx +116 -0
- package/src/happy-path-editor/happy-path-editor.test.tsx +142 -0
- package/src/happy-path-editor/happy-path-editor.tsx +239 -0
- package/src/happy-path-editor/happy-path-step-node.tsx +175 -0
- package/src/happy-path-editor/index.ts +4 -0
- package/src/index.ts +51 -1
- package/src/performance-spectrum/aggregate-segments.test.ts +107 -0
- package/src/performance-spectrum/aggregate-segments.ts +174 -0
- package/src/performance-spectrum/index.ts +25 -0
- package/src/performance-spectrum/performance-spectrum-context.tsx +116 -0
- package/src/performance-spectrum/performance-spectrum.stories.tsx +128 -0
- package/src/performance-spectrum/performance-spectrum.test.tsx +190 -0
- package/src/performance-spectrum/performance-spectrum.tsx +870 -0
- package/src/process-compare/compare-kpi-strip.stories.tsx +48 -0
- package/src/process-compare/compare-kpi-strip.tsx +94 -0
- package/src/process-compare/compare-model.ts +83 -0
- package/src/process-compare/compare-side.tsx +42 -0
- package/src/process-compare/diff-to-graph.ts +104 -0
- package/src/process-compare/index.ts +23 -0
- package/src/process-compare/process-compare.stories.tsx +184 -0
- package/src/process-compare/process-compare.test.tsx +224 -0
- package/src/process-compare/process-compare.tsx +251 -0
- package/src/process-explorer.stories.tsx +1 -1
- package/src/process-filter-bar/index.ts +2 -0
- package/src/process-filter-bar/process-filter-bar.stories.tsx +156 -0
- package/src/process-filter-bar/process-filter-bar.test.tsx +201 -0
- package/src/process-filter-bar/process-filter-bar.tsx +167 -0
- package/src/process-kpi-strip/process-kpi-strip.stories.tsx +47 -0
- package/src/process-kpi-strip/process-kpi-strip.test.tsx +67 -0
- package/src/process-kpi-strip/process-kpi-strip.tsx +148 -8
- package/src/process-map/activity-accent.ts +25 -0
- package/src/process-map/index.ts +1 -0
- package/src/process-map/map-model.test.ts +16 -0
- package/src/process-map/map-model.ts +323 -1
- package/src/process-map/object-centric-map.test.tsx +132 -0
- package/src/process-map/process-activity-node.tsx +152 -14
- package/src/process-map/process-map-object-centric.stories.tsx +219 -0
- package/src/process-map/process-map.stories.tsx +64 -0
- package/src/process-map/process-map.tsx +240 -16
- package/src/process-map/process-transition-edge.test.tsx +47 -0
- package/src/process-map/process-transition-edge.tsx +134 -7
- package/src/process-map/use-process-layout.ts +30 -9
- package/src/process-replay/congestion-heat.tsx +107 -0
- package/src/process-replay/index.ts +14 -0
- package/src/process-replay/process-replay.stories.tsx +168 -0
- package/src/process-replay/process-replay.test.tsx +170 -0
- package/src/process-replay/process-replay.tsx +285 -0
- package/src/process-replay/replay-controls.tsx +147 -0
- package/src/process-replay/replay-format.ts +83 -0
- package/src/process-replay/replay-tokens-context.ts +30 -0
- package/src/process-replay/use-controllable-value.ts +30 -0
- package/src/templates-process-explorer.stories.tsx +1304 -0
- package/src/test/contract.test.ts +66 -0
- package/src/test/contract.ts +107 -6
- package/src/test/doubles.test.tsx +87 -1
- package/src/test/doubles.tsx +174 -3
- package/src/test/index.ts +25 -1
- package/src/use-process-explorer/use-process-explorer.test.ts +44 -0
- package/src/use-process-explorer/use-process-explorer.ts +34 -2
- package/src/variant-explorer/coverage-bar.tsx +36 -0
- package/src/variant-explorer/index.ts +16 -0
- package/src/variant-explorer/sequence-chips.tsx +103 -0
- package/src/variant-explorer/variant-explorer-model.ts +42 -0
- package/src/variant-explorer/variant-explorer.stories.tsx +226 -0
- package/src/variant-explorer/variant-explorer.test.tsx +302 -0
- package/src/variant-explorer/variant-explorer.tsx +567 -0
- package/src/variant-explorer/variant-row.tsx +137 -0
- package/src/violation-list/index.ts +2 -0
- package/src/violation-list/violation-list.stories.tsx +73 -0
- package/src/violation-list/violation-list.test.tsx +84 -0
- package/src/violation-list/violation-list.tsx +259 -0
package/dist/core/index.d.ts
CHANGED
|
@@ -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/
|
|
11
|
-
* and `pnpm process
|
|
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
|
-
|
|
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 };
|