@elabs-ai/components-process 4.1.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) 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__/process-kpi-strip.contract.test.tsx +49 -0
  17. package/src/__contract__/violation-list.contract.test.tsx +49 -0
  18. package/src/abstraction-controls/abstraction-controls-per-type.test.tsx +80 -0
  19. package/src/abstraction-controls/abstraction-controls.stories.tsx +43 -1
  20. package/src/abstraction-controls/abstraction-controls.tsx +198 -5
  21. package/src/case-table/case-table.stories.tsx +89 -0
  22. package/src/case-table/case-table.test.tsx +148 -0
  23. package/src/case-table/case-table.tsx +144 -0
  24. package/src/case-table/columns.ts +116 -0
  25. package/src/case-table/index.ts +11 -0
  26. package/src/case-timeline/case-timeline-model.test.ts +72 -0
  27. package/src/case-timeline/case-timeline-model.ts +112 -0
  28. package/src/case-timeline/case-timeline.stories.tsx +94 -0
  29. package/src/case-timeline/case-timeline.test.tsx +51 -0
  30. package/src/case-timeline/case-timeline.tsx +109 -0
  31. package/src/case-timeline/index.ts +9 -0
  32. package/src/conformance-overlay/conformance-fixture.ts +59 -0
  33. package/src/conformance-overlay/conformance-legend.tsx +109 -0
  34. package/src/conformance-overlay/conformance-overlay.stories.tsx +116 -0
  35. package/src/conformance-overlay/conformance-overlay.test.tsx +88 -0
  36. package/src/conformance-overlay/conformance-overlay.tsx +107 -0
  37. package/src/conformance-overlay/conformance-state.test.ts +79 -0
  38. package/src/conformance-overlay/conformance-state.ts +220 -0
  39. package/src/conformance-overlay/index.ts +4 -0
  40. package/src/core/activity-color-scale.test.ts +107 -0
  41. package/src/core/activity-color-scale.ts +133 -0
  42. package/src/core/adapters/ocel.test.ts +112 -0
  43. package/src/core/adapters/ocel.ts +359 -0
  44. package/src/core/adapters/xes.test.ts +293 -0
  45. package/src/core/adapters/xes.ts +384 -0
  46. package/src/core/cases-from-log.test.ts +72 -0
  47. package/src/core/cases-from-log.ts +85 -0
  48. package/src/core/conformance.test.ts +80 -0
  49. package/src/core/conformance.ts +91 -0
  50. package/src/core/diff-graphs.test.ts +151 -0
  51. package/src/core/diff-graphs.ts +118 -0
  52. package/src/core/discover-object-centric-graph.test.ts +94 -0
  53. package/src/core/discover-object-centric-graph.ts +296 -0
  54. package/src/core/fixtures/ocel-sample.ts +82 -0
  55. package/src/core/fixtures/sample.xes +68 -0
  56. package/src/core/index.ts +113 -0
  57. package/src/core/reference-model.test.ts +43 -0
  58. package/src/core/reference-model.ts +116 -0
  59. package/src/core/replay-timeline.test.ts +161 -0
  60. package/src/core/replay-timeline.ts +260 -0
  61. package/src/core/segments.test.ts +185 -0
  62. package/src/core/segments.ts +153 -0
  63. package/src/core/token-replay.test.ts +218 -0
  64. package/src/core/token-replay.ts +456 -0
  65. package/src/core/types.ts +2 -2
  66. package/src/dotted-chart/compute-dots.test.ts +176 -0
  67. package/src/dotted-chart/compute-dots.ts +241 -0
  68. package/src/dotted-chart/dotted-chart-labels.ts +93 -0
  69. package/src/dotted-chart/dotted-chart.stories.tsx +182 -0
  70. package/src/dotted-chart/dotted-chart.test.tsx +135 -0
  71. package/src/dotted-chart/dotted-chart.tsx +841 -0
  72. package/src/dotted-chart/index.ts +23 -0
  73. package/src/dotted-chart/use-element-size.ts +33 -0
  74. package/src/happy-path-editor/happy-path-editor-context.ts +81 -0
  75. package/src/happy-path-editor/happy-path-editor.stories.tsx +116 -0
  76. package/src/happy-path-editor/happy-path-editor.test.tsx +142 -0
  77. package/src/happy-path-editor/happy-path-editor.tsx +239 -0
  78. package/src/happy-path-editor/happy-path-step-node.tsx +175 -0
  79. package/src/happy-path-editor/index.ts +4 -0
  80. package/src/index.ts +51 -1
  81. package/src/performance-spectrum/aggregate-segments.test.ts +107 -0
  82. package/src/performance-spectrum/aggregate-segments.ts +174 -0
  83. package/src/performance-spectrum/index.ts +25 -0
  84. package/src/performance-spectrum/performance-spectrum-context.tsx +116 -0
  85. package/src/performance-spectrum/performance-spectrum.stories.tsx +128 -0
  86. package/src/performance-spectrum/performance-spectrum.test.tsx +190 -0
  87. package/src/performance-spectrum/performance-spectrum.tsx +870 -0
  88. package/src/process-compare/compare-kpi-strip.stories.tsx +48 -0
  89. package/src/process-compare/compare-kpi-strip.tsx +94 -0
  90. package/src/process-compare/compare-model.ts +83 -0
  91. package/src/process-compare/compare-side.tsx +42 -0
  92. package/src/process-compare/diff-to-graph.ts +104 -0
  93. package/src/process-compare/index.ts +23 -0
  94. package/src/process-compare/process-compare.stories.tsx +184 -0
  95. package/src/process-compare/process-compare.test.tsx +224 -0
  96. package/src/process-compare/process-compare.tsx +251 -0
  97. package/src/process-explorer.stories.tsx +1 -1
  98. package/src/process-filter-bar/index.ts +2 -0
  99. package/src/process-filter-bar/process-filter-bar.stories.tsx +156 -0
  100. package/src/process-filter-bar/process-filter-bar.test.tsx +201 -0
  101. package/src/process-filter-bar/process-filter-bar.tsx +167 -0
  102. package/src/process-kpi-strip/process-kpi-strip.stories.tsx +47 -0
  103. package/src/process-kpi-strip/process-kpi-strip.test.tsx +67 -0
  104. package/src/process-kpi-strip/process-kpi-strip.tsx +148 -8
  105. package/src/process-map/activity-accent.ts +25 -0
  106. package/src/process-map/index.ts +1 -0
  107. package/src/process-map/map-model.test.ts +16 -0
  108. package/src/process-map/map-model.ts +323 -1
  109. package/src/process-map/object-centric-map.test.tsx +132 -0
  110. package/src/process-map/process-activity-node.tsx +152 -14
  111. package/src/process-map/process-map-object-centric.stories.tsx +219 -0
  112. package/src/process-map/process-map.stories.tsx +64 -0
  113. package/src/process-map/process-map.tsx +240 -16
  114. package/src/process-map/process-transition-edge.test.tsx +47 -0
  115. package/src/process-map/process-transition-edge.tsx +134 -7
  116. package/src/process-map/use-process-layout.ts +30 -9
  117. package/src/process-replay/congestion-heat.tsx +107 -0
  118. package/src/process-replay/index.ts +14 -0
  119. package/src/process-replay/process-replay.stories.tsx +168 -0
  120. package/src/process-replay/process-replay.test.tsx +170 -0
  121. package/src/process-replay/process-replay.tsx +285 -0
  122. package/src/process-replay/replay-controls.tsx +147 -0
  123. package/src/process-replay/replay-format.ts +83 -0
  124. package/src/process-replay/replay-tokens-context.ts +30 -0
  125. package/src/process-replay/use-controllable-value.ts +30 -0
  126. package/src/templates-process-explorer.stories.tsx +1304 -0
  127. package/src/test/contract.test.ts +66 -0
  128. package/src/test/contract.ts +107 -6
  129. package/src/test/doubles.test.tsx +87 -1
  130. package/src/test/doubles.tsx +174 -3
  131. package/src/test/index.ts +25 -1
  132. package/src/use-process-explorer/use-process-explorer.test.ts +44 -0
  133. package/src/use-process-explorer/use-process-explorer.ts +34 -2
  134. package/src/variant-explorer/coverage-bar.tsx +36 -0
  135. package/src/variant-explorer/index.ts +16 -0
  136. package/src/variant-explorer/sequence-chips.tsx +103 -0
  137. package/src/variant-explorer/variant-explorer-model.ts +42 -0
  138. package/src/variant-explorer/variant-explorer.stories.tsx +226 -0
  139. package/src/variant-explorer/variant-explorer.test.tsx +302 -0
  140. package/src/variant-explorer/variant-explorer.tsx +567 -0
  141. package/src/variant-explorer/variant-row.tsx +137 -0
  142. package/src/violation-list/index.ts +2 -0
  143. package/src/violation-list/violation-list.stories.tsx +73 -0
  144. package/src/violation-list/violation-list.test.tsx +84 -0
  145. package/src/violation-list/violation-list.tsx +259 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/event-log.ts","../../src/core/adapters/flat.ts","../../src/core/adapters/csv.ts","../../src/core/scale.ts","../../src/core/duration-stats.ts","../../src/core/extract-variants.ts","../../src/core/discover-graph.ts","../../src/core/fixtures/synthetic-log.ts","../../src/core/fixtures/generate-bpi-2012-subset.ts","../../src/core/abstract-graph.ts","../../src/core/reconcile-graph.ts","../../src/core/aggregate-performance.ts","../../src/core/detect-rework.ts","../../src/core/filter-log.ts","../../src/core/worker/process-worker.ts","../../src/core/worker/create-process-worker.ts"],"sourcesContent":["/**\n * Event-log normalization — RM-049.\n *\n * Turns a flat `EventRow[]` into per-case traces of INSTANCES: one entry per real\n * activity execution, carrying a resolved `start` and `end` in epoch milliseconds. Every\n * other module in `/core` consumes the normalized form, so the messy parts of a raw log\n * (three timestamp encodings, lifecycle rows split across two lines, rows arriving out of\n * order) are handled exactly once, here.\n *\n * Deterministic: same rows in, same traces out. No `Date.now()`, no randomness.\n */\nimport type { EventLog, EventRow } from \"./types\";\n\n/** One activity execution, with both ends resolved to epoch milliseconds. */\nexport interface NormalizedEvent {\n activity: string;\n /**\n * Start of the execution. Equals `end` for an atomic event — an event with no\n * `lifecycle` pair and no `startTimestamp` has no observed duration, and inventing one\n * would put fabricated numbers into every downstream statistic.\n */\n start: number;\n /** Completion of the execution. */\n end: number;\n /** `end - start`, never negative (a clock-skewed pair is floored at 0). */\n duration: number;\n resource?: string;\n attributes?: Record<string, string | number | boolean | null>;\n /**\n * True when this instance came from a `lifecycle: \"start\"` row that never got a\n * matching `\"complete\"`. Its `end` is provisional (it equals `start`), so a performance\n * view can choose to exclude it.\n */\n isOpen: boolean;\n}\n\n/** One case (process instance): its trace, plus the case's own extent. */\nexport interface NormalizedCase {\n caseId: string;\n /** Instances in ascending `start` order. Never empty. */\n events: NormalizedEvent[];\n /** Earliest `start` in the trace. */\n start: number;\n /** Latest `end` in the trace. */\n end: number;\n /** `end - start` — the case's throughput time. */\n duration: number;\n /** Per-case attributes carried over from `EventLog.caseAttributes`, when present. */\n attributes?: Record<string, unknown>;\n}\n\n/** A normalized log: cases in first-appearance order, plus totals. */\nexport interface NormalizedLog {\n cases: NormalizedCase[];\n totals: { cases: number; events: number };\n}\n\n/** A log in either shape. Every derivation entry point accepts both. */\nexport type AnyLog = EventLog | NormalizedLog;\n\n/** Narrowing predicate — `true` when `log` has already been through {@link normalizeLog}. */\nexport function isNormalizedLog(log: AnyLog): log is NormalizedLog {\n return Array.isArray((log as NormalizedLog).cases);\n}\n\n/** Normalize `log` unless it already is normalized. The idempotent entry point. */\nexport function asNormalizedLog(log: AnyLog): NormalizedLog {\n return isNormalizedLog(log) ? log : normalizeLog(log);\n}\n\n/**\n * Resolve the three accepted timestamp encodings to epoch milliseconds.\n *\n * Returns `NaN` for an unparseable value rather than throwing: one bad cell in a\n * ten-thousand-row CSV must not abort the whole import. Downstream, a `NaN` sorts as\n * equal (so the row keeps its input position) and is dropped from duration samples.\n */\nexport function toEpochMs(value: string | number | Date | undefined | null): number {\n if (value === undefined || value === null) return Number.NaN;\n if (typeof value === \"number\") return Number.isFinite(value) ? value : Number.NaN;\n if (value instanceof Date) return value.getTime();\n const parsed = Date.parse(value);\n if (!Number.isNaN(parsed)) return parsed;\n // A bare numeric string (\"1735725600000\") is a legitimate epoch encoding that\n // `Date.parse` reads as a year in some engines; treat it as epoch ms explicitly.\n const asNumber = Number(value);\n return Number.isFinite(asNumber) && value.trim() !== \"\" ? asNumber : Number.NaN;\n}\n\n/** Stable comparator that leaves `NaN`-timestamped rows in their input order. */\nfunction byTimestamp(a: { at: number }, b: { at: number }): number {\n if (Number.isNaN(a.at) || Number.isNaN(b.at)) return 0;\n return a.at - b.at;\n}\n\ninterface StagedRow {\n at: number;\n row: EventRow;\n}\n\n/**\n * Group rows by case, order each case in time, and pair `lifecycle` rows into instances.\n *\n * The pairing rule, stated precisely because five downstream items depend on it:\n *\n * - Rows are grouped by `caseId` in ONE pass and each case is sorted independently. The\n * whole log is never sorted — sorting 200 000 rows to then split them is the shape that\n * makes a browser import feel broken.\n * - A `lifecycle: \"start\"` row OPENS an instance at its position in the case's timeline.\n * - A `lifecycle: \"complete\"` row CLOSES the oldest still-open instance of the SAME\n * activity, wherever that start sits in the trace. The two rows therefore do not have\n * to be adjacent: interleaved `A-start, B-start, A-complete, B-complete` pairs\n * correctly, and so does a log whose rows arrive in the wrong order entirely, because\n * the per-case sort runs first.\n * - Repeated executions of one activity in a case pair oldest-start-to-earliest-complete\n * (FIFO), which is the only pairing that keeps intervals non-overlapping for a\n * sequential resource.\n * - A `\"complete\"` with no open start becomes an instance in its own right, using an\n * explicit `startTimestamp` when the row carries one.\n * - A `\"start\"` that is never completed stays OPEN: `end === start` and `isOpen` is true.\n * - A row with no `lifecycle` is atomic: `start` comes from `startTimestamp` when given,\n * otherwise from `timestamp` itself.\n *\n * Rows with an empty `caseId` or `activity` are dropped — they cannot take part in a\n * directly-follows relation, and keeping them would put an unnamed node in every graph.\n */\nexport function normalizeLog(log: EventLog): NormalizedLog {\n const staged = new Map<string, StagedRow[]>();\n const order: string[] = [];\n\n for (const row of log.events) {\n if (!row || typeof row.caseId !== \"string\" || row.caseId === \"\") continue;\n if (typeof row.activity !== \"string\" || row.activity === \"\") continue;\n let bucket = staged.get(row.caseId);\n if (bucket === undefined) {\n bucket = [];\n staged.set(row.caseId, bucket);\n order.push(row.caseId);\n }\n bucket.push({ at: toEpochMs(row.timestamp), row });\n }\n\n const cases: NormalizedCase[] = [];\n let events = 0;\n\n for (const caseId of order) {\n const bucket = staged.get(caseId) as StagedRow[];\n // `Array.prototype.sort` is stable (ES2019), so equal timestamps keep input order.\n bucket.sort(byTimestamp);\n\n const instances: NormalizedEvent[] = [];\n /** activity → FIFO queue of indices into `instances` awaiting a `\"complete\"`. */\n const open = new Map<string, number[]>();\n\n for (const { at, row } of bucket) {\n if (row.lifecycle === \"start\") {\n const index = instances.length;\n instances.push(makeInstance(row, at, at, true));\n const queue = open.get(row.activity);\n if (queue === undefined) open.set(row.activity, [index]);\n else queue.push(index);\n continue;\n }\n\n if (row.lifecycle === \"complete\") {\n const queue = open.get(row.activity);\n const index = queue?.shift();\n if (index !== undefined) {\n const instance = instances[index] as NormalizedEvent;\n instance.end = at;\n instance.duration = durationOf(instance.start, at);\n instance.isOpen = false;\n if (instance.resource === undefined && row.resource !== undefined) {\n instance.resource = row.resource;\n }\n if (instance.attributes === undefined && row.attributes !== undefined) {\n instance.attributes = row.attributes;\n }\n continue;\n }\n }\n\n const end = at;\n const explicitStart = toEpochMs(row.startTimestamp);\n const start = Number.isNaN(explicitStart) ? end : explicitStart;\n instances.push(makeInstance(row, start, end, false));\n }\n\n if (instances.length === 0) continue;\n\n // Pairing can move an instance's start earlier than the row that created it, so the\n // trace is re-ordered by resolved start. It is already almost sorted, which is the\n // best case for the engine's TimSort — this is not a second full sort in practice.\n instances.sort(byStart);\n\n let caseStart = Number.POSITIVE_INFINITY;\n let caseEnd = Number.NEGATIVE_INFINITY;\n for (const instance of instances) {\n if (Number.isFinite(instance.start) && instance.start < caseStart) caseStart = instance.start;\n if (Number.isFinite(instance.end) && instance.end > caseEnd) caseEnd = instance.end;\n }\n const hasExtent =\n caseStart !== Number.POSITIVE_INFINITY && caseEnd !== Number.NEGATIVE_INFINITY;\n\n const normalizedCase: NormalizedCase = {\n caseId,\n events: instances,\n start: hasExtent ? caseStart : Number.NaN,\n end: hasExtent ? caseEnd : Number.NaN,\n duration: hasExtent ? durationOf(caseStart, caseEnd) : Number.NaN,\n };\n const caseAttributes = log.caseAttributes?.[caseId];\n if (caseAttributes !== undefined) normalizedCase.attributes = caseAttributes;\n\n cases.push(normalizedCase);\n events += instances.length;\n }\n\n return { cases, totals: { cases: cases.length, events } };\n}\n\nfunction byStart(a: NormalizedEvent, b: NormalizedEvent): number {\n if (Number.isNaN(a.start) || Number.isNaN(b.start)) return 0;\n return a.start - b.start;\n}\n\nfunction durationOf(start: number, end: number): number {\n const delta = end - start;\n if (!Number.isFinite(delta)) return Number.NaN;\n return delta < 0 ? 0 : delta;\n}\n\nfunction makeInstance(row: EventRow, start: number, end: number, isOpen: boolean): NormalizedEvent {\n const instance: NormalizedEvent = {\n activity: row.activity,\n start,\n end,\n duration: durationOf(start, end),\n isOpen,\n };\n if (row.resource !== undefined) instance.resource = row.resource;\n if (row.attributes !== undefined) instance.attributes = row.attributes;\n return instance;\n}\n","/**\n * Flat tabular adapter — RM-049.\n *\n * Pre-shaped tabular data is the lingua franca of event logs: whatever the source (a\n * warehouse query, a spreadsheet export, an API page), it arrives as rows of columns. This\n * adapter maps those columns onto `EventRow`, so every other importer — the CSV reader\n * next door, and any future one — can be a thin front end over this single mapping step\n * rather than a second, subtly different interpretation of the same log.\n *\n * Pure and deterministic: no clock, no I/O, no mutation of the caller's rows.\n */\nimport type { EventLog, EventRow } from \"../types\";\n\n/** One row of a flat source. Values are read defensively — a source may hand back anything. */\nexport type FlatRow = Record<string, unknown>;\n\n/** Which raw cell to read a lifecycle transition's `\"start\"` / `\"complete\"` from. */\nexport interface LifecycleValues {\n start?: readonly string[];\n complete?: readonly string[];\n}\n\n/** Column names that `fromFlatRows` reads. Only `caseId`, `activity` and `timestamp` are required. */\nexport interface FlatRowMapping {\n /** Column holding the case (process instance) id. */\n caseId: string;\n /** Column holding the activity name. */\n activity: string;\n /** Column holding the event timestamp. */\n timestamp: string;\n /** Column holding an explicit interval start, when the source models one. */\n startTimestamp?: string;\n /** Column holding the executing resource. */\n resource?: string;\n /** Column holding a lifecycle transition. */\n lifecycle?: string;\n /** Extra columns copied verbatim into `EventRow.attributes`. */\n attributes?: readonly string[];\n /**\n * Columns copied into `EventLog.caseAttributes`, taking the FIRST non-empty value seen\n * per case. Case attributes are case-level by definition, so a later row disagreeing\n * with an earlier one is a data problem, not something an adapter should silently\n * resolve by overwriting.\n */\n caseAttributes?: readonly string[];\n /**\n * Raw cell values that mean `\"start\"` / `\"complete\"`, compared case-insensitively after\n * trimming. Defaults to {@link DEFAULT_LIFECYCLE_VALUES}; supply your own for a source\n * that spells them differently (`\"S\"` / `\"C\"`, `\"begin\"` / `\"done\"`).\n */\n lifecycleValues?: LifecycleValues;\n}\n\n/** The lifecycle spellings recognized when a mapping does not override them. */\nexport const DEFAULT_LIFECYCLE_VALUES: Required<LifecycleValues> = {\n start: [\"start\", \"started\", \"begin\", \"assign\", \"schedule\"],\n complete: [\"complete\", \"completed\", \"end\", \"finish\", \"finished\", \"done\"],\n};\n\n/** Read a cell as a trimmed string, treating `null`/`undefined`/`\"\"` as absent. */\nfunction readString(row: FlatRow, column: string | undefined): string | undefined {\n if (column === undefined) return undefined;\n const value = row[column];\n if (value === undefined || value === null) return undefined;\n const text = typeof value === \"string\" ? value.trim() : String(value);\n return text === \"\" ? undefined : text;\n}\n\n/** Read a cell as something `toEpochMs` can resolve, without pre-converting it. */\nfunction readTimestamp(\n row: FlatRow,\n column: string | undefined,\n): string | number | Date | undefined {\n if (column === undefined) return undefined;\n const value = row[column];\n if (value === undefined || value === null) return undefined;\n if (value instanceof Date || typeof value === \"number\") return value;\n const text = String(value).trim();\n return text === \"\" ? undefined : text;\n}\n\n/** Coerce a cell into the `attributes` value union; anything else becomes its string form. */\nfunction readAttribute(value: unknown): string | number | boolean | null {\n if (value === null || value === undefined) return null;\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n return value;\n }\n if (value instanceof Date) return value.toISOString();\n return String(value);\n}\n\n/** Map a raw lifecycle cell onto the canonical pair, or `undefined` when it matches neither. */\nexport function normalizeLifecycle(\n raw: string | undefined,\n values: LifecycleValues = DEFAULT_LIFECYCLE_VALUES,\n): \"start\" | \"complete\" | undefined {\n if (raw === undefined) return undefined;\n const needle = raw.trim().toLowerCase();\n if (needle === \"\") return undefined;\n const starts = values.start ?? DEFAULT_LIFECYCLE_VALUES.start;\n const completes = values.complete ?? DEFAULT_LIFECYCLE_VALUES.complete;\n for (const candidate of starts) if (candidate.toLowerCase() === needle) return \"start\";\n for (const candidate of completes) if (candidate.toLowerCase() === needle) return \"complete\";\n return undefined;\n}\n\n/**\n * Map flat rows onto an {@link EventLog}.\n *\n * Rows missing a case id, an activity or a timestamp are SKIPPED rather than throwing: a\n * single blank trailing line or one incomplete record must not fail an entire import, and\n * an event with no activity cannot take part in a directly-follows relation anyway. Count\n * the difference between `rows.length` and `log.events.length` when you need to surface\n * how many were dropped.\n */\nexport function fromFlatRows(rows: readonly FlatRow[], mapping: FlatRowMapping): EventLog {\n const events: EventRow[] = [];\n let caseAttributes: Record<string, Record<string, unknown>> | undefined;\n\n for (const row of rows) {\n if (row === null || typeof row !== \"object\") continue;\n const caseId = readString(row, mapping.caseId);\n const activity = readString(row, mapping.activity);\n const timestamp = readTimestamp(row, mapping.timestamp);\n if (caseId === undefined || activity === undefined || timestamp === undefined) continue;\n\n const event: EventRow = { caseId, activity, timestamp };\n\n const startTimestamp = readTimestamp(row, mapping.startTimestamp);\n if (startTimestamp !== undefined) event.startTimestamp = startTimestamp;\n\n const resource = readString(row, mapping.resource);\n if (resource !== undefined) event.resource = resource;\n\n const lifecycle = normalizeLifecycle(\n readString(row, mapping.lifecycle),\n mapping.lifecycleValues,\n );\n if (lifecycle !== undefined) event.lifecycle = lifecycle;\n\n if (mapping.attributes !== undefined && mapping.attributes.length > 0) {\n const attributes: Record<string, string | number | boolean | null> = {};\n let any = false;\n for (const column of mapping.attributes) {\n if (!(column in row)) continue;\n attributes[column] = readAttribute(row[column]);\n any = true;\n }\n if (any) event.attributes = attributes;\n }\n\n if (mapping.caseAttributes !== undefined && mapping.caseAttributes.length > 0) {\n caseAttributes ??= {};\n const bucket = (caseAttributes[caseId] ??= {});\n for (const column of mapping.caseAttributes) {\n if (bucket[column] !== undefined) continue;\n const value = readString(row, column);\n if (value !== undefined) bucket[column] = value;\n }\n }\n\n events.push(event);\n }\n\n const log: EventLog = { events };\n if (caseAttributes !== undefined) log.caseAttributes = caseAttributes;\n return log;\n}\n","/**\n * CSV adapter — RM-049.\n *\n * A minimal RFC 4180 reader plus a thin front end onto {@link fromFlatRows}. It is written\n * here rather than pulled from npm on purpose: `/core` is the framework-free,\n * worker-safe leaf, and a parser this small (quoted fields, escaped quotes, embedded\n * newlines, CRLF, a BOM) does not justify a runtime dependency in a package a consumer\n * imports into a web worker.\n *\n * What it deliberately does NOT do: type inference, header de-duplication, streaming, or\n * dialect sniffing. A source that needs any of those parses itself and calls\n * {@link fromFlatRows} with the rows.\n */\nimport type { EventLog } from \"../types\";\nimport { fromFlatRows, type FlatRow, type FlatRowMapping } from \"./flat\";\n\n/** Options for {@link parseDelimited} and {@link fromCsv}. */\nexport interface CsvOptions {\n /** Field separator. Defaults to `\",\"`; pass `\"\\t\"` for TSV, `\";\"` for a European export. */\n delimiter?: string;\n /**\n * Column names, when the text has NO header row. Omit for the usual case, where the\n * first record is the header.\n */\n header?: readonly string[];\n}\n\n/** Everything `fromCsv` needs: the column mapping plus the dialect. */\nexport type CsvMapping = FlatRowMapping & CsvOptions;\n\nconst QUOTE = '\"';\nconst CR = \"\\r\";\nconst LF = \"\\n\";\n\n/**\n * Parse RFC 4180 delimited text into records of raw string fields.\n *\n * Handles: quoted fields; a doubled `\"\"` inside a quoted field as one literal quote;\n * delimiters, `CR`, `LF` and `CRLF` inside a quoted field; `CRLF` or bare `LF` record\n * separators; a leading UTF-8 BOM; and a trailing newline (which does NOT produce a\n * phantom empty record).\n *\n * A field is returned exactly as written, without trimming — trimming is a MAPPING\n * decision, and `fromFlatRows` makes it. Unterminated quotes are tolerated: the field runs\n * to the end of the text rather than throwing, so a truncated download yields the rows it\n * did contain.\n */\nexport function parseDelimited(text: string, delimiter = \",\"): string[][] {\n const source = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;\n const records: string[][] = [];\n let record: string[] = [];\n let field = \"\";\n let quoted = false;\n let dirty = false; // has anything been read into the current record at all?\n const delimiterChar = delimiter.length > 0 ? delimiter[0] : \",\";\n\n const endField = (): void => {\n record.push(field);\n field = \"\";\n dirty = true;\n };\n const endRecord = (): void => {\n endField();\n records.push(record);\n record = [];\n dirty = false;\n };\n\n for (let i = 0; i < source.length; i += 1) {\n const char = source[i] as string;\n\n if (quoted) {\n if (char === QUOTE) {\n if (source[i + 1] === QUOTE) {\n field += QUOTE;\n i += 1;\n } else {\n quoted = false;\n }\n } else {\n field += char;\n }\n continue;\n }\n\n if (char === QUOTE && field === \"\") {\n quoted = true;\n dirty = true;\n continue;\n }\n if (char === delimiterChar) {\n endField();\n continue;\n }\n if (char === CR) {\n if (source[i + 1] === LF) i += 1;\n endRecord();\n continue;\n }\n if (char === LF) {\n endRecord();\n continue;\n }\n field += char;\n dirty = true;\n }\n\n if (field !== \"\" || dirty) endRecord();\n return records;\n}\n\n/** Turn parsed records into objects keyed by `header`. Missing trailing cells read as `\"\"`. */\nexport function toObjects(records: readonly string[][], header: readonly string[]): FlatRow[] {\n const rows: FlatRow[] = [];\n for (const record of records) {\n const row: FlatRow = {};\n for (let i = 0; i < header.length; i += 1) {\n row[header[i] as string] = record[i] ?? \"\";\n }\n rows.push(row);\n }\n return rows;\n}\n\n/**\n * Read delimited text into an {@link EventLog}.\n *\n * The first record is the header unless `mapping.header` supplies one. Column names in\n * the header are trimmed (a `\", activity\"` from a hand-edited export still matches\n * `activity`); field VALUES are not, so `fromFlatRows` stays the only place that decides\n * what an empty cell means.\n *\n * Empty text yields an empty log rather than throwing.\n */\nexport function fromCsv(text: string, mapping: CsvMapping): EventLog {\n const records = parseDelimited(text, mapping.delimiter ?? \",\");\n let header = mapping.header;\n let body = records;\n if (header === undefined) {\n const first = records[0];\n if (first === undefined) return { events: [] };\n header = first.map((name) => name.trim());\n body = records.slice(1);\n }\n return fromFlatRows(toObjects(body, header), mapping);\n}\n","/**\n * Visual-scale helpers shared by every process view — RM-049.\n *\n * One implementation, so RM-051's edge width, RM-054's coverage bar and RM-050's\n * abstraction threshold all agree on what \"the 90th percentile\" and \"map this count to a\n * stroke width\" mean. Re-deriving a quantile per component is how two views end up\n * disagreeing about the same log.\n *\n * All three are pure and total: they never throw, and they answer for the degenerate\n * inputs (empty array, zero-width domain) rather than returning `NaN`.\n */\n\n/** Ascending numeric comparator. Extracted so every sort in `/core` uses the same one. */\nexport function ascending(a: number, b: number): number {\n return a - b;\n}\n\n/** Only finite samples take part in a scale; `NaN`/`Infinity` are dropped, not propagated. */\nfunction finiteOnly(values: readonly number[]): number[] {\n const out: number[] = [];\n for (const v of values) if (Number.isFinite(v)) out.push(v);\n return out;\n}\n\n/**\n * The extent of `values`, ignoring non-finite entries.\n *\n * @returns `[min, max]`, or `[0, 0]` when there is nothing finite to measure.\n */\nexport function minMax(values: readonly number[]): [number, number] {\n let min = Number.POSITIVE_INFINITY;\n let max = Number.NEGATIVE_INFINITY;\n let seen = false;\n for (const v of values) {\n if (!Number.isFinite(v)) continue;\n seen = true;\n if (v < min) min = v;\n if (v > max) max = v;\n }\n return seen ? [min, max] : [0, 0];\n}\n\n/**\n * The `q`-quantile of an ALREADY ASCENDING-SORTED, all-finite array, by linear\n * interpolation between order statistics (the R-7 / `d3.quantile` definition).\n *\n * Exported for callers that already hold a sorted array — {@link durationStats} sorts\n * once and reads several quantiles off it. Use {@link quantile} when the input is not\n * known to be sorted.\n */\nexport function quantileSorted(sorted: readonly number[], q: number): number {\n const n = sorted.length;\n if (n === 0) return 0;\n if (n === 1) return sorted[0] as number;\n const clamped = q < 0 ? 0 : q > 1 ? 1 : q;\n const pos = clamped * (n - 1);\n const lo = Math.floor(pos);\n const hi = Math.ceil(pos);\n const loValue = sorted[lo] as number;\n if (lo === hi) return loValue;\n const hiValue = sorted[hi] as number;\n return loValue + (hiValue - loValue) * (pos - lo);\n}\n\n/**\n * The `q`-quantile of `values` (0 ≤ `q` ≤ 1; out-of-range values are clamped).\n *\n * Sorts a COPY, so the caller's array is untouched. Non-finite entries are dropped;\n * an empty (or all-non-finite) input answers `0`.\n */\nexport function quantile(values: readonly number[], q: number): number {\n return quantileSorted(finiteOnly(values).sort(ascending), q);\n}\n\n/**\n * Map `value` from `domain` onto `range`, clamped to the range's endpoints.\n *\n * The workhorse behind \"frequency → stroke width\" and \"share → bar length\". A\n * DEGENERATE domain (`d0 === d1`, e.g. a graph whose every edge has the same count)\n * answers `range[0]`: with a zero-width domain every input sits at the domain floor, so\n * the range floor is the consistent answer — and it is what the clamped interpolation\n * gives for any `value <= d0`. A caller that wants a different neutral (the widest\n * stroke, say) special-cases it explicitly rather than relying on a surprise here.\n *\n * A descending domain (`d0 > d1`) is honoured — it simply inverts the mapping.\n */\nexport function clampWidth(\n value: number,\n domain: readonly [number, number],\n range: readonly [number, number],\n): number {\n const [d0, d1] = domain;\n const [r0, r1] = range;\n if (!Number.isFinite(value)) return r0;\n const span = d1 - d0;\n if (span === 0) return r0;\n const t = (value - d0) / span;\n const clamped = t < 0 ? 0 : t > 1 ? 1 : t;\n return r0 + (r1 - r0) * clamped;\n}\n","/**\n * Duration summary statistics — RM-049.\n *\n * The single implementation every part of the core reads: per-activity execution time,\n * per-edge flow time and per-variant case time all pass through here, which is what makes\n * \"the variant's median agrees with the edge's median on the same fixture\" true by\n * construction rather than by coincidence.\n *\n * Algorithm shape (which statistics a discovered graph carries per activity and per edge)\n * follows pm4js — see `ATTRIBUTION.md`. No pm4js code is copied; the arithmetic below is\n * the ordinary textbook definition.\n */\nimport { ascending, quantileSorted } from \"./scale\";\nimport type { DurationStats } from \"./types\";\n\n/** The fraction discarded from EACH tail by `trimmedMean`. */\nexport const TRIM_FRACTION = 0.1;\n\n/** All-zero statistics — the answer for an empty sample set. */\nexport const EMPTY_DURATION_STATS: Readonly<DurationStats> = Object.freeze({\n min: 0,\n max: 0,\n mean: 0,\n median: 0,\n p90: 0,\n sum: 0,\n trimmedMean: 0,\n});\n\n/** A fresh mutable copy of {@link EMPTY_DURATION_STATS}. */\nexport function emptyDurationStats(): DurationStats {\n return { ...EMPTY_DURATION_STATS };\n}\n\n/**\n * Summarize `samples` (milliseconds) as {@link DurationStats}.\n *\n * - Non-finite samples are DROPPED, not propagated — one unparseable timestamp upstream\n * must not turn an entire activity's statistics into `NaN`.\n * - The input array is not mutated (a copy is sorted).\n * - `p90` uses linear interpolation between order statistics (R-7 / `d3.quantile`).\n * - `trimmedMean` discards the lowest and highest {@link TRIM_FRACTION} of samples. When\n * trimming would leave nothing (fewer than 5 samples, where `floor(n * 0.1)` is 0 or\n * the tails meet), it degrades to the plain mean rather than to `NaN`.\n * - An empty (or all-non-finite) input answers all zeros.\n */\nexport function durationStats(samples: readonly number[]): DurationStats {\n const sorted: number[] = [];\n for (const s of samples) if (Number.isFinite(s)) sorted.push(s);\n const n = sorted.length;\n if (n === 0) return emptyDurationStats();\n sorted.sort(ascending);\n\n let sum = 0;\n for (const s of sorted) sum += s;\n\n const trim = Math.floor(n * TRIM_FRACTION);\n const lo = trim;\n const hi = n - trim;\n let trimmedMean: number;\n if (hi - lo <= 0) {\n trimmedMean = sum / n;\n } else {\n let trimmedSum = 0;\n for (let i = lo; i < hi; i += 1) trimmedSum += sorted[i] as number;\n trimmedMean = trimmedSum / (hi - lo);\n }\n\n return {\n min: sorted[0] as number,\n max: sorted[n - 1] as number,\n mean: sum / n,\n median: quantileSorted(sorted, 0.5),\n p90: quantileSorted(sorted, 0.9),\n sum,\n trimmedMean,\n };\n}\n\n/** Default reservoir capacity for a single activity's or edge's duration samples. */\nexport const DURATION_SAMPLE_CAP = 4096;\n\n/**\n * A deterministic 32-bit PRNG (mulberry32). Same seed, same stream — which is the whole\n * point: the reservoir below must make identical keep/discard decisions on every run, in\n * every engine, or two runs over one log would produce different `p90`s.\n */\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Bounded duration-sample collector.\n *\n * A log with a million events would otherwise hold a million numbers per hot edge just to\n * compute seven statistics from them. The first {@link DURATION_SAMPLE_CAP} samples are\n * kept outright; past the cap the collector switches to reservoir sampling (Algorithm R),\n * so the retained set stays a uniform sample of everything seen while memory stays flat.\n *\n * `count` keeps the TRUE number of samples offered, and `sum` the true total, so a\n * capped edge still reports an exact `sum` and an exact `mean`; only the order statistics\n * (`median`, `p90`, `min`, `max`, `trimmedMean`) are estimated from the reservoir.\n */\nexport class DurationSampler {\n private readonly capacity: number;\n private readonly random: () => number;\n private readonly reservoir: number[] = [];\n /** Number of finite samples offered, including those the reservoir discarded. */\n private seen = 0;\n private total = 0;\n private lowest = Number.POSITIVE_INFINITY;\n private highest = Number.NEGATIVE_INFINITY;\n\n constructor(seed = 0x9e3779b9, capacity: number = DURATION_SAMPLE_CAP) {\n this.capacity = capacity > 0 ? capacity : 1;\n this.random = mulberry32(seed);\n }\n\n /** Offer one sample. Non-finite values are ignored. */\n add(sample: number): void {\n if (!Number.isFinite(sample)) return;\n this.total += sample;\n if (sample < this.lowest) this.lowest = sample;\n if (sample > this.highest) this.highest = sample;\n if (this.reservoir.length < this.capacity) {\n this.reservoir.push(sample);\n } else {\n const j = Math.floor(this.random() * (this.seen + 1));\n if (j < this.capacity) this.reservoir[j] = sample;\n }\n this.seen += 1;\n }\n\n /** True number of finite samples offered. */\n get size(): number {\n return this.seen;\n }\n\n /**\n * Summarize what was collected. `sum`, `mean`, `min` and `max` are EXACT even past the\n * cap (they are accumulated, not read off the reservoir); the remaining three are\n * computed from the retained sample.\n */\n stats(): DurationStats {\n if (this.seen === 0) return emptyDurationStats();\n const fromReservoir = durationStats(this.reservoir);\n return {\n ...fromReservoir,\n min: this.lowest,\n max: this.highest,\n sum: this.total,\n mean: this.total / this.seen,\n };\n }\n}\n","/**\n * Variant extraction — RM-049.\n *\n * A \"variant\" is one distinct activity sequence, together with every case that follows\n * it. Grouping traces by their sequence and ranking those groups by frequency is the\n * standard second view onto a log (the first being the directly-follows graph), and the\n * grouping shape below follows pm4js; see `ATTRIBUTION.md`. No pm4js code is copied.\n *\n * Deterministic: ids are hashes of the sequence, ordering is fully tie-broken, and the\n * cumulative share is computed from counts rather than by summing floats, so it is exactly\n * `1` on the last variant instead of `0.9999999999999998`.\n */\nimport { durationStats } from \"./duration-stats\";\nimport { asNormalizedLog, type AnyLog, type NormalizedCase } from \"./event-log\";\nimport type { Variant } from \"./types\";\n\n/**\n * Separator joining a sequence into its variant key.\n *\n * U+0001 (START OF HEADING) is a C0 control character. No real activity name contains\n * one, which is what makes joining on it lossless where a printable separator\n * (`\" -> \"`, `\",\"`) would collide with a name that happens to contain the separator.\n */\nexport const VARIANT_KEY_SEPARATOR = \"\\u0001\";\n\n/** Join an activity sequence into its canonical variant key. */\nexport function variantKey(sequence: readonly string[]): string {\n return sequence.join(VARIANT_KEY_SEPARATOR);\n}\n\n/** FNV-1a, 32-bit. Small, fast, dependency-free and identical in every engine. */\nfunction fnv1a32(text: string, basis: number): number {\n let hash = basis >>> 0;\n for (let i = 0; i < text.length; i += 1) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash >>> 0;\n}\n\nfunction hex8(value: number): string {\n return (value >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * The stable id for an activity sequence: `v` followed by 64 bits of FNV-1a over the\n * variant key, as hex.\n *\n * Exported because a selection is a wave-wide concern — RM-052's variant explorer and\n * RM-053's case table both need to name the same variant without holding the object, and\n * a hash of the sequence is the only identifier that survives a re-run, a worker boundary\n * and a URL.\n */\nexport function variantId(sequence: readonly string[]): string {\n const key = variantKey(sequence);\n return `v${hex8(fnv1a32(key, 0x811c9dc5))}${hex8(fnv1a32(key, 0x84222325))}`;\n}\n\ninterface VariantAccumulator {\n key: string;\n sequence: string[];\n caseIds: string[];\n durations: number[];\n}\n\n/**\n * Group `log`'s cases into variants, ranked by frequency.\n *\n * Ordering is `count` descending, ties broken by the variant key ascending — a total\n * order, so the same log always produces the same array in the same positions.\n *\n * `share` is `count / cases`. `cumulativeShare` is the running `cumulativeCount / cases`,\n * which is monotonically non-decreasing by construction and exactly `1` on the last\n * entry. `duration` summarizes the END-TO-END case durations of the variant's cases, so\n * it answers \"how long does this path take\", not \"how long does one step take\".\n *\n * An empty log yields an empty array — never a single zero-count variant.\n */\nexport function extractVariants(log: AnyLog): Variant[] {\n const normalized = asNormalizedLog(log);\n const totalCases = normalized.cases.length;\n if (totalCases === 0) return [];\n\n const groups = new Map<string, VariantAccumulator>();\n for (const kase of normalized.cases) {\n const sequence = sequenceOf(kase);\n const key = variantKey(sequence);\n let group = groups.get(key);\n if (group === undefined) {\n group = { key, sequence, caseIds: [], durations: [] };\n groups.set(key, group);\n }\n group.caseIds.push(kase.caseId);\n group.durations.push(kase.duration);\n }\n\n const ranked = [...groups.values()].sort(\n (a, b) => b.caseIds.length - a.caseIds.length || (a.key < b.key ? -1 : a.key > b.key ? 1 : 0),\n );\n\n const taken = new Set<string>();\n const variants: Variant[] = [];\n let cumulativeCount = 0;\n\n for (const group of ranked) {\n const count = group.caseIds.length;\n cumulativeCount += count;\n // A 64-bit hash collision between two different sequences is vanishingly unlikely,\n // but \"vanishingly unlikely\" is not \"impossible\" and a duplicate id would silently\n // merge two rows in a table. Disambiguate deterministically: `ranked` is totally\n // ordered, so the suffix an id receives is the same on every run.\n let id = variantId(group.sequence);\n if (taken.has(id)) {\n let suffix = 1;\n while (taken.has(`${id}-${suffix}`)) suffix += 1;\n id = `${id}-${suffix}`;\n }\n taken.add(id);\n\n variants.push({\n id,\n sequence: group.sequence,\n count,\n share: count / totalCases,\n cumulativeShare: cumulativeCount / totalCases,\n caseIds: group.caseIds,\n duration: durationStats(group.durations),\n });\n }\n\n return variants;\n}\n\nfunction sequenceOf(kase: NormalizedCase): string[] {\n const sequence = new Array<string>(kase.events.length);\n for (let i = 0; i < kase.events.length; i += 1) {\n sequence[i] = (kase.events[i] as { activity: string }).activity;\n }\n return sequence;\n}\n","/**\n * Directly-follows graph discovery — RM-049.\n *\n * The directly-follows abstraction (one node per activity, one edge per adjacent pair in\n * a trace, with frequency and performance aggregates on both) is the standard starting\n * point of every process-mining view. The shape of what a discovered graph carries —\n * per-activity instance and case counts, per-edge count and case count, start/end\n * activity tallies — follows pm4js; see `ATTRIBUTION.md`. No pm4js code is copied, and\n * nothing here derives from the AGPL Python reference implementation.\n *\n * Deterministic and layout-free: `isBackEdge` is always `false` here, because whether an\n * edge points backwards is a property of a LAYOUT, not of a log. A layout pass sets it.\n */\nimport { DURATION_SAMPLE_CAP, DurationSampler } from \"./duration-stats\";\nimport { asNormalizedLog, type AnyLog, type NormalizedEvent } from \"./event-log\";\nimport { VARIANT_KEY_SEPARATOR } from \"./extract-variants\";\nimport type { ActivityStats, FlowTime, ProcessGraph, TransitionStats } from \"./types\";\n\n/**\n * Separator used to key an edge. The same C0 control character the variant key uses, and\n * for the same reason: no activity name can contain it, so `source + SEP + target` is an\n * injective key.\n */\nexport const EDGE_KEY_SEPARATOR = VARIANT_KEY_SEPARATOR;\n\n/** Options for {@link discoverGraph}. Every field has a default; `discoverGraph(log)` works. */\nexport interface DiscoverGraphOptions {\n /**\n * Which elapsed time an edge measures. Defaults to `\"idle_time\"` — the wait between the\n * source completing and the target starting, which is the number a bottleneck view\n * wants. For a log of atomic events the two choices coincide.\n */\n flowTime?: FlowTime;\n /**\n * Reservoir capacity per activity and per edge. Lower it for a very wide log; raise it\n * for sharper tail statistics. `sum`/`mean`/`min`/`max` stay exact regardless.\n */\n maxDurationSamples?: number;\n}\n\ninterface ActivityAccumulator {\n instances: number;\n cases: number;\n duration: DurationSampler;\n}\n\ninterface TransitionAccumulator {\n source: string;\n target: string;\n count: number;\n caseCount: number;\n duration: DurationSampler;\n}\n\n/**\n * Derive a {@link ProcessGraph} from a raw or already-normalized log.\n *\n * One pass per trace: each case's activity sequence is walked once, incrementing\n * per-activity and per-edge counters and feeding two duration samplers. Per-case\n * uniqueness (the `cases` and `caseCount` fields) is tracked with two `Set`s that are\n * cleared per case rather than a per-key `Set` of case ids, so memory stays proportional\n * to the widest trace, not to the log.\n *\n * `totals.variants` is counted from the same walk (the joined activity sequence goes into\n * a `Set`), so a caller that only needs the headline number does not also have to run\n * `extractVariants`.\n */\nexport function discoverGraph(log: AnyLog, options: DiscoverGraphOptions = {}): ProcessGraph {\n const flowTime: FlowTime = options.flowTime ?? \"idle_time\";\n const capacity = options.maxDurationSamples ?? DURATION_SAMPLE_CAP;\n const normalized = asNormalizedLog(log);\n\n const activities = new Map<string, ActivityAccumulator>();\n const transitions = new Map<string, TransitionAccumulator>();\n const startActivities = new Map<string, number>();\n const endActivities = new Map<string, number>();\n const variantKeys = new Set<string>();\n\n // Sampler seeds come from a creation counter, never from a clock or from entropy: the\n // iteration order over a given log is fixed, so the same log always seeds the same\n // samplers and the reservoir retains the same rows.\n let samplerIndex = 0;\n const nextSeed = (): number => {\n samplerIndex += 1;\n return (0x9e3779b9 ^ Math.imul(samplerIndex, 0x9e3779b1)) >>> 0;\n };\n\n let events = 0;\n const seenActivities = new Set<string>();\n const seenEdges = new Set<string>();\n\n for (const kase of normalized.cases) {\n const trace = kase.events;\n if (trace.length === 0) continue;\n events += trace.length;\n seenActivities.clear();\n seenEdges.clear();\n\n const sequence = new Array<string>(trace.length);\n\n for (let i = 0; i < trace.length; i += 1) {\n const event = trace[i] as NormalizedEvent;\n const name = event.activity;\n sequence[i] = name;\n\n let activity = activities.get(name);\n if (activity === undefined) {\n activity = { instances: 0, cases: 0, duration: new DurationSampler(nextSeed(), capacity) };\n activities.set(name, activity);\n }\n activity.instances += 1;\n activity.duration.add(event.duration);\n if (!seenActivities.has(name)) {\n seenActivities.add(name);\n activity.cases += 1;\n }\n\n if (i === 0) continue;\n const previous = trace[i - 1] as NormalizedEvent;\n const source = previous.activity;\n const key = `${source}${EDGE_KEY_SEPARATOR}${name}`;\n let edge = transitions.get(key);\n if (edge === undefined) {\n edge = {\n source,\n target: name,\n count: 0,\n caseCount: 0,\n duration: new DurationSampler(nextSeed(), capacity),\n };\n transitions.set(key, edge);\n }\n edge.count += 1;\n edge.duration.add(\n flowTime === \"inter_start_time\" ? event.start - previous.start : event.start - previous.end,\n );\n if (!seenEdges.has(key)) {\n seenEdges.add(key);\n edge.caseCount += 1;\n }\n }\n\n const first = trace[0] as NormalizedEvent;\n const last = trace[trace.length - 1] as NormalizedEvent;\n startActivities.set(first.activity, (startActivities.get(first.activity) ?? 0) + 1);\n endActivities.set(last.activity, (endActivities.get(last.activity) ?? 0) + 1);\n variantKeys.add(sequence.join(VARIANT_KEY_SEPARATOR));\n }\n\n const activityList: ActivityStats[] = [];\n for (const [id, accumulator] of activities) {\n activityList.push({\n id,\n label: id,\n instances: accumulator.instances,\n cases: accumulator.cases,\n isStart: startActivities.has(id),\n isEnd: endActivities.has(id),\n duration: accumulator.duration.stats(),\n });\n }\n // Deterministic, reader-friendly order: busiest first, ties broken by name, so the same\n // log always yields the same array in the same positions (downstream items snapshot it).\n activityList.sort((a, b) => b.instances - a.instances || compareStrings(a.id, b.id));\n\n const transitionList: TransitionStats[] = [];\n for (const accumulator of transitions.values()) {\n transitionList.push({\n source: accumulator.source,\n target: accumulator.target,\n count: accumulator.count,\n caseCount: accumulator.caseCount,\n duration: accumulator.duration.stats(),\n isSelfLoop: accumulator.source === accumulator.target,\n isBackEdge: false,\n });\n }\n transitionList.sort(\n (a, b) =>\n b.count - a.count || compareStrings(a.source, b.source) || compareStrings(a.target, b.target),\n );\n\n return {\n activities: activityList,\n transitions: transitionList,\n startActivities: toSortedRecord(startActivities),\n endActivities: toSortedRecord(endActivities),\n totals: { cases: normalized.cases.length, events, variants: variantKeys.size },\n };\n}\n\n/** Code-unit comparison — locale-independent, so the order is the same on every machine. */\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\n/** Key order is part of the output, so it is sorted rather than left to insertion order. */\nfunction toSortedRecord(counts: Map<string, number>): Record<string, number> {\n const out: Record<string, number> = {};\n for (const key of [...counts.keys()].sort()) out[key] = counts.get(key) as number;\n return out;\n}\n","/**\n * Seeded synthetic event-log generator — RM-049.\n *\n * Produces an order-to-cash shaped log — a happy path, two exception branches, a rework\n * loop back to an earlier activity, an occasional repeat of one step, and a step-order\n * swap — at any size, from a seed. That is what lets tests, stories and benchmarks work\n * with a realistically messy log without this repo ever shipping a large binary fixture,\n * and without any of them depending on a wall clock.\n *\n * Determinism is the whole contract: `generateSyntheticLog({ cases: n, seed: s })` returns\n * a byte-identical log on every run, in every engine, forever. Downstream items snapshot\n * against it.\n */\nimport type { EventLog, EventRow } from \"../types\";\n\n/** Epoch ms the first case starts at: 2026-01-05T08:00:00.000Z, a Monday morning. */\nexport const SYNTHETIC_LOG_EPOCH = 1767600000000;\n\nconst MINUTE = 60_000;\n\n/** Options for {@link generateSyntheticLog}. */\nexport interface SyntheticLogOptions {\n /** How many cases to generate. Values below 1 yield an empty log. */\n cases: number;\n /** PRNG seed. The same seed always produces the same log. */\n seed?: number;\n /** Epoch ms the first case starts at. Defaults to {@link SYNTHETIC_LOG_EPOCH}. */\n startTime?: number;\n}\n\n/**\n * A deterministic 32-bit PRNG (mulberry32).\n *\n * Deliberately its own copy rather than one shared with the core's duration sampler: this\n * generator's output is a FIXTURE that tests assert exact numbers against, so it must not\n * shift because an unrelated internal in `duration-stats.ts` was retuned.\n */\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\nconst RESOURCES = [\n \"A. Novak\",\n \"B. Ferreira\",\n \"C. Okafor\",\n \"D. Lindqvist\",\n \"E. Haddad\",\n \"Credit Service\",\n \"Warehouse Robot\",\n] as const;\n\nconst REGIONS = [\"North\", \"South\", \"East\", \"West\"] as const;\nconst SEGMENTS = [\"Enterprise\", \"Mid-market\", \"SMB\"] as const;\n\n/** Activity vocabulary. Exported so a story can label or colour by name without guessing. */\nexport const SYNTHETIC_ACTIVITIES = [\n \"Create Order\",\n \"Check Credit\",\n \"Amend Order\",\n \"Approve Order\",\n \"Reject Order\",\n \"Reserve Stock\",\n \"Cancel Order\",\n \"Pick Items\",\n \"Ship Order\",\n \"Send Invoice\",\n \"Receive Payment\",\n] as const;\n\n/** One activity name from {@link SYNTHETIC_ACTIVITIES}. */\nexport type SyntheticActivity = (typeof SYNTHETIC_ACTIVITIES)[number];\n\n/** Build the activity sequence of a single case. Pure given `random`. */\nfunction buildTrace(random: () => number): SyntheticActivity[] {\n const trace: SyntheticActivity[] = [\"Create Order\", \"Check Credit\"];\n\n // Rework: an amendment sends the order back through the credit check, at most twice.\n let reworks = 0;\n while (reworks < 2 && random() < 0.14) {\n trace.push(\"Amend Order\", \"Check Credit\");\n reworks += 1;\n }\n\n if (random() < 0.09) {\n trace.push(\"Reject Order\");\n return trace;\n }\n\n trace.push(\"Approve Order\", \"Reserve Stock\");\n\n if (random() < 0.05) {\n trace.push(\"Cancel Order\");\n return trace;\n }\n\n trace.push(\"Pick Items\");\n // A short-picked order is picked again — the log's only self-loop.\n if (random() < 0.07) trace.push(\"Pick Items\");\n\n // Roughly a third of orders are invoiced before they ship.\n if (random() < 0.32) trace.push(\"Send Invoice\", \"Ship Order\");\n else trace.push(\"Ship Order\", \"Send Invoice\");\n\n trace.push(\"Receive Payment\");\n return trace;\n}\n\n/**\n * Generate a synthetic order-to-cash log.\n *\n * Every event is an INTERVAL event: it carries both a `startTimestamp` and a `timestamp`,\n * so activity durations and edge idle times are both non-zero and a performance view has\n * something real to show. Timestamps are epoch numbers rather than ISO strings — a\n * 13 000-case log is a quarter of a million rows, and making a benchmark of the discovery\n * pass mostly measure `Date.parse` would be measuring the wrong thing.\n *\n * Cases are emitted in order, one per `caseId` of the form `case-00001`, zero-padded so\n * lexical and numeric order agree.\n */\nexport function generateSyntheticLog(options: SyntheticLogOptions): EventLog {\n const total = Math.max(0, Math.floor(options.cases));\n const random = mulberry32(options.seed ?? 1);\n const epoch = options.startTime ?? SYNTHETIC_LOG_EPOCH;\n\n const events: EventRow[] = [];\n const caseAttributes: Record<string, Record<string, unknown>> = {};\n const pad = String(total).length;\n\n for (let index = 0; index < total; index += 1) {\n const caseId = `case-${String(index + 1).padStart(Math.max(5, pad), \"0\")}`;\n const trace = buildTrace(random);\n\n caseAttributes[caseId] = {\n region: REGIONS[Math.floor(random() * REGIONS.length)] as string,\n segment: SEGMENTS[Math.floor(random() * SEGMENTS.length)] as string,\n // Whole euros, so a table can render the value without a rounding decision.\n orderValue: 250 + Math.floor(random() * 9750),\n };\n\n // Cases arrive about an hour apart, with a jitter that keeps them from lining up.\n let cursor = epoch + index * 61 * MINUTE + Math.floor(random() * 40) * MINUTE;\n\n for (const activity of trace) {\n const idle = Math.floor(random() * 180) * MINUTE;\n const work = (2 + Math.floor(random() * 44)) * MINUTE;\n const start = cursor + idle;\n const end = start + work;\n events.push({\n caseId,\n activity,\n timestamp: end,\n startTimestamp: start,\n resource: RESOURCES[Math.floor(random() * RESOURCES.length)] as string,\n });\n cursor = end;\n }\n }\n\n return { events, caseAttributes };\n}\n","/**\n * Seeded BPI-2012-shaped event-log generator — RM-053.\n *\n * A SEPARATE generator from `synthetic-log.ts` (RM-049), not a second copy of it: RM-049's\n * `generateSyntheticLog` produces an order-to-cash shaped log (Create Order, Check Credit,\n * Ship Order, …); this one produces a log shaped like the public BPI Challenge 2012 event\n * log (van Dongen, 2012) — a Dutch financial institution's loan-application process, whose\n * activity vocabulary follows the dataset's own three-lifecycle-prefix convention\n * (`A_` application state, `O_` offer state, `W_` work item). The BPI Challenge datasets are\n * a public academic research corpus published for the annual Business Process Intelligence\n * Challenge; naming one here names a published research artifact, not a commercial vendor.\n *\n * This is what analysis §4 R21 asks for: a ~13,000-case fixture large enough to exercise\n * `discoverGraph`'s performance budget (already covered by RM-049's own\n * `generateSyntheticLog({ cases: 13_000 })` in `discover-graph.test.ts`) and, from RM-051\n * onward, a Storybook \"LargeGraph\" story that wants a visually denser, more tangled graph\n * than the order-to-cash shape produces (more branching, more rework, a wider activity\n * vocabulary).\n *\n * Determinism is the whole contract, exactly as in `synthetic-log.ts`:\n * `generateBpi2012Subset({ cases: n, seed: s })` returns a byte-identical log on every run.\n *\n * ## This module is BROWSER-SAFE — no Node built-ins\n *\n * Deliberately: `discoverGraph`/`extractVariants` demos (the RM-053 placeholder Storybook\n * story, and any future browser-rendered fixture preview) import this module directly, and\n * Storybook's story tests run in a real browser (`@storybook/addon-vitest`'s Playwright\n * provider) where `node:fs`/`node:path`/`node:url` are externalized and throw on access. So\n * this file exports the PURE generator only — no filesystem write, no `import.meta.url`\n * resolution, no `process.argv` read. The file that writes the on-disk fixture is the\n * sibling `generate-bpi-2012-subset.write.ts` (see below), which is the only place those\n * Node-only imports appear.\n *\n * ## Producing the on-disk fixture\n *\n * The 13k-case output is a ~250k-row JSON document — too large to commit as a binary (see\n * the repo's \"no committed large binary fixture\" convention, already followed by\n * `synthetic-log.ts`). It is a GENERATED artifact: run\n *\n * ```\n * pnpm --filter @elabs-ai/components-process generate:fixtures\n * ```\n *\n * which writes `bpi-2012-subset.json` next to this file (git-ignored — see the repo root\n * `.gitignore`), via `generate-bpi-2012-subset.write.ts` (run through `vitest`, not `tsx` —\n * see that file's docblock for why). A consumer of the generated JSON should treat it as a\n * build artifact, not a checked-in fixture; import {@link generateBpi2012Subset} directly\n * wherever possible instead of reading the file.\n */\nimport type { EventLog, EventRow } from \"../types\";\n\n/** Epoch ms the first case starts at: 2012-01-02T08:00:00.000Z, a Monday morning. */\nexport const BPI_2012_SUBSET_EPOCH = 1325487600000;\n\nconst MINUTE = 60_000;\n\n/** Options for {@link generateBpi2012Subset}. */\nexport interface Bpi2012SubsetOptions {\n /** How many cases to generate. Values below 1 yield an empty log. */\n cases: number;\n /** PRNG seed. The same seed always produces the same log. */\n seed?: number;\n /** Epoch ms the first case starts at. Defaults to {@link BPI_2012_SUBSET_EPOCH}. */\n startTime?: number;\n}\n\n/**\n * A deterministic 32-bit PRNG (mulberry32).\n *\n * Deliberately its OWN copy rather than one imported from `synthetic-log.ts`: that file's\n * `mulberry32` is intentionally unexported (its own doc comment explains why — a fixture\n * generator's output must not shift because an unrelated generator's internals changed), and\n * this generator's output is equally a byte-identical fixture contract. Same reasoning,\n * same repo convention, independently applied.\n */\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\nconst RESOURCES = [\n \"Loan Officer 1\",\n \"Loan Officer 2\",\n \"Loan Officer 3\",\n \"Credit Assessor\",\n \"Automated System\",\n] as const;\n\n/**\n * Activity vocabulary, following the public BPI Challenge 2012 log's own naming convention:\n * `A_` (application), `O_` (offer) and `W_` (work item) lifecycle prefixes.\n */\nexport const BPI_2012_ACTIVITIES = [\n \"A_SUBMITTED\",\n \"A_PARTLYSUBMITTED\",\n \"A_PREACCEPTED\",\n \"W_Completeren aanvraag\",\n \"A_ACCEPTED\",\n \"O_SELECTED\",\n \"A_FINALIZED\",\n \"O_CREATED\",\n \"O_SENT\",\n \"W_Nabellen offertes\",\n \"O_SENT_BACK\",\n \"A_REGISTERED\",\n \"A_APPROVED\",\n \"A_ACTIVATED\",\n \"O_ACCEPTED\",\n \"A_DECLINED\",\n \"O_DECLINED\",\n \"O_CANCELLED\",\n \"A_CANCELLED\",\n] as const;\n\n/** One activity name from {@link BPI_2012_ACTIVITIES}. */\nexport type Bpi2012Activity = (typeof BPI_2012_ACTIVITIES)[number];\n\n/** Build the activity sequence of a single case. Pure given `random`. */\nfunction buildTrace(random: () => number): Bpi2012Activity[] {\n const trace: Bpi2012Activity[] = [\"A_SUBMITTED\", \"A_PARTLYSUBMITTED\"];\n\n if (random() < 0.06) {\n trace.push(\"A_DECLINED\");\n return trace;\n }\n\n trace.push(\"A_PREACCEPTED\", \"W_Completeren aanvraag\");\n\n // Rework: an incomplete dossier sends the applicant back through completion, at most twice.\n let reworks = 0;\n while (reworks < 2 && random() < 0.18) {\n trace.push(\"W_Completeren aanvraag\");\n reworks += 1;\n }\n\n if (random() < 0.08) {\n trace.push(\"A_CANCELLED\");\n return trace;\n }\n\n trace.push(\n \"A_ACCEPTED\",\n \"O_SELECTED\",\n \"A_FINALIZED\",\n \"O_CREATED\",\n \"O_SENT\",\n \"W_Nabellen offertes\",\n );\n\n // About a quarter of offers are sent back at least once before acceptance.\n if (random() < 0.25) trace.push(\"O_SENT_BACK\", \"O_SENT\", \"W_Nabellen offertes\");\n\n // About 1 in 10 offers ends without acceptance: most are declined outright, a smaller\n // share are withdrawn (O_CANCELLED) while still outstanding — both close the case, one\n // via A_CANCELLED and the other via either terminal state, mirroring the public BPI 2012\n // log's own O_CANCELLED lifecycle event (#228 — this branch is what makes it reachable;\n // see generate-bpi-2012-subset.test.ts's vocabulary set-equality test).\n if (random() < 0.1) {\n if (random() < 0.3) {\n trace.push(\"O_CANCELLED\", \"A_CANCELLED\");\n } else {\n trace.push(\"O_DECLINED\", random() < 0.5 ? \"A_CANCELLED\" : \"A_DECLINED\");\n }\n return trace;\n }\n\n trace.push(\"O_ACCEPTED\", \"A_REGISTERED\", \"A_APPROVED\", \"A_ACTIVATED\");\n return trace;\n}\n\n/**\n * Generate a synthetic BPI-2012-shaped loan-application log.\n *\n * Every event is an INTERVAL event (`startTimestamp` + `timestamp`), and cases are emitted\n * as `caseId`s of the form `bpi-000001`, zero-padded so lexical and numeric order agree —\n * the same shape `generateSyntheticLog` uses, so downstream consumers do not need a second\n * case-id convention.\n */\nexport function generateBpi2012Subset(options: Bpi2012SubsetOptions): EventLog {\n const total = Math.max(0, Math.floor(options.cases));\n const random = mulberry32(options.seed ?? 1);\n const epoch = options.startTime ?? BPI_2012_SUBSET_EPOCH;\n\n const events: EventRow[] = [];\n const pad = String(total).length;\n\n for (let index = 0; index < total; index += 1) {\n const caseId = `bpi-${String(index + 1).padStart(Math.max(6, pad), \"0\")}`;\n const trace = buildTrace(random);\n\n let cursor = epoch + index * 47 * MINUTE + Math.floor(random() * 30) * MINUTE;\n\n for (const activity of trace) {\n const idle = Math.floor(random() * 240) * MINUTE;\n const work = (1 + Math.floor(random() * 20)) * MINUTE;\n const start = cursor + idle;\n const end = start + work;\n events.push({\n caseId,\n activity,\n timestamp: end,\n startTimestamp: start,\n resource: RESOURCES[Math.floor(random() * RESOURCES.length)] as string,\n });\n cursor = end;\n }\n }\n\n return { events };\n}\n","/**\n * Slider-driven graph abstraction — RM-050.\n *\n * Every process-mining product surveyed in the wave-1 analysis exposes the same two\n * controls: an ACTIVITIES slider and a PATHS slider, both of which reduce what is DRAWN\n * without touching what was MEASURED. That rule is the whole point of this module — a\n * reader who drags a slider is filtering a view, not re-running an analysis, so the\n * numbers under their cursor must not move.\n *\n * Two invariants follow, and both are asserted in `abstract-graph.test.ts`:\n *\n * 1. **Statistics are never recomputed.** The returned graph reuses the very same\n * {@link ActivityStats} and {@link TransitionStats} OBJECTS the input carried — this\n * function filters arrays, it never builds a statistic. Nothing here mutates the input.\n * 2. **Kept nodes stay connected.** With `keepConnected` (the default) every kept activity\n * is reachable from a start activity and can reach an end activity, because a graph\n * with an island in it reads as a broken process rather than a simplified one.\n *\n * Deterministic: every ranking is totally ordered and the repair walks a fixed-cost graph,\n * so the same input always yields the same reduced view.\n */\nimport { EDGE_KEY_SEPARATOR } from \"./discover-graph\";\nimport type { ActivityStats, ProcessGraph, TransitionStats } from \"./types\";\n\n/** Options for {@link abstractGraph}. Both fractions are `0..1` and both are required. */\nexport interface AbstractionOptions {\n /** Fraction of activities to KEEP, `0..1`. At least one activity is always kept. */\n activities: number;\n /** Fraction of paths (transitions) to KEEP, `0..1`, over the kept-activity subgraph. */\n paths: number;\n /**\n * Re-add whatever it takes to keep every kept activity reachable from a start activity\n * and able to reach an end activity. Defaults to `true` — an island reads as a broken\n * process, not a simplified one.\n */\n keepConnected?: boolean;\n /** Hide the MOST frequent instead of the least — the \"what is rare here\" view. */\n invert?: boolean;\n}\n\n/** What {@link abstractGraph} returns: a `ProcessGraph` plus what it left out. */\nexport interface AbstractedGraph extends ProcessGraph {\n hidden: {\n /** Activities present in the input graph and absent from this one. */\n activities: number;\n /** Transitions present in the input graph and absent from this one. */\n paths: number;\n };\n}\n\n/** Key an edge exactly the way discovery keys it, so the two agree by construction. */\nfunction edgeKey(source: string, target: string): string {\n return `${source}${EDGE_KEY_SEPARATOR}${target}`;\n}\n\n/** Code-unit comparison — locale-independent, so the order is the same on every machine. */\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\n/** A non-finite fraction is read as `1` (keep everything) rather than as an error. */\nfunction clampFraction(value: number): number {\n if (!Number.isFinite(value)) return 1;\n return value < 0 ? 0 : value > 1 ? 1 : value;\n}\n\n/**\n * How many of `total` a fraction keeps.\n *\n * `Math.round` (half up) rather than floor or ceil, so the midpoint of the slider keeps\n * half the elements on an even count and the larger half on an odd one — and, crucially,\n * `fraction === 1` keeps exactly `total`, which is what makes the identity property hold.\n * `atLeast` is enforced for activities so a reduced graph is never empty; paths pass `0`,\n * because a node-only view is a legitimate thing to ask for.\n */\nfunction countToKeep(total: number, fraction: number, atLeast: number): number {\n if (total === 0) return 0;\n const kept = Math.round(total * fraction);\n return kept < atLeast ? Math.min(atLeast, total) : kept;\n}\n\n/** A minimal binary min-heap over `(node, cost)` — Dijkstra's queue, no dependency. */\nclass MinHeap {\n private readonly nodes: string[] = [];\n private readonly costs: number[] = [];\n\n get size(): number {\n return this.nodes.length;\n }\n\n push(node: string, cost: number): void {\n this.nodes.push(node);\n this.costs.push(cost);\n let i = this.nodes.length - 1;\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if ((this.costs[parent] as number) <= (this.costs[i] as number)) break;\n this.swap(i, parent);\n i = parent;\n }\n }\n\n pop(): { node: string; cost: number } | undefined {\n if (this.nodes.length === 0) return undefined;\n const node = this.nodes[0] as string;\n const cost = this.costs[0] as number;\n const lastNode = this.nodes.pop() as string;\n const lastCost = this.costs.pop() as number;\n if (this.nodes.length > 0) {\n this.nodes[0] = lastNode;\n this.costs[0] = lastCost;\n let i = 0;\n for (;;) {\n const left = i * 2 + 1;\n const right = left + 1;\n let smallest = i;\n const size = this.nodes.length;\n if (left < size && (this.costs[left] as number) < (this.costs[smallest] as number)) {\n smallest = left;\n }\n if (right < size && (this.costs[right] as number) < (this.costs[smallest] as number)) {\n smallest = right;\n }\n if (smallest === i) break;\n this.swap(i, smallest);\n i = smallest;\n }\n }\n return { node, cost };\n }\n\n private swap(a: number, b: number): void {\n const node = this.nodes[a] as string;\n this.nodes[a] = this.nodes[b] as string;\n this.nodes[b] = node;\n const cost = this.costs[a] as number;\n this.costs[a] = this.costs[b] as number;\n this.costs[b] = cost;\n }\n}\n\ninterface Route {\n /** Activities the route uses, including any the reduction had hidden. */\n activities: Set<string>;\n /** Edge keys the route uses. */\n edges: Set<string>;\n}\n\n/**\n * Reduce `graph` to the fraction of activities and paths a reader asked to see.\n *\n * Activities rank by `cases` (how many process instances touch them), ties broken by\n * `instances` then by name; paths rank by `count`, ties broken by `caseCount` then by\n * endpoint names. Ranking on `cases` rather than `instances` is deliberate: an activity\n * executed forty times inside one case is a loop, not a backbone step, and a frequency\n * slider that promoted it would hide the shape of the process.\n *\n * The returned arrays hold the INPUT's own statistic objects, in the input's order.\n * Treat the result as read-only: mutating a returned `ActivityStats` mutates the source\n * graph's, by design — that shared identity is what proves no recomputation happened.\n */\nexport function abstractGraph(graph: ProcessGraph, opts: AbstractionOptions): AbstractedGraph {\n const keepConnected = opts.keepConnected ?? true;\n const invert = opts.invert ?? false;\n const activityFraction = clampFraction(opts.activities);\n const pathFraction = clampFraction(opts.paths);\n\n const rankedActivities = [...graph.activities].sort(\n (a, b) => b.cases - a.cases || b.instances - a.instances || compareStrings(a.id, b.id),\n );\n const activityKeepCount = countToKeep(rankedActivities.length, activityFraction, 1);\n const keptActivities = new Set<string>(\n (invert\n ? rankedActivities.slice(rankedActivities.length - activityKeepCount)\n : rankedActivities.slice(0, activityKeepCount)\n ).map((activity) => activity.id),\n );\n\n const candidateEdges = graph.transitions.filter(\n (edge) => keptActivities.has(edge.source) && keptActivities.has(edge.target),\n );\n const rankedEdges = [...candidateEdges].sort(\n (a, b) =>\n b.count - a.count ||\n b.caseCount - a.caseCount ||\n compareStrings(a.source, b.source) ||\n compareStrings(a.target, b.target),\n );\n const edgeKeepCount = countToKeep(rankedEdges.length, pathFraction, 0);\n const keptEdges = new Set<string>(\n (invert\n ? rankedEdges.slice(rankedEdges.length - edgeKeepCount)\n : rankedEdges.slice(0, edgeKeepCount)\n ).map((edge) => edgeKey(edge.source, edge.target)),\n );\n\n if (keepConnected && keptActivities.size > 0) {\n reconnect(graph, keptActivities, keptEdges);\n }\n\n const activities: ActivityStats[] = graph.activities.filter((activity) =>\n keptActivities.has(activity.id),\n );\n const transitions: TransitionStats[] = graph.transitions.filter((edge) =>\n keptEdges.has(edgeKey(edge.source, edge.target)),\n );\n\n return {\n activities,\n transitions,\n startActivities: pick(graph.startActivities, keptActivities),\n endActivities: pick(graph.endActivities, keptActivities),\n // Passed through by reference: totals describe the LOG, and abstraction is a view.\n totals: graph.totals,\n hidden: {\n activities: graph.activities.length - activities.length,\n paths: graph.transitions.length - transitions.length,\n },\n };\n}\n\n/** Keep the entries of `record` whose key survived, in the record's own key order. */\nfunction pick(record: Record<string, number>, keep: ReadonlySet<string>): Record<string, number> {\n const out: Record<string, number> = {};\n for (const name of Object.keys(record)) {\n if (keep.has(name)) out[name] = record[name] as number;\n }\n return out;\n}\n\n/**\n * Repair connectivity in place, growing `keptActivities` / `keptEdges` as little as it can.\n *\n * For every kept activity that no start activity can reach, the cheapest route from a\n * start activity is found by Dijkstra over edge cost `log(maxCount) − log(count)` — the\n * non-negative form of `−log(count)`, which is what Dijkstra requires and which orders\n * paths identically: minimizing a sum of `−log` weights maximizes the PRODUCT of the edge\n * counts along the route, so the one made of the most-travelled edges wins. Every hidden\n * edge on that route is brought back; a mirrored pass over reversed edges does the same\n * for activities that cannot reach an end activity.\n *\n * The roadmap describes this as re-adding \"the max-weight edge on a shortest path\". One\n * edge is not enough to restore reachability when a route is several hops long, so ALL of\n * the route's hidden edges are re-added — the smallest change that actually holds the\n * invariant the acceptance criterion asserts.\n *\n * Two escalations, both deliberate and both visible through `hidden`:\n *\n * - If NO kept activity is a start (or end) activity, the busiest one is brought back —\n * otherwise there is no anchor to connect to and the whole graph is an island.\n * - If a route exists only through a hidden activity, that activity is brought back too.\n * The alternative is to leave the node stranded. Both escalations only ever ADD, so the\n * guarantee is exactly: an activity connected in `graph` is connected in the result.\n */\nfunction reconnect(graph: ProcessGraph, keptActivities: Set<string>, keptEdges: Set<string>): void {\n const forward = new Map<string, TransitionStats[]>();\n const backward = new Map<string, TransitionStats[]>();\n let maxCount = 1;\n for (const edge of graph.transitions) {\n if (edge.count > maxCount) maxCount = edge.count;\n index(forward, edge.source, edge);\n index(backward, edge.target, edge);\n }\n const logMax = Math.log(maxCount);\n const cost = (count: number): number => (count > 0 ? logMax - Math.log(count) : logMax);\n\n // The two passes are COUPLED: repairing \"can reach an end\" may bring back an activity\n // that nothing reaches from a start, and vice versa. So they run to a fixpoint rather\n // than once each. Both passes only ever ADD, and the sets are bounded by the input\n // graph, so the loop terminates — the bound is belt-and-braces against a future edit\n // that makes a pass able to remove something.\n const bound = graph.activities.length + graph.transitions.length + 2;\n for (let round = 0; round < bound; round += 1) {\n const before = keptActivities.size + keptEdges.size;\n repairDirection(graph.startActivities, forward, true);\n repairDirection(graph.endActivities, backward, false);\n if (keptActivities.size + keptEdges.size === before) break;\n }\n\n function repairDirection(\n seedCounts: Record<string, number>,\n adjacency: Map<string, TransitionStats[]>,\n downstream: boolean,\n ): void {\n const allSeeds = Object.keys(seedCounts);\n if (allSeeds.length === 0) return;\n if (!allSeeds.some((id) => keptActivities.has(id))) anchor(seedCounts, keptActivities);\n const keptSeeds = allSeeds.filter((id) => keptActivities.has(id));\n if (keptSeeds.length === 0) return;\n const reached = spread(keptSeeds, adjacency, downstream);\n\n // A stable, deterministic repair order: the graph's own activity order.\n for (const activity of graph.activities) {\n if (!keptActivities.has(activity.id) || reached.has(activity.id)) continue;\n // Escalate in three steps, cheapest first — kept route, then a route through hidden\n // activities, then a route to an anchor the reduction hid entirely. The third step\n // is what an INVERTED reduction needs: keeping only the rare activities can hide\n // every end activity the backbone actually leads to, and then the only honest repair\n // is to bring one of those back.\n const route =\n shortestRoute(keptSeeds, activity.id, adjacency, downstream, true) ??\n shortestRoute(keptSeeds, activity.id, adjacency, downstream, false) ??\n shortestRoute(allSeeds, activity.id, adjacency, downstream, false);\n if (route === undefined) continue; // genuinely unreachable in the FULL graph too.\n for (const id of route.activities) keptActivities.add(id);\n for (const key of route.edges) keptEdges.add(key);\n for (const id of spread([...route.activities], adjacency, downstream)) reached.add(id);\n }\n }\n\n /** BFS over the KEPT edges only — which nodes the anchors already reach. */\n function spread(\n seeds: readonly string[],\n adjacency: Map<string, TransitionStats[]>,\n downstream: boolean,\n ): Set<string> {\n const seen = new Set<string>(seeds.filter((id) => keptActivities.has(id)));\n const queue = [...seen];\n for (let head = 0; head < queue.length; head += 1) {\n const node = queue[head] as string;\n for (const edge of adjacency.get(node) ?? []) {\n const next = downstream ? edge.target : edge.source;\n if (!keptActivities.has(next)) continue;\n if (!keptEdges.has(edgeKey(edge.source, edge.target))) continue;\n if (seen.has(next)) continue;\n seen.add(next);\n queue.push(next);\n }\n }\n return seen;\n }\n\n /**\n * Cheapest route from any seed to `target`, as the activities and edge keys it uses.\n * `keptOnly` restricts the walk to already-kept activities; the caller retries without\n * it when that fails, which is the second escalation described above.\n */\n function shortestRoute(\n seeds: readonly string[],\n target: string,\n adjacency: Map<string, TransitionStats[]>,\n downstream: boolean,\n keptOnly: boolean,\n ): Route | undefined {\n const distance = new Map<string, number>();\n const previous = new Map<string, TransitionStats>();\n const settled = new Set<string>();\n const heap = new MinHeap();\n for (const seed of seeds) {\n distance.set(seed, 0);\n heap.push(seed, 0);\n }\n\n while (heap.size > 0) {\n const top = heap.pop() as { node: string; cost: number };\n if (settled.has(top.node)) continue;\n settled.add(top.node);\n if (top.node === target) break;\n for (const edge of adjacency.get(top.node) ?? []) {\n const next = downstream ? edge.target : edge.source;\n if (next === top.node) continue; // a self-loop cannot shorten anything.\n if (keptOnly && !keptActivities.has(next)) continue;\n const candidate = top.cost + cost(edge.count);\n const known = distance.get(next);\n // A strict improvement only, so a tie keeps the first route found — and the walk\n // order is the graph's own deterministic edge order.\n if (known !== undefined && known <= candidate) continue;\n distance.set(next, candidate);\n previous.set(next, edge);\n heap.push(next, candidate);\n }\n }\n\n if (!settled.has(target)) return undefined;\n const route: Route = { activities: new Set([target]), edges: new Set() };\n let cursor = target;\n for (;;) {\n const edge = previous.get(cursor);\n if (edge === undefined) break;\n route.edges.add(edgeKey(edge.source, edge.target));\n route.activities.add(edge.source);\n route.activities.add(edge.target);\n cursor = downstream ? edge.source : edge.target;\n }\n return route;\n }\n\n /** Bring back the busiest start/end activity when the reduction kept none of them. */\n function anchor(counts: Record<string, number>, kept: Set<string>): void {\n const names = Object.keys(counts);\n if (names.length === 0) return;\n if (names.some((name) => kept.has(name))) return;\n let best = names[0] as string;\n for (const name of names) {\n const value = counts[name] as number;\n const bestValue = counts[best] as number;\n if (value > bestValue || (value === bestValue && compareStrings(name, best) < 0)) best = name;\n }\n kept.add(best);\n }\n}\n\nfunction index(map: Map<string, TransitionStats[]>, key: string, edge: TransitionStats): void {\n const bucket = map.get(key);\n if (bucket === undefined) map.set(key, [edge]);\n else bucket.push(edge);\n}\n","/**\n * reconcileGraph — merge a FILTERED graph's statistics onto a PRESENTED graph's element set\n * (RM-052 round 2, issue #227, Invariant F).\n *\n * The maintainer's ruling on filtering: keep every process-map step visible, dim the ones a\n * filter excludes — never remove them. Invariant F makes that ruling mechanical: the set of\n * activities and transitions a process map renders is a function of the log and the\n * abstraction alone, never of the active filter intents. Applying or clearing a filter can\n * only change an element's STATE and the statistics it carries; it can never add or remove a\n * node or an edge.\n *\n * `reconcileGraph` is the pure operation that keeps that promise: given the graph a map is\n * about to PRESENT (`presented` — the full, abstracted graph, computed with no filter\n * applied) and the graph the ACTIVE filter actually produced (`filtered`), it returns a graph\n * with `presented`'s exact element set, where every element also present in `filtered` reads\n * `filtered`'s statistics, and every element `filtered` dropped becomes a \"ghost\" — the same\n * id, at zero. A ghost's statistics are the FILTERED ones (zero), not the full-log ones —\n * painting full-log numbers on an excluded element would make filtering cosmetic.\n *\n * `startActivities`, `endActivities` and `totals` come from `filtered` outright (they are\n * graph-level facts about what actually survived the filter, not per-element stats to\n * reconcile). Extra fields the presented graph carries beyond `ProcessGraph` itself (e.g.\n * `AbstractedGraph.hidden`) pass through untouched — `reconcileGraph` never removes what it\n * did not add.\n *\n * Pipeline ordering is load-bearing: abstraction runs on the FULL graph, BEFORE\n * reconciliation with the filtered graph. Reconciling first (running abstraction on an\n * already-filtered graph) would let the zeroed ghosts get treated as least-frequent and\n * deleted by abstraction's own ranking — reinstating the \"filtering removes elements\"\n * behaviour the maintainer ruled against, by a second route.\n */\nimport { emptyDurationStats } from \"./duration-stats\";\nimport { EDGE_KEY_SEPARATOR } from \"./discover-graph\";\nimport type { ProcessGraph, TransitionStats } from \"./types\";\n\n/** The result of {@link reconcileGraph}. */\nexport interface ReconciledGraph<G extends ProcessGraph> {\n /** `presented`'s exact element set, with `filtered`'s statistics where an element survived. */\n graph: G;\n /** Activity ids `presented` carries that `filtered` dropped — rendered as zeroed ghosts. */\n excludedActivities: string[];\n /** Transition keys (`source` + {@link EDGE_KEY_SEPARATOR} + `target`) `filtered` dropped. */\n excludedTransitions: string[];\n}\n\nfunction transitionKey(transition: Pick<TransitionStats, \"source\" | \"target\">): string {\n return `${transition.source}${EDGE_KEY_SEPARATOR}${transition.target}`;\n}\n\n/**\n * Reconcile a presented graph's element set against what a filter actually produced.\n *\n * `G` is generic over `ProcessGraph` so an already-abstracted graph (`AbstractedGraph`, which\n * adds `hidden`) reconciles without losing its extra field — the return type is `G`, and the\n * implementation spreads `presented` before overwriting only the fields this function owns.\n */\nexport function reconcileGraph<G extends ProcessGraph>(\n presented: G,\n filtered: ProcessGraph,\n): ReconciledGraph<G> {\n const filteredActivities = new Map(\n filtered.activities.map((activity) => [activity.id, activity]),\n );\n const filteredTransitions = new Map(\n filtered.transitions.map((transition) => [transitionKey(transition), transition]),\n );\n\n const excludedActivities: string[] = [];\n const activities = presented.activities.map((activity) => {\n const survivor = filteredActivities.get(activity.id);\n if (survivor) return survivor;\n excludedActivities.push(activity.id);\n return {\n ...activity,\n instances: 0,\n cases: 0,\n isStart: false,\n isEnd: false,\n duration: emptyDurationStats(),\n };\n });\n\n const excludedTransitions: string[] = [];\n const transitions = presented.transitions.map((transition) => {\n const key = transitionKey(transition);\n const survivor = filteredTransitions.get(key);\n if (survivor) return survivor;\n excludedTransitions.push(key);\n return {\n ...transition,\n count: 0,\n caseCount: 0,\n duration: emptyDurationStats(),\n };\n });\n\n const graph: G = {\n ...presented,\n activities,\n transitions,\n startActivities: filtered.startActivities,\n endActivities: filtered.endActivities,\n totals: filtered.totals,\n };\n\n return { graph, excludedActivities, excludedTransitions };\n}\n","/**\n * The performance metric layer — RM-050.\n *\n * A process map is read twice: once for FREQUENCY (how often does this happen) and once\n * for PERFORMANCE (how long does it take). The frequency numbers come straight off\n * discovery; the performance numbers need three further decisions that only a reader can\n * make — which aggregate (median, mean, p90 …), which elapsed time an edge measures\n * (waiting or cycle), and which unit the answer is spoken in. This module applies all\n * three to an already-discovered graph and hands back the scalar each node and edge\n * should be painted with.\n *\n * The returned graph is a NEW object graph: unlike {@link abstractGraph}, which filters,\n * this function re-expresses. The input is never mutated.\n */\nimport { discoverGraph } from \"./discover-graph\";\nimport { EDGE_KEY_SEPARATOR } from \"./discover-graph\";\nimport type { AnyLog } from \"./event-log\";\nimport { minMax } from \"./scale\";\nimport type {\n ActivityStats,\n DurationStats,\n FlowTime,\n PerformanceAgg,\n ProcessGraph,\n TransitionStats,\n} from \"./types\";\n\n/** The units a duration can be spoken in. Everything upstream is milliseconds. */\nexport type DurationUnit = \"ms\" | \"s\" | \"min\" | \"h\" | \"d\";\n\n/** How many milliseconds each {@link DurationUnit} is worth. */\nexport const DURATION_UNIT_MS: Readonly<Record<DurationUnit, number>> = Object.freeze({\n ms: 1,\n s: 1_000,\n min: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n});\n\n/** Options for {@link aggregatePerformance}. */\nexport interface PerformanceOptions {\n /** Which member of a {@link DurationStats} the layer reads. */\n agg: PerformanceAgg;\n /**\n * Which elapsed time an edge measures. Honoured only when `log` is supplied — a graph\n * alone cannot be converted from waiting time to cycle time, because the two are not\n * derivable from each other's summary statistics. Without `log` this field DECLARES\n * what the graph already carries and nothing is re-derived.\n */\n flowTime: FlowTime;\n /** The unit every returned duration is expressed in. */\n unit: DurationUnit;\n /**\n * The log the graph came from. Supply it to re-derive edge durations at `flowTime`;\n * pass an already-normalized log (see `asNormalizedLog`) to skip a second parse.\n */\n log?: AnyLog;\n /**\n * Reservoir capacity per activity and per edge when re-deriving. Order statistics\n * (`median`, `p90`, `trimmedMean`) are ESTIMATES past this many samples per key; raise\n * it when a threshold has to be exact. Only meaningful together with `log`.\n */\n maxDurationSamples?: number;\n}\n\n/** The per-element scalars a performance view paints with, plus the domains to scale them on. */\nexport interface PerformanceLayer {\n agg: PerformanceAgg;\n flowTime: FlowTime;\n unit: DurationUnit;\n /** Activity id → the selected aggregate of its execution time, in `unit`. */\n activities: Record<string, number>;\n /** `source␁target` (see `EDGE_KEY_SEPARATOR`) → the selected aggregate, in `unit`. */\n transitions: Record<string, number>;\n /** `[min, max]` over `activities` — `[0, 0]` when there are none. */\n activityDomain: [number, number];\n /** `[min, max]` over `transitions` — `[0, 0]` when there are none. */\n transitionDomain: [number, number];\n}\n\n/** What {@link aggregatePerformance} returns: a `ProcessGraph` plus its metric layer. */\nexport interface PerformanceGraph extends ProcessGraph {\n performance: PerformanceLayer;\n}\n\n/**\n * Read the member of `stats` a {@link PerformanceAgg} names.\n *\n * The one place the mapping lives, so a control, a legend and a renderer cannot disagree\n * about what \"p90\" is. `trimmed_mean` is the snake_case spelling of `trimmedMean` —\n * the type is a wire/URL value, the field is a property name.\n */\nexport function performanceValue(stats: DurationStats, agg: PerformanceAgg): number {\n switch (agg) {\n case \"mean\":\n return stats.mean;\n case \"min\":\n return stats.min;\n case \"max\":\n return stats.max;\n case \"sum\":\n return stats.sum;\n case \"p90\":\n return stats.p90;\n case \"trimmed_mean\":\n return stats.trimmedMean;\n case \"median\":\n default:\n return stats.median;\n }\n}\n\n/** Scale every member of `stats` by `factor`. Returns a new object; `stats` is untouched. */\nfunction scaleStats(stats: DurationStats, factor: number): DurationStats {\n if (factor === 1) return { ...stats };\n return {\n min: stats.min * factor,\n max: stats.max * factor,\n mean: stats.mean * factor,\n median: stats.median * factor,\n p90: stats.p90 * factor,\n sum: stats.sum * factor,\n trimmedMean: stats.trimmedMean * factor,\n };\n}\n\nfunction edgeKey(source: string, target: string): string {\n return `${source}${EDGE_KEY_SEPARATOR}${target}`;\n}\n\n/**\n * Express `graph`'s durations as a performance layer.\n *\n * Every `DurationStats` in the returned graph is converted to `opts.unit`, and\n * `performance` carries the single scalar per activity and per transition that\n * `opts.agg` selects, together with the two domains a scale needs.\n *\n * With `opts.log`, edge and activity durations are RE-DERIVED at `opts.flowTime` from the\n * FULL log — which is what keeps a performance layer honest on an abstracted graph: the\n * numbers describe everything that happened, not only the part currently drawn. Elements\n * the re-derivation does not know about keep the statistics they arrived with.\n *\n * Order statistics past the reservoir cap are estimates (see `maxDurationSamples`);\n * `sum`, `mean`, `min` and `max` are exact at any log size.\n */\nexport function aggregatePerformance(\n graph: ProcessGraph,\n opts: PerformanceOptions,\n): PerformanceGraph {\n const factor = 1 / (DURATION_UNIT_MS[opts.unit] ?? 1);\n\n let activitySource: Map<string, DurationStats> | undefined;\n let transitionSource: Map<string, DurationStats> | undefined;\n if (opts.log !== undefined) {\n const derived = discoverGraph(opts.log, {\n flowTime: opts.flowTime,\n ...(opts.maxDurationSamples === undefined\n ? {}\n : { maxDurationSamples: opts.maxDurationSamples }),\n });\n activitySource = new Map(derived.activities.map((a) => [a.id, a.duration]));\n transitionSource = new Map(\n derived.transitions.map((t) => [edgeKey(t.source, t.target), t.duration]),\n );\n }\n\n const activityValues: Record<string, number> = {};\n const activities: ActivityStats[] = graph.activities.map((activity) => {\n const source = activitySource?.get(activity.id) ?? activity.duration;\n const duration = scaleStats(source, factor);\n activityValues[activity.id] = performanceValue(duration, opts.agg);\n return { ...activity, duration };\n });\n\n const transitionValues: Record<string, number> = {};\n const transitions: TransitionStats[] = graph.transitions.map((edge) => {\n const key = edgeKey(edge.source, edge.target);\n const source = transitionSource?.get(key) ?? edge.duration;\n const duration = scaleStats(source, factor);\n transitionValues[key] = performanceValue(duration, opts.agg);\n return { ...edge, duration };\n });\n\n return {\n activities,\n transitions,\n startActivities: { ...graph.startActivities },\n endActivities: { ...graph.endActivities },\n totals: { ...graph.totals },\n performance: {\n agg: opts.agg,\n flowTime: opts.flowTime,\n unit: opts.unit,\n activities: activityValues,\n transitions: transitionValues,\n activityDomain: minMax(Object.values(activityValues)),\n transitionDomain: minMax(Object.values(transitionValues)),\n },\n };\n}\n","/**\n * Rework detection — RM-050.\n *\n * \"Rework\" is the part of a process that happens more than once inside a single case: a\n * step retried immediately (a SELF-LOOP), or a step the case comes back to after going\n * somewhere else (a LOOP). Both are repeats, and separating them matters — a self-loop is\n * usually a retry or a batch, a loop is usually a rejection sending work backwards, and\n * the two lead to different conversations about the process.\n *\n * Counted per OCCURRENCE, over the normalized trace: the first execution of an activity in\n * a case is never rework, and every later one is exactly one of the two kinds. So\n * `selfLoops + loops` is precisely the number of repeated executions in the log.\n *\n * Deterministic and framework-free, like everything else in `/core`.\n */\nimport { asNormalizedLog, type AnyLog } from \"./event-log\";\n\n/** Per-activity rework tallies. */\nexport interface ActivityRework {\n /** Repeats of this activity immediately after itself. */\n selfLoops: number;\n /** Repeats of this activity later in the same case, with something else in between. */\n loops: number;\n}\n\n/** What {@link detectRework} answers. */\nexport interface ReworkStats {\n /** Adjacent repeats across the whole log. */\n selfLoops: number;\n /** Non-adjacent repeats across the whole log. */\n loops: number;\n /** Fraction of cases carrying at least one self-loop or loop. `0` for an empty log. */\n caseReworkRate: number;\n /**\n * Every activity in the log, in ascending name order, with its own two tallies. An\n * activity that never repeats is present with zeros — a renderer joining this against\n * a graph's activities never has to distinguish \"no rework\" from \"unknown activity\".\n */\n perActivity: Record<string, ActivityRework>;\n}\n\n/**\n * Count rework in `log`.\n *\n * Accepts a raw or an already-normalized log and normalizes at most once\n * ({@link asNormalizedLog} is idempotent), so a caller that has already normalized for\n * `discoverGraph` pays nothing here.\n */\nexport function detectRework(log: AnyLog): ReworkStats {\n const normalized = asNormalizedLog(log);\n\n const tallies = new Map<string, ActivityRework>();\n let selfLoops = 0;\n let loops = 0;\n let casesWithRework = 0;\n\n const seen = new Set<string>();\n for (const kase of normalized.cases) {\n seen.clear();\n let caseHasRework = false;\n let previous: string | undefined;\n\n for (const event of kase.events) {\n const name = event.activity;\n let tally = tallies.get(name);\n if (tally === undefined) {\n tally = { selfLoops: 0, loops: 0 };\n tallies.set(name, tally);\n }\n\n if (seen.has(name)) {\n caseHasRework = true;\n if (name === previous) {\n tally.selfLoops += 1;\n selfLoops += 1;\n } else {\n tally.loops += 1;\n loops += 1;\n }\n } else {\n seen.add(name);\n }\n previous = name;\n }\n\n if (caseHasRework) casesWithRework += 1;\n }\n\n const perActivity: Record<string, ActivityRework> = {};\n for (const name of [...tallies.keys()].sort()) {\n perActivity[name] = tallies.get(name) as ActivityRework;\n }\n\n return {\n selfLoops,\n loops,\n caseReworkRate: normalized.cases.length === 0 ? 0 : casesWithRework / normalized.cases.length,\n perActivity,\n };\n}\n","/**\n * Case-level log filtering — RM-050.\n *\n * Every filter in a process-mining tool is a CASE predicate, not an event predicate:\n * \"cases that contain Reject Order\", \"cases that took longer than a day\". Removing single\n * events would rewrite traces and invent directly-follows relations that never happened,\n * so a filter here only ever keeps or drops a case whole. That is what makes the result\n * safe to hand straight back to `discoverGraph` / `extractVariants`.\n *\n * Specs AND together — an empty list keeps everything.\n */\nimport { asNormalizedLog, type AnyLog, type NormalizedCase, type NormalizedLog } from \"./event-log\";\nimport { variantId } from \"./extract-variants\";\nimport type { EventLog } from \"./types\";\n\n/** One case-level predicate. {@link filterLog} ANDs a list of them. */\nexport type FilterSpec =\n /** The case contains `activity` at least once. */\n | { kind: \"with\"; activity: string }\n /** The case never contains `activity`. */\n | { kind: \"without\"; activity: string }\n /** The case's FIRST activity is `activity`. */\n | { kind: \"startsWith\"; activity: string }\n /** The case's LAST activity is `activity`. */\n | { kind: \"endsWith\"; activity: string }\n /**\n * `b` follows `a` in the case — directly (adjacent) when `direct`, otherwise eventually\n * (anywhere later in the trace).\n */\n | { kind: \"follower\"; a: string; b: string; direct?: boolean }\n /** An attribute comparison — see {@link caseMatchesFilters} for how `key` is resolved. */\n | { kind: \"attribute\"; key: string; op: \"eq\" | \"ne\" | \"gt\" | \"lt\" | \"in\"; value: unknown }\n /** The case's throughput time, in milliseconds. Both bounds are INCLUSIVE. */\n | { kind: \"duration\"; min?: number; max?: number }\n /** The case follows one of these variants (see `variantId`). */\n | { kind: \"variant\"; ids: string[] }\n /** The case is one of these case ids. */\n | { kind: \"cases\"; ids: string[] };\n\n/** The activity sequence of a normalized case. */\nfunction sequenceOf(kase: NormalizedCase): string[] {\n const sequence = new Array<string>(kase.events.length);\n for (let i = 0; i < kase.events.length; i += 1) {\n sequence[i] = (kase.events[i] as { activity: string }).activity;\n }\n return sequence;\n}\n\n/**\n * Every value the case offers for `key`.\n *\n * Case attributes win: when `caseAttributes` defines the key, that single value IS the\n * case's answer. Otherwise the key is looked for on the events — including `resource`,\n * which is a first-class field rather than an entry in `attributes` — and the case offers\n * every distinct value its events carry. So \"cases handled by the credit service\" is\n * expressible without the caller pre-aggregating anything.\n */\nfunction valuesFor(kase: NormalizedCase, key: string): unknown[] {\n const attributes = kase.attributes;\n if (attributes !== undefined && Object.hasOwn(attributes, key)) return [attributes[key]];\n\n const values: unknown[] = [];\n const seen = new Set<unknown>();\n for (const event of kase.events) {\n const value = key === \"resource\" ? event.resource : event.attributes?.[key];\n if (value === undefined) continue;\n if (seen.has(value)) continue;\n seen.add(value);\n values.push(value);\n }\n return values;\n}\n\n/** `>` / `<` over two numbers or two strings; anything else does not compare. */\nfunction ordered(left: unknown, right: unknown, wantGreater: boolean): boolean {\n if (typeof left === \"number\" && typeof right === \"number\") {\n return wantGreater ? left > right : left < right;\n }\n if (typeof left === \"string\" && typeof right === \"string\") {\n return wantGreater ? left > right : left < right;\n }\n return false;\n}\n\nfunction matchesAttribute(\n kase: NormalizedCase,\n spec: Extract<FilterSpec, { kind: \"attribute\" }>,\n): boolean {\n const values = valuesFor(kase, spec.key);\n switch (spec.op) {\n case \"eq\":\n return values.some((value) => Object.is(value, spec.value));\n // `ne` is the negation of `eq`, not \"some value differs\" — otherwise a case offering\n // two resources would satisfy both `eq` and `ne` against the same value.\n case \"ne\":\n return !values.some((value) => Object.is(value, spec.value));\n case \"gt\":\n return values.some((value) => ordered(value, spec.value, true));\n case \"lt\":\n return values.some((value) => ordered(value, spec.value, false));\n case \"in\": {\n if (!Array.isArray(spec.value)) return false;\n const allowed = spec.value as unknown[];\n return values.some((value) => allowed.some((candidate) => Object.is(value, candidate)));\n }\n default:\n return false;\n }\n}\n\nfunction matchesOne(kase: NormalizedCase, sequence: readonly string[], spec: FilterSpec): boolean {\n switch (spec.kind) {\n case \"with\":\n return sequence.includes(spec.activity);\n case \"without\":\n return !sequence.includes(spec.activity);\n case \"startsWith\":\n return sequence[0] === spec.activity;\n case \"endsWith\":\n return sequence[sequence.length - 1] === spec.activity;\n case \"follower\": {\n if (spec.direct === true) {\n for (let i = 0; i + 1 < sequence.length; i += 1) {\n if (sequence[i] === spec.a && sequence[i + 1] === spec.b) return true;\n }\n return false;\n }\n const first = sequence.indexOf(spec.a);\n if (first < 0) return false;\n return sequence.indexOf(spec.b, first + 1) >= 0;\n }\n case \"attribute\":\n return matchesAttribute(kase, spec);\n case \"duration\": {\n if (spec.min === undefined && spec.max === undefined) return true;\n // An unmeasurable case (every timestamp unparseable) cannot satisfy a bound.\n if (!Number.isFinite(kase.duration)) return false;\n if (spec.min !== undefined && kase.duration < spec.min) return false;\n if (spec.max !== undefined && kase.duration > spec.max) return false;\n return true;\n }\n case \"variant\":\n return spec.ids.includes(variantId(sequence));\n case \"cases\":\n return spec.ids.includes(kase.caseId);\n default:\n return true;\n }\n}\n\n/**\n * Does `kase` satisfy every spec?\n *\n * Exported because a case table and a variant list want to HIGHLIGHT what a filter\n * selects as often as they want to remove what it does not.\n */\nexport function caseMatchesFilters(kase: NormalizedCase, specs: readonly FilterSpec[]): boolean {\n if (specs.length === 0) return true;\n const sequence = sequenceOf(kase);\n for (const spec of specs) {\n if (!matchesOne(kase, sequence, spec)) return false;\n }\n return true;\n}\n\n/**\n * Filter a NORMALIZED log — the composable form.\n *\n * This is the one to reach for in a pipeline: it accepts either shape, normalizes at most\n * once, and returns the normalized result, so\n * `discoverGraph(filterNormalizedLog(normalized, specs))` re-parses nothing. `totals` are\n * recomputed over the surviving cases; case order is preserved.\n */\nexport function filterNormalizedLog(log: AnyLog, specs: readonly FilterSpec[]): NormalizedLog {\n const normalized = asNormalizedLog(log);\n if (specs.length === 0) return normalized;\n\n const cases: NormalizedCase[] = [];\n let events = 0;\n for (const kase of normalized.cases) {\n if (!caseMatchesFilters(kase, specs)) continue;\n cases.push(kase);\n events += kase.events.length;\n }\n return { cases, totals: { cases: cases.length, events } };\n}\n\n/**\n * Filter a raw {@link EventLog}, answering a raw {@link EventLog}.\n *\n * Every ROW of a surviving case is kept, in input order, including rows normalization\n * itself discards (an unpaired lifecycle half, say) — so the answer round-trips: it can be\n * re-exported, handed to a different adapter, or re-normalized with different options.\n * `caseAttributes` is narrowed to the surviving cases.\n *\n * Use {@link filterNormalizedLog} instead when the next step is another `/core` derivation;\n * this form necessarily makes the caller normalize again.\n */\nexport function filterLog(log: EventLog, specs: readonly FilterSpec[]): EventLog {\n if (specs.length === 0) return log;\n const kept = new Set<string>();\n for (const kase of filterNormalizedLog(log, specs).cases) kept.add(kase.caseId);\n\n const filtered: EventLog = { events: log.events.filter((row) => kept.has(row?.caseId)) };\n if (log.caseAttributes !== undefined) {\n const caseAttributes: Record<string, Record<string, unknown>> = {};\n for (const caseId of Object.keys(log.caseAttributes)) {\n if (kept.has(caseId)) {\n caseAttributes[caseId] = log.caseAttributes[caseId] as Record<string, unknown>;\n }\n }\n filtered.caseAttributes = caseAttributes;\n }\n return filtered;\n}\n","/**\n * The worker entry point — RM-050.\n *\n * Two things live here, deliberately in one module: the PURE request handler, and the\n * `message` bootstrap that runs it inside a real worker. `createProcessWorker` imports the\n * handler for its inline path, so the code that answers on the main thread and the code\n * that answers off it are the SAME function — parity is structural, not something a test\n * has to keep true.\n *\n * The bootstrap is guarded by a real worker-scope check, so importing this module from the\n * main thread registers nothing.\n */\nimport { discoverGraph, type DiscoverGraphOptions } from \"../discover-graph\";\nimport type { AnyLog } from \"../event-log\";\nimport { extractVariants } from \"../extract-variants\";\nimport type { ProcessGraph, Variant } from \"../types\";\n\n/** What the main thread asks the worker to compute. Structured-cloneable, by construction. */\nexport type ProcessWorkerRequest =\n | { id: number; kind: \"discover\"; log: AnyLog; options?: DiscoverGraphOptions }\n | { id: number; kind: \"variants\"; log: AnyLog };\n\n/** What comes back. `ok: false` carries a message, never an `Error` (which clones poorly). */\nexport type ProcessWorkerResponse =\n | { id: number; ok: true; kind: \"discover\"; graph: ProcessGraph }\n | { id: number; ok: true; kind: \"variants\"; variants: Variant[] }\n | { id: number; ok: false; error: string };\n\n/**\n * Answer one request.\n *\n * Pure and synchronous: the whole point of the worker is that this is the expensive part,\n * and moving it off the main thread is the only thing the worker adds. Throwing is\n * converted to an `ok: false` response so a bad log cannot silently kill the worker and\n * leave every later request hanging.\n */\nexport function handleProcessRequest(request: ProcessWorkerRequest): ProcessWorkerResponse {\n try {\n if (request.kind === \"discover\") {\n return {\n id: request.id,\n ok: true,\n kind: \"discover\",\n graph: discoverGraph(request.log, request.options ?? {}),\n };\n }\n return { id: request.id, ok: true, kind: \"variants\", variants: extractVariants(request.log) };\n } catch (error) {\n return { id: request.id, ok: false, error: describe(error) };\n }\n}\n\nfunction describe(error: unknown): string {\n if (error instanceof Error) return error.message;\n return String(error);\n}\n\n/**\n * Is this module executing INSIDE a worker?\n *\n * `self instanceof WorkerGlobalScope` is the specified test. A duck-type on `postMessage`\n * would be wrong: `window.postMessage` exists on every page, so the main thread would\n * install a listener that answers its own messages.\n */\nfunction inWorkerScope(): boolean {\n const scope = globalThis as { WorkerGlobalScope?: new () => unknown; self?: unknown };\n const constructor = scope.WorkerGlobalScope;\n if (typeof constructor !== \"function\") return false;\n return scope.self instanceof constructor;\n}\n\nif (inWorkerScope()) {\n const scope = self as unknown as {\n addEventListener: (type: \"message\", listener: (event: MessageEvent) => void) => void;\n postMessage: (message: unknown) => void;\n };\n scope.addEventListener(\"message\", (event: MessageEvent) => {\n scope.postMessage(handleProcessRequest(event.data as ProcessWorkerRequest));\n });\n}\n","/**\n * Off-thread discovery — RM-050.\n *\n * Discovering a graph from a raw log is O(events) with a couple of hash lookups per event;\n * on a 200 000-row import that is long enough to drop frames, and a dropped frame during a\n * file import is exactly the moment an app reads as broken. So the derivation moves to a\n * worker — but ONLY as an optimization: the caller never branches on the environment.\n *\n * **Degrade, never fail.** When there is no `Worker` (a server render, a Node test, an\n * embedded webview), when constructing one throws, or when a live one errors, the same\n * functions run inline on the calling thread and the promise still resolves with the same\n * answer. `handleProcessRequest` is literally shared with the worker entry, so \"the inline\n * path agrees with the worker path\" is a property of the code, not of a test.\n *\n * **Normalize once.** Every method takes an `AnyLog`; hand it an already-normalized log\n * (`asNormalizedLog`) and neither side re-parses. A `NormalizedLog` is plain data and\n * clones across `postMessage` unchanged.\n */\nimport type { DiscoverGraphOptions } from \"../discover-graph\";\nimport type { AnyLog } from \"../event-log\";\nimport type { ProcessGraph, Variant } from \"../types\";\nimport {\n handleProcessRequest,\n type ProcessWorkerRequest,\n type ProcessWorkerResponse,\n} from \"./process-worker\";\n\n/**\n * The slice of the `Worker` interface this module uses.\n *\n * Narrow on purpose: it is the seam a test (or a bundler with its own worker construction)\n * substitutes, and demanding the full DOM `Worker` surface for that would be pointless\n * ceremony. A real `Worker` satisfies it.\n */\nexport interface ProcessWorkerLike {\n postMessage(message: unknown): void;\n terminate(): void;\n addEventListener(\n type: \"message\" | \"error\" | \"messageerror\",\n listener: (event: unknown) => void,\n ): void;\n}\n\n/** Options for {@link createProcessWorker}. */\nexport interface CreateProcessWorkerOptions {\n /** Skip the worker entirely and run inline. Useful for benchmarks and for tests. */\n forceInline?: boolean;\n /**\n * Build the worker yourself.\n *\n * The default construction is\n * `new Worker(new URL(\"./process-worker.ts\", import.meta.url), { type: \"module\" })`.\n * A bundler compiling THIS SOURCE (Vite, webpack) rewrites that literal itself; a\n * consumer of the BUILT package gets `dist/core/process-worker.js`, which\n * `tsup.config.ts` emits as its own pass and points the built bundle at — a bundler\n * rewrites the source it compiles, never a `dist` it merely consumes, so that file has\n * to be real. A host that needs its own URL supplies this instead. Throwing from it is\n * safe: the handle falls back to the inline path.\n */\n createWorker?: () => ProcessWorkerLike;\n}\n\n/** What {@link createProcessWorker} hands back. */\nexport interface ProcessWorkerHandle {\n /** Discover a directly-follows graph. */\n discover(log: AnyLog, options?: DiscoverGraphOptions): Promise<ProcessGraph>;\n /** Group the log's cases into variants. */\n variants(log: AnyLog): Promise<Variant[]>;\n /** Stop the worker. Pending promises reject, and so does every later call. */\n terminate(): void;\n /**\n * `true` when work is currently running on the CALLING thread — either because the\n * environment has no worker, or because one failed and the handle degraded.\n */\n readonly inline: boolean;\n}\n\ninterface Pending {\n request: ProcessWorkerRequest;\n settle: (response: ProcessWorkerResponse) => void;\n}\n\nfunction workerConstructible(): boolean {\n return typeof Worker !== \"undefined\" && typeof URL !== \"undefined\";\n}\n\n/**\n * Create a handle that computes off-thread when it can and inline when it cannot.\n *\n * The worker is created LAZILY, on the first request, so constructing a handle costs\n * nothing in an environment that never uses it.\n */\nexport function createProcessWorker(options: CreateProcessWorkerOptions = {}): ProcessWorkerHandle {\n const construct = options.createWorker;\n let inline = options.forceInline === true || (construct === undefined && !workerConstructible());\n let terminated = false;\n let worker: ProcessWorkerLike | undefined;\n let nextId = 0;\n const pending = new Map<number, Pending>();\n\n /** Give up on the worker and answer everything outstanding on this thread. */\n function degrade(): void {\n inline = true;\n const stale = [...pending.values()];\n pending.clear();\n if (worker !== undefined) {\n const dying = worker;\n worker = undefined;\n try {\n dying.terminate();\n } catch {\n // A worker that cannot be terminated is already gone; nothing to recover.\n }\n }\n for (const entry of stale) entry.settle(handleProcessRequest(entry.request));\n }\n\n function onMessage(event: unknown): void {\n const response = (event as { data?: unknown }).data as ProcessWorkerResponse | undefined;\n if (response === undefined || typeof response.id !== \"number\") return;\n const entry = pending.get(response.id);\n if (entry === undefined) return;\n pending.delete(response.id);\n entry.settle(response);\n }\n\n function ensureWorker(): ProcessWorkerLike | undefined {\n if (inline || terminated) return undefined;\n if (worker !== undefined) return worker;\n try {\n worker =\n construct === undefined\n ? (new Worker(new URL(\"./process-worker.ts\", import.meta.url), {\n type: \"module\",\n }) as unknown as ProcessWorkerLike)\n : construct();\n } catch {\n degrade();\n return undefined;\n }\n worker.addEventListener(\"message\", onMessage);\n worker.addEventListener(\"error\", degrade);\n worker.addEventListener(\"messageerror\", degrade);\n return worker;\n }\n\n function send(build: (id: number) => ProcessWorkerRequest): Promise<ProcessWorkerResponse> {\n if (terminated) return Promise.reject(new Error(\"process worker terminated\"));\n nextId += 1;\n const request = build(nextId);\n const active = ensureWorker();\n if (active === undefined) {\n // Inline, but still a microtask later, so the caller's own frame returns first.\n return Promise.resolve().then(() => handleProcessRequest(request));\n }\n return new Promise<ProcessWorkerResponse>((resolve, reject) => {\n pending.set(request.id, { request, settle: resolve });\n try {\n active.postMessage(request);\n } catch {\n pending.delete(request.id);\n degrade();\n try {\n resolve(handleProcessRequest(request));\n } catch (error) {\n reject(error instanceof Error ? error : new Error(String(error)));\n }\n }\n });\n }\n\n function unwrap<T>(response: ProcessWorkerResponse, read: (ok: ProcessWorkerResponse) => T): T {\n if (!response.ok) throw new Error(response.error);\n return read(response);\n }\n\n return {\n async discover(log, discoverOptions) {\n const response = await send((id) =>\n discoverOptions === undefined\n ? { id, kind: \"discover\", log }\n : { id, kind: \"discover\", log, options: discoverOptions },\n );\n return unwrap(response, (ok) => (ok as { graph: ProcessGraph }).graph);\n },\n async variants(log) {\n const response = await send((id) => ({ id, kind: \"variants\", log }));\n return unwrap(response, (ok) => (ok as { variants: Variant[] }).variants);\n },\n terminate() {\n terminated = true;\n const stale = [...pending.values()];\n pending.clear();\n for (const entry of stale) {\n entry.settle({ id: entry.request.id, ok: false, error: \"process worker terminated\" });\n }\n if (worker !== undefined) {\n const dying = worker;\n worker = undefined;\n try {\n dying.terminate();\n } catch {\n // Already gone.\n }\n }\n },\n get inline(): boolean {\n return inline;\n },\n };\n}\n"],"mappings":"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","names":["index","mulberry32","index","MINUTE","mulberry32","RESOURCES","buildTrace","index","compareStrings","edgeKey","sequenceOf"]}
1
+ {"version":3,"sources":["../../src/core/event-log.ts","../../src/core/adapters/flat.ts","../../src/core/adapters/csv.ts","../../src/core/scale.ts","../../src/core/duration-stats.ts","../../src/core/extract-variants.ts","../../src/core/discover-graph.ts","../../src/core/fixtures/synthetic-log.ts","../../src/core/fixtures/generate-bpi-2012-subset.ts","../../src/core/abstract-graph.ts","../../src/core/reconcile-graph.ts","../../src/core/aggregate-performance.ts","../../src/core/detect-rework.ts","../../src/core/filter-log.ts","../../src/core/worker/process-worker.ts","../../src/core/worker/create-process-worker.ts","../../src/core/activity-color-scale.ts","../../src/core/cases-from-log.ts","../../src/core/reference-model.ts","../../src/core/token-replay.ts","../../src/core/conformance.ts","../../src/core/adapters/xes.ts","../../src/core/segments.ts","../../src/core/diff-graphs.ts","../../src/core/replay-timeline.ts","../../src/core/adapters/ocel.ts","../../src/core/discover-object-centric-graph.ts"],"sourcesContent":["/**\n * Event-log normalization — RM-049.\n *\n * Turns a flat `EventRow[]` into per-case traces of INSTANCES: one entry per real\n * activity execution, carrying a resolved `start` and `end` in epoch milliseconds. Every\n * other module in `/core` consumes the normalized form, so the messy parts of a raw log\n * (three timestamp encodings, lifecycle rows split across two lines, rows arriving out of\n * order) are handled exactly once, here.\n *\n * Deterministic: same rows in, same traces out. No `Date.now()`, no randomness.\n */\nimport type { EventLog, EventRow } from \"./types\";\n\n/** One activity execution, with both ends resolved to epoch milliseconds. */\nexport interface NormalizedEvent {\n activity: string;\n /**\n * Start of the execution. Equals `end` for an atomic event — an event with no\n * `lifecycle` pair and no `startTimestamp` has no observed duration, and inventing one\n * would put fabricated numbers into every downstream statistic.\n */\n start: number;\n /** Completion of the execution. */\n end: number;\n /** `end - start`, never negative (a clock-skewed pair is floored at 0). */\n duration: number;\n resource?: string;\n attributes?: Record<string, string | number | boolean | null>;\n /**\n * True when this instance came from a `lifecycle: \"start\"` row that never got a\n * matching `\"complete\"`. Its `end` is provisional (it equals `start`), so a performance\n * view can choose to exclude it.\n */\n isOpen: boolean;\n}\n\n/** One case (process instance): its trace, plus the case's own extent. */\nexport interface NormalizedCase {\n caseId: string;\n /** Instances in ascending `start` order. Never empty. */\n events: NormalizedEvent[];\n /** Earliest `start` in the trace. */\n start: number;\n /** Latest `end` in the trace. */\n end: number;\n /** `end - start` — the case's throughput time. */\n duration: number;\n /** Per-case attributes carried over from `EventLog.caseAttributes`, when present. */\n attributes?: Record<string, unknown>;\n}\n\n/** A normalized log: cases in first-appearance order, plus totals. */\nexport interface NormalizedLog {\n cases: NormalizedCase[];\n totals: { cases: number; events: number };\n}\n\n/** A log in either shape. Every derivation entry point accepts both. */\nexport type AnyLog = EventLog | NormalizedLog;\n\n/** Narrowing predicate — `true` when `log` has already been through {@link normalizeLog}. */\nexport function isNormalizedLog(log: AnyLog): log is NormalizedLog {\n return Array.isArray((log as NormalizedLog).cases);\n}\n\n/** Normalize `log` unless it already is normalized. The idempotent entry point. */\nexport function asNormalizedLog(log: AnyLog): NormalizedLog {\n return isNormalizedLog(log) ? log : normalizeLog(log);\n}\n\n/**\n * Resolve the three accepted timestamp encodings to epoch milliseconds.\n *\n * Returns `NaN` for an unparseable value rather than throwing: one bad cell in a\n * ten-thousand-row CSV must not abort the whole import. Downstream, a `NaN` sorts as\n * equal (so the row keeps its input position) and is dropped from duration samples.\n */\nexport function toEpochMs(value: string | number | Date | undefined | null): number {\n if (value === undefined || value === null) return Number.NaN;\n if (typeof value === \"number\") return Number.isFinite(value) ? value : Number.NaN;\n if (value instanceof Date) return value.getTime();\n const parsed = Date.parse(value);\n if (!Number.isNaN(parsed)) return parsed;\n // A bare numeric string (\"1735725600000\") is a legitimate epoch encoding that\n // `Date.parse` reads as a year in some engines; treat it as epoch ms explicitly.\n const asNumber = Number(value);\n return Number.isFinite(asNumber) && value.trim() !== \"\" ? asNumber : Number.NaN;\n}\n\n/** Stable comparator that leaves `NaN`-timestamped rows in their input order. */\nfunction byTimestamp(a: { at: number }, b: { at: number }): number {\n if (Number.isNaN(a.at) || Number.isNaN(b.at)) return 0;\n return a.at - b.at;\n}\n\ninterface StagedRow {\n at: number;\n row: EventRow;\n}\n\n/**\n * Group rows by case, order each case in time, and pair `lifecycle` rows into instances.\n *\n * The pairing rule, stated precisely because five downstream items depend on it:\n *\n * - Rows are grouped by `caseId` in ONE pass and each case is sorted independently. The\n * whole log is never sorted — sorting 200 000 rows to then split them is the shape that\n * makes a browser import feel broken.\n * - A `lifecycle: \"start\"` row OPENS an instance at its position in the case's timeline.\n * - A `lifecycle: \"complete\"` row CLOSES the oldest still-open instance of the SAME\n * activity, wherever that start sits in the trace. The two rows therefore do not have\n * to be adjacent: interleaved `A-start, B-start, A-complete, B-complete` pairs\n * correctly, and so does a log whose rows arrive in the wrong order entirely, because\n * the per-case sort runs first.\n * - Repeated executions of one activity in a case pair oldest-start-to-earliest-complete\n * (FIFO), which is the only pairing that keeps intervals non-overlapping for a\n * sequential resource.\n * - A `\"complete\"` with no open start becomes an instance in its own right, using an\n * explicit `startTimestamp` when the row carries one.\n * - A `\"start\"` that is never completed stays OPEN: `end === start` and `isOpen` is true.\n * - A row with no `lifecycle` is atomic: `start` comes from `startTimestamp` when given,\n * otherwise from `timestamp` itself.\n *\n * Rows with an empty `caseId` or `activity` are dropped — they cannot take part in a\n * directly-follows relation, and keeping them would put an unnamed node in every graph.\n */\nexport function normalizeLog(log: EventLog): NormalizedLog {\n const staged = new Map<string, StagedRow[]>();\n const order: string[] = [];\n\n for (const row of log.events) {\n if (!row || typeof row.caseId !== \"string\" || row.caseId === \"\") continue;\n if (typeof row.activity !== \"string\" || row.activity === \"\") continue;\n let bucket = staged.get(row.caseId);\n if (bucket === undefined) {\n bucket = [];\n staged.set(row.caseId, bucket);\n order.push(row.caseId);\n }\n bucket.push({ at: toEpochMs(row.timestamp), row });\n }\n\n const cases: NormalizedCase[] = [];\n let events = 0;\n\n for (const caseId of order) {\n const bucket = staged.get(caseId) as StagedRow[];\n // `Array.prototype.sort` is stable (ES2019), so equal timestamps keep input order.\n bucket.sort(byTimestamp);\n\n const instances: NormalizedEvent[] = [];\n /** activity → FIFO queue of indices into `instances` awaiting a `\"complete\"`. */\n const open = new Map<string, number[]>();\n\n for (const { at, row } of bucket) {\n if (row.lifecycle === \"start\") {\n const index = instances.length;\n instances.push(makeInstance(row, at, at, true));\n const queue = open.get(row.activity);\n if (queue === undefined) open.set(row.activity, [index]);\n else queue.push(index);\n continue;\n }\n\n if (row.lifecycle === \"complete\") {\n const queue = open.get(row.activity);\n const index = queue?.shift();\n if (index !== undefined) {\n const instance = instances[index] as NormalizedEvent;\n instance.end = at;\n instance.duration = durationOf(instance.start, at);\n instance.isOpen = false;\n if (instance.resource === undefined && row.resource !== undefined) {\n instance.resource = row.resource;\n }\n if (instance.attributes === undefined && row.attributes !== undefined) {\n instance.attributes = row.attributes;\n }\n continue;\n }\n }\n\n const end = at;\n const explicitStart = toEpochMs(row.startTimestamp);\n const start = Number.isNaN(explicitStart) ? end : explicitStart;\n instances.push(makeInstance(row, start, end, false));\n }\n\n if (instances.length === 0) continue;\n\n // Pairing can move an instance's start earlier than the row that created it, so the\n // trace is re-ordered by resolved start. It is already almost sorted, which is the\n // best case for the engine's TimSort — this is not a second full sort in practice.\n instances.sort(byStart);\n\n let caseStart = Number.POSITIVE_INFINITY;\n let caseEnd = Number.NEGATIVE_INFINITY;\n for (const instance of instances) {\n if (Number.isFinite(instance.start) && instance.start < caseStart) caseStart = instance.start;\n if (Number.isFinite(instance.end) && instance.end > caseEnd) caseEnd = instance.end;\n }\n const hasExtent =\n caseStart !== Number.POSITIVE_INFINITY && caseEnd !== Number.NEGATIVE_INFINITY;\n\n const normalizedCase: NormalizedCase = {\n caseId,\n events: instances,\n start: hasExtent ? caseStart : Number.NaN,\n end: hasExtent ? caseEnd : Number.NaN,\n duration: hasExtent ? durationOf(caseStart, caseEnd) : Number.NaN,\n };\n const caseAttributes = log.caseAttributes?.[caseId];\n if (caseAttributes !== undefined) normalizedCase.attributes = caseAttributes;\n\n cases.push(normalizedCase);\n events += instances.length;\n }\n\n return { cases, totals: { cases: cases.length, events } };\n}\n\nfunction byStart(a: NormalizedEvent, b: NormalizedEvent): number {\n if (Number.isNaN(a.start) || Number.isNaN(b.start)) return 0;\n return a.start - b.start;\n}\n\nfunction durationOf(start: number, end: number): number {\n const delta = end - start;\n if (!Number.isFinite(delta)) return Number.NaN;\n return delta < 0 ? 0 : delta;\n}\n\nfunction makeInstance(row: EventRow, start: number, end: number, isOpen: boolean): NormalizedEvent {\n const instance: NormalizedEvent = {\n activity: row.activity,\n start,\n end,\n duration: durationOf(start, end),\n isOpen,\n };\n if (row.resource !== undefined) instance.resource = row.resource;\n if (row.attributes !== undefined) instance.attributes = row.attributes;\n return instance;\n}\n","/**\n * Flat tabular adapter — RM-049.\n *\n * Pre-shaped tabular data is the lingua franca of event logs: whatever the source (a\n * warehouse query, a spreadsheet export, an API page), it arrives as rows of columns. This\n * adapter maps those columns onto `EventRow`, so every other importer — the CSV reader\n * next door, and any future one — can be a thin front end over this single mapping step\n * rather than a second, subtly different interpretation of the same log.\n *\n * Pure and deterministic: no clock, no I/O, no mutation of the caller's rows.\n */\nimport type { EventLog, EventRow } from \"../types\";\n\n/** One row of a flat source. Values are read defensively — a source may hand back anything. */\nexport type FlatRow = Record<string, unknown>;\n\n/** Which raw cell to read a lifecycle transition's `\"start\"` / `\"complete\"` from. */\nexport interface LifecycleValues {\n start?: readonly string[];\n complete?: readonly string[];\n}\n\n/** Column names that `fromFlatRows` reads. Only `caseId`, `activity` and `timestamp` are required. */\nexport interface FlatRowMapping {\n /** Column holding the case (process instance) id. */\n caseId: string;\n /** Column holding the activity name. */\n activity: string;\n /** Column holding the event timestamp. */\n timestamp: string;\n /** Column holding an explicit interval start, when the source models one. */\n startTimestamp?: string;\n /** Column holding the executing resource. */\n resource?: string;\n /** Column holding a lifecycle transition. */\n lifecycle?: string;\n /** Extra columns copied verbatim into `EventRow.attributes`. */\n attributes?: readonly string[];\n /**\n * Columns copied into `EventLog.caseAttributes`, taking the FIRST non-empty value seen\n * per case. Case attributes are case-level by definition, so a later row disagreeing\n * with an earlier one is a data problem, not something an adapter should silently\n * resolve by overwriting.\n */\n caseAttributes?: readonly string[];\n /**\n * Raw cell values that mean `\"start\"` / `\"complete\"`, compared case-insensitively after\n * trimming. Defaults to {@link DEFAULT_LIFECYCLE_VALUES}; supply your own for a source\n * that spells them differently (`\"S\"` / `\"C\"`, `\"begin\"` / `\"done\"`).\n */\n lifecycleValues?: LifecycleValues;\n}\n\n/** The lifecycle spellings recognized when a mapping does not override them. */\nexport const DEFAULT_LIFECYCLE_VALUES: Required<LifecycleValues> = {\n start: [\"start\", \"started\", \"begin\", \"assign\", \"schedule\"],\n complete: [\"complete\", \"completed\", \"end\", \"finish\", \"finished\", \"done\"],\n};\n\n/** Read a cell as a trimmed string, treating `null`/`undefined`/`\"\"` as absent. */\nfunction readString(row: FlatRow, column: string | undefined): string | undefined {\n if (column === undefined) return undefined;\n const value = row[column];\n if (value === undefined || value === null) return undefined;\n const text = typeof value === \"string\" ? value.trim() : String(value);\n return text === \"\" ? undefined : text;\n}\n\n/** Read a cell as something `toEpochMs` can resolve, without pre-converting it. */\nfunction readTimestamp(\n row: FlatRow,\n column: string | undefined,\n): string | number | Date | undefined {\n if (column === undefined) return undefined;\n const value = row[column];\n if (value === undefined || value === null) return undefined;\n if (value instanceof Date || typeof value === \"number\") return value;\n const text = String(value).trim();\n return text === \"\" ? undefined : text;\n}\n\n/** Coerce a cell into the `attributes` value union; anything else becomes its string form. */\nfunction readAttribute(value: unknown): string | number | boolean | null {\n if (value === null || value === undefined) return null;\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n return value;\n }\n if (value instanceof Date) return value.toISOString();\n return String(value);\n}\n\n/** Map a raw lifecycle cell onto the canonical pair, or `undefined` when it matches neither. */\nexport function normalizeLifecycle(\n raw: string | undefined,\n values: LifecycleValues = DEFAULT_LIFECYCLE_VALUES,\n): \"start\" | \"complete\" | undefined {\n if (raw === undefined) return undefined;\n const needle = raw.trim().toLowerCase();\n if (needle === \"\") return undefined;\n const starts = values.start ?? DEFAULT_LIFECYCLE_VALUES.start;\n const completes = values.complete ?? DEFAULT_LIFECYCLE_VALUES.complete;\n for (const candidate of starts) if (candidate.toLowerCase() === needle) return \"start\";\n for (const candidate of completes) if (candidate.toLowerCase() === needle) return \"complete\";\n return undefined;\n}\n\n/**\n * Map flat rows onto an {@link EventLog}.\n *\n * Rows missing a case id, an activity or a timestamp are SKIPPED rather than throwing: a\n * single blank trailing line or one incomplete record must not fail an entire import, and\n * an event with no activity cannot take part in a directly-follows relation anyway. Count\n * the difference between `rows.length` and `log.events.length` when you need to surface\n * how many were dropped.\n */\nexport function fromFlatRows(rows: readonly FlatRow[], mapping: FlatRowMapping): EventLog {\n const events: EventRow[] = [];\n let caseAttributes: Record<string, Record<string, unknown>> | undefined;\n\n for (const row of rows) {\n if (row === null || typeof row !== \"object\") continue;\n const caseId = readString(row, mapping.caseId);\n const activity = readString(row, mapping.activity);\n const timestamp = readTimestamp(row, mapping.timestamp);\n if (caseId === undefined || activity === undefined || timestamp === undefined) continue;\n\n const event: EventRow = { caseId, activity, timestamp };\n\n const startTimestamp = readTimestamp(row, mapping.startTimestamp);\n if (startTimestamp !== undefined) event.startTimestamp = startTimestamp;\n\n const resource = readString(row, mapping.resource);\n if (resource !== undefined) event.resource = resource;\n\n const lifecycle = normalizeLifecycle(\n readString(row, mapping.lifecycle),\n mapping.lifecycleValues,\n );\n if (lifecycle !== undefined) event.lifecycle = lifecycle;\n\n if (mapping.attributes !== undefined && mapping.attributes.length > 0) {\n const attributes: Record<string, string | number | boolean | null> = {};\n let any = false;\n for (const column of mapping.attributes) {\n if (!(column in row)) continue;\n attributes[column] = readAttribute(row[column]);\n any = true;\n }\n if (any) event.attributes = attributes;\n }\n\n if (mapping.caseAttributes !== undefined && mapping.caseAttributes.length > 0) {\n caseAttributes ??= {};\n const bucket = (caseAttributes[caseId] ??= {});\n for (const column of mapping.caseAttributes) {\n if (bucket[column] !== undefined) continue;\n const value = readString(row, column);\n if (value !== undefined) bucket[column] = value;\n }\n }\n\n events.push(event);\n }\n\n const log: EventLog = { events };\n if (caseAttributes !== undefined) log.caseAttributes = caseAttributes;\n return log;\n}\n","/**\n * CSV adapter — RM-049.\n *\n * A minimal RFC 4180 reader plus a thin front end onto {@link fromFlatRows}. It is written\n * here rather than pulled from npm on purpose: `/core` is the framework-free,\n * worker-safe leaf, and a parser this small (quoted fields, escaped quotes, embedded\n * newlines, CRLF, a BOM) does not justify a runtime dependency in a package a consumer\n * imports into a web worker.\n *\n * What it deliberately does NOT do: type inference, header de-duplication, streaming, or\n * dialect sniffing. A source that needs any of those parses itself and calls\n * {@link fromFlatRows} with the rows.\n */\nimport type { EventLog } from \"../types\";\nimport { fromFlatRows, type FlatRow, type FlatRowMapping } from \"./flat\";\n\n/** Options for {@link parseDelimited} and {@link fromCsv}. */\nexport interface CsvOptions {\n /** Field separator. Defaults to `\",\"`; pass `\"\\t\"` for TSV, `\";\"` for a European export. */\n delimiter?: string;\n /**\n * Column names, when the text has NO header row. Omit for the usual case, where the\n * first record is the header.\n */\n header?: readonly string[];\n}\n\n/** Everything `fromCsv` needs: the column mapping plus the dialect. */\nexport type CsvMapping = FlatRowMapping & CsvOptions;\n\nconst QUOTE = '\"';\nconst CR = \"\\r\";\nconst LF = \"\\n\";\n\n/**\n * Parse RFC 4180 delimited text into records of raw string fields.\n *\n * Handles: quoted fields; a doubled `\"\"` inside a quoted field as one literal quote;\n * delimiters, `CR`, `LF` and `CRLF` inside a quoted field; `CRLF` or bare `LF` record\n * separators; a leading UTF-8 BOM; and a trailing newline (which does NOT produce a\n * phantom empty record).\n *\n * A field is returned exactly as written, without trimming — trimming is a MAPPING\n * decision, and `fromFlatRows` makes it. Unterminated quotes are tolerated: the field runs\n * to the end of the text rather than throwing, so a truncated download yields the rows it\n * did contain.\n */\nexport function parseDelimited(text: string, delimiter = \",\"): string[][] {\n const source = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;\n const records: string[][] = [];\n let record: string[] = [];\n let field = \"\";\n let quoted = false;\n let dirty = false; // has anything been read into the current record at all?\n const delimiterChar = delimiter.length > 0 ? delimiter[0] : \",\";\n\n const endField = (): void => {\n record.push(field);\n field = \"\";\n dirty = true;\n };\n const endRecord = (): void => {\n endField();\n records.push(record);\n record = [];\n dirty = false;\n };\n\n for (let i = 0; i < source.length; i += 1) {\n const char = source[i] as string;\n\n if (quoted) {\n if (char === QUOTE) {\n if (source[i + 1] === QUOTE) {\n field += QUOTE;\n i += 1;\n } else {\n quoted = false;\n }\n } else {\n field += char;\n }\n continue;\n }\n\n if (char === QUOTE && field === \"\") {\n quoted = true;\n dirty = true;\n continue;\n }\n if (char === delimiterChar) {\n endField();\n continue;\n }\n if (char === CR) {\n if (source[i + 1] === LF) i += 1;\n endRecord();\n continue;\n }\n if (char === LF) {\n endRecord();\n continue;\n }\n field += char;\n dirty = true;\n }\n\n if (field !== \"\" || dirty) endRecord();\n return records;\n}\n\n/** Turn parsed records into objects keyed by `header`. Missing trailing cells read as `\"\"`. */\nexport function toObjects(records: readonly string[][], header: readonly string[]): FlatRow[] {\n const rows: FlatRow[] = [];\n for (const record of records) {\n const row: FlatRow = {};\n for (let i = 0; i < header.length; i += 1) {\n row[header[i] as string] = record[i] ?? \"\";\n }\n rows.push(row);\n }\n return rows;\n}\n\n/**\n * Read delimited text into an {@link EventLog}.\n *\n * The first record is the header unless `mapping.header` supplies one. Column names in\n * the header are trimmed (a `\", activity\"` from a hand-edited export still matches\n * `activity`); field VALUES are not, so `fromFlatRows` stays the only place that decides\n * what an empty cell means.\n *\n * Empty text yields an empty log rather than throwing.\n */\nexport function fromCsv(text: string, mapping: CsvMapping): EventLog {\n const records = parseDelimited(text, mapping.delimiter ?? \",\");\n let header = mapping.header;\n let body = records;\n if (header === undefined) {\n const first = records[0];\n if (first === undefined) return { events: [] };\n header = first.map((name) => name.trim());\n body = records.slice(1);\n }\n return fromFlatRows(toObjects(body, header), mapping);\n}\n","/**\n * Visual-scale helpers shared by every process view — RM-049.\n *\n * One implementation, so RM-051's edge width, RM-054's coverage bar and RM-050's\n * abstraction threshold all agree on what \"the 90th percentile\" and \"map this count to a\n * stroke width\" mean. Re-deriving a quantile per component is how two views end up\n * disagreeing about the same log.\n *\n * All three are pure and total: they never throw, and they answer for the degenerate\n * inputs (empty array, zero-width domain) rather than returning `NaN`.\n */\n\n/** Ascending numeric comparator. Extracted so every sort in `/core` uses the same one. */\nexport function ascending(a: number, b: number): number {\n return a - b;\n}\n\n/** Only finite samples take part in a scale; `NaN`/`Infinity` are dropped, not propagated. */\nfunction finiteOnly(values: readonly number[]): number[] {\n const out: number[] = [];\n for (const v of values) if (Number.isFinite(v)) out.push(v);\n return out;\n}\n\n/**\n * The extent of `values`, ignoring non-finite entries.\n *\n * @returns `[min, max]`, or `[0, 0]` when there is nothing finite to measure.\n */\nexport function minMax(values: readonly number[]): [number, number] {\n let min = Number.POSITIVE_INFINITY;\n let max = Number.NEGATIVE_INFINITY;\n let seen = false;\n for (const v of values) {\n if (!Number.isFinite(v)) continue;\n seen = true;\n if (v < min) min = v;\n if (v > max) max = v;\n }\n return seen ? [min, max] : [0, 0];\n}\n\n/**\n * The `q`-quantile of an ALREADY ASCENDING-SORTED, all-finite array, by linear\n * interpolation between order statistics (the R-7 / `d3.quantile` definition).\n *\n * Exported for callers that already hold a sorted array — {@link durationStats} sorts\n * once and reads several quantiles off it. Use {@link quantile} when the input is not\n * known to be sorted.\n */\nexport function quantileSorted(sorted: readonly number[], q: number): number {\n const n = sorted.length;\n if (n === 0) return 0;\n if (n === 1) return sorted[0] as number;\n const clamped = q < 0 ? 0 : q > 1 ? 1 : q;\n const pos = clamped * (n - 1);\n const lo = Math.floor(pos);\n const hi = Math.ceil(pos);\n const loValue = sorted[lo] as number;\n if (lo === hi) return loValue;\n const hiValue = sorted[hi] as number;\n return loValue + (hiValue - loValue) * (pos - lo);\n}\n\n/**\n * The `q`-quantile of `values` (0 ≤ `q` ≤ 1; out-of-range values are clamped).\n *\n * Sorts a COPY, so the caller's array is untouched. Non-finite entries are dropped;\n * an empty (or all-non-finite) input answers `0`.\n */\nexport function quantile(values: readonly number[], q: number): number {\n return quantileSorted(finiteOnly(values).sort(ascending), q);\n}\n\n/**\n * Map `value` from `domain` onto `range`, clamped to the range's endpoints.\n *\n * The workhorse behind \"frequency → stroke width\" and \"share → bar length\". A\n * DEGENERATE domain (`d0 === d1`, e.g. a graph whose every edge has the same count)\n * answers `range[0]`: with a zero-width domain every input sits at the domain floor, so\n * the range floor is the consistent answer — and it is what the clamped interpolation\n * gives for any `value <= d0`. A caller that wants a different neutral (the widest\n * stroke, say) special-cases it explicitly rather than relying on a surprise here.\n *\n * A descending domain (`d0 > d1`) is honoured — it simply inverts the mapping.\n */\nexport function clampWidth(\n value: number,\n domain: readonly [number, number],\n range: readonly [number, number],\n): number {\n const [d0, d1] = domain;\n const [r0, r1] = range;\n if (!Number.isFinite(value)) return r0;\n const span = d1 - d0;\n if (span === 0) return r0;\n const t = (value - d0) / span;\n const clamped = t < 0 ? 0 : t > 1 ? 1 : t;\n return r0 + (r1 - r0) * clamped;\n}\n","/**\n * Duration summary statistics — RM-049.\n *\n * The single implementation every part of the core reads: per-activity execution time,\n * per-edge flow time and per-variant case time all pass through here, which is what makes\n * \"the variant's median agrees with the edge's median on the same fixture\" true by\n * construction rather than by coincidence.\n *\n * Algorithm shape (which statistics a discovered graph carries per activity and per edge)\n * follows pm4js — see `ATTRIBUTION.md`. No pm4js code is copied; the arithmetic below is\n * the ordinary textbook definition.\n */\nimport { ascending, quantileSorted } from \"./scale\";\nimport type { DurationStats } from \"./types\";\n\n/** The fraction discarded from EACH tail by `trimmedMean`. */\nexport const TRIM_FRACTION = 0.1;\n\n/** All-zero statistics — the answer for an empty sample set. */\nexport const EMPTY_DURATION_STATS: Readonly<DurationStats> = Object.freeze({\n min: 0,\n max: 0,\n mean: 0,\n median: 0,\n p90: 0,\n sum: 0,\n trimmedMean: 0,\n});\n\n/** A fresh mutable copy of {@link EMPTY_DURATION_STATS}. */\nexport function emptyDurationStats(): DurationStats {\n return { ...EMPTY_DURATION_STATS };\n}\n\n/**\n * Summarize `samples` (milliseconds) as {@link DurationStats}.\n *\n * - Non-finite samples are DROPPED, not propagated — one unparseable timestamp upstream\n * must not turn an entire activity's statistics into `NaN`.\n * - The input array is not mutated (a copy is sorted).\n * - `p90` uses linear interpolation between order statistics (R-7 / `d3.quantile`).\n * - `trimmedMean` discards the lowest and highest {@link TRIM_FRACTION} of samples. When\n * trimming would leave nothing (fewer than 5 samples, where `floor(n * 0.1)` is 0 or\n * the tails meet), it degrades to the plain mean rather than to `NaN`.\n * - An empty (or all-non-finite) input answers all zeros.\n */\nexport function durationStats(samples: readonly number[]): DurationStats {\n const sorted: number[] = [];\n for (const s of samples) if (Number.isFinite(s)) sorted.push(s);\n const n = sorted.length;\n if (n === 0) return emptyDurationStats();\n sorted.sort(ascending);\n\n let sum = 0;\n for (const s of sorted) sum += s;\n\n const trim = Math.floor(n * TRIM_FRACTION);\n const lo = trim;\n const hi = n - trim;\n let trimmedMean: number;\n if (hi - lo <= 0) {\n trimmedMean = sum / n;\n } else {\n let trimmedSum = 0;\n for (let i = lo; i < hi; i += 1) trimmedSum += sorted[i] as number;\n trimmedMean = trimmedSum / (hi - lo);\n }\n\n return {\n min: sorted[0] as number,\n max: sorted[n - 1] as number,\n mean: sum / n,\n median: quantileSorted(sorted, 0.5),\n p90: quantileSorted(sorted, 0.9),\n sum,\n trimmedMean,\n };\n}\n\n/** Default reservoir capacity for a single activity's or edge's duration samples. */\nexport const DURATION_SAMPLE_CAP = 4096;\n\n/**\n * A deterministic 32-bit PRNG (mulberry32). Same seed, same stream — which is the whole\n * point: the reservoir below must make identical keep/discard decisions on every run, in\n * every engine, or two runs over one log would produce different `p90`s.\n */\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Bounded duration-sample collector.\n *\n * A log with a million events would otherwise hold a million numbers per hot edge just to\n * compute seven statistics from them. The first {@link DURATION_SAMPLE_CAP} samples are\n * kept outright; past the cap the collector switches to reservoir sampling (Algorithm R),\n * so the retained set stays a uniform sample of everything seen while memory stays flat.\n *\n * `count` keeps the TRUE number of samples offered, and `sum` the true total, so a\n * capped edge still reports an exact `sum` and an exact `mean`; only the order statistics\n * (`median`, `p90`, `min`, `max`, `trimmedMean`) are estimated from the reservoir.\n */\nexport class DurationSampler {\n private readonly capacity: number;\n private readonly random: () => number;\n private readonly reservoir: number[] = [];\n /** Number of finite samples offered, including those the reservoir discarded. */\n private seen = 0;\n private total = 0;\n private lowest = Number.POSITIVE_INFINITY;\n private highest = Number.NEGATIVE_INFINITY;\n\n constructor(seed = 0x9e3779b9, capacity: number = DURATION_SAMPLE_CAP) {\n this.capacity = capacity > 0 ? capacity : 1;\n this.random = mulberry32(seed);\n }\n\n /** Offer one sample. Non-finite values are ignored. */\n add(sample: number): void {\n if (!Number.isFinite(sample)) return;\n this.total += sample;\n if (sample < this.lowest) this.lowest = sample;\n if (sample > this.highest) this.highest = sample;\n if (this.reservoir.length < this.capacity) {\n this.reservoir.push(sample);\n } else {\n const j = Math.floor(this.random() * (this.seen + 1));\n if (j < this.capacity) this.reservoir[j] = sample;\n }\n this.seen += 1;\n }\n\n /** True number of finite samples offered. */\n get size(): number {\n return this.seen;\n }\n\n /**\n * Summarize what was collected. `sum`, `mean`, `min` and `max` are EXACT even past the\n * cap (they are accumulated, not read off the reservoir); the remaining three are\n * computed from the retained sample.\n */\n stats(): DurationStats {\n if (this.seen === 0) return emptyDurationStats();\n const fromReservoir = durationStats(this.reservoir);\n return {\n ...fromReservoir,\n min: this.lowest,\n max: this.highest,\n sum: this.total,\n mean: this.total / this.seen,\n };\n }\n}\n","/**\n * Variant extraction — RM-049.\n *\n * A \"variant\" is one distinct activity sequence, together with every case that follows\n * it. Grouping traces by their sequence and ranking those groups by frequency is the\n * standard second view onto a log (the first being the directly-follows graph), and the\n * grouping shape below follows pm4js; see `ATTRIBUTION.md`. No pm4js code is copied.\n *\n * Deterministic: ids are hashes of the sequence, ordering is fully tie-broken, and the\n * cumulative share is computed from counts rather than by summing floats, so it is exactly\n * `1` on the last variant instead of `0.9999999999999998`.\n */\nimport { durationStats } from \"./duration-stats\";\nimport { asNormalizedLog, type AnyLog, type NormalizedCase } from \"./event-log\";\nimport type { Variant } from \"./types\";\n\n/**\n * Separator joining a sequence into its variant key.\n *\n * U+0001 (START OF HEADING) is a C0 control character. No real activity name contains\n * one, which is what makes joining on it lossless where a printable separator\n * (`\" -> \"`, `\",\"`) would collide with a name that happens to contain the separator.\n */\nexport const VARIANT_KEY_SEPARATOR = \"\\u0001\";\n\n/** Join an activity sequence into its canonical variant key. */\nexport function variantKey(sequence: readonly string[]): string {\n return sequence.join(VARIANT_KEY_SEPARATOR);\n}\n\n/** FNV-1a, 32-bit. Small, fast, dependency-free and identical in every engine. */\nfunction fnv1a32(text: string, basis: number): number {\n let hash = basis >>> 0;\n for (let i = 0; i < text.length; i += 1) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash >>> 0;\n}\n\nfunction hex8(value: number): string {\n return (value >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * The stable id for an activity sequence: `v` followed by 64 bits of FNV-1a over the\n * variant key, as hex.\n *\n * Exported because a selection is a wave-wide concern — RM-052's variant explorer and\n * RM-053's case table both need to name the same variant without holding the object, and\n * a hash of the sequence is the only identifier that survives a re-run, a worker boundary\n * and a URL.\n */\nexport function variantId(sequence: readonly string[]): string {\n const key = variantKey(sequence);\n return `v${hex8(fnv1a32(key, 0x811c9dc5))}${hex8(fnv1a32(key, 0x84222325))}`;\n}\n\ninterface VariantAccumulator {\n key: string;\n sequence: string[];\n caseIds: string[];\n durations: number[];\n}\n\n/**\n * Group `log`'s cases into variants, ranked by frequency.\n *\n * Ordering is `count` descending, ties broken by the variant key ascending — a total\n * order, so the same log always produces the same array in the same positions.\n *\n * `share` is `count / cases`. `cumulativeShare` is the running `cumulativeCount / cases`,\n * which is monotonically non-decreasing by construction and exactly `1` on the last\n * entry. `duration` summarizes the END-TO-END case durations of the variant's cases, so\n * it answers \"how long does this path take\", not \"how long does one step take\".\n *\n * An empty log yields an empty array — never a single zero-count variant.\n */\nexport function extractVariants(log: AnyLog): Variant[] {\n const normalized = asNormalizedLog(log);\n const totalCases = normalized.cases.length;\n if (totalCases === 0) return [];\n\n const groups = new Map<string, VariantAccumulator>();\n for (const kase of normalized.cases) {\n const sequence = sequenceOf(kase);\n const key = variantKey(sequence);\n let group = groups.get(key);\n if (group === undefined) {\n group = { key, sequence, caseIds: [], durations: [] };\n groups.set(key, group);\n }\n group.caseIds.push(kase.caseId);\n group.durations.push(kase.duration);\n }\n\n const ranked = [...groups.values()].sort(\n (a, b) => b.caseIds.length - a.caseIds.length || (a.key < b.key ? -1 : a.key > b.key ? 1 : 0),\n );\n\n const taken = new Set<string>();\n const variants: Variant[] = [];\n let cumulativeCount = 0;\n\n for (const group of ranked) {\n const count = group.caseIds.length;\n cumulativeCount += count;\n // A 64-bit hash collision between two different sequences is vanishingly unlikely,\n // but \"vanishingly unlikely\" is not \"impossible\" and a duplicate id would silently\n // merge two rows in a table. Disambiguate deterministically: `ranked` is totally\n // ordered, so the suffix an id receives is the same on every run.\n let id = variantId(group.sequence);\n if (taken.has(id)) {\n let suffix = 1;\n while (taken.has(`${id}-${suffix}`)) suffix += 1;\n id = `${id}-${suffix}`;\n }\n taken.add(id);\n\n variants.push({\n id,\n sequence: group.sequence,\n count,\n share: count / totalCases,\n cumulativeShare: cumulativeCount / totalCases,\n caseIds: group.caseIds,\n duration: durationStats(group.durations),\n });\n }\n\n return variants;\n}\n\nfunction sequenceOf(kase: NormalizedCase): string[] {\n const sequence = new Array<string>(kase.events.length);\n for (let i = 0; i < kase.events.length; i += 1) {\n sequence[i] = (kase.events[i] as { activity: string }).activity;\n }\n return sequence;\n}\n","/**\n * Directly-follows graph discovery — RM-049.\n *\n * The directly-follows abstraction (one node per activity, one edge per adjacent pair in\n * a trace, with frequency and performance aggregates on both) is the standard starting\n * point of every process-mining view. The shape of what a discovered graph carries —\n * per-activity instance and case counts, per-edge count and case count, start/end\n * activity tallies — follows pm4js; see `ATTRIBUTION.md`. No pm4js code is copied, and\n * nothing here derives from the AGPL Python reference implementation.\n *\n * Deterministic and layout-free: `isBackEdge` is always `false` here, because whether an\n * edge points backwards is a property of a LAYOUT, not of a log. A layout pass sets it.\n */\nimport { DURATION_SAMPLE_CAP, DurationSampler } from \"./duration-stats\";\nimport { asNormalizedLog, type AnyLog, type NormalizedEvent } from \"./event-log\";\nimport { VARIANT_KEY_SEPARATOR } from \"./extract-variants\";\nimport type { ActivityStats, FlowTime, ProcessGraph, TransitionStats } from \"./types\";\n\n/**\n * Separator used to key an edge. The same C0 control character the variant key uses, and\n * for the same reason: no activity name can contain it, so `source + SEP + target` is an\n * injective key.\n */\nexport const EDGE_KEY_SEPARATOR = VARIANT_KEY_SEPARATOR;\n\n/** Options for {@link discoverGraph}. Every field has a default; `discoverGraph(log)` works. */\nexport interface DiscoverGraphOptions {\n /**\n * Which elapsed time an edge measures. Defaults to `\"idle_time\"` — the wait between the\n * source completing and the target starting, which is the number a bottleneck view\n * wants. For a log of atomic events the two choices coincide.\n */\n flowTime?: FlowTime;\n /**\n * Reservoir capacity per activity and per edge. Lower it for a very wide log; raise it\n * for sharper tail statistics. `sum`/`mean`/`min`/`max` stay exact regardless.\n */\n maxDurationSamples?: number;\n}\n\ninterface ActivityAccumulator {\n instances: number;\n cases: number;\n duration: DurationSampler;\n}\n\ninterface TransitionAccumulator {\n source: string;\n target: string;\n count: number;\n caseCount: number;\n duration: DurationSampler;\n}\n\n/**\n * Derive a {@link ProcessGraph} from a raw or already-normalized log.\n *\n * One pass per trace: each case's activity sequence is walked once, incrementing\n * per-activity and per-edge counters and feeding two duration samplers. Per-case\n * uniqueness (the `cases` and `caseCount` fields) is tracked with two `Set`s that are\n * cleared per case rather than a per-key `Set` of case ids, so memory stays proportional\n * to the widest trace, not to the log.\n *\n * `totals.variants` is counted from the same walk (the joined activity sequence goes into\n * a `Set`), so a caller that only needs the headline number does not also have to run\n * `extractVariants`.\n */\nexport function discoverGraph(log: AnyLog, options: DiscoverGraphOptions = {}): ProcessGraph {\n const flowTime: FlowTime = options.flowTime ?? \"idle_time\";\n const capacity = options.maxDurationSamples ?? DURATION_SAMPLE_CAP;\n const normalized = asNormalizedLog(log);\n\n const activities = new Map<string, ActivityAccumulator>();\n const transitions = new Map<string, TransitionAccumulator>();\n const startActivities = new Map<string, number>();\n const endActivities = new Map<string, number>();\n const variantKeys = new Set<string>();\n\n // Sampler seeds come from a creation counter, never from a clock or from entropy: the\n // iteration order over a given log is fixed, so the same log always seeds the same\n // samplers and the reservoir retains the same rows.\n let samplerIndex = 0;\n const nextSeed = (): number => {\n samplerIndex += 1;\n return (0x9e3779b9 ^ Math.imul(samplerIndex, 0x9e3779b1)) >>> 0;\n };\n\n let events = 0;\n const seenActivities = new Set<string>();\n const seenEdges = new Set<string>();\n\n for (const kase of normalized.cases) {\n const trace = kase.events;\n if (trace.length === 0) continue;\n events += trace.length;\n seenActivities.clear();\n seenEdges.clear();\n\n const sequence = new Array<string>(trace.length);\n\n for (let i = 0; i < trace.length; i += 1) {\n const event = trace[i] as NormalizedEvent;\n const name = event.activity;\n sequence[i] = name;\n\n let activity = activities.get(name);\n if (activity === undefined) {\n activity = { instances: 0, cases: 0, duration: new DurationSampler(nextSeed(), capacity) };\n activities.set(name, activity);\n }\n activity.instances += 1;\n activity.duration.add(event.duration);\n if (!seenActivities.has(name)) {\n seenActivities.add(name);\n activity.cases += 1;\n }\n\n if (i === 0) continue;\n const previous = trace[i - 1] as NormalizedEvent;\n const source = previous.activity;\n const key = `${source}${EDGE_KEY_SEPARATOR}${name}`;\n let edge = transitions.get(key);\n if (edge === undefined) {\n edge = {\n source,\n target: name,\n count: 0,\n caseCount: 0,\n duration: new DurationSampler(nextSeed(), capacity),\n };\n transitions.set(key, edge);\n }\n edge.count += 1;\n edge.duration.add(\n flowTime === \"inter_start_time\" ? event.start - previous.start : event.start - previous.end,\n );\n if (!seenEdges.has(key)) {\n seenEdges.add(key);\n edge.caseCount += 1;\n }\n }\n\n const first = trace[0] as NormalizedEvent;\n const last = trace[trace.length - 1] as NormalizedEvent;\n startActivities.set(first.activity, (startActivities.get(first.activity) ?? 0) + 1);\n endActivities.set(last.activity, (endActivities.get(last.activity) ?? 0) + 1);\n variantKeys.add(sequence.join(VARIANT_KEY_SEPARATOR));\n }\n\n const activityList: ActivityStats[] = [];\n for (const [id, accumulator] of activities) {\n activityList.push({\n id,\n label: id,\n instances: accumulator.instances,\n cases: accumulator.cases,\n isStart: startActivities.has(id),\n isEnd: endActivities.has(id),\n duration: accumulator.duration.stats(),\n });\n }\n // Deterministic, reader-friendly order: busiest first, ties broken by name, so the same\n // log always yields the same array in the same positions (downstream items snapshot it).\n activityList.sort((a, b) => b.instances - a.instances || compareStrings(a.id, b.id));\n\n const transitionList: TransitionStats[] = [];\n for (const accumulator of transitions.values()) {\n transitionList.push({\n source: accumulator.source,\n target: accumulator.target,\n count: accumulator.count,\n caseCount: accumulator.caseCount,\n duration: accumulator.duration.stats(),\n isSelfLoop: accumulator.source === accumulator.target,\n isBackEdge: false,\n });\n }\n transitionList.sort(\n (a, b) =>\n b.count - a.count || compareStrings(a.source, b.source) || compareStrings(a.target, b.target),\n );\n\n return {\n activities: activityList,\n transitions: transitionList,\n startActivities: toSortedRecord(startActivities),\n endActivities: toSortedRecord(endActivities),\n totals: { cases: normalized.cases.length, events, variants: variantKeys.size },\n };\n}\n\n/** Code-unit comparison — locale-independent, so the order is the same on every machine. */\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\n/** Key order is part of the output, so it is sorted rather than left to insertion order. */\nfunction toSortedRecord(counts: Map<string, number>): Record<string, number> {\n const out: Record<string, number> = {};\n for (const key of [...counts.keys()].sort()) out[key] = counts.get(key) as number;\n return out;\n}\n","/**\n * Seeded synthetic event-log generator — RM-049.\n *\n * Produces an order-to-cash shaped log — a happy path, two exception branches, a rework\n * loop back to an earlier activity, an occasional repeat of one step, and a step-order\n * swap — at any size, from a seed. That is what lets tests, stories and benchmarks work\n * with a realistically messy log without this repo ever shipping a large binary fixture,\n * and without any of them depending on a wall clock.\n *\n * Determinism is the whole contract: `generateSyntheticLog({ cases: n, seed: s })` returns\n * a byte-identical log on every run, in every engine, forever. Downstream items snapshot\n * against it.\n */\nimport type { EventLog, EventRow } from \"../types\";\n\n/** Epoch ms the first case starts at: 2026-01-05T08:00:00.000Z, a Monday morning. */\nexport const SYNTHETIC_LOG_EPOCH = 1767600000000;\n\nconst MINUTE = 60_000;\n\n/** Options for {@link generateSyntheticLog}. */\nexport interface SyntheticLogOptions {\n /** How many cases to generate. Values below 1 yield an empty log. */\n cases: number;\n /** PRNG seed. The same seed always produces the same log. */\n seed?: number;\n /** Epoch ms the first case starts at. Defaults to {@link SYNTHETIC_LOG_EPOCH}. */\n startTime?: number;\n}\n\n/**\n * A deterministic 32-bit PRNG (mulberry32).\n *\n * Deliberately its own copy rather than one shared with the core's duration sampler: this\n * generator's output is a FIXTURE that tests assert exact numbers against, so it must not\n * shift because an unrelated internal in `duration-stats.ts` was retuned.\n */\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\nconst RESOURCES = [\n \"A. Novak\",\n \"B. Ferreira\",\n \"C. Okafor\",\n \"D. Lindqvist\",\n \"E. Haddad\",\n \"Credit Service\",\n \"Warehouse Robot\",\n] as const;\n\nconst REGIONS = [\"North\", \"South\", \"East\", \"West\"] as const;\nconst SEGMENTS = [\"Enterprise\", \"Mid-market\", \"SMB\"] as const;\n\n/** Activity vocabulary. Exported so a story can label or colour by name without guessing. */\nexport const SYNTHETIC_ACTIVITIES = [\n \"Create Order\",\n \"Check Credit\",\n \"Amend Order\",\n \"Approve Order\",\n \"Reject Order\",\n \"Reserve Stock\",\n \"Cancel Order\",\n \"Pick Items\",\n \"Ship Order\",\n \"Send Invoice\",\n \"Receive Payment\",\n] as const;\n\n/** One activity name from {@link SYNTHETIC_ACTIVITIES}. */\nexport type SyntheticActivity = (typeof SYNTHETIC_ACTIVITIES)[number];\n\n/** Build the activity sequence of a single case. Pure given `random`. */\nfunction buildTrace(random: () => number): SyntheticActivity[] {\n const trace: SyntheticActivity[] = [\"Create Order\", \"Check Credit\"];\n\n // Rework: an amendment sends the order back through the credit check, at most twice.\n let reworks = 0;\n while (reworks < 2 && random() < 0.14) {\n trace.push(\"Amend Order\", \"Check Credit\");\n reworks += 1;\n }\n\n if (random() < 0.09) {\n trace.push(\"Reject Order\");\n return trace;\n }\n\n trace.push(\"Approve Order\", \"Reserve Stock\");\n\n if (random() < 0.05) {\n trace.push(\"Cancel Order\");\n return trace;\n }\n\n trace.push(\"Pick Items\");\n // A short-picked order is picked again — the log's only self-loop.\n if (random() < 0.07) trace.push(\"Pick Items\");\n\n // Roughly a third of orders are invoiced before they ship.\n if (random() < 0.32) trace.push(\"Send Invoice\", \"Ship Order\");\n else trace.push(\"Ship Order\", \"Send Invoice\");\n\n trace.push(\"Receive Payment\");\n return trace;\n}\n\n/**\n * Generate a synthetic order-to-cash log.\n *\n * Every event is an INTERVAL event: it carries both a `startTimestamp` and a `timestamp`,\n * so activity durations and edge idle times are both non-zero and a performance view has\n * something real to show. Timestamps are epoch numbers rather than ISO strings — a\n * 13 000-case log is a quarter of a million rows, and making a benchmark of the discovery\n * pass mostly measure `Date.parse` would be measuring the wrong thing.\n *\n * Cases are emitted in order, one per `caseId` of the form `case-00001`, zero-padded so\n * lexical and numeric order agree.\n */\nexport function generateSyntheticLog(options: SyntheticLogOptions): EventLog {\n const total = Math.max(0, Math.floor(options.cases));\n const random = mulberry32(options.seed ?? 1);\n const epoch = options.startTime ?? SYNTHETIC_LOG_EPOCH;\n\n const events: EventRow[] = [];\n const caseAttributes: Record<string, Record<string, unknown>> = {};\n const pad = String(total).length;\n\n for (let index = 0; index < total; index += 1) {\n const caseId = `case-${String(index + 1).padStart(Math.max(5, pad), \"0\")}`;\n const trace = buildTrace(random);\n\n caseAttributes[caseId] = {\n region: REGIONS[Math.floor(random() * REGIONS.length)] as string,\n segment: SEGMENTS[Math.floor(random() * SEGMENTS.length)] as string,\n // Whole euros, so a table can render the value without a rounding decision.\n orderValue: 250 + Math.floor(random() * 9750),\n };\n\n // Cases arrive about an hour apart, with a jitter that keeps them from lining up.\n let cursor = epoch + index * 61 * MINUTE + Math.floor(random() * 40) * MINUTE;\n\n for (const activity of trace) {\n const idle = Math.floor(random() * 180) * MINUTE;\n const work = (2 + Math.floor(random() * 44)) * MINUTE;\n const start = cursor + idle;\n const end = start + work;\n events.push({\n caseId,\n activity,\n timestamp: end,\n startTimestamp: start,\n resource: RESOURCES[Math.floor(random() * RESOURCES.length)] as string,\n });\n cursor = end;\n }\n }\n\n return { events, caseAttributes };\n}\n","/**\n * Seeded BPI-2012-shaped event-log generator — RM-053.\n *\n * A SEPARATE generator from `synthetic-log.ts` (RM-049), not a second copy of it: RM-049's\n * `generateSyntheticLog` produces an order-to-cash shaped log (Create Order, Check Credit,\n * Ship Order, …); this one produces a log shaped like the public BPI Challenge 2012 event\n * log (van Dongen, 2012) — a Dutch financial institution's loan-application process, whose\n * activity vocabulary follows the dataset's own three-lifecycle-prefix convention\n * (`A_` application state, `O_` offer state, `W_` work item). The BPI Challenge datasets are\n * a public academic research corpus published for the annual Business Process Intelligence\n * Challenge; naming one here names a published research artifact, not a commercial vendor.\n *\n * This is what analysis §4 R21 asks for: a ~13,000-case fixture large enough to exercise\n * `discoverGraph`'s performance budget (already covered by RM-049's own\n * `generateSyntheticLog({ cases: 13_000 })` in `discover-graph.test.ts`) and, from RM-051\n * onward, a Storybook \"LargeGraph\" story that wants a visually denser, more tangled graph\n * than the order-to-cash shape produces (more branching, more rework, a wider activity\n * vocabulary).\n *\n * Determinism is the whole contract, exactly as in `synthetic-log.ts`:\n * `generateBpi2012Subset({ cases: n, seed: s })` returns a byte-identical log on every run.\n *\n * ## This module is BROWSER-SAFE — no Node built-ins\n *\n * Deliberately: `discoverGraph`/`extractVariants` demos (the RM-053 placeholder Storybook\n * story, and any future browser-rendered fixture preview) import this module directly, and\n * Storybook's story tests run in a real browser (`@storybook/addon-vitest`'s Playwright\n * provider) where `node:fs`/`node:path`/`node:url` are externalized and throw on access. So\n * this file exports the PURE generator only — no filesystem write, no `import.meta.url`\n * resolution, no `process.argv` read. The file that writes the on-disk fixture is the\n * sibling `generate-bpi-2012-subset.write.ts` (see below), which is the only place those\n * Node-only imports appear.\n *\n * ## Producing the on-disk fixture\n *\n * The 13k-case output is a ~250k-row JSON document — too large to commit as a binary (see\n * the repo's \"no committed large binary fixture\" convention, already followed by\n * `synthetic-log.ts`). It is a GENERATED artifact: run\n *\n * ```\n * pnpm --filter @elabs-ai/components-process generate:fixtures\n * ```\n *\n * which writes `bpi-2012-subset.json` next to this file (git-ignored — see the repo root\n * `.gitignore`), via `generate-bpi-2012-subset.write.ts` (run through `vitest`, not `tsx` —\n * see that file's docblock for why). A consumer of the generated JSON should treat it as a\n * build artifact, not a checked-in fixture; import {@link generateBpi2012Subset} directly\n * wherever possible instead of reading the file.\n */\nimport type { EventLog, EventRow } from \"../types\";\n\n/** Epoch ms the first case starts at: 2012-01-02T08:00:00.000Z, a Monday morning. */\nexport const BPI_2012_SUBSET_EPOCH = 1325487600000;\n\nconst MINUTE = 60_000;\n\n/** Options for {@link generateBpi2012Subset}. */\nexport interface Bpi2012SubsetOptions {\n /** How many cases to generate. Values below 1 yield an empty log. */\n cases: number;\n /** PRNG seed. The same seed always produces the same log. */\n seed?: number;\n /** Epoch ms the first case starts at. Defaults to {@link BPI_2012_SUBSET_EPOCH}. */\n startTime?: number;\n}\n\n/**\n * A deterministic 32-bit PRNG (mulberry32).\n *\n * Deliberately its OWN copy rather than one imported from `synthetic-log.ts`: that file's\n * `mulberry32` is intentionally unexported (its own doc comment explains why — a fixture\n * generator's output must not shift because an unrelated generator's internals changed), and\n * this generator's output is equally a byte-identical fixture contract. Same reasoning,\n * same repo convention, independently applied.\n */\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\nconst RESOURCES = [\n \"Loan Officer 1\",\n \"Loan Officer 2\",\n \"Loan Officer 3\",\n \"Credit Assessor\",\n \"Automated System\",\n] as const;\n\n/**\n * Activity vocabulary, following the public BPI Challenge 2012 log's own naming convention:\n * `A_` (application), `O_` (offer) and `W_` (work item) lifecycle prefixes.\n */\nexport const BPI_2012_ACTIVITIES = [\n \"A_SUBMITTED\",\n \"A_PARTLYSUBMITTED\",\n \"A_PREACCEPTED\",\n \"W_Completeren aanvraag\",\n \"A_ACCEPTED\",\n \"O_SELECTED\",\n \"A_FINALIZED\",\n \"O_CREATED\",\n \"O_SENT\",\n \"W_Nabellen offertes\",\n \"O_SENT_BACK\",\n \"A_REGISTERED\",\n \"A_APPROVED\",\n \"A_ACTIVATED\",\n \"O_ACCEPTED\",\n \"A_DECLINED\",\n \"O_DECLINED\",\n \"O_CANCELLED\",\n \"A_CANCELLED\",\n] as const;\n\n/** One activity name from {@link BPI_2012_ACTIVITIES}. */\nexport type Bpi2012Activity = (typeof BPI_2012_ACTIVITIES)[number];\n\n/** Build the activity sequence of a single case. Pure given `random`. */\nfunction buildTrace(random: () => number): Bpi2012Activity[] {\n const trace: Bpi2012Activity[] = [\"A_SUBMITTED\", \"A_PARTLYSUBMITTED\"];\n\n if (random() < 0.06) {\n trace.push(\"A_DECLINED\");\n return trace;\n }\n\n trace.push(\"A_PREACCEPTED\", \"W_Completeren aanvraag\");\n\n // Rework: an incomplete dossier sends the applicant back through completion, at most twice.\n let reworks = 0;\n while (reworks < 2 && random() < 0.18) {\n trace.push(\"W_Completeren aanvraag\");\n reworks += 1;\n }\n\n if (random() < 0.08) {\n trace.push(\"A_CANCELLED\");\n return trace;\n }\n\n trace.push(\n \"A_ACCEPTED\",\n \"O_SELECTED\",\n \"A_FINALIZED\",\n \"O_CREATED\",\n \"O_SENT\",\n \"W_Nabellen offertes\",\n );\n\n // About a quarter of offers are sent back at least once before acceptance.\n if (random() < 0.25) trace.push(\"O_SENT_BACK\", \"O_SENT\", \"W_Nabellen offertes\");\n\n // About 1 in 10 offers ends without acceptance: most are declined outright, a smaller\n // share are withdrawn (O_CANCELLED) while still outstanding — both close the case, one\n // via A_CANCELLED and the other via either terminal state, mirroring the public BPI 2012\n // log's own O_CANCELLED lifecycle event (#228 — this branch is what makes it reachable;\n // see generate-bpi-2012-subset.test.ts's vocabulary set-equality test).\n if (random() < 0.1) {\n if (random() < 0.3) {\n trace.push(\"O_CANCELLED\", \"A_CANCELLED\");\n } else {\n trace.push(\"O_DECLINED\", random() < 0.5 ? \"A_CANCELLED\" : \"A_DECLINED\");\n }\n return trace;\n }\n\n trace.push(\"O_ACCEPTED\", \"A_REGISTERED\", \"A_APPROVED\", \"A_ACTIVATED\");\n return trace;\n}\n\n/**\n * Generate a synthetic BPI-2012-shaped loan-application log.\n *\n * Every event is an INTERVAL event (`startTimestamp` + `timestamp`), and cases are emitted\n * as `caseId`s of the form `bpi-000001`, zero-padded so lexical and numeric order agree —\n * the same shape `generateSyntheticLog` uses, so downstream consumers do not need a second\n * case-id convention.\n */\nexport function generateBpi2012Subset(options: Bpi2012SubsetOptions): EventLog {\n const total = Math.max(0, Math.floor(options.cases));\n const random = mulberry32(options.seed ?? 1);\n const epoch = options.startTime ?? BPI_2012_SUBSET_EPOCH;\n\n const events: EventRow[] = [];\n const pad = String(total).length;\n\n for (let index = 0; index < total; index += 1) {\n const caseId = `bpi-${String(index + 1).padStart(Math.max(6, pad), \"0\")}`;\n const trace = buildTrace(random);\n\n let cursor = epoch + index * 47 * MINUTE + Math.floor(random() * 30) * MINUTE;\n\n for (const activity of trace) {\n const idle = Math.floor(random() * 240) * MINUTE;\n const work = (1 + Math.floor(random() * 20)) * MINUTE;\n const start = cursor + idle;\n const end = start + work;\n events.push({\n caseId,\n activity,\n timestamp: end,\n startTimestamp: start,\n resource: RESOURCES[Math.floor(random() * RESOURCES.length)] as string,\n });\n cursor = end;\n }\n }\n\n return { events };\n}\n","/**\n * Slider-driven graph abstraction — RM-050.\n *\n * Every process-mining product surveyed in the wave-1 analysis exposes the same two\n * controls: an ACTIVITIES slider and a PATHS slider, both of which reduce what is DRAWN\n * without touching what was MEASURED. That rule is the whole point of this module — a\n * reader who drags a slider is filtering a view, not re-running an analysis, so the\n * numbers under their cursor must not move.\n *\n * Two invariants follow, and both are asserted in `abstract-graph.test.ts`:\n *\n * 1. **Statistics are never recomputed.** The returned graph reuses the very same\n * {@link ActivityStats} and {@link TransitionStats} OBJECTS the input carried — this\n * function filters arrays, it never builds a statistic. Nothing here mutates the input.\n * 2. **Kept nodes stay connected.** With `keepConnected` (the default) every kept activity\n * is reachable from a start activity and can reach an end activity, because a graph\n * with an island in it reads as a broken process rather than a simplified one.\n *\n * Deterministic: every ranking is totally ordered and the repair walks a fixed-cost graph,\n * so the same input always yields the same reduced view.\n */\nimport { EDGE_KEY_SEPARATOR } from \"./discover-graph\";\nimport type { ActivityStats, ProcessGraph, TransitionStats } from \"./types\";\n\n/** Options for {@link abstractGraph}. Both fractions are `0..1` and both are required. */\nexport interface AbstractionOptions {\n /** Fraction of activities to KEEP, `0..1`. At least one activity is always kept. */\n activities: number;\n /** Fraction of paths (transitions) to KEEP, `0..1`, over the kept-activity subgraph. */\n paths: number;\n /**\n * Re-add whatever it takes to keep every kept activity reachable from a start activity\n * and able to reach an end activity. Defaults to `true` — an island reads as a broken\n * process, not a simplified one.\n */\n keepConnected?: boolean;\n /** Hide the MOST frequent instead of the least — the \"what is rare here\" view. */\n invert?: boolean;\n}\n\n/** What {@link abstractGraph} returns: a `ProcessGraph` plus what it left out. */\nexport interface AbstractedGraph extends ProcessGraph {\n hidden: {\n /** Activities present in the input graph and absent from this one. */\n activities: number;\n /** Transitions present in the input graph and absent from this one. */\n paths: number;\n };\n}\n\n/** Key an edge exactly the way discovery keys it, so the two agree by construction. */\nfunction edgeKey(source: string, target: string): string {\n return `${source}${EDGE_KEY_SEPARATOR}${target}`;\n}\n\n/** Code-unit comparison — locale-independent, so the order is the same on every machine. */\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\n/** A non-finite fraction is read as `1` (keep everything) rather than as an error. */\nfunction clampFraction(value: number): number {\n if (!Number.isFinite(value)) return 1;\n return value < 0 ? 0 : value > 1 ? 1 : value;\n}\n\n/**\n * How many of `total` a fraction keeps.\n *\n * `Math.round` (half up) rather than floor or ceil, so the midpoint of the slider keeps\n * half the elements on an even count and the larger half on an odd one — and, crucially,\n * `fraction === 1` keeps exactly `total`, which is what makes the identity property hold.\n * `atLeast` is enforced for activities so a reduced graph is never empty; paths pass `0`,\n * because a node-only view is a legitimate thing to ask for.\n */\nfunction countToKeep(total: number, fraction: number, atLeast: number): number {\n if (total === 0) return 0;\n const kept = Math.round(total * fraction);\n return kept < atLeast ? Math.min(atLeast, total) : kept;\n}\n\n/** A minimal binary min-heap over `(node, cost)` — Dijkstra's queue, no dependency. */\nclass MinHeap {\n private readonly nodes: string[] = [];\n private readonly costs: number[] = [];\n\n get size(): number {\n return this.nodes.length;\n }\n\n push(node: string, cost: number): void {\n this.nodes.push(node);\n this.costs.push(cost);\n let i = this.nodes.length - 1;\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if ((this.costs[parent] as number) <= (this.costs[i] as number)) break;\n this.swap(i, parent);\n i = parent;\n }\n }\n\n pop(): { node: string; cost: number } | undefined {\n if (this.nodes.length === 0) return undefined;\n const node = this.nodes[0] as string;\n const cost = this.costs[0] as number;\n const lastNode = this.nodes.pop() as string;\n const lastCost = this.costs.pop() as number;\n if (this.nodes.length > 0) {\n this.nodes[0] = lastNode;\n this.costs[0] = lastCost;\n let i = 0;\n for (;;) {\n const left = i * 2 + 1;\n const right = left + 1;\n let smallest = i;\n const size = this.nodes.length;\n if (left < size && (this.costs[left] as number) < (this.costs[smallest] as number)) {\n smallest = left;\n }\n if (right < size && (this.costs[right] as number) < (this.costs[smallest] as number)) {\n smallest = right;\n }\n if (smallest === i) break;\n this.swap(i, smallest);\n i = smallest;\n }\n }\n return { node, cost };\n }\n\n private swap(a: number, b: number): void {\n const node = this.nodes[a] as string;\n this.nodes[a] = this.nodes[b] as string;\n this.nodes[b] = node;\n const cost = this.costs[a] as number;\n this.costs[a] = this.costs[b] as number;\n this.costs[b] = cost;\n }\n}\n\ninterface Route {\n /** Activities the route uses, including any the reduction had hidden. */\n activities: Set<string>;\n /** Edge keys the route uses. */\n edges: Set<string>;\n}\n\n/**\n * Reduce `graph` to the fraction of activities and paths a reader asked to see.\n *\n * Activities rank by `cases` (how many process instances touch them), ties broken by\n * `instances` then by name; paths rank by `count`, ties broken by `caseCount` then by\n * endpoint names. Ranking on `cases` rather than `instances` is deliberate: an activity\n * executed forty times inside one case is a loop, not a backbone step, and a frequency\n * slider that promoted it would hide the shape of the process.\n *\n * The returned arrays hold the INPUT's own statistic objects, in the input's order.\n * Treat the result as read-only: mutating a returned `ActivityStats` mutates the source\n * graph's, by design — that shared identity is what proves no recomputation happened.\n */\nexport function abstractGraph(graph: ProcessGraph, opts: AbstractionOptions): AbstractedGraph {\n const keepConnected = opts.keepConnected ?? true;\n const invert = opts.invert ?? false;\n const activityFraction = clampFraction(opts.activities);\n const pathFraction = clampFraction(opts.paths);\n\n const rankedActivities = [...graph.activities].sort(\n (a, b) => b.cases - a.cases || b.instances - a.instances || compareStrings(a.id, b.id),\n );\n const activityKeepCount = countToKeep(rankedActivities.length, activityFraction, 1);\n const keptActivities = new Set<string>(\n (invert\n ? rankedActivities.slice(rankedActivities.length - activityKeepCount)\n : rankedActivities.slice(0, activityKeepCount)\n ).map((activity) => activity.id),\n );\n\n const candidateEdges = graph.transitions.filter(\n (edge) => keptActivities.has(edge.source) && keptActivities.has(edge.target),\n );\n const rankedEdges = [...candidateEdges].sort(\n (a, b) =>\n b.count - a.count ||\n b.caseCount - a.caseCount ||\n compareStrings(a.source, b.source) ||\n compareStrings(a.target, b.target),\n );\n const edgeKeepCount = countToKeep(rankedEdges.length, pathFraction, 0);\n const keptEdges = new Set<string>(\n (invert\n ? rankedEdges.slice(rankedEdges.length - edgeKeepCount)\n : rankedEdges.slice(0, edgeKeepCount)\n ).map((edge) => edgeKey(edge.source, edge.target)),\n );\n\n if (keepConnected && keptActivities.size > 0) {\n reconnect(graph, keptActivities, keptEdges);\n }\n\n const activities: ActivityStats[] = graph.activities.filter((activity) =>\n keptActivities.has(activity.id),\n );\n const transitions: TransitionStats[] = graph.transitions.filter((edge) =>\n keptEdges.has(edgeKey(edge.source, edge.target)),\n );\n\n return {\n activities,\n transitions,\n startActivities: pick(graph.startActivities, keptActivities),\n endActivities: pick(graph.endActivities, keptActivities),\n // Passed through by reference: totals describe the LOG, and abstraction is a view.\n totals: graph.totals,\n hidden: {\n activities: graph.activities.length - activities.length,\n paths: graph.transitions.length - transitions.length,\n },\n };\n}\n\n/** Keep the entries of `record` whose key survived, in the record's own key order. */\nfunction pick(record: Record<string, number>, keep: ReadonlySet<string>): Record<string, number> {\n const out: Record<string, number> = {};\n for (const name of Object.keys(record)) {\n if (keep.has(name)) out[name] = record[name] as number;\n }\n return out;\n}\n\n/**\n * Repair connectivity in place, growing `keptActivities` / `keptEdges` as little as it can.\n *\n * For every kept activity that no start activity can reach, the cheapest route from a\n * start activity is found by Dijkstra over edge cost `log(maxCount) − log(count)` — the\n * non-negative form of `−log(count)`, which is what Dijkstra requires and which orders\n * paths identically: minimizing a sum of `−log` weights maximizes the PRODUCT of the edge\n * counts along the route, so the one made of the most-travelled edges wins. Every hidden\n * edge on that route is brought back; a mirrored pass over reversed edges does the same\n * for activities that cannot reach an end activity.\n *\n * The roadmap describes this as re-adding \"the max-weight edge on a shortest path\". One\n * edge is not enough to restore reachability when a route is several hops long, so ALL of\n * the route's hidden edges are re-added — the smallest change that actually holds the\n * invariant the acceptance criterion asserts.\n *\n * Two escalations, both deliberate and both visible through `hidden`:\n *\n * - If NO kept activity is a start (or end) activity, the busiest one is brought back —\n * otherwise there is no anchor to connect to and the whole graph is an island.\n * - If a route exists only through a hidden activity, that activity is brought back too.\n * The alternative is to leave the node stranded. Both escalations only ever ADD, so the\n * guarantee is exactly: an activity connected in `graph` is connected in the result.\n */\nfunction reconnect(graph: ProcessGraph, keptActivities: Set<string>, keptEdges: Set<string>): void {\n const forward = new Map<string, TransitionStats[]>();\n const backward = new Map<string, TransitionStats[]>();\n let maxCount = 1;\n for (const edge of graph.transitions) {\n if (edge.count > maxCount) maxCount = edge.count;\n index(forward, edge.source, edge);\n index(backward, edge.target, edge);\n }\n const logMax = Math.log(maxCount);\n const cost = (count: number): number => (count > 0 ? logMax - Math.log(count) : logMax);\n\n // The two passes are COUPLED: repairing \"can reach an end\" may bring back an activity\n // that nothing reaches from a start, and vice versa. So they run to a fixpoint rather\n // than once each. Both passes only ever ADD, and the sets are bounded by the input\n // graph, so the loop terminates — the bound is belt-and-braces against a future edit\n // that makes a pass able to remove something.\n const bound = graph.activities.length + graph.transitions.length + 2;\n for (let round = 0; round < bound; round += 1) {\n const before = keptActivities.size + keptEdges.size;\n repairDirection(graph.startActivities, forward, true);\n repairDirection(graph.endActivities, backward, false);\n if (keptActivities.size + keptEdges.size === before) break;\n }\n\n function repairDirection(\n seedCounts: Record<string, number>,\n adjacency: Map<string, TransitionStats[]>,\n downstream: boolean,\n ): void {\n const allSeeds = Object.keys(seedCounts);\n if (allSeeds.length === 0) return;\n if (!allSeeds.some((id) => keptActivities.has(id))) anchor(seedCounts, keptActivities);\n const keptSeeds = allSeeds.filter((id) => keptActivities.has(id));\n if (keptSeeds.length === 0) return;\n const reached = spread(keptSeeds, adjacency, downstream);\n\n // A stable, deterministic repair order: the graph's own activity order.\n for (const activity of graph.activities) {\n if (!keptActivities.has(activity.id) || reached.has(activity.id)) continue;\n // Escalate in three steps, cheapest first — kept route, then a route through hidden\n // activities, then a route to an anchor the reduction hid entirely. The third step\n // is what an INVERTED reduction needs: keeping only the rare activities can hide\n // every end activity the backbone actually leads to, and then the only honest repair\n // is to bring one of those back.\n const route =\n shortestRoute(keptSeeds, activity.id, adjacency, downstream, true) ??\n shortestRoute(keptSeeds, activity.id, adjacency, downstream, false) ??\n shortestRoute(allSeeds, activity.id, adjacency, downstream, false);\n if (route === undefined) continue; // genuinely unreachable in the FULL graph too.\n for (const id of route.activities) keptActivities.add(id);\n for (const key of route.edges) keptEdges.add(key);\n for (const id of spread([...route.activities], adjacency, downstream)) reached.add(id);\n }\n }\n\n /** BFS over the KEPT edges only — which nodes the anchors already reach. */\n function spread(\n seeds: readonly string[],\n adjacency: Map<string, TransitionStats[]>,\n downstream: boolean,\n ): Set<string> {\n const seen = new Set<string>(seeds.filter((id) => keptActivities.has(id)));\n const queue = [...seen];\n for (let head = 0; head < queue.length; head += 1) {\n const node = queue[head] as string;\n for (const edge of adjacency.get(node) ?? []) {\n const next = downstream ? edge.target : edge.source;\n if (!keptActivities.has(next)) continue;\n if (!keptEdges.has(edgeKey(edge.source, edge.target))) continue;\n if (seen.has(next)) continue;\n seen.add(next);\n queue.push(next);\n }\n }\n return seen;\n }\n\n /**\n * Cheapest route from any seed to `target`, as the activities and edge keys it uses.\n * `keptOnly` restricts the walk to already-kept activities; the caller retries without\n * it when that fails, which is the second escalation described above.\n */\n function shortestRoute(\n seeds: readonly string[],\n target: string,\n adjacency: Map<string, TransitionStats[]>,\n downstream: boolean,\n keptOnly: boolean,\n ): Route | undefined {\n const distance = new Map<string, number>();\n const previous = new Map<string, TransitionStats>();\n const settled = new Set<string>();\n const heap = new MinHeap();\n for (const seed of seeds) {\n distance.set(seed, 0);\n heap.push(seed, 0);\n }\n\n while (heap.size > 0) {\n const top = heap.pop() as { node: string; cost: number };\n if (settled.has(top.node)) continue;\n settled.add(top.node);\n if (top.node === target) break;\n for (const edge of adjacency.get(top.node) ?? []) {\n const next = downstream ? edge.target : edge.source;\n if (next === top.node) continue; // a self-loop cannot shorten anything.\n if (keptOnly && !keptActivities.has(next)) continue;\n const candidate = top.cost + cost(edge.count);\n const known = distance.get(next);\n // A strict improvement only, so a tie keeps the first route found — and the walk\n // order is the graph's own deterministic edge order.\n if (known !== undefined && known <= candidate) continue;\n distance.set(next, candidate);\n previous.set(next, edge);\n heap.push(next, candidate);\n }\n }\n\n if (!settled.has(target)) return undefined;\n const route: Route = { activities: new Set([target]), edges: new Set() };\n let cursor = target;\n for (;;) {\n const edge = previous.get(cursor);\n if (edge === undefined) break;\n route.edges.add(edgeKey(edge.source, edge.target));\n route.activities.add(edge.source);\n route.activities.add(edge.target);\n cursor = downstream ? edge.source : edge.target;\n }\n return route;\n }\n\n /** Bring back the busiest start/end activity when the reduction kept none of them. */\n function anchor(counts: Record<string, number>, kept: Set<string>): void {\n const names = Object.keys(counts);\n if (names.length === 0) return;\n if (names.some((name) => kept.has(name))) return;\n let best = names[0] as string;\n for (const name of names) {\n const value = counts[name] as number;\n const bestValue = counts[best] as number;\n if (value > bestValue || (value === bestValue && compareStrings(name, best) < 0)) best = name;\n }\n kept.add(best);\n }\n}\n\nfunction index(map: Map<string, TransitionStats[]>, key: string, edge: TransitionStats): void {\n const bucket = map.get(key);\n if (bucket === undefined) map.set(key, [edge]);\n else bucket.push(edge);\n}\n","/**\n * reconcileGraph — merge a FILTERED graph's statistics onto a PRESENTED graph's element set\n * (RM-052 round 2, issue #227, Invariant F).\n *\n * The maintainer's ruling on filtering: keep every process-map step visible, dim the ones a\n * filter excludes — never remove them. Invariant F makes that ruling mechanical: the set of\n * activities and transitions a process map renders is a function of the log and the\n * abstraction alone, never of the active filter intents. Applying or clearing a filter can\n * only change an element's STATE and the statistics it carries; it can never add or remove a\n * node or an edge.\n *\n * `reconcileGraph` is the pure operation that keeps that promise: given the graph a map is\n * about to PRESENT (`presented` — the full, abstracted graph, computed with no filter\n * applied) and the graph the ACTIVE filter actually produced (`filtered`), it returns a graph\n * with `presented`'s exact element set, where every element also present in `filtered` reads\n * `filtered`'s statistics, and every element `filtered` dropped becomes a \"ghost\" — the same\n * id, at zero. A ghost's statistics are the FILTERED ones (zero), not the full-log ones —\n * painting full-log numbers on an excluded element would make filtering cosmetic.\n *\n * `startActivities`, `endActivities` and `totals` come from `filtered` outright (they are\n * graph-level facts about what actually survived the filter, not per-element stats to\n * reconcile). Extra fields the presented graph carries beyond `ProcessGraph` itself (e.g.\n * `AbstractedGraph.hidden`) pass through untouched — `reconcileGraph` never removes what it\n * did not add.\n *\n * Pipeline ordering is load-bearing: abstraction runs on the FULL graph, BEFORE\n * reconciliation with the filtered graph. Reconciling first (running abstraction on an\n * already-filtered graph) would let the zeroed ghosts get treated as least-frequent and\n * deleted by abstraction's own ranking — reinstating the \"filtering removes elements\"\n * behaviour the maintainer ruled against, by a second route.\n */\nimport { emptyDurationStats } from \"./duration-stats\";\nimport { EDGE_KEY_SEPARATOR } from \"./discover-graph\";\nimport type { ProcessGraph, TransitionStats } from \"./types\";\n\n/** The result of {@link reconcileGraph}. */\nexport interface ReconciledGraph<G extends ProcessGraph> {\n /** `presented`'s exact element set, with `filtered`'s statistics where an element survived. */\n graph: G;\n /** Activity ids `presented` carries that `filtered` dropped — rendered as zeroed ghosts. */\n excludedActivities: string[];\n /** Transition keys (`source` + {@link EDGE_KEY_SEPARATOR} + `target`) `filtered` dropped. */\n excludedTransitions: string[];\n}\n\nfunction transitionKey(transition: Pick<TransitionStats, \"source\" | \"target\">): string {\n return `${transition.source}${EDGE_KEY_SEPARATOR}${transition.target}`;\n}\n\n/**\n * Reconcile a presented graph's element set against what a filter actually produced.\n *\n * `G` is generic over `ProcessGraph` so an already-abstracted graph (`AbstractedGraph`, which\n * adds `hidden`) reconciles without losing its extra field — the return type is `G`, and the\n * implementation spreads `presented` before overwriting only the fields this function owns.\n */\nexport function reconcileGraph<G extends ProcessGraph>(\n presented: G,\n filtered: ProcessGraph,\n): ReconciledGraph<G> {\n const filteredActivities = new Map(\n filtered.activities.map((activity) => [activity.id, activity]),\n );\n const filteredTransitions = new Map(\n filtered.transitions.map((transition) => [transitionKey(transition), transition]),\n );\n\n const excludedActivities: string[] = [];\n const activities = presented.activities.map((activity) => {\n const survivor = filteredActivities.get(activity.id);\n if (survivor) return survivor;\n excludedActivities.push(activity.id);\n return {\n ...activity,\n instances: 0,\n cases: 0,\n isStart: false,\n isEnd: false,\n duration: emptyDurationStats(),\n };\n });\n\n const excludedTransitions: string[] = [];\n const transitions = presented.transitions.map((transition) => {\n const key = transitionKey(transition);\n const survivor = filteredTransitions.get(key);\n if (survivor) return survivor;\n excludedTransitions.push(key);\n return {\n ...transition,\n count: 0,\n caseCount: 0,\n duration: emptyDurationStats(),\n };\n });\n\n const graph: G = {\n ...presented,\n activities,\n transitions,\n startActivities: filtered.startActivities,\n endActivities: filtered.endActivities,\n totals: filtered.totals,\n };\n\n return { graph, excludedActivities, excludedTransitions };\n}\n","/**\n * The performance metric layer — RM-050.\n *\n * A process map is read twice: once for FREQUENCY (how often does this happen) and once\n * for PERFORMANCE (how long does it take). The frequency numbers come straight off\n * discovery; the performance numbers need three further decisions that only a reader can\n * make — which aggregate (median, mean, p90 …), which elapsed time an edge measures\n * (waiting or cycle), and which unit the answer is spoken in. This module applies all\n * three to an already-discovered graph and hands back the scalar each node and edge\n * should be painted with.\n *\n * The returned graph is a NEW object graph: unlike {@link abstractGraph}, which filters,\n * this function re-expresses. The input is never mutated.\n */\nimport { discoverGraph } from \"./discover-graph\";\nimport { EDGE_KEY_SEPARATOR } from \"./discover-graph\";\nimport type { AnyLog } from \"./event-log\";\nimport { minMax } from \"./scale\";\nimport type {\n ActivityStats,\n DurationStats,\n FlowTime,\n PerformanceAgg,\n ProcessGraph,\n TransitionStats,\n} from \"./types\";\n\n/** The units a duration can be spoken in. Everything upstream is milliseconds. */\nexport type DurationUnit = \"ms\" | \"s\" | \"min\" | \"h\" | \"d\";\n\n/** How many milliseconds each {@link DurationUnit} is worth. */\nexport const DURATION_UNIT_MS: Readonly<Record<DurationUnit, number>> = Object.freeze({\n ms: 1,\n s: 1_000,\n min: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n});\n\n/** Options for {@link aggregatePerformance}. */\nexport interface PerformanceOptions {\n /** Which member of a {@link DurationStats} the layer reads. */\n agg: PerformanceAgg;\n /**\n * Which elapsed time an edge measures. Honoured only when `log` is supplied — a graph\n * alone cannot be converted from waiting time to cycle time, because the two are not\n * derivable from each other's summary statistics. Without `log` this field DECLARES\n * what the graph already carries and nothing is re-derived.\n */\n flowTime: FlowTime;\n /** The unit every returned duration is expressed in. */\n unit: DurationUnit;\n /**\n * The log the graph came from. Supply it to re-derive edge durations at `flowTime`;\n * pass an already-normalized log (see `asNormalizedLog`) to skip a second parse.\n */\n log?: AnyLog;\n /**\n * Reservoir capacity per activity and per edge when re-deriving. Order statistics\n * (`median`, `p90`, `trimmedMean`) are ESTIMATES past this many samples per key; raise\n * it when a threshold has to be exact. Only meaningful together with `log`.\n */\n maxDurationSamples?: number;\n}\n\n/** The per-element scalars a performance view paints with, plus the domains to scale them on. */\nexport interface PerformanceLayer {\n agg: PerformanceAgg;\n flowTime: FlowTime;\n unit: DurationUnit;\n /** Activity id → the selected aggregate of its execution time, in `unit`. */\n activities: Record<string, number>;\n /** `source␁target` (see `EDGE_KEY_SEPARATOR`) → the selected aggregate, in `unit`. */\n transitions: Record<string, number>;\n /** `[min, max]` over `activities` — `[0, 0]` when there are none. */\n activityDomain: [number, number];\n /** `[min, max]` over `transitions` — `[0, 0]` when there are none. */\n transitionDomain: [number, number];\n}\n\n/** What {@link aggregatePerformance} returns: a `ProcessGraph` plus its metric layer. */\nexport interface PerformanceGraph extends ProcessGraph {\n performance: PerformanceLayer;\n}\n\n/**\n * Read the member of `stats` a {@link PerformanceAgg} names.\n *\n * The one place the mapping lives, so a control, a legend and a renderer cannot disagree\n * about what \"p90\" is. `trimmed_mean` is the snake_case spelling of `trimmedMean` —\n * the type is a wire/URL value, the field is a property name.\n */\nexport function performanceValue(stats: DurationStats, agg: PerformanceAgg): number {\n switch (agg) {\n case \"mean\":\n return stats.mean;\n case \"min\":\n return stats.min;\n case \"max\":\n return stats.max;\n case \"sum\":\n return stats.sum;\n case \"p90\":\n return stats.p90;\n case \"trimmed_mean\":\n return stats.trimmedMean;\n case \"median\":\n default:\n return stats.median;\n }\n}\n\n/** Scale every member of `stats` by `factor`. Returns a new object; `stats` is untouched. */\nfunction scaleStats(stats: DurationStats, factor: number): DurationStats {\n if (factor === 1) return { ...stats };\n return {\n min: stats.min * factor,\n max: stats.max * factor,\n mean: stats.mean * factor,\n median: stats.median * factor,\n p90: stats.p90 * factor,\n sum: stats.sum * factor,\n trimmedMean: stats.trimmedMean * factor,\n };\n}\n\nfunction edgeKey(source: string, target: string): string {\n return `${source}${EDGE_KEY_SEPARATOR}${target}`;\n}\n\n/**\n * Express `graph`'s durations as a performance layer.\n *\n * Every `DurationStats` in the returned graph is converted to `opts.unit`, and\n * `performance` carries the single scalar per activity and per transition that\n * `opts.agg` selects, together with the two domains a scale needs.\n *\n * With `opts.log`, edge and activity durations are RE-DERIVED at `opts.flowTime` from the\n * FULL log — which is what keeps a performance layer honest on an abstracted graph: the\n * numbers describe everything that happened, not only the part currently drawn. Elements\n * the re-derivation does not know about keep the statistics they arrived with.\n *\n * Order statistics past the reservoir cap are estimates (see `maxDurationSamples`);\n * `sum`, `mean`, `min` and `max` are exact at any log size.\n */\nexport function aggregatePerformance(\n graph: ProcessGraph,\n opts: PerformanceOptions,\n): PerformanceGraph {\n const factor = 1 / (DURATION_UNIT_MS[opts.unit] ?? 1);\n\n let activitySource: Map<string, DurationStats> | undefined;\n let transitionSource: Map<string, DurationStats> | undefined;\n if (opts.log !== undefined) {\n const derived = discoverGraph(opts.log, {\n flowTime: opts.flowTime,\n ...(opts.maxDurationSamples === undefined\n ? {}\n : { maxDurationSamples: opts.maxDurationSamples }),\n });\n activitySource = new Map(derived.activities.map((a) => [a.id, a.duration]));\n transitionSource = new Map(\n derived.transitions.map((t) => [edgeKey(t.source, t.target), t.duration]),\n );\n }\n\n const activityValues: Record<string, number> = {};\n const activities: ActivityStats[] = graph.activities.map((activity) => {\n const source = activitySource?.get(activity.id) ?? activity.duration;\n const duration = scaleStats(source, factor);\n activityValues[activity.id] = performanceValue(duration, opts.agg);\n return { ...activity, duration };\n });\n\n const transitionValues: Record<string, number> = {};\n const transitions: TransitionStats[] = graph.transitions.map((edge) => {\n const key = edgeKey(edge.source, edge.target);\n const source = transitionSource?.get(key) ?? edge.duration;\n const duration = scaleStats(source, factor);\n transitionValues[key] = performanceValue(duration, opts.agg);\n return { ...edge, duration };\n });\n\n return {\n activities,\n transitions,\n startActivities: { ...graph.startActivities },\n endActivities: { ...graph.endActivities },\n totals: { ...graph.totals },\n performance: {\n agg: opts.agg,\n flowTime: opts.flowTime,\n unit: opts.unit,\n activities: activityValues,\n transitions: transitionValues,\n activityDomain: minMax(Object.values(activityValues)),\n transitionDomain: minMax(Object.values(transitionValues)),\n },\n };\n}\n","/**\n * Rework detection — RM-050.\n *\n * \"Rework\" is the part of a process that happens more than once inside a single case: a\n * step retried immediately (a SELF-LOOP), or a step the case comes back to after going\n * somewhere else (a LOOP). Both are repeats, and separating them matters — a self-loop is\n * usually a retry or a batch, a loop is usually a rejection sending work backwards, and\n * the two lead to different conversations about the process.\n *\n * Counted per OCCURRENCE, over the normalized trace: the first execution of an activity in\n * a case is never rework, and every later one is exactly one of the two kinds. So\n * `selfLoops + loops` is precisely the number of repeated executions in the log.\n *\n * Deterministic and framework-free, like everything else in `/core`.\n */\nimport { asNormalizedLog, type AnyLog } from \"./event-log\";\n\n/** Per-activity rework tallies. */\nexport interface ActivityRework {\n /** Repeats of this activity immediately after itself. */\n selfLoops: number;\n /** Repeats of this activity later in the same case, with something else in between. */\n loops: number;\n}\n\n/** What {@link detectRework} answers. */\nexport interface ReworkStats {\n /** Adjacent repeats across the whole log. */\n selfLoops: number;\n /** Non-adjacent repeats across the whole log. */\n loops: number;\n /** Fraction of cases carrying at least one self-loop or loop. `0` for an empty log. */\n caseReworkRate: number;\n /**\n * Every activity in the log, in ascending name order, with its own two tallies. An\n * activity that never repeats is present with zeros — a renderer joining this against\n * a graph's activities never has to distinguish \"no rework\" from \"unknown activity\".\n */\n perActivity: Record<string, ActivityRework>;\n}\n\n/**\n * Count rework in `log`.\n *\n * Accepts a raw or an already-normalized log and normalizes at most once\n * ({@link asNormalizedLog} is idempotent), so a caller that has already normalized for\n * `discoverGraph` pays nothing here.\n */\nexport function detectRework(log: AnyLog): ReworkStats {\n const normalized = asNormalizedLog(log);\n\n const tallies = new Map<string, ActivityRework>();\n let selfLoops = 0;\n let loops = 0;\n let casesWithRework = 0;\n\n const seen = new Set<string>();\n for (const kase of normalized.cases) {\n seen.clear();\n let caseHasRework = false;\n let previous: string | undefined;\n\n for (const event of kase.events) {\n const name = event.activity;\n let tally = tallies.get(name);\n if (tally === undefined) {\n tally = { selfLoops: 0, loops: 0 };\n tallies.set(name, tally);\n }\n\n if (seen.has(name)) {\n caseHasRework = true;\n if (name === previous) {\n tally.selfLoops += 1;\n selfLoops += 1;\n } else {\n tally.loops += 1;\n loops += 1;\n }\n } else {\n seen.add(name);\n }\n previous = name;\n }\n\n if (caseHasRework) casesWithRework += 1;\n }\n\n const perActivity: Record<string, ActivityRework> = {};\n for (const name of [...tallies.keys()].sort()) {\n perActivity[name] = tallies.get(name) as ActivityRework;\n }\n\n return {\n selfLoops,\n loops,\n caseReworkRate: normalized.cases.length === 0 ? 0 : casesWithRework / normalized.cases.length,\n perActivity,\n };\n}\n","/**\n * Case-level log filtering — RM-050.\n *\n * Every filter in a process-mining tool is a CASE predicate, not an event predicate:\n * \"cases that contain Reject Order\", \"cases that took longer than a day\". Removing single\n * events would rewrite traces and invent directly-follows relations that never happened,\n * so a filter here only ever keeps or drops a case whole. That is what makes the result\n * safe to hand straight back to `discoverGraph` / `extractVariants`.\n *\n * Specs AND together — an empty list keeps everything.\n */\nimport { asNormalizedLog, type AnyLog, type NormalizedCase, type NormalizedLog } from \"./event-log\";\nimport { variantId } from \"./extract-variants\";\nimport type { EventLog } from \"./types\";\n\n/** One case-level predicate. {@link filterLog} ANDs a list of them. */\nexport type FilterSpec =\n /** The case contains `activity` at least once. */\n | { kind: \"with\"; activity: string }\n /** The case never contains `activity`. */\n | { kind: \"without\"; activity: string }\n /** The case's FIRST activity is `activity`. */\n | { kind: \"startsWith\"; activity: string }\n /** The case's LAST activity is `activity`. */\n | { kind: \"endsWith\"; activity: string }\n /**\n * `b` follows `a` in the case — directly (adjacent) when `direct`, otherwise eventually\n * (anywhere later in the trace).\n */\n | { kind: \"follower\"; a: string; b: string; direct?: boolean }\n /** An attribute comparison — see {@link caseMatchesFilters} for how `key` is resolved. */\n | { kind: \"attribute\"; key: string; op: \"eq\" | \"ne\" | \"gt\" | \"lt\" | \"in\"; value: unknown }\n /** The case's throughput time, in milliseconds. Both bounds are INCLUSIVE. */\n | { kind: \"duration\"; min?: number; max?: number }\n /** The case follows one of these variants (see `variantId`). */\n | { kind: \"variant\"; ids: string[] }\n /** The case is one of these case ids. */\n | { kind: \"cases\"; ids: string[] };\n\n/** The activity sequence of a normalized case. */\nfunction sequenceOf(kase: NormalizedCase): string[] {\n const sequence = new Array<string>(kase.events.length);\n for (let i = 0; i < kase.events.length; i += 1) {\n sequence[i] = (kase.events[i] as { activity: string }).activity;\n }\n return sequence;\n}\n\n/**\n * Every value the case offers for `key`.\n *\n * Case attributes win: when `caseAttributes` defines the key, that single value IS the\n * case's answer. Otherwise the key is looked for on the events — including `resource`,\n * which is a first-class field rather than an entry in `attributes` — and the case offers\n * every distinct value its events carry. So \"cases handled by the credit service\" is\n * expressible without the caller pre-aggregating anything.\n */\nfunction valuesFor(kase: NormalizedCase, key: string): unknown[] {\n const attributes = kase.attributes;\n if (attributes !== undefined && Object.hasOwn(attributes, key)) return [attributes[key]];\n\n const values: unknown[] = [];\n const seen = new Set<unknown>();\n for (const event of kase.events) {\n const value = key === \"resource\" ? event.resource : event.attributes?.[key];\n if (value === undefined) continue;\n if (seen.has(value)) continue;\n seen.add(value);\n values.push(value);\n }\n return values;\n}\n\n/** `>` / `<` over two numbers or two strings; anything else does not compare. */\nfunction ordered(left: unknown, right: unknown, wantGreater: boolean): boolean {\n if (typeof left === \"number\" && typeof right === \"number\") {\n return wantGreater ? left > right : left < right;\n }\n if (typeof left === \"string\" && typeof right === \"string\") {\n return wantGreater ? left > right : left < right;\n }\n return false;\n}\n\nfunction matchesAttribute(\n kase: NormalizedCase,\n spec: Extract<FilterSpec, { kind: \"attribute\" }>,\n): boolean {\n const values = valuesFor(kase, spec.key);\n switch (spec.op) {\n case \"eq\":\n return values.some((value) => Object.is(value, spec.value));\n // `ne` is the negation of `eq`, not \"some value differs\" — otherwise a case offering\n // two resources would satisfy both `eq` and `ne` against the same value.\n case \"ne\":\n return !values.some((value) => Object.is(value, spec.value));\n case \"gt\":\n return values.some((value) => ordered(value, spec.value, true));\n case \"lt\":\n return values.some((value) => ordered(value, spec.value, false));\n case \"in\": {\n if (!Array.isArray(spec.value)) return false;\n const allowed = spec.value as unknown[];\n return values.some((value) => allowed.some((candidate) => Object.is(value, candidate)));\n }\n default:\n return false;\n }\n}\n\nfunction matchesOne(kase: NormalizedCase, sequence: readonly string[], spec: FilterSpec): boolean {\n switch (spec.kind) {\n case \"with\":\n return sequence.includes(spec.activity);\n case \"without\":\n return !sequence.includes(spec.activity);\n case \"startsWith\":\n return sequence[0] === spec.activity;\n case \"endsWith\":\n return sequence[sequence.length - 1] === spec.activity;\n case \"follower\": {\n if (spec.direct === true) {\n for (let i = 0; i + 1 < sequence.length; i += 1) {\n if (sequence[i] === spec.a && sequence[i + 1] === spec.b) return true;\n }\n return false;\n }\n const first = sequence.indexOf(spec.a);\n if (first < 0) return false;\n return sequence.indexOf(spec.b, first + 1) >= 0;\n }\n case \"attribute\":\n return matchesAttribute(kase, spec);\n case \"duration\": {\n if (spec.min === undefined && spec.max === undefined) return true;\n // An unmeasurable case (every timestamp unparseable) cannot satisfy a bound.\n if (!Number.isFinite(kase.duration)) return false;\n if (spec.min !== undefined && kase.duration < spec.min) return false;\n if (spec.max !== undefined && kase.duration > spec.max) return false;\n return true;\n }\n case \"variant\":\n return spec.ids.includes(variantId(sequence));\n case \"cases\":\n return spec.ids.includes(kase.caseId);\n default:\n return true;\n }\n}\n\n/**\n * Does `kase` satisfy every spec?\n *\n * Exported because a case table and a variant list want to HIGHLIGHT what a filter\n * selects as often as they want to remove what it does not.\n */\nexport function caseMatchesFilters(kase: NormalizedCase, specs: readonly FilterSpec[]): boolean {\n if (specs.length === 0) return true;\n const sequence = sequenceOf(kase);\n for (const spec of specs) {\n if (!matchesOne(kase, sequence, spec)) return false;\n }\n return true;\n}\n\n/**\n * Filter a NORMALIZED log — the composable form.\n *\n * This is the one to reach for in a pipeline: it accepts either shape, normalizes at most\n * once, and returns the normalized result, so\n * `discoverGraph(filterNormalizedLog(normalized, specs))` re-parses nothing. `totals` are\n * recomputed over the surviving cases; case order is preserved.\n */\nexport function filterNormalizedLog(log: AnyLog, specs: readonly FilterSpec[]): NormalizedLog {\n const normalized = asNormalizedLog(log);\n if (specs.length === 0) return normalized;\n\n const cases: NormalizedCase[] = [];\n let events = 0;\n for (const kase of normalized.cases) {\n if (!caseMatchesFilters(kase, specs)) continue;\n cases.push(kase);\n events += kase.events.length;\n }\n return { cases, totals: { cases: cases.length, events } };\n}\n\n/**\n * Filter a raw {@link EventLog}, answering a raw {@link EventLog}.\n *\n * Every ROW of a surviving case is kept, in input order, including rows normalization\n * itself discards (an unpaired lifecycle half, say) — so the answer round-trips: it can be\n * re-exported, handed to a different adapter, or re-normalized with different options.\n * `caseAttributes` is narrowed to the surviving cases.\n *\n * Use {@link filterNormalizedLog} instead when the next step is another `/core` derivation;\n * this form necessarily makes the caller normalize again.\n */\nexport function filterLog(log: EventLog, specs: readonly FilterSpec[]): EventLog {\n if (specs.length === 0) return log;\n const kept = new Set<string>();\n for (const kase of filterNormalizedLog(log, specs).cases) kept.add(kase.caseId);\n\n const filtered: EventLog = { events: log.events.filter((row) => kept.has(row?.caseId)) };\n if (log.caseAttributes !== undefined) {\n const caseAttributes: Record<string, Record<string, unknown>> = {};\n for (const caseId of Object.keys(log.caseAttributes)) {\n if (kept.has(caseId)) {\n caseAttributes[caseId] = log.caseAttributes[caseId] as Record<string, unknown>;\n }\n }\n filtered.caseAttributes = caseAttributes;\n }\n return filtered;\n}\n","/**\n * The worker entry point — RM-050.\n *\n * Two things live here, deliberately in one module: the PURE request handler, and the\n * `message` bootstrap that runs it inside a real worker. `createProcessWorker` imports the\n * handler for its inline path, so the code that answers on the main thread and the code\n * that answers off it are the SAME function — parity is structural, not something a test\n * has to keep true.\n *\n * The bootstrap is guarded by a real worker-scope check, so importing this module from the\n * main thread registers nothing.\n */\nimport { discoverGraph, type DiscoverGraphOptions } from \"../discover-graph\";\nimport type { AnyLog } from \"../event-log\";\nimport { extractVariants } from \"../extract-variants\";\nimport type { ProcessGraph, Variant } from \"../types\";\n\n/** What the main thread asks the worker to compute. Structured-cloneable, by construction. */\nexport type ProcessWorkerRequest =\n | { id: number; kind: \"discover\"; log: AnyLog; options?: DiscoverGraphOptions }\n | { id: number; kind: \"variants\"; log: AnyLog };\n\n/** What comes back. `ok: false` carries a message, never an `Error` (which clones poorly). */\nexport type ProcessWorkerResponse =\n | { id: number; ok: true; kind: \"discover\"; graph: ProcessGraph }\n | { id: number; ok: true; kind: \"variants\"; variants: Variant[] }\n | { id: number; ok: false; error: string };\n\n/**\n * Answer one request.\n *\n * Pure and synchronous: the whole point of the worker is that this is the expensive part,\n * and moving it off the main thread is the only thing the worker adds. Throwing is\n * converted to an `ok: false` response so a bad log cannot silently kill the worker and\n * leave every later request hanging.\n */\nexport function handleProcessRequest(request: ProcessWorkerRequest): ProcessWorkerResponse {\n try {\n if (request.kind === \"discover\") {\n return {\n id: request.id,\n ok: true,\n kind: \"discover\",\n graph: discoverGraph(request.log, request.options ?? {}),\n };\n }\n return { id: request.id, ok: true, kind: \"variants\", variants: extractVariants(request.log) };\n } catch (error) {\n return { id: request.id, ok: false, error: describe(error) };\n }\n}\n\nfunction describe(error: unknown): string {\n if (error instanceof Error) return error.message;\n return String(error);\n}\n\n/**\n * Is this module executing INSIDE a worker?\n *\n * `self instanceof WorkerGlobalScope` is the specified test. A duck-type on `postMessage`\n * would be wrong: `window.postMessage` exists on every page, so the main thread would\n * install a listener that answers its own messages.\n */\nfunction inWorkerScope(): boolean {\n const scope = globalThis as { WorkerGlobalScope?: new () => unknown; self?: unknown };\n const constructor = scope.WorkerGlobalScope;\n if (typeof constructor !== \"function\") return false;\n return scope.self instanceof constructor;\n}\n\nif (inWorkerScope()) {\n const scope = self as unknown as {\n addEventListener: (type: \"message\", listener: (event: MessageEvent) => void) => void;\n postMessage: (message: unknown) => void;\n };\n scope.addEventListener(\"message\", (event: MessageEvent) => {\n scope.postMessage(handleProcessRequest(event.data as ProcessWorkerRequest));\n });\n}\n","/**\n * Off-thread discovery — RM-050.\n *\n * Discovering a graph from a raw log is O(events) with a couple of hash lookups per event;\n * on a 200 000-row import that is long enough to drop frames, and a dropped frame during a\n * file import is exactly the moment an app reads as broken. So the derivation moves to a\n * worker — but ONLY as an optimization: the caller never branches on the environment.\n *\n * **Degrade, never fail.** When there is no `Worker` (a server render, a Node test, an\n * embedded webview), when constructing one throws, or when a live one errors, the same\n * functions run inline on the calling thread and the promise still resolves with the same\n * answer. `handleProcessRequest` is literally shared with the worker entry, so \"the inline\n * path agrees with the worker path\" is a property of the code, not of a test.\n *\n * **Normalize once.** Every method takes an `AnyLog`; hand it an already-normalized log\n * (`asNormalizedLog`) and neither side re-parses. A `NormalizedLog` is plain data and\n * clones across `postMessage` unchanged.\n */\nimport type { DiscoverGraphOptions } from \"../discover-graph\";\nimport type { AnyLog } from \"../event-log\";\nimport type { ProcessGraph, Variant } from \"../types\";\nimport {\n handleProcessRequest,\n type ProcessWorkerRequest,\n type ProcessWorkerResponse,\n} from \"./process-worker\";\n\n/**\n * The slice of the `Worker` interface this module uses.\n *\n * Narrow on purpose: it is the seam a test (or a bundler with its own worker construction)\n * substitutes, and demanding the full DOM `Worker` surface for that would be pointless\n * ceremony. A real `Worker` satisfies it.\n */\nexport interface ProcessWorkerLike {\n postMessage(message: unknown): void;\n terminate(): void;\n addEventListener(\n type: \"message\" | \"error\" | \"messageerror\",\n listener: (event: unknown) => void,\n ): void;\n}\n\n/** Options for {@link createProcessWorker}. */\nexport interface CreateProcessWorkerOptions {\n /** Skip the worker entirely and run inline. Useful for benchmarks and for tests. */\n forceInline?: boolean;\n /**\n * Build the worker yourself.\n *\n * The default construction is\n * `new Worker(new URL(\"./process-worker.ts\", import.meta.url), { type: \"module\" })`.\n * A bundler compiling THIS SOURCE (Vite, webpack) rewrites that literal itself; a\n * consumer of the BUILT package gets `dist/core/process-worker.js`, which\n * `tsup.config.ts` emits as its own pass and points the built bundle at — a bundler\n * rewrites the source it compiles, never a `dist` it merely consumes, so that file has\n * to be real. A host that needs its own URL supplies this instead. Throwing from it is\n * safe: the handle falls back to the inline path.\n */\n createWorker?: () => ProcessWorkerLike;\n}\n\n/** What {@link createProcessWorker} hands back. */\nexport interface ProcessWorkerHandle {\n /** Discover a directly-follows graph. */\n discover(log: AnyLog, options?: DiscoverGraphOptions): Promise<ProcessGraph>;\n /** Group the log's cases into variants. */\n variants(log: AnyLog): Promise<Variant[]>;\n /** Stop the worker. Pending promises reject, and so does every later call. */\n terminate(): void;\n /**\n * `true` when work is currently running on the CALLING thread — either because the\n * environment has no worker, or because one failed and the handle degraded.\n */\n readonly inline: boolean;\n}\n\ninterface Pending {\n request: ProcessWorkerRequest;\n settle: (response: ProcessWorkerResponse) => void;\n}\n\nfunction workerConstructible(): boolean {\n return typeof Worker !== \"undefined\" && typeof URL !== \"undefined\";\n}\n\n/**\n * Create a handle that computes off-thread when it can and inline when it cannot.\n *\n * The worker is created LAZILY, on the first request, so constructing a handle costs\n * nothing in an environment that never uses it.\n */\nexport function createProcessWorker(options: CreateProcessWorkerOptions = {}): ProcessWorkerHandle {\n const construct = options.createWorker;\n let inline = options.forceInline === true || (construct === undefined && !workerConstructible());\n let terminated = false;\n let worker: ProcessWorkerLike | undefined;\n let nextId = 0;\n const pending = new Map<number, Pending>();\n\n /** Give up on the worker and answer everything outstanding on this thread. */\n function degrade(): void {\n inline = true;\n const stale = [...pending.values()];\n pending.clear();\n if (worker !== undefined) {\n const dying = worker;\n worker = undefined;\n try {\n dying.terminate();\n } catch {\n // A worker that cannot be terminated is already gone; nothing to recover.\n }\n }\n for (const entry of stale) entry.settle(handleProcessRequest(entry.request));\n }\n\n function onMessage(event: unknown): void {\n const response = (event as { data?: unknown }).data as ProcessWorkerResponse | undefined;\n if (response === undefined || typeof response.id !== \"number\") return;\n const entry = pending.get(response.id);\n if (entry === undefined) return;\n pending.delete(response.id);\n entry.settle(response);\n }\n\n function ensureWorker(): ProcessWorkerLike | undefined {\n if (inline || terminated) return undefined;\n if (worker !== undefined) return worker;\n try {\n worker =\n construct === undefined\n ? (new Worker(new URL(\"./process-worker.ts\", import.meta.url), {\n type: \"module\",\n }) as unknown as ProcessWorkerLike)\n : construct();\n } catch {\n degrade();\n return undefined;\n }\n worker.addEventListener(\"message\", onMessage);\n worker.addEventListener(\"error\", degrade);\n worker.addEventListener(\"messageerror\", degrade);\n return worker;\n }\n\n function send(build: (id: number) => ProcessWorkerRequest): Promise<ProcessWorkerResponse> {\n if (terminated) return Promise.reject(new Error(\"process worker terminated\"));\n nextId += 1;\n const request = build(nextId);\n const active = ensureWorker();\n if (active === undefined) {\n // Inline, but still a microtask later, so the caller's own frame returns first.\n return Promise.resolve().then(() => handleProcessRequest(request));\n }\n return new Promise<ProcessWorkerResponse>((resolve, reject) => {\n pending.set(request.id, { request, settle: resolve });\n try {\n active.postMessage(request);\n } catch {\n pending.delete(request.id);\n degrade();\n try {\n resolve(handleProcessRequest(request));\n } catch (error) {\n reject(error instanceof Error ? error : new Error(String(error)));\n }\n }\n });\n }\n\n function unwrap<T>(response: ProcessWorkerResponse, read: (ok: ProcessWorkerResponse) => T): T {\n if (!response.ok) throw new Error(response.error);\n return read(response);\n }\n\n return {\n async discover(log, discoverOptions) {\n const response = await send((id) =>\n discoverOptions === undefined\n ? { id, kind: \"discover\", log }\n : { id, kind: \"discover\", log, options: discoverOptions },\n );\n return unwrap(response, (ok) => (ok as { graph: ProcessGraph }).graph);\n },\n async variants(log) {\n const response = await send((id) => ({ id, kind: \"variants\", log }));\n return unwrap(response, (ok) => (ok as { variants: Variant[] }).variants);\n },\n terminate() {\n terminated = true;\n const stale = [...pending.values()];\n pending.clear();\n for (const entry of stale) {\n entry.settle({ id: entry.request.id, ok: false, error: \"process worker terminated\" });\n }\n if (worker !== undefined) {\n const dying = worker;\n worker = undefined;\n try {\n dying.terminate();\n } catch {\n // Already gone.\n }\n }\n },\n get inline(): boolean {\n return inline;\n },\n };\n}\n","/**\n * Activity colour scale — RM-054.\n *\n * One activity, one colour, across every process view. `ProcessMap` paints the colour as\n * a small accent mark on the activity node; `VariantExplorer` paints it on the sequence\n * chips. Both read the SAME scale instance built from one graph, so \"Create Order\" is the\n * same swatch in the map and in the variant list.\n *\n * ## The colour budget\n *\n * The chart palette ships twelve series tokens (`--chart-1` … `--chart-12`). The eleven\n * most frequent activities (ranked by the number of cases they occur in) take\n * `--chart-1` … `--chart-11`; every remaining activity shares `--chart-12` and is flagged\n * `pattern: \"other\"`, which the views render as a hatch so \"other\" never reads as a\n * twelfth distinct activity. Colour is never the only channel: every view that paints a\n * swatch also prints the activity label (or its two-letter {@link ActivityColorScale.codeFor}\n * code) as text.\n *\n * Pure and framework-free: returns token NAMES, never resolved colours, so a theme switch\n * re-inks every swatch with no recomputation.\n */\nimport type { ProcessGraph } from \"./types\";\n\n/** How many activities get a distinct palette slot before the rest share \"other\". */\nexport const ACTIVITY_COLOR_SLOTS = 11;\n\n/** The token every activity outside the top {@link ACTIVITY_COLOR_SLOTS} shares. */\nexport const ACTIVITY_OTHER_TOKEN = \"--chart-12\";\n\n/** The colour one activity is painted with. */\nexport interface ActivityColor {\n /** A CSS custom-property NAME, e.g. `\"--chart-3\"`. Paint with `var(${token})`. */\n token: string;\n /** Present for the shared \"other\" bucket — render the hatch, not a flat swatch. */\n pattern?: \"other\";\n}\n\n/** One legend entry, in rank order. */\nexport interface ActivityColorLegendEntry extends ActivityColor {\n activityId: string;\n label: string;\n /** A two-character code unique within the scale, for abbreviated (\"DNA strip\") chips. */\n code: string;\n}\n\n/** The shared activity→colour mapping. */\nexport interface ActivityColorScale {\n /** The colour for an activity. An id the graph never contained is \"other\". */\n colorFor(activityId: string): ActivityColor;\n /** The two-character code for an activity; derived from the id when it is unknown. */\n codeFor(activityId: string): string;\n /** The activity's display label; the id itself when it is unknown. */\n labelFor(activityId: string): string;\n /** Every activity in the graph, ranked by case count descending, ties by id. */\n legend: ActivityColorLegendEntry[];\n}\n\nconst OTHER: ActivityColor = Object.freeze({ token: ACTIVITY_OTHER_TOKEN, pattern: \"other\" });\n\n/** Upper-cased letters and digits of a label — the material codes are built from. */\nfunction alphanumerics(label: string): string {\n return label.replace(/[^\\p{L}\\p{N}]/gu, \"\").toUpperCase();\n}\n\n/**\n * Candidate two-character codes for a label, most readable first: initials of the first\n * two words (\"Create Order\" → \"CO\"), the first two letters (\"Approve\" → \"AP\"), then the\n * first letter paired with every later letter, then the first letter plus a digit.\n */\nfunction codeCandidates(label: string): string[] {\n const out: string[] = [];\n const words = label.split(/[^\\p{L}\\p{N}]+/u).filter(Boolean);\n const first = words[0];\n const second = words[1];\n if (first && second) out.push(`${first[0]}${second[0]}`.toUpperCase());\n const letters = alphanumerics(label);\n const head = letters[0] ?? \"?\";\n if (letters.length >= 2) out.push(letters.slice(0, 2));\n for (let i = 2; i < letters.length; i += 1) out.push(`${head}${letters[i]}`);\n for (let digit = 1; digit <= 9; digit += 1) out.push(`${head}${digit}`);\n return out;\n}\n\n/** A code for a label, avoiding every code already in `taken`. */\nfunction uniqueCode(label: string, taken: Set<string>): string {\n for (const candidate of codeCandidates(label)) {\n if (!taken.has(candidate)) return candidate;\n }\n // Pathological: more than ~40 activities sharing one first letter. Fall back to a\n // base-36 counter so the code is still two characters and still unique.\n for (let n = 0; n < 36 * 36; n += 1) {\n const candidate = n.toString(36).toUpperCase().padStart(2, \"0\");\n if (!taken.has(candidate)) return candidate;\n }\n return (alphanumerics(label).slice(0, 2) || \"??\").padEnd(2, \"?\");\n}\n\n/**\n * Build the shared colour scale for a graph.\n *\n * Deterministic: the same graph always yields the same assignment, regardless of the\n * order `graph.activities` arrives in. Build it from the FULL (unfiltered, unabstracted)\n * graph so colours stay put while a reader filters or abstracts.\n */\nexport function activityColorScale(graph: ProcessGraph): ActivityColorScale {\n const ranked = [...graph.activities].sort(\n (a, b) => b.cases - a.cases || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0),\n );\n\n const colors = new Map<string, ActivityColor>();\n const codes = new Map<string, string>();\n const labels = new Map<string, string>();\n const taken = new Set<string>();\n const legend: ActivityColorLegendEntry[] = ranked.map((activity, index) => {\n const color: ActivityColor =\n index < ACTIVITY_COLOR_SLOTS ? { token: `--chart-${index + 1}` } : OTHER;\n const label = activity.label || activity.id;\n const code = uniqueCode(label, taken);\n taken.add(code);\n colors.set(activity.id, color);\n codes.set(activity.id, code);\n labels.set(activity.id, label);\n return { activityId: activity.id, label, code, ...color };\n });\n\n return {\n colorFor: (activityId) => colors.get(activityId) ?? OTHER,\n codeFor: (activityId) =>\n codes.get(activityId) ?? (alphanumerics(activityId).slice(0, 2) || \"??\").padEnd(2, \"?\"),\n labelFor: (activityId) => labels.get(activityId) ?? activityId,\n legend,\n };\n}\n","/**\n * casesFromLog — RM-055.\n *\n * A pure per-case summary over an event log: one {@link CaseRow} per case, shaped for\n * `CaseTable`'s default columns and CSV export (`toCsv`). `variantId` is read off\n * {@link extractVariants} rather than recomputed independently — `extractVariants` is the\n * one place that disambiguates a 64-bit hash collision between two different sequences\n * with a `-1`/`-2` suffix, so recomputing `variantId(sequence)` here directly would\n * silently diverge from a variant explorer over the SAME log on the rare log where that\n * disambiguation fires. Reading it off `extractVariants`'s own `caseIds` keeps the two\n * views byte-identical always, not just usually.\n *\n * Framework-free, deterministic: no React, no `Date.now()`, no randomness. See\n * `.claude/rules/data.md` — nothing under `src/core/` may import React or an\n * `@elabs-ai/components-*` package.\n */\nimport { asNormalizedLog, type NormalizedCase } from \"./event-log\";\nimport { extractVariants } from \"./extract-variants\";\nimport type { EventLog } from \"./types\";\n\n/**\n * One case's own summary — its extent, size and path identity, not its trace. The shape\n * `CaseTable` (case-table/) renders one row per, and `CaseTimeline`'s own model reads a\n * single case's raw `EventRow[]` separately (a summary row has no per-activity detail to\n * build a Gantt row from).\n */\nexport interface CaseRow {\n caseId: string;\n /** ISO 8601. Empty string when the case has no resolvable extent — see `normalizeLog`. */\n start: string;\n end: string;\n durationMs: number;\n eventCount: number;\n /** Identical to the `id` {@link extractVariants} assigns the SAME case over the SAME log. */\n variantId: string;\n /**\n * Never set here: no conformance model lives in `/core` (`ProcessKpiStrip`'s own\n * `conformance` prop treats it the same way — a fitted value the HOST supplies, never a\n * number this package invents). A caller with a conformance result attaches it per row.\n */\n conformance?: \"conforming\" | \"nonConforming\" | \"unknown\";\n /** Carried over from `EventLog.caseAttributes`, untouched. */\n attributes?: Record<string, string | number | boolean | null>;\n}\n\n/** `NaN` (no resolvable extent) becomes `\"\"`, never a thrown `RangeError` or `\"Invalid Date\"`. */\nfunction toIso(ms: number): string {\n return Number.isFinite(ms) ? new Date(ms).toISOString() : \"\";\n}\n\nfunction toCaseRow(kase: NormalizedCase, variantId: string): CaseRow {\n const row: CaseRow = {\n caseId: kase.caseId,\n start: toIso(kase.start),\n end: toIso(kase.end),\n durationMs: Number.isFinite(kase.duration) ? kase.duration : 0,\n eventCount: kase.events.length,\n variantId,\n };\n if (kase.attributes !== undefined) {\n row.attributes = kase.attributes as Record<string, string | number | boolean | null>;\n }\n return row;\n}\n\n/**\n * One summary row per case in `log`, in the same first-appearance order `normalizeLog`\n * produces. `variantId` is sourced from {@link extractVariants} over the identical log —\n * see the module docblock for why that, and not a direct `variantId(sequence)` call, is\n * what keeps a `CaseTable` and a variant explorer over the same log always agreeing on\n * which cases share a path.\n *\n * An empty log answers an empty array.\n */\nexport function casesFromLog(log: EventLog): CaseRow[] {\n const normalized = asNormalizedLog(log);\n if (normalized.cases.length === 0) return [];\n\n const variantIdByCase = new Map<string, string>();\n for (const variant of extractVariants(normalized)) {\n for (const caseId of variant.caseIds) variantIdByCase.set(caseId, variant.id);\n }\n\n return normalized.cases.map((kase) => toCaseRow(kase, variantIdByCase.get(kase.caseId) ?? \"\"));\n}\n","/**\n * Reference model — RM-061.\n *\n * A HAPPY PATH (the prescribed sequence of activities an owner or auditor expects) lifted\n * to the smallest workflow-net-like structure token replay can run against: one place\n * between consecutive steps, one visible transition per step, a silent SKIP transition\n * beside every optional step and a SELF-LOOP transition after every repeatable one.\n *\n * Scope boundary (analysis §9 risk 2): this is deliberately NOT a general Petri-net\n * importer. No BPMN, no parallel gateways, no alignments beyond skip/repeat. A host that\n * needs full alignment-based conformance brings a backend; this module never grows one.\n *\n * Framework-free, deterministic: no React, no `@elabs-ai/components-*` import.\n */\n\n/** One prescribed step of a happy path. */\nexport interface HappyPathStep {\n /** Activity name, matched exactly against `EventRow.activity`. */\n activity: string;\n /** The step may be left out without a deviation (a silent skip arc is added). */\n optional?: boolean;\n /** The step may run several times in a row without a deviation (a self-loop is added). */\n repeatable?: boolean;\n}\n\n/** A prescribed process: an ordered list of steps. */\nexport interface HappyPath {\n id: string;\n label: string;\n steps: HappyPathStep[];\n}\n\n/**\n * What a transition stands for. `\"step\"` fires a step's activity for the first time,\n * `\"repeat\"` fires it again from the place after it, `\"skip\"` is SILENT (no activity is\n * observed) and moves the token past an optional step.\n */\nexport type ReplayTransitionKind = \"step\" | \"skip\" | \"repeat\";\n\n/** One transition of a {@link ReplayModel}. */\nexport interface ReplayTransition {\n id: string;\n /** The activity this transition fires on. The skipped step's activity for a `\"skip\"`. */\n activity: string;\n kind: ReplayTransitionKind;\n /** Input places — one token each is consumed when the transition fires. */\n consumes: string[];\n /** Output places — one token each is produced when the transition fires. */\n produces: string[];\n}\n\n/** A workflow-net-like replay structure. Places are listed in path order. */\nexport interface ReplayModel {\n places: string[];\n initialMarking: string[];\n finalMarking: string[];\n transitions: ReplayTransition[];\n}\n\n/** Name of the place before step `index` (and after step `index - 1`). */\nfunction placeName(index: number): string {\n return `p${index}`;\n}\n\n/**\n * Lift a happy path to a {@link ReplayModel}.\n *\n * For `n` steps the model has places `p0 … pn`, starts with one token in `p0` and ends\n * with one token in `pn`. Step `i` gets transition `t<i>` (`p<i>` → `p<i+1>`); an optional\n * step also gets a silent `skip<i>` over the same places; a repeatable step also gets\n * `repeat<i>` consuming and producing `p<i+1>`. An empty path lifts to a single place\n * that is both initial and final.\n */\nexport function liftHappyPath(path: HappyPath): ReplayModel {\n const steps = path.steps;\n const places: string[] = [];\n for (let i = 0; i <= steps.length; i += 1) places.push(placeName(i));\n\n const transitions: ReplayTransition[] = [];\n steps.forEach((step, i) => {\n const before = placeName(i);\n const after = placeName(i + 1);\n transitions.push({\n id: `t${i}`,\n activity: step.activity,\n kind: \"step\",\n consumes: [before],\n produces: [after],\n });\n if (step.optional) {\n transitions.push({\n id: `skip${i}`,\n activity: step.activity,\n kind: \"skip\",\n consumes: [before],\n produces: [after],\n });\n }\n if (step.repeatable) {\n transitions.push({\n id: `repeat${i}`,\n activity: step.activity,\n kind: \"repeat\",\n consumes: [after],\n produces: [after],\n });\n }\n });\n\n return {\n places,\n initialMarking: [placeName(0)],\n finalMarking: [placeName(steps.length)],\n transitions,\n };\n}\n","/**\n * Token-based replay — RM-061.\n *\n * Replays each case of an event log against a {@link ReplayModel} (a lifted happy path)\n * and scores it with the classic produced / consumed / missing / remaining token counts:\n *\n * fitness = ½ · (1 − missing / consumed) + ½ · (1 − remaining / produced)\n *\n * The replay procedure — seed the initial marking, fire an enabled transition per event,\n * enable a disabled one through silent transitions first, force-insert missing tokens\n * only when that fails, then consume the final marking and count what is left over —\n * follows the token-based replay algorithm published in pm4js (BSD-3-Clause, credited in\n * `scripts/attributions.sources.json`). It is re-typed in TypeScript for the restricted\n * nets `liftHappyPath` builds; no pm4js code is copied, and nothing here derives from the\n * AGPL Python reference implementation.\n *\n * Deviation typing is this module's own layer on top of the counts: every forced token\n * (and every unreachable final token) is explained by at least one\n * {@link Deviation}, so a violation list can say WHY a case lost fitness.\n *\n * Scope boundary (analysis §9 risk 2): no alignment search. Replay is greedy and\n * single-pass per trace.\n *\n * Framework-free, deterministic: no React, no `@elabs-ai/components-*` import.\n */\nimport type { ConformanceResult } from \"./conformance\";\nimport { EDGE_KEY_SEPARATOR } from \"./discover-graph\";\nimport { asNormalizedLog, normalizeLog, type AnyLog, type NormalizedEvent } from \"./event-log\";\nimport type { ReplayModel, ReplayTransition } from \"./reference-model\";\nimport type { EventRow } from \"./types\";\n\n/**\n * Why a case lost fitness.\n *\n * - `undesired` — the activity is not in the model at all.\n * - `skipped` — a required modelled activity was jumped over and never observed in the case.\n * - `wrongOrder` — the activity is modelled but its preceding place was unmarked, and the\n * gap is not explained by an unobserved step (it ran too early, too late, or again).\n * - `wrongStart` — the first event of the case is not one the initial marking enables.\n * - `incomplete` — the case ended before reaching the model's final marking.\n */\nexport type DeviationType = \"undesired\" | \"skipped\" | \"wrongOrder\" | \"wrongStart\" | \"incomplete\";\n\n/** Every {@link DeviationType}, in a stable display order. */\nexport const DEVIATION_TYPES: readonly DeviationType[] = [\n \"undesired\",\n \"skipped\",\n \"wrongOrder\",\n \"wrongStart\",\n \"incomplete\",\n];\n\n/** One deviation found while replaying a case. */\nexport interface Deviation {\n type: DeviationType;\n /**\n * The activity the deviation is about: the observed event for `undesired`/`wrongOrder`/\n * `wrongStart`, the missing step for `skipped`, the last observed event for `incomplete`.\n */\n activity?: string;\n /** The activity the model expected next at that point, when one is determinable. */\n expected?: string;\n /**\n * Zero-based index into the case's ordered trace of the event where the deviation was\n * detected. `incomplete` uses the trace length (the position after the last event).\n */\n at: number;\n}\n\n/** Replay result for one case. */\nexport interface TraceReplayResult {\n caseId: string;\n produced: number;\n consumed: number;\n missing: number;\n remaining: number;\n /** `½(1 − missing/consumed) + ½(1 − remaining/produced)`, in `[0, 1]`. */\n fitness: number;\n deviations: Deviation[];\n}\n\n// ── Compiled model ────────────────────────────────────────────────────────────\n\ninterface CompiledModel {\n model: ReplayModel;\n /** Visible (`step`/`repeat`) transitions by activity, `step`s first. */\n byActivity: Map<string, ReplayTransition[]>;\n /** place → outgoing single-input transitions, silent ones first. */\n outgoing: Map<string, ReplayTransition[]>;\n /** place → outgoing silent single-input transitions. */\n silentOutgoing: Map<string, ReplayTransition[]>;\n /** `step` transitions in model order — the candidates for `expected`. */\n steps: ReplayTransition[];\n}\n\nconst compiled = new WeakMap<ReplayModel, CompiledModel>();\n\nfunction pushTo<K, V>(map: Map<K, V[]>, key: K, value: V): void {\n const list = map.get(key);\n if (list === undefined) map.set(key, [value]);\n else list.push(value);\n}\n\nfunction compile(model: ReplayModel): CompiledModel {\n const cached = compiled.get(model);\n if (cached !== undefined) return cached;\n\n const byActivity = new Map<string, ReplayTransition[]>();\n const outgoing = new Map<string, ReplayTransition[]>();\n const silentOutgoing = new Map<string, ReplayTransition[]>();\n const steps: ReplayTransition[] = [];\n\n const silent = model.transitions.filter((t) => t.kind === \"skip\");\n const visible = model.transitions.filter((t) => t.kind !== \"skip\");\n\n for (const t of [...visible].sort((a, b) => rank(a) - rank(b))) {\n pushTo(byActivity, t.activity, t);\n }\n for (const t of visible) if (t.kind === \"step\") steps.push(t);\n // Silent first, so a breadth-first search reaches a place over a skip arc before it\n // reaches it over the parallel visible step — an optional step is never reported\n // `skipped`.\n for (const t of [...silent, ...visible]) {\n if (t.consumes.length !== 1 || t.kind === \"repeat\") continue;\n const from = t.consumes[0] as string;\n pushTo(outgoing, from, t);\n if (t.kind === \"skip\") pushTo(silentOutgoing, from, t);\n }\n\n const result: CompiledModel = { model, byActivity, outgoing, silentOutgoing, steps };\n compiled.set(model, result);\n return result;\n}\n\nfunction rank(t: ReplayTransition): number {\n return t.kind === \"step\" ? 0 : 1;\n}\n\n// ── Marking helpers ───────────────────────────────────────────────────────────\n\ntype Marking = Map<string, number>;\n\nfunction tokens(marking: Marking, place: string): number {\n return marking.get(place) ?? 0;\n}\n\nfunction isEnabled(marking: Marking, t: ReplayTransition): boolean {\n const need = new Map<string, number>();\n for (const place of t.consumes) need.set(place, (need.get(place) ?? 0) + 1);\n for (const [place, count] of need) if (tokens(marking, place) < count) return false;\n return true;\n}\n\nfunction fire(marking: Marking, t: ReplayTransition): void {\n for (const place of t.consumes) marking.set(place, tokens(marking, place) - 1);\n for (const place of t.produces) marking.set(place, tokens(marking, place) + 1);\n}\n\n/**\n * Shortest chain of transitions from any marked place to `target`, walking `edges`.\n * Returns `undefined` when no marked place reaches it. A place that is already marked\n * yields an empty chain.\n */\nfunction shortestChain(\n marking: Marking,\n target: string,\n edges: Map<string, ReplayTransition[]>,\n): ReplayTransition[] | undefined {\n if (tokens(marking, target) > 0) return [];\n const parent = new Map<string, ReplayTransition | null>();\n const queue: string[] = [];\n for (const [place, count] of marking) {\n if (count > 0 && !parent.has(place)) {\n parent.set(place, null);\n queue.push(place);\n }\n }\n for (let head = 0; head < queue.length; head += 1) {\n const place = queue[head] as string;\n for (const t of edges.get(place) ?? []) {\n for (const next of t.produces) {\n if (parent.has(next)) continue;\n parent.set(next, t);\n if (next === target) return unwind(parent, target);\n queue.push(next);\n }\n }\n }\n return undefined;\n}\n\nfunction unwind(parent: Map<string, ReplayTransition | null>, target: string): ReplayTransition[] {\n const chain: ReplayTransition[] = [];\n let at = parent.get(target);\n while (at !== null && at !== undefined) {\n chain.unshift(at);\n at = parent.get(at.consumes[0] as string);\n }\n return chain;\n}\n\ninterface Counters {\n produced: number;\n consumed: number;\n missing: number;\n}\n\n/**\n * Try to put a token into every place in `places` by firing silent transitions only.\n * Commits the firings (and counts them) only when every place ends up marked.\n */\nfunction enableSilently(\n cm: CompiledModel,\n marking: Marking,\n places: string[],\n counters: Counters,\n): boolean {\n const trial = new Map(marking);\n let produced = 0;\n let consumed = 0;\n for (const place of places) {\n if (tokens(trial, place) > 0) continue;\n const chain = shortestChain(trial, place, cm.silentOutgoing);\n if (chain === undefined) return false;\n for (const t of chain) {\n if (!isEnabled(trial, t)) return false;\n fire(trial, t);\n consumed += t.consumes.length;\n produced += t.produces.length;\n }\n }\n for (const [place, count] of trial) marking.set(place, count);\n counters.produced += produced;\n counters.consumed += consumed;\n return true;\n}\n\n/** The first step the model would accept next from `marking`, looking through skip arcs. */\nfunction expectedNext(cm: CompiledModel, marking: Marking): string | undefined {\n const reachable = new Set<string>();\n const queue: string[] = [];\n for (const [place, count] of marking) {\n if (count > 0) {\n reachable.add(place);\n queue.push(place);\n }\n }\n for (let head = 0; head < queue.length; head += 1) {\n for (const t of cm.silentOutgoing.get(queue[head] as string) ?? []) {\n for (const next of t.produces) {\n if (reachable.has(next)) continue;\n reachable.add(next);\n queue.push(next);\n }\n }\n }\n for (const t of cm.steps) {\n if (t.consumes.every((place) => reachable.has(place))) return t.activity;\n }\n return undefined;\n}\n\nfunction fitnessOf(c: Counters & { remaining: number }): number {\n const missingTerm = c.consumed > 0 ? 1 - c.missing / c.consumed : 1;\n const remainingTerm = c.produced > 0 ? 1 - c.remaining / c.produced : 1;\n return Math.min(1, Math.max(0, 0.5 * missingTerm + 0.5 * remainingTerm));\n}\n\n// ── Replay ────────────────────────────────────────────────────────────────────\n\n/**\n * Replay an already-ordered activity sequence. The shared engine behind\n * {@link replayTrace}, {@link tokenReplay} and `conformanceRateSeries`.\n */\nexport function replayActivities(\n caseId: string,\n trace: readonly string[],\n model: ReplayModel,\n): TraceReplayResult {\n const cm = compile(model);\n const marking: Marking = new Map();\n const counters: Counters = { produced: 0, consumed: 0, missing: 0 };\n const deviations: Deviation[] = [];\n const observed = new Set(trace);\n\n for (const place of model.initialMarking) {\n marking.set(place, tokens(marking, place) + 1);\n counters.produced += 1;\n }\n\n trace.forEach((activity, at) => {\n const candidates = cm.byActivity.get(activity);\n\n if (candidates === undefined || candidates.length === 0) {\n // Not in the model: a phantom firing that consumes one forced (missing) token.\n deviations.push({ type: \"undesired\", activity, expected: expectedNext(cm, marking), at });\n counters.missing += 1;\n counters.consumed += 1;\n return;\n }\n\n const enabled =\n candidates.find((t) => isEnabled(marking, t)) ??\n candidates.find((t) => enableSilently(cm, marking, t.consumes, counters));\n if (enabled !== undefined) {\n fire(marking, enabled);\n counters.consumed += enabled.consumes.length;\n counters.produced += enabled.produces.length;\n return;\n }\n\n // Force: pick the candidate the current marking is closest to, else the first `step`.\n const expected = expectedNext(cm, marking);\n let chosen = candidates[0] as ReplayTransition;\n let chain: ReplayTransition[] | undefined;\n for (const t of candidates) {\n const gap = t.consumes.find((place) => tokens(marking, place) === 0);\n if (gap === undefined) continue;\n const found = shortestChain(marking, gap, cm.outgoing);\n if (found !== undefined && (chain === undefined || found.length < chain.length)) {\n chain = found;\n chosen = t;\n }\n }\n\n if (at === 0) {\n deviations.push({ type: \"wrongStart\", activity, expected, at });\n } else {\n const unobserved = (chain ?? []).filter(\n (t) => t.kind === \"step\" && !observed.has(t.activity),\n );\n if (unobserved.length > 0) {\n const seen = new Set<string>();\n for (const t of unobserved) {\n if (seen.has(t.activity)) continue;\n seen.add(t.activity);\n deviations.push({ type: \"skipped\", activity: t.activity, expected: t.activity, at });\n }\n } else {\n deviations.push({ type: \"wrongOrder\", activity, expected, at });\n }\n }\n\n for (const place of chosen.consumes) {\n if (tokens(marking, place) === 0) {\n marking.set(place, 1);\n counters.missing += 1;\n }\n }\n fire(marking, chosen);\n counters.consumed += chosen.consumes.length;\n counters.produced += chosen.produces.length;\n });\n\n // Consume the final marking, reaching it through skip arcs where the tail is optional.\n enableSilently(cm, marking, model.finalMarking, counters);\n const expected = expectedNext(cm, marking);\n let reachedEnd = true;\n for (const place of model.finalMarking) {\n if (tokens(marking, place) > 0) marking.set(place, tokens(marking, place) - 1);\n else {\n counters.missing += 1;\n reachedEnd = false;\n }\n counters.consumed += 1;\n }\n if (!reachedEnd) {\n deviations.push({\n type: \"incomplete\",\n activity: trace.length > 0 ? trace[trace.length - 1] : undefined,\n expected,\n at: trace.length,\n });\n }\n\n let remaining = 0;\n for (const count of marking.values()) remaining += count;\n\n const result = { caseId, ...counters, remaining };\n return { ...result, fitness: fitnessOf(result), deviations };\n}\n\nfunction activitiesOf(events: readonly NormalizedEvent[]): string[] {\n return events.map((event) => event.activity);\n}\n\n/**\n * Replay ONE case's raw rows against `model`.\n *\n * Rows are normalized first (ordered in time, lifecycle pairs merged into one instance),\n * exactly as every other `/core` derivation sees them. The rows are expected to share one\n * `caseId`; if they do not, every instance is replayed as one trace in start order under\n * the first row's `caseId`.\n */\nexport function replayTrace(events: EventRow[], model: ReplayModel): TraceReplayResult {\n const normalized = normalizeLog({ events });\n const instances = normalized.cases.flatMap((kase) => kase.events);\n if (normalized.cases.length > 1) instances.sort((a, b) => a.start - b.start);\n const caseId = normalized.cases[0]?.caseId ?? events[0]?.caseId ?? \"\";\n return replayActivities(caseId, activitiesOf(instances), model);\n}\n\n/** A {@link DeviationType}-keyed tally with every type present at zero. */\nexport function emptyDeviationCounts(): Record<DeviationType, number> {\n return { undesired: 0, skipped: 0, wrongOrder: 0, wrongStart: 0, incomplete: 0 };\n}\n\n/**\n * Replay every case of `log` against `model` and fold the results.\n *\n * - `deviationCounts[type]` — total deviations of that type across all cases.\n * - `perActivity[activity].deviations` — every deviation carries an activity, so these sum\n * to the total deviation count.\n * - `perEdge[source + EDGE_KEY_SEPARATOR + target].deviations` — a deviation detected at an\n * event with a predecessor is charged to the OBSERVED directly-follows edge into that\n * event (the key `discoverGraph` uses for the same edge). A deviation at the first event\n * or at the end of the case has no edge, so these sum to at most the total.\n * - `overallFitness` — mean trace fitness; `0` for an empty log.\n */\nexport function tokenReplay(log: AnyLog, model: ReplayModel): ConformanceResult {\n const normalized = asNormalizedLog(log);\n const traces: TraceReplayResult[] = [];\n const deviationCounts = emptyDeviationCounts();\n // Prototype-free, so an activity named `constructor` or `__proto__` is just a key.\n const perActivity = Object.create(null) as Record<string, { deviations: number }>;\n const perEdge = Object.create(null) as Record<string, { deviations: number }>;\n let fitnessSum = 0;\n\n for (const kase of normalized.cases) {\n const trace = activitiesOf(kase.events);\n const result = replayActivities(kase.caseId, trace, model);\n traces.push(result);\n fitnessSum += result.fitness;\n\n for (const deviation of result.deviations) {\n deviationCounts[deviation.type] += 1;\n if (deviation.activity !== undefined) {\n const entry = (perActivity[deviation.activity] ??= { deviations: 0 });\n entry.deviations += 1;\n }\n if (deviation.at > 0 && deviation.at < trace.length) {\n const key = `${trace[deviation.at - 1]}${EDGE_KEY_SEPARATOR}${trace[deviation.at]}`;\n const entry = (perEdge[key] ??= { deviations: 0 });\n entry.deviations += 1;\n }\n }\n }\n\n return {\n overallFitness: traces.length > 0 ? fitnessSum / traces.length : 0,\n traces,\n deviationCounts,\n perActivity,\n perEdge,\n };\n}\n","/**\n * Conformance model — RM-061.\n *\n * The log-level result of replaying an event log against a reference model\n * ({@link ConformanceResult}, produced by `tokenReplay`) and the fitness-over-time series a\n * KPI sparkline plots ({@link conformanceRateSeries}).\n *\n * Scope boundary (analysis §9 risk 2): token replay against a lifted happy path only — no\n * alignments, no BPMN import. A host needing either brings a backend.\n *\n * Framework-free, deterministic: no React, no `Date.now()`, no `@elabs-ai/components-*`\n * import.\n */\nimport { asNormalizedLog, type AnyLog } from \"./event-log\";\nimport type { ReplayModel } from \"./reference-model\";\nimport { replayActivities, type DeviationType, type TraceReplayResult } from \"./token-replay\";\n\n/** Log-level conformance of an event log against a reference model. */\nexport interface ConformanceResult {\n /** Mean trace fitness in `[0, 1]`; `0` for an empty log. */\n overallFitness: number;\n /** One result per case, in the log's case order. */\n traces: TraceReplayResult[];\n /** Total deviations per type across all cases; every type is present. */\n deviationCounts: Record<DeviationType, number>;\n /** Deviations charged to each activity. Sums to the total deviation count. */\n perActivity: Record<string, { deviations: number }>;\n /**\n * Deviations charged to each OBSERVED directly-follows edge, keyed\n * `source + EDGE_KEY_SEPARATOR + target` — the same key `discoverGraph` gives the edge.\n * Sums to at most the total deviation count.\n */\n perEdge: Record<string, { deviations: number }>;\n}\n\n/** Calendar granularity of {@link conformanceRateSeries}. All buckets are UTC. */\nexport type ConformanceBucket = \"day\" | \"week\" | \"month\";\n\n/** One point of {@link conformanceRateSeries}. */\nexport interface ConformanceRatePoint {\n /** `YYYY-MM-DD` (day; for week, the UTC Monday that starts it) or `YYYY-MM` (month). */\n bucket: string;\n /** Mean fitness of the cases that started in this bucket. */\n fitness: number;\n caseCount: number;\n}\n\nconst DAY_MS = 86_400_000;\n\nfunction bucketOf(ms: number, bucket: ConformanceBucket): string {\n const date = new Date(ms);\n if (bucket === \"week\") {\n // getUTCDay(): 0 = Sunday … 6 = Saturday; shift so Monday is day 0.\n const offset = (date.getUTCDay() + 6) % 7;\n const monday = new Date(\n Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()) - offset * DAY_MS,\n );\n return monday.toISOString().slice(0, 10);\n }\n const iso = date.toISOString();\n return bucket === \"month\" ? iso.slice(0, 7) : iso.slice(0, 10);\n}\n\n/**\n * Mean replay fitness per calendar bucket, keyed by each case's START time, in ascending\n * bucket order. Empty buckets are not emitted. A case with no resolvable start timestamp\n * cannot be placed in time and is left out, so `caseCount`s sum to the number of cases\n * with a valid start.\n */\nexport function conformanceRateSeries(\n log: AnyLog,\n model: ReplayModel,\n bucket: ConformanceBucket,\n): ConformanceRatePoint[] {\n const totals = new Map<string, { sum: number; count: number }>();\n for (const kase of asNormalizedLog(log).cases) {\n if (!Number.isFinite(kase.start)) continue;\n const key = bucketOf(kase.start, bucket);\n const trace = kase.events.map((event) => event.activity);\n const { fitness } = replayActivities(kase.caseId, trace, model);\n const entry = totals.get(key);\n if (entry === undefined) totals.set(key, { sum: fitness, count: 1 });\n else {\n entry.sum += fitness;\n entry.count += 1;\n }\n }\n return [...totals.entries()]\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([key, { sum, count }]) => ({ bucket: key, fitness: sum / count, caseCount: count }));\n}\n","/**\n * XES adapter — RM-063.\n *\n * IEEE 1849-2016's interchange format for event logs: a `<log>` of `<trace>`s of\n * `<event>`s, each carrying `key`/`value` attributes typed by their own element name\n * (`string`, `date`, `int`, `float`, `boolean`). It is the format most public benchmark\n * logs — including the BPI Challenge series RM-049's synthetic fixture is modelled on —\n * actually ship in.\n *\n * Parsing is hand-rolled here on purpose, and NOT behind a `DOMParser` fast path:\n * `/core` must run identically in Node, in a worker and in a browser main thread, and a\n * parser this narrow — six element names, attribute-only values, the five standard XML\n * entities, self-closing tags — does not need a general XML engine. `DOMParser` is\n * unavailable in Node/workers anyway, and branching on its presence would leave one of\n * the two code paths permanently untested. Do not reach for an XML dependency or\n * `DOMParser` here; extend the tokenizer below instead.\n */\nimport type { EventLog, EventRow } from \"../types\";\n\n/** Options for {@link fromXes}. */\nexport interface XesParseOptions {\n /**\n * Event attribute keys that jointly identify an activity, applied in order and joined\n * with `\"+\"` when there is more than one — the same semantics as an XES `<classifier>`\n * element's `keys` attribute. Defaults to the log's own first `<classifier>`, or\n * `[\"concept:name\"]` when the log declares none.\n */\n classifiers?: string[];\n /**\n * How `lifecycle:transition` maps onto {@link EventRow.lifecycle}. `\"standard\"`\n * (default) maps the literal value `\"start\"` to `\"start\"` and every other XES\n * lifecycle value (`\"complete\"`, `\"schedule\"`, `\"suspend\"`, …) to `\"complete\"`;\n * `\"none\"` ignores the attribute entirely, so every event stays atomic.\n */\n lifecycleModel?: \"standard\" | \"none\";\n}\n\n/** One recoverable problem found while reading a XES document. */\nexport interface XesParseError {\n type: \"malformed_xml\" | \"missing_trace\" | \"missing_concept_name\" | \"missing_timestamp\";\n message: string;\n traceIndex?: number;\n eventIndex?: number;\n}\n\n/**\n * `fromXes`'s result — mirrors the worker's `{ ok: true, … } | { ok: false, … }` result\n * convention (`worker/process-worker.ts`), because unlike `fromCsv`/`fromFlatRows` (which\n * silently skip an incomplete row) a XES document can be genuinely malformed XML, which\n * has nothing sound left to skip to.\n */\nexport type XesParseResult = { ok: true; log: EventLog } | { ok: false; errors: XesParseError[] };\n\ntype XesAttributeValue = string | number | boolean | null;\n\n/** A parsed XML element, scoped to the tags XES actually uses (see the file header). */\ninterface XmlElement {\n tag: string;\n attrs: Record<string, string>;\n children: XmlElement[];\n}\n\n/** Thrown only inside {@link parseXmlDocument}; always caught and turned into `malformed_xml`. */\nclass XmlSyntaxError extends Error {}\n\nconst NAME_STOP = /[\\s/>=]/;\nconst ENTITY_NAMES: Record<string, string> = { amp: \"&\", lt: \"<\", gt: \">\", quot: '\"', apos: \"'\" };\n\n/** Resolve `&amp;`, `&#39;`, `&#x27;` and friends. Anything unrecognized is left as written. */\nfunction decodeXmlEntities(text: string): string {\n return text.replace(/&(#x[0-9a-fA-F]+|#\\d+|[a-zA-Z]+);/g, (match, body: string) => {\n if (body[0] === \"#\") {\n const isHex = body[1] === \"x\" || body[1] === \"X\";\n const codePoint = Number.parseInt(body.slice(isHex ? 2 : 1), isHex ? 16 : 10);\n return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match;\n }\n return ENTITY_NAMES[body] ?? match;\n });\n}\n\n/**\n * Parse well-formed XML into a tree of {@link XmlElement}, decoding entities as it goes.\n * Deliberately narrow: no namespaces, no DTDs, no processing-instruction content, no\n * nested-attribute (XES 2.0) support — the trace/event/string/date/int/float/boolean/\n * classifier/global vocabulary this adapter reads needs none of that.\n */\nfunction parseXmlDocument(text: string): XmlElement {\n let i = 0;\n const n = text.length;\n\n const skipWhitespace = (): void => {\n while (i < n && /\\s/.test(text[i] as string)) i += 1;\n };\n const skipUntil = (marker: string, what: string): void => {\n const at = text.indexOf(marker, i);\n if (at === -1) throw new XmlSyntaxError(`unterminated ${what}`);\n i = at + marker.length;\n };\n const readName = (): string => {\n const start = i;\n while (i < n && !NAME_STOP.test(text[i] as string)) i += 1;\n if (i === start) throw new XmlSyntaxError(`expected a name at offset ${start}`);\n return text.slice(start, i);\n };\n const readAttrs = (): Record<string, string> => {\n const attrs: Record<string, string> = {};\n for (;;) {\n skipWhitespace();\n const c = text[i];\n if (c === undefined) throw new XmlSyntaxError(\"unexpected end of input in a start tag\");\n if (c === \"/\" || c === \">\") return attrs;\n const name = readName();\n skipWhitespace();\n if (text[i] !== \"=\") throw new XmlSyntaxError(`expected \"=\" after attribute \"${name}\"`);\n i += 1;\n skipWhitespace();\n const quote = text[i];\n if (quote !== '\"' && quote !== \"'\") {\n throw new XmlSyntaxError(`expected a quoted value for attribute \"${name}\"`);\n }\n i += 1;\n const start = i;\n while (i < n && text[i] !== quote) i += 1;\n if (i >= n) throw new XmlSyntaxError(`unterminated value for attribute \"${name}\"`);\n attrs[name] = decodeXmlEntities(text.slice(start, i));\n i += 1;\n }\n };\n const readElement = (): XmlElement => {\n i += 1; // consume '<'\n const tag = readName();\n const attrs = readAttrs();\n skipWhitespace();\n if (text[i] === \"/\" && text[i + 1] === \">\") {\n i += 2;\n return { tag, attrs, children: [] };\n }\n if (text[i] !== \">\") throw new XmlSyntaxError(`expected \">\" closing \"<${tag}>\"`);\n i += 1;\n const children: XmlElement[] = [];\n for (;;) {\n const lt = text.indexOf(\"<\", i);\n if (lt === -1) throw new XmlSyntaxError(`unterminated element \"<${tag}>\"`);\n i = lt;\n if (text.startsWith(\"<!--\", i)) {\n skipUntil(\"-->\", \"comment\");\n continue;\n }\n if (text.startsWith(\"<![CDATA[\", i)) {\n skipUntil(\"]]>\", \"CDATA section\");\n continue;\n }\n if (text.startsWith(\"</\", i)) {\n i += 2;\n const closeName = readName();\n skipWhitespace();\n if (text[i] !== \">\") throw new XmlSyntaxError(`expected \">\" closing \"</${closeName}>\"`);\n i += 1;\n if (closeName !== tag) {\n throw new XmlSyntaxError(\n `mismatched closing tag: expected \"</${tag}>\", found \"</${closeName}>\"`,\n );\n }\n return { tag, attrs, children };\n }\n children.push(readElement());\n }\n };\n\n for (;;) {\n skipWhitespace();\n if (i >= n) throw new XmlSyntaxError(\"empty document\");\n if (text.startsWith(\"<?\", i)) {\n skipUntil(\"?>\", \"processing instruction\");\n continue;\n }\n if (text.startsWith(\"<!--\", i)) {\n skipUntil(\"-->\", \"comment\");\n continue;\n }\n if (text.startsWith(\"<!\", i)) {\n skipUntil(\">\", \"declaration\");\n continue;\n }\n break;\n }\n if (text[i] !== \"<\") throw new XmlSyntaxError(\"expected a root element\");\n return readElement();\n}\n\nconst XES_VALUE_TAGS = new Set([\"string\", \"date\", \"int\", \"float\", \"boolean\"]);\n\n/** Coerce one `<string|date|int|float|boolean key=\"…\" value=\"…\">` into a typed value. */\nfunction readXesValue(element: XmlElement): XesAttributeValue {\n const raw = element.attrs.value;\n if (raw === undefined) return null;\n if (element.tag === \"int\") {\n const value = Number.parseInt(raw, 10);\n return Number.isFinite(value) ? value : raw;\n }\n if (element.tag === \"float\") {\n const value = Number.parseFloat(raw);\n return Number.isFinite(value) ? value : raw;\n }\n if (element.tag === \"boolean\") return raw.trim().toLowerCase() === \"true\";\n return raw;\n}\n\n/** Read every direct `key`/`value` attribute child (`string`/`date`/`int`/`float`/`boolean`). */\nfunction readAttributeChildren(children: readonly XmlElement[]): Record<string, XesAttributeValue> {\n const result: Record<string, XesAttributeValue> = {};\n for (const child of children) {\n if (!XES_VALUE_TAGS.has(child.tag)) continue;\n const key = child.attrs.key;\n if (key === undefined) continue;\n result[key] = readXesValue(child);\n }\n return result;\n}\n\n/** `own` values win; a `<global>` default only fills a key `own` never set. */\nfunction withDefaults(\n own: Record<string, XesAttributeValue>,\n defaults: Record<string, XesAttributeValue> | undefined,\n): Record<string, XesAttributeValue> {\n return defaults === undefined ? own : { ...defaults, ...own };\n}\n\n/** Merge every `<global scope=\"…\">` of the given scope, in document order. */\nfunction collectGlobalDefaults(\n root: XmlElement,\n scope: \"event\" | \"trace\",\n): Record<string, XesAttributeValue> | undefined {\n let defaults: Record<string, XesAttributeValue> | undefined;\n for (const child of root.children) {\n if (child.tag !== \"global\" || child.attrs.scope !== scope) continue;\n defaults = withDefaults(readAttributeChildren(child.children), defaults);\n }\n return defaults;\n}\n\n/**\n * The keys that jointly name an activity: `options.classifiers` when given, else the\n * log's own first `<classifier>`, else the XES default of `concept:name` alone.\n */\nfunction resolveClassifierKeys(root: XmlElement, options: XesParseOptions | undefined): string[] {\n if (options?.classifiers !== undefined && options.classifiers.length > 0) {\n return options.classifiers;\n }\n const classifier = root.children.find((child) => child.tag === \"classifier\");\n const keys = classifier?.attrs.keys?.trim();\n if (keys !== undefined && keys !== \"\") return keys.split(/\\s+/);\n return [\"concept:name\"];\n}\n\n/** The literal value `\"start\"` maps to `\"start\"`; every other value collapses to `\"complete\"`. */\nfunction mapLifecycle(\n raw: XesAttributeValue | undefined,\n model: \"standard\" | \"none\",\n): \"start\" | \"complete\" | undefined {\n if (typeof raw !== \"string\" || model === \"none\") return undefined;\n const value = raw.trim().toLowerCase();\n if (value === \"\") return undefined;\n return value === \"start\" ? \"start\" : \"complete\";\n}\n\n/**\n * Read a IEEE 1849 XES document into an {@link EventLog}.\n *\n * Collects every recoverable problem (a trace with no events, an event missing its\n * activity classifier key(s) or `time:timestamp`) rather than stopping at the first one;\n * only malformed XML itself — which leaves nothing sound to keep reading — short-circuits\n * with a single `malformed_xml` error. Output feeds directly into `normalizeLog`, exactly\n * like `fromCsv`/`fromFlatRows` — no XES-specific branching downstream.\n */\nexport function fromXes(source: string, options?: XesParseOptions): XesParseResult {\n let root: XmlElement;\n try {\n root = parseXmlDocument(source);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { ok: false, errors: [{ type: \"malformed_xml\", message }] };\n }\n\n const lifecycleModel = options?.lifecycleModel ?? \"standard\";\n const classifierKeys = resolveClassifierKeys(root, options);\n const consumedKeys = new Set([\n ...classifierKeys,\n \"time:timestamp\",\n \"org:resource\",\n \"lifecycle:transition\",\n ]);\n const eventDefaults = collectGlobalDefaults(root, \"event\");\n const traceDefaults = collectGlobalDefaults(root, \"trace\");\n\n const errors: XesParseError[] = [];\n const events: EventRow[] = [];\n let caseAttributes: Record<string, Record<string, unknown>> | undefined;\n\n const traces = root.children.filter((child) => child.tag === \"trace\");\n traces.forEach((trace, traceIndex) => {\n const traceEvents = trace.children.filter((child) => child.tag === \"event\");\n if (traceEvents.length === 0) {\n errors.push({ type: \"missing_trace\", message: \"trace has no events\", traceIndex });\n return;\n }\n\n const { \"concept:name\": rawCaseId, ...traceValues } = withDefaults(\n readAttributeChildren(trace.children),\n traceDefaults,\n );\n const caseId =\n typeof rawCaseId === \"string\" && rawCaseId !== \"\" ? rawCaseId : `trace-${traceIndex}`;\n\n if (Object.keys(traceValues).length > 0) {\n caseAttributes ??= {};\n caseAttributes[caseId] = traceValues;\n }\n\n traceEvents.forEach((eventElement, eventIndex) => {\n const values = withDefaults(readAttributeChildren(eventElement.children), eventDefaults);\n\n const activityParts: string[] = [];\n let missingActivityKey = false;\n for (const key of classifierKeys) {\n const value = values[key];\n if (value === undefined || value === null) {\n missingActivityKey = true;\n break;\n }\n activityParts.push(String(value));\n }\n if (missingActivityKey) {\n errors.push({\n type: \"missing_concept_name\",\n message: \"event is missing its classifier key(s)\",\n traceIndex,\n eventIndex,\n });\n return;\n }\n\n const rawTimestamp = values[\"time:timestamp\"];\n if (rawTimestamp === undefined || rawTimestamp === null) {\n errors.push({\n type: \"missing_timestamp\",\n message: \"event is missing time:timestamp\",\n traceIndex,\n eventIndex,\n });\n return;\n }\n\n const row: EventRow = {\n caseId,\n activity: activityParts.join(\"+\"),\n timestamp: String(rawTimestamp),\n };\n\n const resource = values[\"org:resource\"];\n if (typeof resource === \"string\" && resource !== \"\") row.resource = resource;\n\n const lifecycle = mapLifecycle(values[\"lifecycle:transition\"], lifecycleModel);\n if (lifecycle !== undefined) row.lifecycle = lifecycle;\n\n const attributes: Record<string, string | number | boolean | null> = {};\n let anyAttribute = false;\n for (const [key, value] of Object.entries(values)) {\n if (consumedKeys.has(key)) continue;\n attributes[key] = value;\n anyAttribute = true;\n }\n if (anyAttribute) row.attributes = attributes;\n\n events.push(row);\n });\n });\n\n if (errors.length > 0) return { ok: false, errors };\n\n const log: EventLog = { events };\n if (caseAttributes !== undefined) log.caseAttributes = caseAttributes;\n return { ok: true, log };\n}\n","/**\n * Segment occurrences — RM-060.\n *\n * A performance spectrum (ProM's PSM) draws one line per case through a FIXED, chosen\n * sequence of segments, where a segment is one directly-follows pair `from → to`. That\n * needs something `TransitionStats` does not carry: the individual, time-ordered\n * OCCURRENCES of a pair (which case, when it entered, when it left), not one aggregate\n * across the whole log. Batching, FIFO violations and queue build-up are visible only\n * in the occurrences.\n *\n * ## What an occurrence measures\n *\n * `start` is the moment the `from` event COMPLETES and `end` the moment the `to` event\n * STARTS — the same idle-time reading `discoverGraph` defaults to, so a spectrum row and\n * the map's edge median agree about one pair. For atomic events (the common case) start\n * and completion coincide, so this is simply the two event timestamps. Overlapping\n * (parallel) events would give `end < start`; `end` is clamped to `start`, so `duration`\n * is never negative and a line never runs backwards.\n *\n * Deterministic and framework-free: no React, no `@elabs-ai/components-*`.\n */\n\nimport { EDGE_KEY_SEPARATOR } from \"./discover-graph\";\nimport { asNormalizedLog, type AnyLog, type NormalizedEvent } from \"./event-log\";\nimport { ascending, quantileSorted } from \"./scale\";\nimport type { ProcessGraph, Variant } from \"./types\";\n\n/** One row of a spectrum: the directly-follows pair `from → to`. */\nexport interface SegmentDefinition {\n from: string;\n to: string;\n /** Display label. Defaults to `\"from → to\"` in a view. */\n label?: string;\n}\n\n/** One case passing through one segment. */\nexport interface SegmentOccurrence {\n /** The segment's key — {@link segmentKey}`(from, to)`. */\n segment: string;\n caseId: string;\n /** When the `from` event completed, epoch ms. */\n start: number;\n /** When the `to` event started, epoch ms. Never before `start`. */\n end: number;\n /** `end - start`, in ms. */\n duration: number;\n}\n\n/** A duration quartile, `1` = fastest quarter, `4` = slowest. */\nexport type DurationQuartile = 1 | 2 | 3 | 4;\n\n/** The three cut points (25th, 50th, 75th percentile) a quartile is read against. */\nexport type QuartileThresholds = readonly [number, number, number];\n\n/**\n * The key of a segment — the same `source + separator + target` edge key `discoverGraph`\n * and `ProcessMap` use, so a segment round-trips to a transition selection unchanged.\n */\nexport function segmentKey(from: string, to: string): string {\n return `${from}${EDGE_KEY_SEPARATOR}${to}`;\n}\n\n/**\n * Every occurrence of the segments in `order`, walking each case's normalised sequence\n * once. Pairs not in `order` are skipped — a spectrum shows a chosen sequence, never the\n * whole graph. Output is in log order: case by case, and within a case in trace order.\n * Duplicate definitions in `order` are ignored.\n */\nexport function segmentsFor(log: AnyLog, order: readonly SegmentDefinition[]): SegmentOccurrence[] {\n const wanted = new Set<string>();\n for (const def of order) wanted.add(segmentKey(def.from, def.to));\n const out: SegmentOccurrence[] = [];\n if (wanted.size === 0) return out;\n\n for (const kase of asNormalizedLog(log).cases) {\n const trace = kase.events;\n for (let i = 1; i < trace.length; i += 1) {\n const previous = trace[i - 1] as NormalizedEvent;\n const event = trace[i] as NormalizedEvent;\n const key = segmentKey(previous.activity, event.activity);\n if (!wanted.has(key)) continue;\n const start = previous.end;\n if (!Number.isFinite(start) || !Number.isFinite(event.start)) continue;\n const end = Math.max(start, event.start);\n out.push({ segment: key, caseId: kase.caseId, start, end, duration: end - start });\n }\n }\n return out;\n}\n\n/**\n * The `limit` most frequent directly-follows pairs of `graph`, busiest first. Reads\n * `graph.transitions` in the order `discoverGraph` already ranks them (count descending,\n * ties by source then target), so the two can never disagree about \"the top N\".\n */\nexport function segmentOrderByFrequency(\n graph: ProcessGraph,\n limit: number = Number.POSITIVE_INFINITY,\n): SegmentDefinition[] {\n const n = Math.max(0, Math.floor(limit));\n return graph.transitions.slice(0, n).map((t) => ({ from: t.source, to: t.target }));\n}\n\n/**\n * The consecutive pairs of `variant.sequence`, in path order. A pair repeated by a loop\n * (`A, B, A, B`) appears once, at its first position — a spectrum has one row per segment.\n */\nexport function segmentOrderForVariant(variant: Variant): SegmentDefinition[] {\n const seen = new Set<string>();\n const out: SegmentDefinition[] = [];\n for (let i = 1; i < variant.sequence.length; i += 1) {\n const from = variant.sequence[i - 1] as string;\n const to = variant.sequence[i] as string;\n const key = segmentKey(from, to);\n if (seen.has(key)) continue;\n seen.add(key);\n out.push({ from, to });\n }\n return out;\n}\n\n/**\n * The 25th/50th/75th percentile of `durations` (R-7 interpolation, the same `quantile`\n * every process view uses). Non-finite samples are dropped; an empty input gives zeros.\n * Compute this ONCE per segment and read many occurrences against it with\n * {@link quartileOf} — {@link durationQuartile} sorts on every call.\n */\nexport function durationQuartileThresholds(durations: readonly number[]): QuartileThresholds {\n const sorted: number[] = [];\n for (const d of durations) if (Number.isFinite(d)) sorted.push(d);\n sorted.sort(ascending);\n if (sorted.length === 0) return [0, 0, 0];\n return [quantileSorted(sorted, 0.25), quantileSorted(sorted, 0.5), quantileSorted(sorted, 0.75)];\n}\n\n/** Which quartile `duration` falls in, against precomputed thresholds (upper bounds inclusive). */\nexport function quartileOf(duration: number, thresholds: QuartileThresholds): DurationQuartile {\n if (duration <= thresholds[0]) return 1;\n if (duration <= thresholds[1]) return 2;\n if (duration <= thresholds[2]) return 3;\n return 4;\n}\n\n/**\n * Buckets `occurrence` against ITS OWN segment's duration distribution — not the whole\n * log's — which is PSM's colour convention: a slow line is slow for that segment.\n */\nexport function durationQuartile(\n occurrence: SegmentOccurrence,\n allDurationsForSegment: readonly number[],\n): DurationQuartile {\n return quartileOf(occurrence.duration, durationQuartileThresholds(allDurationsForSegment));\n}\n","/**\n * diffGraphs — RM-064's pure diff between two discovered `ProcessGraph`s, the model\n * `ProcessCompare` renders (side-by-side or superimposed).\n *\n * Activities are matched by `id` (the activity name — the same identity `/core` uses\n * everywhere else); transitions are matched by `source`+{@link EDGE_KEY_SEPARATOR}+`target`,\n * exactly the key `reconcileGraph`'s own `transitionKey` builds, so a transition that moved\n * between the two graphs is never mistaken for two unrelated ones. An element present on\n * both sides is `\"common\"`; present only in `a` is `\"aOnly\"`; present only in `b` is\n * `\"bOnly\"` — every element in the union appears in exactly one of the three states.\n *\n * `delta`/`ratio` are computed once BOTH sides have the element (a `\"common\"` entry) —\n * comparing a real reading against nothing would be a fabricated number, not a diff. The\n * reference metric is each element's plain occurrence count — `instances` for an activity,\n * `count` for a transition — because those are the only two fields every `ActivityStats`/\n * `TransitionStats` always carries (never derived, never optional), so a delta is always\n * well-defined for a `\"common\"` entry, whatever metric a downstream view happens to paint\n * with. `ratio` is omitted rather than `Infinity` when `a`'s value is `0`.\n *\n * Pure, deterministic, framework-free — see the module-level rule in `types.ts`.\n */\nimport { EDGE_KEY_SEPARATOR } from \"./discover-graph\";\nimport type { ActivityStats, ProcessGraph, TransitionStats } from \"./types\";\n\n/** Which side(s) of the diff an activity or transition survives in. */\nexport type DiffState = \"common\" | \"aOnly\" | \"bOnly\";\n\n/** One activity's or transition's diff — its identity, its state, and (when `\"common\"`) its delta. */\nexport interface DiffEntry<Stats> {\n id: string;\n state: DiffState;\n /** Present unless the element is `\"bOnly\"`. */\n a?: Stats;\n /** Present unless the element is `\"aOnly\"`. */\n b?: Stats;\n /** `b`'s reference value minus `a`'s. Only set for a `\"common\"` entry. */\n delta?: number;\n /** `b`'s reference value divided by `a`'s. Only set for a `\"common\"` entry with `a > 0`. */\n ratio?: number;\n}\n\n/** The full diff between two graphs. */\nexport interface ProcessGraphDiff {\n activities: DiffEntry<ActivityStats>[];\n transitions: DiffEntry<TransitionStats>[];\n totals: { a: ProcessGraph[\"totals\"]; b: ProcessGraph[\"totals\"] };\n}\n\n/** `source`+{@link EDGE_KEY_SEPARATOR}+`target` — the same key `reconcileGraph` builds. */\nfunction transitionKey(transition: Pick<TransitionStats, \"source\" | \"target\">): string {\n return `${transition.source}${EDGE_KEY_SEPARATOR}${transition.target}`;\n}\n\n/** Every id in `first`, in order, then every id `second` adds that `first` did not have. */\nfunction unionIds(first: string[], second: string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const id of first) {\n if (seen.has(id)) continue;\n seen.add(id);\n out.push(id);\n }\n for (const id of second) {\n if (seen.has(id)) continue;\n seen.add(id);\n out.push(id);\n }\n return out;\n}\n\nfunction diffEntries<Stats>(\n aById: ReadonlyMap<string, Stats>,\n bById: ReadonlyMap<string, Stats>,\n ids: string[],\n referenceValue: (stats: Stats) => number,\n): DiffEntry<Stats>[] {\n return ids.map((id) => {\n const a = aById.get(id);\n const b = bById.get(id);\n const state: DiffState = a !== undefined && b !== undefined ? \"common\" : a ? \"aOnly\" : \"bOnly\";\n const entry: DiffEntry<Stats> = { id, state };\n if (a !== undefined) entry.a = a;\n if (b !== undefined) entry.b = b;\n if (a !== undefined && b !== undefined) {\n const aValue = referenceValue(a);\n const bValue = referenceValue(b);\n entry.delta = bValue - aValue;\n if (aValue > 0) entry.ratio = bValue / aValue;\n }\n return entry;\n });\n}\n\n/**\n * Diff two discovered graphs. Neither input is mutated; the entries reference the original\n * `ActivityStats`/`TransitionStats` objects, never copies.\n */\nexport function diffGraphs(a: ProcessGraph, b: ProcessGraph): ProcessGraphDiff {\n const aActivities = new Map(a.activities.map((activity) => [activity.id, activity]));\n const bActivities = new Map(b.activities.map((activity) => [activity.id, activity]));\n const activityIds = unionIds(\n a.activities.map((activity) => activity.id),\n b.activities.map((activity) => activity.id),\n );\n\n const aTransitions = new Map(a.transitions.map((t) => [transitionKey(t), t]));\n const bTransitions = new Map(b.transitions.map((t) => [transitionKey(t), t]));\n const transitionIds = unionIds(\n a.transitions.map((t) => transitionKey(t)),\n b.transitions.map((t) => transitionKey(t)),\n );\n\n return {\n activities: diffEntries(aActivities, bActivities, activityIds, (stats) => stats.instances),\n transitions: diffEntries(aTransitions, bTransitions, transitionIds, (stats) => stats.count),\n totals: { a: a.totals, b: b.totals },\n };\n}\n","/**\n * replayTimeline — the timing model behind `ProcessReplay` (RM-065).\n *\n * Pure, deterministic, framework-free: no DOM, no clock, no randomness. It turns a log into\n * the moves each case makes along the directly-follows edges of its trace, then answers two\n * questions for any playhead `t`:\n *\n * - **Where is every in-flight case?** A token per case on the edge it is travelling, with\n * `progress` 0..1 from the source activity's completion to the target activity's start\n * (the idle time the edge measures). {@link replayFrameAt}.\n * - **How congested is each edge?** The number of distinct cases on it at any moment of the\n * current bucket (`bucketMs` wide). {@link ReplayFrame.congestion}.\n *\n * Edge ids are `/core`'s `source + EDGE_KEY_SEPARATOR + target` — the same string\n * `discoverGraph`'s transitions and the process map's React Flow edges use.\n *\n * Time is RELATIVE to the timeline's `origin`: `t = 0` is the earliest event in wall-clock\n * mode, or each case's own first activity in `synchronizedStart` mode (Disco's\n * \"synchronized start\"), where `origin` is `0` and absolute timestamps are meaningless.\n */\nimport { EDGE_KEY_SEPARATOR } from \"./discover-graph\";\nimport { asNormalizedLog, type AnyLog } from \"./event-log\";\n\n/** One case travelling one edge. Times are relative to {@link ReplayTimeline.origin}. */\nexport interface ReplaySegment {\n caseId: string;\n /** `source + EDGE_KEY_SEPARATOR + target`. */\n edgeId: string;\n source: string;\n target: string;\n /** The source activity's completion. */\n enterAt: number;\n /** The target activity's start — never before {@link enterAt}. */\n exitAt: number;\n}\n\n/** One case's token on one edge at a playhead. */\nexport interface ReplayFrameToken {\n caseId: string;\n edgeId: string;\n /** 0 at the source end, 1 at the target end. */\n progress: number;\n}\n\n/** The replay at one playhead. */\nexport interface ReplayFrame {\n /** Playhead, relative to the timeline origin. */\n t: number;\n /** One token per in-flight case, in segment order. */\n tokens: ReplayFrameToken[];\n /** Edge id → distinct cases on that edge during the bucket starting at `t`. Zero edges are omitted. */\n congestion: Record<string, number>;\n}\n\n/** Options for {@link replayTimeline}. */\nexport interface ReplayTimelineOptions {\n /** Bucket width in ms. Defaults to {@link defaultReplayBucketMs}. Raised when it would exceed {@link REPLAY_MAX_FRAMES}. */\n bucketMs?: number;\n /** Align every case's first activity to `t = 0`. @default false */\n synchronizedStart?: boolean;\n}\n\n/** A replay-ready timeline. */\nexport interface ReplayTimeline {\n /** Epoch ms of `t = 0` in wall-clock mode; `0` in synchronized-start mode. */\n origin: number;\n /** Last relative instant any case reaches. `0` for an empty log. */\n duration: number;\n /** The bucket width actually used. */\n bucketMs: number;\n synchronizedStart: boolean;\n /** Every edge move of every case, ordered by `enterAt` then case order. */\n segments: ReplaySegment[];\n /** One frame per bucket, `frames[i].t === i * bucketMs`, covering `[0, duration]`. */\n frames: ReplayFrame[];\n /** Highest congestion any edge reaches in any bucket. `0` when nothing moves. */\n peakCongestion: number;\n}\n\n/** How many frames {@link defaultReplayBucketMs} aims for. */\nexport const REPLAY_TARGET_FRAMES = 300;\n/** Upper bound on frames; a finer `bucketMs` is widened to respect it. */\nexport const REPLAY_MAX_FRAMES = 5000;\n\n/** The default bucket: the log span over {@link REPLAY_TARGET_FRAMES}, at least 1 ms. */\nexport function defaultReplayBucketMs(duration: number): number {\n if (!Number.isFinite(duration) || duration <= 0) return 1;\n return Math.max(1, duration / REPLAY_TARGET_FRAMES);\n}\n\n/** Every case's edge moves, relative to the chosen origin. */\nfunction buildSegments(\n log: AnyLog,\n synchronizedStart: boolean,\n): { origin: number; duration: number; segments: ReplaySegment[] } {\n const { cases } = asNormalizedLog(log);\n if (cases.length === 0) return { origin: 0, duration: 0, segments: [] };\n const origin = synchronizedStart ? 0 : Math.min(...cases.map((c) => c.start));\n const segments: ReplaySegment[] = [];\n let duration = 0;\n for (const trace of cases) {\n const base = synchronizedStart ? trace.start : origin;\n duration = Math.max(duration, trace.end - base);\n for (let i = 1; i < trace.events.length; i++) {\n const from = trace.events[i - 1]!;\n const to = trace.events[i]!;\n const enterAt = from.end - base;\n segments.push({\n caseId: trace.caseId,\n edgeId: `${from.activity}${EDGE_KEY_SEPARATOR}${to.activity}`,\n source: from.activity,\n target: to.activity,\n enterAt,\n exitAt: Math.max(enterAt, to.start - base),\n });\n }\n }\n // Stable: equal `enterAt` keeps case order.\n segments.sort((a, b) => a.enterAt - b.enterAt);\n return { origin, duration, segments };\n}\n\n/** Tokens at `t`: a segment is in flight over `[enterAt, exitAt)`. */\nfunction tokensAt(segments: readonly ReplaySegment[], t: number): ReplayFrameToken[] {\n const tokens: ReplayFrameToken[] = [];\n for (const segment of segments) {\n if (segment.enterAt > t) break;\n if (t >= segment.exitAt) continue;\n tokens.push({\n caseId: segment.caseId,\n edgeId: segment.edgeId,\n progress: (t - segment.enterAt) / (segment.exitAt - segment.enterAt),\n });\n }\n return tokens;\n}\n\n/** Distinct cases per edge overlapping `[start, end)`. A zero-length move counts where it happens. */\nfunction congestionIn(\n segments: readonly ReplaySegment[],\n start: number,\n end: number,\n): Record<string, number> {\n const casesByEdge = new Map<string, Set<string>>();\n for (const segment of segments) {\n if (segment.enterAt >= end) break;\n const overlaps = segment.exitAt > start || segment.enterAt >= start;\n if (!overlaps) continue;\n let cases = casesByEdge.get(segment.edgeId);\n if (!cases) casesByEdge.set(segment.edgeId, (cases = new Set()));\n cases.add(segment.caseId);\n }\n const congestion: Record<string, number> = {};\n for (const [edgeId, cases] of casesByEdge) congestion[edgeId] = cases.size;\n return congestion;\n}\n\n/**\n * Bucket a log into replay frames.\n *\n * @example\n * ```ts\n * const timeline = replayTimeline(log, { synchronizedStart: true });\n * const frame = replayFrameAt(timeline, timeline.duration / 2);\n * ```\n */\nexport function replayTimeline(log: AnyLog, options: ReplayTimelineOptions = {}): ReplayTimeline {\n const synchronizedStart = options.synchronizedStart ?? false;\n const { origin, duration, segments } = buildSegments(log, synchronizedStart);\n const requested =\n options.bucketMs !== undefined && Number.isFinite(options.bucketMs) && options.bucketMs > 0\n ? options.bucketMs\n : defaultReplayBucketMs(duration);\n const bucketMs = Math.max(requested, duration / (REPLAY_MAX_FRAMES - 1));\n const frameCount = Math.floor(duration / bucketMs) + 1;\n const frames: ReplayFrame[] = [];\n let peakCongestion = 0;\n for (let i = 0; i < frameCount; i++) {\n const t = i * bucketMs;\n const congestion = congestionIn(segments, t, t + bucketMs);\n for (const count of Object.values(congestion)) peakCongestion = Math.max(peakCongestion, count);\n frames.push({ t, tokens: tokensAt(segments, t), congestion });\n }\n return { origin, duration, bucketMs, synchronizedStart, segments, frames, peakCongestion };\n}\n\n/**\n * The replay at any playhead — tokens at exactly `t` (not snapped), congestion from the\n * bucket containing `t`. `t` is clamped to `[0, duration]`.\n */\nexport function replayFrameAt(timeline: ReplayTimeline, t: number): ReplayFrame {\n const clamped = Math.min(Math.max(Number.isFinite(t) ? t : 0, 0), timeline.duration);\n const bucket = Math.min(Math.floor(clamped / timeline.bucketMs), timeline.frames.length - 1);\n return {\n t: clamped,\n tokens: tokensAt(timeline.segments, clamped),\n congestion: timeline.frames[Math.max(bucket, 0)]?.congestion ?? {},\n };\n}\n\n/** One transition in {@link rankReplayCongestion}'s list. */\nexport interface ReplayCongestionEntry {\n edgeId: string;\n source: string;\n target: string;\n /** Most distinct cases on the edge in one bucket. */\n peak: number;\n /** Relative start of the first bucket reaching {@link peak}. */\n peakAt: number;\n /** Mean congestion over every bucket of the timeline. */\n mean: number;\n}\n\n/** Transitions ranked by peak congestion, then mean, then edge id. Edges that never carry a case are omitted. */\nexport function rankReplayCongestion(\n timeline: ReplayTimeline,\n limit = Number.POSITIVE_INFINITY,\n): ReplayCongestionEntry[] {\n const ends = new Map<string, { source: string; target: string }>();\n for (const s of timeline.segments) ends.set(s.edgeId, { source: s.source, target: s.target });\n const byEdge = new Map<string, ReplayCongestionEntry>();\n for (const frame of timeline.frames) {\n for (const [edgeId, count] of Object.entries(frame.congestion)) {\n let entry = byEdge.get(edgeId);\n if (!entry) {\n const { source, target } = ends.get(edgeId)!;\n byEdge.set(edgeId, (entry = { edgeId, source, target, peak: 0, peakAt: 0, mean: 0 }));\n }\n if (count > entry.peak) {\n entry.peak = count;\n entry.peakAt = frame.t;\n }\n entry.mean += count;\n }\n }\n const frameCount = Math.max(timeline.frames.length, 1);\n return [...byEdge.values()]\n .map((entry) => ({ ...entry, mean: entry.mean / frameCount }))\n .sort(\n (a, b) =>\n b.peak - a.peak ||\n b.mean - a.mean ||\n (a.edgeId < b.edgeId ? -1 : a.edgeId > b.edgeId ? 1 : 0),\n )\n .slice(0, limit);\n}\n\n/** Smallest and largest token radius, in px. */\nexport const REPLAY_TOKEN_RADIUS_RANGE = Object.freeze([3, 8] as const);\n\n/**\n * Token radius for an edge's congestion: the radius above the minimum grows with\n * √(count / peak), so a busier edge's blob grows by area rather than by diameter.\n */\nexport function replayTokenRadius(congestion: number, peak: number): number {\n const [min, max] = REPLAY_TOKEN_RADIUS_RANGE;\n if (!(peak > 0) || !(congestion > 0)) return min;\n const share = Math.min(congestion / peak, 1);\n return min + (max - min) * Math.sqrt(share);\n}\n","/**\n * OCEL 2.0 adapter — RM-066.\n *\n * The Object-Centric Event Log standard (OCEL 2.0, ocel-standard.org) drops the single\n * case notion: an event references any number of OBJECTS of any number of object TYPES\n * (`order`, `item`, `package`, …). This adapter reads the standard's JSON serialization\n * and FLATTENS it once per object type — the projection every object-centric\n * directly-follows graph starts from: for object type `T`, every object of type `T` is a\n * case, and every event that references that object is one row of that case.\n *\n * ## JSON only, on purpose\n *\n * OCEL 2.0 also ships as XML and SQLite. Only JSON is read here: it needs nothing beyond\n * `JSON.parse`, which runs identically in Node, a worker and a browser main thread, and\n * `/core` takes no parser dependency (the same format-choice precedent `fromXes` set in\n * RM-063). SQLite would need a WASM engine; XML would need a second hand-rolled tokenizer\n * for a format whose producers all also emit JSON. Convert an XML/SQLite log to JSON with\n * the producing tool first.\n *\n * ## What survives the flattening\n *\n * Each emitted {@link EventRow} carries two reserved attributes so the object-centric\n * discovery step can re-merge the projections:\n *\n * - `__ocelEventId` — the OCEL event's own id, so a shared activity is counted once per\n * EVENT rather than once per referenced object;\n * - `__objectRefs` — the event's full object map (`{ order: [\"o1\"], item: [\"i1\", \"i2\"] }`),\n * JSON-encoded because `EventRow.attributes` values are scalars by contract (RM-049's\n * frozen types). Read it back with {@link readObjectRefs}.\n */\nimport type { EventLog, EventRow } from \"../types\";\n\n/** One `{ name, … }` type declaration (`objectTypes` / `eventTypes`). */\nexport interface OcelTypeDeclaration {\n name: string;\n attributes?: { name: string; type?: string }[];\n}\n\n/** An event or object attribute value. Object attributes carry a `time` (they can change). */\nexport interface OcelAttribute {\n name: string;\n value: string | number | boolean | null;\n time?: string;\n}\n\n/** A qualified reference from an event (or object) to an object. */\nexport interface OcelRelationship {\n objectId: string;\n qualifier?: string;\n}\n\n/** One OCEL 2.0 event. `type` is the activity; `time` is an ISO-8601 timestamp. */\nexport interface OcelEvent {\n id: string;\n type: string;\n time: string;\n attributes?: OcelAttribute[];\n relationships?: OcelRelationship[];\n}\n\n/** One OCEL 2.0 object. */\nexport interface OcelObject {\n id: string;\n type: string;\n attributes?: OcelAttribute[];\n relationships?: OcelRelationship[];\n}\n\n/** An OCEL 2.0 JSON document (OCEL 2.0 specification, JSON serialization). */\nexport interface OcelJson {\n objectTypes: OcelTypeDeclaration[];\n eventTypes?: OcelTypeDeclaration[];\n objects: OcelObject[];\n events: OcelEvent[];\n}\n\n/** Options for {@link fromOcel}. */\nexport interface OcelParseOptions {\n /**\n * Which object types to project a log for, in output order. Defaults to every declared\n * object type, in declaration order. Naming an undeclared type is an error.\n */\n objectTypes?: string[];\n}\n\n/** One problem found while reading an OCEL 2.0 document. */\nexport interface OcelParseError {\n type:\n | \"malformed_json\"\n | \"invalid_document\"\n | \"missing_event_id\"\n | \"missing_event_type\"\n | \"missing_timestamp\"\n | \"unknown_object\"\n | \"unknown_object_type\";\n message: string;\n eventIndex?: number;\n objectIndex?: number;\n}\n\n/**\n * `fromOcel`'s result — the same `{ ok: true, … } | { ok: false, errors }` shape as\n * `fromXes`. `logs` is keyed by object type; `activities` lists every event type that\n * occurs, in first-appearance order; `objectTypes` is the projected types, in order.\n */\nexport type OcelParseResult =\n | { ok: true; logs: Record<string, EventLog>; activities: string[]; objectTypes: string[] }\n | { ok: false; errors: OcelParseError[] };\n\n/** The reserved attribute holding an emitted row's OCEL event id. */\nexport const OCEL_EVENT_ID_ATTRIBUTE = \"__ocelEventId\";\n/** The reserved attribute holding an emitted row's JSON-encoded object map. */\nexport const OCEL_OBJECT_REFS_ATTRIBUTE = \"__objectRefs\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isScalar(value: unknown): value is string | number | boolean | null {\n return (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n );\n}\n\nfunction nonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value !== \"\";\n}\n\n/**\n * An object's attributes as a flat record: for a time-varying attribute the value with the\n * LATEST `time` wins (document order breaks ties), which is the object's final state.\n */\nfunction objectAttributes(object: Record<string, unknown>): Record<string, unknown> | undefined {\n if (!Array.isArray(object.attributes)) return undefined;\n const latest = new Map<string, { time: number; value: unknown }>();\n for (const raw of object.attributes) {\n if (!isRecord(raw) || !nonEmptyString(raw.name) || !isScalar(raw.value)) continue;\n const parsed = typeof raw.time === \"string\" ? Date.parse(raw.time) : Number.NEGATIVE_INFINITY;\n const time = Number.isNaN(parsed) ? Number.NEGATIVE_INFINITY : parsed;\n const previous = latest.get(raw.name);\n if (previous === undefined || time >= previous.time) {\n latest.set(raw.name, { time, value: raw.value });\n }\n }\n if (latest.size === 0) return undefined;\n const out: Record<string, unknown> = {};\n for (const [name, entry] of latest) out[name] = entry.value;\n return out;\n}\n\n/**\n * Read an OCEL 2.0 JSON document (a string, or an already-parsed object) into one\n * {@link EventLog} per object type.\n *\n * Collects every per-event problem (a missing id, type or parsable `time`; a relationship\n * to an object the document never declares) rather than stopping at the first; only a\n * document that is not JSON, or not shaped like OCEL at all, short-circuits. Any error\n * fails the whole read — a silently-dropped event would change every count downstream.\n */\nexport function fromOcel(input: string | OcelJson, options?: OcelParseOptions): OcelParseResult {\n let document: unknown = input;\n if (typeof input === \"string\") {\n try {\n document = JSON.parse(input);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { ok: false, errors: [{ type: \"malformed_json\", message }] };\n }\n }\n\n if (\n !isRecord(document) ||\n !Array.isArray(document.objectTypes) ||\n !Array.isArray(document.objects) ||\n !Array.isArray(document.events)\n ) {\n return {\n ok: false,\n errors: [\n {\n type: \"invalid_document\",\n message: \"expected an object with objectTypes, objects and events arrays\",\n },\n ],\n };\n }\n\n const errors: OcelParseError[] = [];\n\n const declaredTypes: string[] = [];\n for (const declaration of document.objectTypes) {\n if (isRecord(declaration) && nonEmptyString(declaration.name)) {\n if (!declaredTypes.includes(declaration.name)) declaredTypes.push(declaration.name);\n }\n }\n\n const projected = options?.objectTypes ?? declaredTypes;\n for (const type of projected) {\n if (!declaredTypes.includes(type)) {\n errors.push({\n type: \"unknown_object_type\",\n message: `object type \"${type}\" is not declared in objectTypes`,\n });\n }\n }\n const projectedSet = new Set(projected);\n\n const typeOfObject = new Map<string, string>();\n const attributesOfObject = new Map<string, Record<string, unknown>>();\n document.objects.forEach((object, objectIndex) => {\n if (!isRecord(object) || !nonEmptyString(object.id) || !nonEmptyString(object.type)) {\n errors.push({\n type: \"invalid_document\",\n message: \"object is missing its id or type\",\n objectIndex,\n });\n return;\n }\n if (!declaredTypes.includes(object.type)) {\n errors.push({\n type: \"unknown_object_type\",\n message: `object \"${object.id}\" has undeclared type \"${object.type}\"`,\n objectIndex,\n });\n return;\n }\n typeOfObject.set(object.id, object.type);\n const attributes = objectAttributes(object);\n if (attributes !== undefined) attributesOfObject.set(object.id, attributes);\n });\n\n const rowsByType = new Map<string, EventRow[]>(projected.map((type) => [type, []]));\n const activities: string[] = [];\n const seenActivities = new Set<string>();\n\n document.events.forEach((event, eventIndex) => {\n if (!isRecord(event)) {\n errors.push({ type: \"invalid_document\", message: \"event is not an object\", eventIndex });\n return;\n }\n if (!nonEmptyString(event.id)) {\n errors.push({ type: \"missing_event_id\", message: \"event is missing its id\", eventIndex });\n return;\n }\n if (!nonEmptyString(event.type)) {\n errors.push({\n type: \"missing_event_type\",\n message: `event \"${event.id}\" is missing its type`,\n eventIndex,\n });\n return;\n }\n if (!nonEmptyString(event.time) || Number.isNaN(Date.parse(event.time))) {\n errors.push({\n type: \"missing_timestamp\",\n message: `event \"${event.id}\" has no parsable time`,\n eventIndex,\n });\n return;\n }\n\n // Group this event's references by object type, deduplicated (the same object can be\n // referenced twice under two qualifiers) and in document order.\n const refs: Record<string, string[]> = {};\n let broken = false;\n const relationships = Array.isArray(event.relationships) ? event.relationships : [];\n for (const relationship of relationships) {\n if (!isRecord(relationship) || !nonEmptyString(relationship.objectId)) continue;\n const type = typeOfObject.get(relationship.objectId);\n if (type === undefined) {\n errors.push({\n type: \"unknown_object\",\n message: `event \"${event.id}\" references undeclared object \"${relationship.objectId}\"`,\n eventIndex,\n });\n broken = true;\n continue;\n }\n const ids = (refs[type] ??= []);\n if (!ids.includes(relationship.objectId)) ids.push(relationship.objectId);\n }\n if (broken) return;\n\n if (!seenActivities.has(event.type)) {\n seenActivities.add(event.type);\n activities.push(event.type);\n }\n\n let attributes: Record<string, string | number | boolean | null> | undefined;\n if (Array.isArray(event.attributes)) {\n for (const attribute of event.attributes) {\n if (!isRecord(attribute) || !nonEmptyString(attribute.name)) continue;\n if (!isScalar(attribute.value)) continue;\n attributes ??= {};\n attributes[attribute.name] = attribute.value;\n }\n }\n const encodedRefs = JSON.stringify(refs);\n\n for (const [type, objectIds] of Object.entries(refs)) {\n if (!projectedSet.has(type)) continue;\n const rows = rowsByType.get(type) as EventRow[];\n for (const objectId of objectIds) {\n rows.push({\n caseId: objectId,\n activity: event.type,\n timestamp: event.time,\n attributes: {\n ...attributes,\n [OCEL_EVENT_ID_ATTRIBUTE]: event.id,\n [OCEL_OBJECT_REFS_ATTRIBUTE]: encodedRefs,\n },\n });\n }\n }\n });\n\n if (errors.length > 0) return { ok: false, errors };\n\n const logs: Record<string, EventLog> = {};\n for (const type of projected) {\n const events = rowsByType.get(type) as EventRow[];\n const log: EventLog = { events };\n let caseAttributes: Record<string, Record<string, unknown>> | undefined;\n for (const row of events) {\n const attributes = attributesOfObject.get(row.caseId);\n if (attributes === undefined || caseAttributes?.[row.caseId] !== undefined) continue;\n caseAttributes ??= {};\n caseAttributes[row.caseId] = attributes;\n }\n if (caseAttributes !== undefined) log.caseAttributes = caseAttributes;\n logs[type] = log;\n }\n\n return { ok: true, logs, activities, objectTypes: [...projected] };\n}\n\n/**\n * The object map an emitted row was flattened from (`{ order: [\"o1\"], item: [\"i1\"] }`), or\n * `undefined` for a row that did not come from {@link fromOcel}.\n */\nexport function readObjectRefs(row: EventRow): Record<string, string[]> | undefined {\n const raw = row.attributes?.[OCEL_OBJECT_REFS_ATTRIBUTE];\n if (typeof raw !== \"string\") return undefined;\n try {\n const parsed: unknown = JSON.parse(raw);\n if (!isRecord(parsed)) return undefined;\n const out: Record<string, string[]> = {};\n for (const [type, ids] of Object.entries(parsed)) {\n if (Array.isArray(ids)) out[type] = ids.filter((id): id is string => typeof id === \"string\");\n }\n return out;\n } catch {\n return undefined;\n }\n}\n","/**\n * Object-centric directly-follows graph (OC-DFG) discovery — RM-066.\n *\n * An OC-DFG is one directly-follows graph PER OBJECT TYPE, drawn on one canvas: activities\n * shared between types merge into a single node that keeps a per-type breakdown, while\n * edges stay per type — an `order` following and an `item` following are different\n * relations, so they are never summed into one arrow. The merge semantics (per-type\n * projection, shared-activity join, per-type edges) follow pm4js's documented OC-DFG\n * construction; see `ATTRIBUTION.md`. No pm4js code is copied, and nothing here derives\n * from the AGPL Python reference implementation.\n *\n * Built entirely on `discoverGraph` and `abstractGraph`: every per-type number is exactly\n * what the single-case pipeline would print for that type's projection.\n */\nimport { abstractGraph, type AbstractionOptions } from \"./abstract-graph\";\nimport { activityColorScale, type ActivityColorScale } from \"./activity-color-scale\";\nimport { OCEL_EVENT_ID_ATTRIBUTE } from \"./adapters/ocel\";\nimport { discoverGraph, EDGE_KEY_SEPARATOR, type DiscoverGraphOptions } from \"./discover-graph\";\nimport type { ActivityStats, EventLog, ProcessGraph, TransitionStats } from \"./types\";\n\n/** One object type's share of a merged activity. */\nexport interface ObjectTypeActivityCounts {\n /** Occurrences in that type's projection (one per event × referenced object). */\n instances: number;\n /** Distinct objects of that type the activity touches. */\n cases: number;\n}\n\n/** A merged activity: shared across object types, with a per-type breakdown. */\nexport interface ObjectCentricActivityStats extends Omit<ActivityStats, \"instances\" | \"cases\"> {\n /**\n * Distinct OCEL events of this activity across every type — an event that references an\n * order and two items counts once. Falls back to the largest per-type `instances` when\n * the logs carry no `__ocelEventId` (hand-built logs).\n */\n events: number;\n /** Object type → counts, only for the types this activity occurs in. */\n perType: Record<string, ObjectTypeActivityCounts>;\n}\n\n/** An object-centric directly-follows graph. */\nexport interface ObjectCentricGraph {\n /** Merged activities, busiest (by `events`) first, ties by id. */\n activities: ObjectCentricActivityStats[];\n /** Object type → that type's own directly-follows edges. Never merged across types. */\n transitionsByType: Record<string, TransitionStats[]>;\n /** The object types, in the caller's order. */\n objectTypes: string[];\n /** Object type → the full per-type graph the merge was built from. */\n graphsByType: Record<string, ProcessGraph>;\n}\n\n/** {@link abstractObjectCentricGraph}'s result — what each type's abstraction hid. */\nexport interface AbstractedObjectCentricGraph extends ObjectCentricGraph {\n hiddenByType: Record<string, { activities: number; paths: number }>;\n}\n\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\n/** Distinct `__ocelEventId`s per activity across every log, or `undefined` if none carry one. */\nfunction countDistinctEvents(logs: readonly EventLog[]): Map<string, number> | undefined {\n const ids = new Map<string, Set<string>>();\n let any = false;\n for (const log of logs) {\n for (const row of log.events) {\n const id = row.attributes?.[OCEL_EVENT_ID_ATTRIBUTE];\n if (typeof id !== \"string\") continue;\n any = true;\n let set = ids.get(row.activity);\n if (set === undefined) {\n set = new Set();\n ids.set(row.activity, set);\n }\n set.add(id);\n }\n }\n if (!any) return undefined;\n const out = new Map<string, number>();\n for (const [activity, set] of ids) out.set(activity, set.size);\n return out;\n}\n\n/**\n * Merge per-type graphs into one {@link ObjectCentricGraph}.\n *\n * `eventCounts` supplies each activity's distinct-event count; an activity it does not\n * name falls back to its largest per-type `instances`. A merged activity's `duration` is\n * the duration of the type with the most instances of it (ties: earlier type), because\n * durations of the same events seen through two projections are not additive.\n */\nexport function mergeObjectCentricGraphs(\n graphsByType: Record<string, ProcessGraph>,\n objectTypes: readonly string[] = Object.keys(graphsByType),\n eventCounts?: ReadonlyMap<string, number>,\n): ObjectCentricGraph {\n const merged = new Map<string, ObjectCentricActivityStats>();\n const bestInstances = new Map<string, number>();\n const transitionsByType: Record<string, TransitionStats[]> = {};\n const types = objectTypes.filter((type) => graphsByType[type] !== undefined);\n\n for (const type of types) {\n const graph = graphsByType[type] as ProcessGraph;\n transitionsByType[type] = graph.transitions;\n for (const activity of graph.activities) {\n let entry = merged.get(activity.id);\n if (entry === undefined) {\n entry = {\n id: activity.id,\n label: activity.label,\n isStart: false,\n isEnd: false,\n duration: activity.duration,\n events: 0,\n perType: {},\n };\n merged.set(activity.id, entry);\n }\n entry.perType[type] = { instances: activity.instances, cases: activity.cases };\n entry.isStart ||= activity.isStart;\n entry.isEnd ||= activity.isEnd;\n if (activity.instances > (bestInstances.get(activity.id) ?? -1)) {\n bestInstances.set(activity.id, activity.instances);\n entry.duration = activity.duration;\n }\n }\n }\n\n const activities = [...merged.values()];\n for (const activity of activities) {\n activity.events = eventCounts?.get(activity.id) ?? bestInstances.get(activity.id) ?? 0;\n }\n activities.sort((a, b) => b.events - a.events || compareStrings(a.id, b.id));\n\n const graphs: Record<string, ProcessGraph> = {};\n for (const type of types) graphs[type] = graphsByType[type] as ProcessGraph;\n\n return { activities, transitionsByType, objectTypes: types, graphsByType: graphs };\n}\n\n/**\n * Discover an {@link ObjectCentricGraph} from per-object-type logs (typically\n * `fromOcel(…).logs`). Runs `discoverGraph` once per type, then merges.\n */\nexport function discoverObjectCentricGraph(\n logsByType: Record<string, EventLog>,\n options: DiscoverGraphOptions & { objectTypes?: string[] } = {},\n): ObjectCentricGraph {\n const { objectTypes = Object.keys(logsByType), ...discoverOptions } = options;\n const types = objectTypes.filter((type) => logsByType[type] !== undefined);\n const graphsByType: Record<string, ProcessGraph> = {};\n for (const type of types) {\n graphsByType[type] = discoverGraph(logsByType[type] as EventLog, discoverOptions);\n }\n const eventCounts = countDistinctEvents(types.map((type) => logsByType[type] as EventLog));\n return mergeObjectCentricGraphs(graphsByType, types, eventCounts);\n}\n\n/**\n * Abstract each object type independently, then re-merge.\n *\n * `perType[type]` wins for a type it names; `fallback` applies to every other type; a type\n * with neither is kept whole. Activity `events` counts are carried over from the input,\n * so abstraction hides nodes and edges without restating a statistic.\n */\nexport function abstractObjectCentricGraph(\n graph: ObjectCentricGraph,\n perType: Readonly<Record<string, AbstractionOptions>> = {},\n fallback?: AbstractionOptions,\n): AbstractedObjectCentricGraph {\n const graphsByType: Record<string, ProcessGraph> = {};\n const hiddenByType: Record<string, { activities: number; paths: number }> = {};\n for (const type of graph.objectTypes) {\n const source = graph.graphsByType[type] as ProcessGraph;\n const options = perType[type] ?? fallback;\n if (options === undefined) {\n graphsByType[type] = source;\n hiddenByType[type] = { activities: 0, paths: 0 };\n continue;\n }\n const { hidden, ...abstracted } = abstractGraph(source, options);\n graphsByType[type] = abstracted;\n hiddenByType[type] = hidden;\n }\n const eventCounts = new Map(graph.activities.map((activity) => [activity.id, activity.events]));\n return {\n ...mergeObjectCentricGraphs(graphsByType, graph.objectTypes, eventCounts),\n hiddenByType,\n };\n}\n\n/**\n * Flatten an {@link ObjectCentricGraph} into one {@link ProcessGraph} — the shape the\n * process map lays out and reads its node metrics from.\n *\n * - an activity's `instances` is its distinct-event count and its `cases` is the number of\n * objects (of any type) it touches;\n * - a pair of activities joined in several types becomes ONE transition whose `count` and\n * `caseCount` sum the types' — used for layout and selection only; the map still draws\n * the per-type edges from `transitionsByType`;\n * - `totals.cases` counts objects, `totals.events` distinct events.\n */\nexport function objectCentricProcessGraph(graph: ObjectCentricGraph): ProcessGraph {\n const activities: ActivityStats[] = graph.activities.map((activity) => {\n let cases = 0;\n for (const counts of Object.values(activity.perType)) cases += counts.cases;\n return {\n id: activity.id,\n label: activity.label,\n instances: activity.events,\n cases,\n isStart: activity.isStart,\n isEnd: activity.isEnd,\n duration: activity.duration,\n };\n });\n\n const transitions = new Map<string, TransitionStats>();\n const startActivities: Record<string, number> = {};\n const endActivities: Record<string, number> = {};\n const totals = { cases: 0, events: 0, variants: 0 };\n for (const activity of graph.activities) totals.events += activity.events;\n\n for (const type of graph.objectTypes) {\n const typeGraph = graph.graphsByType[type] as ProcessGraph;\n totals.cases += typeGraph.totals.cases;\n totals.variants += typeGraph.totals.variants;\n for (const [id, count] of Object.entries(typeGraph.startActivities)) {\n startActivities[id] = (startActivities[id] ?? 0) + count;\n }\n for (const [id, count] of Object.entries(typeGraph.endActivities)) {\n endActivities[id] = (endActivities[id] ?? 0) + count;\n }\n for (const transition of graph.transitionsByType[type] ?? []) {\n const key = `${transition.source}${EDGE_KEY_SEPARATOR}${transition.target}`;\n const existing = transitions.get(key);\n if (existing === undefined) {\n transitions.set(key, { ...transition });\n continue;\n }\n if (transition.count > existing.count) existing.duration = transition.duration;\n existing.count += transition.count;\n existing.caseCount += transition.caseCount;\n }\n }\n\n const transitionList = [...transitions.values()].sort(\n (a, b) =>\n b.count - a.count || compareStrings(a.source, b.source) || compareStrings(a.target, b.target),\n );\n\n const sorted = (record: Record<string, number>): Record<string, number> => {\n const out: Record<string, number> = {};\n for (const key of Object.keys(record).sort()) out[key] = record[key] as number;\n return out;\n };\n\n return {\n activities,\n transitions: transitionList,\n startActivities: sorted(startActivities),\n endActivities: sorted(endActivities),\n totals,\n };\n}\n\n/**\n * The object-type colour scale — RM-054's `activityColorScale`, reused rather than\n * reimplemented: object types are ranked by how many objects each has (ties by name) and\n * take `--chart-1` … `--chart-11` in that order, beyond which they share the hatched\n * \"other\" slot. `codeFor(type)` is the two-character text code painted beside every\n * swatch, so a type is never identified by colour alone.\n */\nexport function objectTypeColorScale(graph: ObjectCentricGraph): ActivityColorScale {\n const zero = { min: 0, max: 0, mean: 0, median: 0, p90: 0, sum: 0, trimmedMean: 0 };\n const activities: ActivityStats[] = graph.objectTypes.map((type) => {\n const typeGraph = graph.graphsByType[type] as ProcessGraph;\n return {\n id: type,\n label: type,\n instances: typeGraph.totals.events,\n cases: typeGraph.totals.cases,\n isStart: false,\n isEnd: false,\n duration: zero,\n };\n });\n return activityColorScale({\n activities,\n transitions: [],\n startActivities: {},\n endActivities: {},\n totals: { cases: 0, events: 0, variants: 0 },\n });\n}\n"],"mappings":"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","names":["index","mulberry32","index","MINUTE","mulberry32","RESOURCES","buildTrace","index","compareStrings","edgeKey","sequenceOf","index","variantId","index","expected","transitionKey","tokens","compareStrings"]}