@elabs-ai/components-process 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/core/index.d.ts +1029 -0
  4. package/dist/core/index.js +1553 -0
  5. package/dist/core/index.js.map +1 -0
  6. package/dist/core/process-worker.js +462 -0
  7. package/dist/core/process-worker.js.map +1 -0
  8. package/dist/index.d.ts +1153 -0
  9. package/dist/index.js +3146 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/test/index.d.ts +196 -0
  12. package/dist/test/index.js +527 -0
  13. package/dist/test/index.js.map +1 -0
  14. package/package.json +80 -0
  15. package/src/abstraction-controls/abstraction-controls-fixtures.ts +86 -0
  16. package/src/abstraction-controls/abstraction-controls.stories.tsx +188 -0
  17. package/src/abstraction-controls/abstraction-controls.test.tsx +226 -0
  18. package/src/abstraction-controls/abstraction-controls.tsx +288 -0
  19. package/src/abstraction-controls/auto-abstraction.test.ts +196 -0
  20. package/src/abstraction-controls/auto-abstraction.ts +128 -0
  21. package/src/abstraction-controls/index.ts +4 -0
  22. package/src/core/abstract-graph.test.ts +209 -0
  23. package/src/core/abstract-graph.ts +407 -0
  24. package/src/core/adapters/csv.test.ts +131 -0
  25. package/src/core/adapters/csv.ts +146 -0
  26. package/src/core/adapters/flat.test.ts +149 -0
  27. package/src/core/adapters/flat.ts +168 -0
  28. package/src/core/aggregate-performance.test.ts +208 -0
  29. package/src/core/aggregate-performance.ts +200 -0
  30. package/src/core/detect-rework.test.ts +134 -0
  31. package/src/core/detect-rework.ts +100 -0
  32. package/src/core/discover-graph.test.ts +378 -0
  33. package/src/core/discover-graph.ts +202 -0
  34. package/src/core/duration-stats.test.ts +116 -0
  35. package/src/core/duration-stats.ts +162 -0
  36. package/src/core/event-log.test.ts +224 -0
  37. package/src/core/event-log.ts +244 -0
  38. package/src/core/extract-variants.test.ts +126 -0
  39. package/src/core/extract-variants.ts +140 -0
  40. package/src/core/filter-log.test.ts +193 -0
  41. package/src/core/filter-log.ts +215 -0
  42. package/src/core/fixtures/generate-bpi-2012-subset.test.ts +50 -0
  43. package/src/core/fixtures/generate-bpi-2012-subset.ts +216 -0
  44. package/src/core/fixtures/generate-bpi-2012-subset.write.ts +40 -0
  45. package/src/core/fixtures/order-to-cash-small.json +200 -0
  46. package/src/core/fixtures/synthetic-log.test.ts +109 -0
  47. package/src/core/fixtures/synthetic-log.ts +167 -0
  48. package/src/core/index.ts +118 -0
  49. package/src/core/reconcile-graph.test.ts +175 -0
  50. package/src/core/reconcile-graph.ts +107 -0
  51. package/src/core/scale.test.ts +80 -0
  52. package/src/core/scale.ts +100 -0
  53. package/src/core/types.ts +151 -0
  54. package/src/core/worker/create-process-worker.test.ts +255 -0
  55. package/src/core/worker/create-process-worker.ts +211 -0
  56. package/src/core/worker/process-worker.ts +80 -0
  57. package/src/index.ts +29 -0
  58. package/src/metric-layer-switch/index.ts +6 -0
  59. package/src/metric-layer-switch/metric-layer-switch.stories.tsx +131 -0
  60. package/src/metric-layer-switch/metric-layer-switch.test.tsx +102 -0
  61. package/src/metric-layer-switch/metric-layer-switch.tsx +276 -0
  62. package/src/process-explorer.stories.tsx +392 -0
  63. package/src/process-kpi-strip/index.ts +6 -0
  64. package/src/process-kpi-strip/process-kpi-strip.stories.tsx +128 -0
  65. package/src/process-kpi-strip/process-kpi-strip.test.tsx +106 -0
  66. package/src/process-kpi-strip/process-kpi-strip.tsx +237 -0
  67. package/src/process-map/index.ts +13 -0
  68. package/src/process-map/map-model.test.ts +326 -0
  69. package/src/process-map/map-model.ts +873 -0
  70. package/src/process-map/process-activity-node.tsx +200 -0
  71. package/src/process-map/process-map-context.ts +71 -0
  72. package/src/process-map/process-map.stories.tsx +673 -0
  73. package/src/process-map/process-map.test.tsx +523 -0
  74. package/src/process-map/process-map.tsx +979 -0
  75. package/src/process-map/process-transition-edge.test.tsx +160 -0
  76. package/src/process-map/process-transition-edge.tsx +151 -0
  77. package/src/process-map/use-process-layout.test.tsx +265 -0
  78. package/src/process-map/use-process-layout.ts +315 -0
  79. package/src/test/contract.test.ts +99 -0
  80. package/src/test/contract.ts +118 -0
  81. package/src/test/doubles.test.tsx +51 -0
  82. package/src/test/doubles.tsx +82 -0
  83. package/src/test/index.ts +34 -0
  84. package/src/test/primitives.tsx +35 -0
  85. package/src/use-process-explorer/index.ts +8 -0
  86. package/src/use-process-explorer/use-process-explorer.test.ts +564 -0
  87. package/src/use-process-explorer/use-process-explorer.ts +540 -0
@@ -0,0 +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"]}