@demlik/tea 0.6.0 → 0.8.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 (158) hide show
  1. package/dist/agent/index.d.ts +6 -5
  2. package/dist/agent/index.js +10 -10
  3. package/dist/authed-call/index.d.ts +2 -2
  4. package/dist/authed-call/index.js +7 -7
  5. package/dist/await-terminal/index.d.ts +3 -2
  6. package/dist/await-terminal/index.js +2 -2
  7. package/dist/batch-window/index.d.ts +4 -3
  8. package/dist/batch-window/index.js +3 -3
  9. package/dist/cache/index.d.ts +2 -2
  10. package/dist/cache/index.js +4 -4
  11. package/dist/chart/index.d.ts +217 -0
  12. package/dist/chart/index.js +16 -0
  13. package/dist/chart/index.js.map +1 -0
  14. package/dist/chart/inspect/index.d.ts +85 -0
  15. package/dist/chart/inspect/index.js +5 -0
  16. package/dist/chart/inspect/index.js.map +1 -0
  17. package/dist/chart/inspect/react.d.ts +112 -0
  18. package/dist/chart/inspect/react.js +11 -0
  19. package/dist/chart/inspect/react.js.map +1 -0
  20. package/dist/chart/inspect/styles.css +286 -0
  21. package/dist/chart/lane/index.d.ts +361 -0
  22. package/dist/chart/lane/index.js +9 -0
  23. package/dist/chart/lane/index.js.map +1 -0
  24. package/dist/chart/lane/react.d.ts +83 -0
  25. package/dist/chart/lane/react.js +559 -0
  26. package/dist/chart/lane/react.js.map +1 -0
  27. package/dist/chart/lane/styles.css +498 -0
  28. package/dist/chart/report/index.d.ts +116 -0
  29. package/dist/chart/report/index.js +32 -0
  30. package/dist/chart/report/index.js.map +1 -0
  31. package/dist/{chunk-2UGTDYOK.js → chunk-3JU6657S.js} +3 -3
  32. package/dist/{chunk-2UGTDYOK.js.map → chunk-3JU6657S.js.map} +1 -1
  33. package/dist/{chunk-AZ3NHUPD.js → chunk-4KTWLPL5.js} +3 -3
  34. package/dist/{chunk-AZ3NHUPD.js.map → chunk-4KTWLPL5.js.map} +1 -1
  35. package/dist/{chunk-ZH454CR5.js → chunk-5EHLEUTL.js} +24 -4
  36. package/dist/chunk-5EHLEUTL.js.map +1 -0
  37. package/dist/chunk-5KJKJOFC.js +758 -0
  38. package/dist/chunk-5KJKJOFC.js.map +1 -0
  39. package/dist/{chunk-WF46FABO.js → chunk-7SVIRMC6.js} +3 -3
  40. package/dist/{chunk-WF46FABO.js.map → chunk-7SVIRMC6.js.map} +1 -1
  41. package/dist/{chunk-XMHZ44QC.js → chunk-7V4EVLZA.js} +4 -4
  42. package/dist/{chunk-XMHZ44QC.js.map → chunk-7V4EVLZA.js.map} +1 -1
  43. package/dist/{chunk-IVHQCVFX.js → chunk-A56OR5RF.js} +3 -3
  44. package/dist/{chunk-IVHQCVFX.js.map → chunk-A56OR5RF.js.map} +1 -1
  45. package/dist/{chunk-O7ED34JH.js → chunk-B5VXOFMZ.js} +47 -15
  46. package/dist/chunk-B5VXOFMZ.js.map +1 -0
  47. package/dist/chunk-E6GEDSF4.js +307 -0
  48. package/dist/chunk-E6GEDSF4.js.map +1 -0
  49. package/dist/{chunk-EC6FO42Q.js → chunk-EUF7XOEE.js} +6 -6
  50. package/dist/{chunk-EC6FO42Q.js.map → chunk-EUF7XOEE.js.map} +1 -1
  51. package/dist/{chunk-XOAP3LOT.js → chunk-I5OV65MS.js} +3 -3
  52. package/dist/{chunk-XOAP3LOT.js.map → chunk-I5OV65MS.js.map} +1 -1
  53. package/dist/chunk-JER5O7PQ.js +416 -0
  54. package/dist/chunk-JER5O7PQ.js.map +1 -0
  55. package/dist/{chunk-ASX7PL2K.js → chunk-JGVL3WPL.js} +3 -3
  56. package/dist/{chunk-ASX7PL2K.js.map → chunk-JGVL3WPL.js.map} +1 -1
  57. package/dist/{chunk-S6Y3RNX4.js → chunk-JP5D6GSL.js} +4 -4
  58. package/dist/{chunk-S6Y3RNX4.js.map → chunk-JP5D6GSL.js.map} +1 -1
  59. package/dist/{chunk-ERYY5P6W.js → chunk-ND3DH6KM.js} +3 -3
  60. package/dist/{chunk-ERYY5P6W.js.map → chunk-ND3DH6KM.js.map} +1 -1
  61. package/dist/{chunk-A7USKARI.js → chunk-SUST46KO.js} +3 -3
  62. package/dist/{chunk-A7USKARI.js.map → chunk-SUST46KO.js.map} +1 -1
  63. package/dist/{chunk-2KBEIGYP.js → chunk-TP5UPGLY.js} +3 -3
  64. package/dist/{chunk-2KBEIGYP.js.map → chunk-TP5UPGLY.js.map} +1 -1
  65. package/dist/chunk-UOC2PL46.js +13 -0
  66. package/dist/chunk-UOC2PL46.js.map +1 -0
  67. package/dist/{chunk-66S2KEQQ.js → chunk-WLGO64WC.js} +4 -4
  68. package/dist/{chunk-66S2KEQQ.js.map → chunk-WLGO64WC.js.map} +1 -1
  69. package/dist/chunk-WRAA7GVZ.js +209 -0
  70. package/dist/chunk-WRAA7GVZ.js.map +1 -0
  71. package/dist/chunk-ZCL7SCBK.js +685 -0
  72. package/dist/chunk-ZCL7SCBK.js.map +1 -0
  73. package/dist/chunk-ZD6PNZBK.js +457 -0
  74. package/dist/chunk-ZD6PNZBK.js.map +1 -0
  75. package/dist/{chunk-UV7VCTNC.js → chunk-ZF6WDK2D.js} +7 -7
  76. package/dist/{chunk-UV7VCTNC.js.map → chunk-ZF6WDK2D.js.map} +1 -1
  77. package/dist/{core-CUN5HDKL.d.ts → core-Bjc0bS8j.d.ts} +22 -1
  78. package/dist/deadline/index.d.ts +2 -2
  79. package/dist/deadline/index.js +3 -3
  80. package/dist/devtools/index.js +2 -207
  81. package/dist/devtools/index.js.map +1 -1
  82. package/dist/do/index.d.ts +7 -6
  83. package/dist/do/index.js +10 -10
  84. package/dist/{durable-effects-CXMOi-sp.d.ts → durable-effects-D8SqU_if.d.ts} +1 -1
  85. package/dist/extension/index.d.ts +3 -2
  86. package/dist/extension/index.js +3 -3
  87. package/dist/extension/react.js +3 -3
  88. package/dist/extension/subs/index.d.ts +2 -2
  89. package/dist/fan-out/index.d.ts +3 -2
  90. package/dist/fold-Blbhru48.d.ts +435 -0
  91. package/dist/graph-CcjWZVrd.d.ts +923 -0
  92. package/dist/idempotent-intake/index.d.ts +3 -2
  93. package/dist/index.d.ts +5 -4
  94. package/dist/index.js +2 -2
  95. package/dist/llm-call/index.d.ts +2 -2
  96. package/dist/llm-call/index.js +7 -7
  97. package/dist/{machine-CoWpvp8x.d.ts → machine-0OmPvsy_.d.ts} +1 -1
  98. package/dist/machine-viz/index.d.ts +19 -2
  99. package/dist/machine-viz/index.js +4 -10
  100. package/dist/machine-viz/index.js.map +1 -1
  101. package/dist/mem/index.d.ts +3 -2
  102. package/dist/monitored-run/index.d.ts +4 -3
  103. package/dist/monitored-run/index.js +5 -5
  104. package/dist/node/index.d.ts +3 -2
  105. package/dist/paginated-walk/index.d.ts +2 -2
  106. package/dist/paginated-walk/index.js +7 -7
  107. package/dist/parity/index.d.ts +3 -2
  108. package/dist/parity/index.js +3 -3
  109. package/dist/pbt/arbitraries/index.d.ts +1 -1
  110. package/dist/pbt/index.d.ts +1 -1
  111. package/dist/pbt/index.js +3 -3
  112. package/dist/pbt/runners/index.d.ts +1 -1
  113. package/dist/pbt/runners/index.js +3 -3
  114. package/dist/poller/index.d.ts +28 -7
  115. package/dist/poller/index.js +3 -3
  116. package/dist/poller/index.js.map +1 -1
  117. package/dist/prediction/index.d.ts +1 -1
  118. package/dist/prediction/index.js +2 -2
  119. package/dist/pure/index.d.ts +1 -1
  120. package/dist/pure/index.js +2 -2
  121. package/dist/react/index.d.ts +3 -2
  122. package/dist/react/index.js +2 -2
  123. package/dist/reconciler/index.d.ts +2 -2
  124. package/dist/reconciler/index.js +7 -7
  125. package/dist/recorder/index.d.ts +3 -2
  126. package/dist/reducer-BqyoVyBG.d.ts +447 -0
  127. package/dist/resilient-call/index.d.ts +2 -2
  128. package/dist/resilient-call/index.js +6 -6
  129. package/dist/run-BVeqJDBF.d.ts +713 -0
  130. package/dist/{runtime-types-BD3ZFfdw.d.ts → runtime-types-Bxc637bp.d.ts} +53 -2
  131. package/dist/saga/index.d.ts +3 -2
  132. package/dist/samples-DCt6nF8H.d.ts +59 -0
  133. package/dist/snapshot/index.d.ts +1 -1
  134. package/dist/snapshot/index.js +3 -3
  135. package/dist/subs/index.d.ts +4 -3
  136. package/dist/subs/index.js +3 -3
  137. package/dist/testing/index.d.ts +3 -2
  138. package/dist/testing/index.js +2 -2
  139. package/dist/throttled-input/index.d.ts +2 -2
  140. package/dist/throttled-input/index.js +6 -6
  141. package/dist/token-refresh/index.d.ts +1 -1
  142. package/dist/token-refresh/index.js +3 -3
  143. package/dist/trace-replay/index.d.ts +3 -2
  144. package/dist/trace-replay/index.js +3 -3
  145. package/dist/{types-CjAkI83U.d.ts → types-D7u9DkH7.d.ts} +1 -1
  146. package/dist/with-deadline/index.d.ts +1 -1
  147. package/dist/with-deadline/index.js +2 -2
  148. package/dist/with-resilience/index.d.ts +2 -2
  149. package/dist/with-resilience/index.js +6 -6
  150. package/dist/with-telemetry/index.d.ts +1 -1
  151. package/dist/with-telemetry/index.js +2 -2
  152. package/dist/work-queue/adapter.d.ts +3 -2
  153. package/dist/work-queue/index.d.ts +3 -2
  154. package/dist/work-queue/ops.d.ts +3 -2
  155. package/dist/workflow/index.d.ts +3 -3
  156. package/package.json +31 -1
  157. package/dist/chunk-O7ED34JH.js.map +0 -1
  158. package/dist/chunk-ZH454CR5.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/monitored-run/index.ts"],"names":[],"mappings":"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","file":"chunk-66S2KEQQ.js","sourcesContent":["/**\n * @demlik/tea/monitored-run — a long-running operation that is BOTH staged and\n * watched: an ordered stage pipeline whose POSITION survives eviction, wrapped\n * by a no-progress safety deadline and (optionally) a periodic durable\n * checkpoint.\n *\n * This is the merge of two field-guide concerns the audit-agent machine\n * hand-wired together (`seed/audit-agent-machine`): the OUTER pipeline position\n * (`RunStage` — planning → testing → summarizing → reporting, replayed on cold\n * wake by re-emitting the one outstanding effect) and the no-progress safety\n * alarm (`subs.ts`, keyed on `progressSeq` so every progress event retires the\n * old alarm and arms a fresh one). `monitored-run` lifts both into one config-\n * driven knob with the uniform L2 contract `resilient-call` established:\n * `init()`, the verbs (`start` / `advance` / `progress` / `onDeadline` /\n * `boot`), `subs(state)`, and `handlers(ports)`.\n *\n * - It is **staged, optionally**. Omit `stages` and the run is single-shot:\n * `start` enters `running`, `advance` finishes it to `done`. Provide\n * `stages` and it becomes an ordered pipeline — each `advance` retires the\n * current stage and claims the next, finishing to `done` only when the last\n * stage is retired. The pipeline POSITION lives in the Model (a\n * `work-queue` of stage items), so an eviction mid-pipeline resumes at the\n * same stage via `boot` — the audit machine's \"pipeline position survives\n * eviction\" property, generalized.\n * - It is **watched**. While `running`, a no-progress deadline is armed at\n * `lastProgressAt + deadlineMs` and keyed on `progressSeq`. Every\n * `progress` / `advance` bumps the seq, so the substrate retires the old\n * alarm and arms a new one — a self-rearming watchdog. If the alarm fires\n * (`onDeadline`) the run goes `failed` with a deadline reason. Omit\n * `deadlineMs` → no watchdog (the `stale` phase is never reachable).\n * - It **checkpoints, optionally**. Provide `snapshotEvery` and every\n * progress unit is accounted to a `../snapshot` slice; once the cadence is\n * reached a checkpoint Cmd is emitted (interpreted by the spliced snapshot\n * `handlers`), so a crashed run restarts from the last durable checkpoint\n * rather than from zero. Omit it → no checkpointing.\n *\n * ## Why the run lifecycle is INLINE (run-tracker not extracted)\n *\n * The phase union (`idle` / `running` / `stale` / `done` / `failed`, a\n * discriminated union carrying only each phase's own data) and its bookkeeping\n * (`runId`, `startedAt`, `lastProgressAt`, `progressSeq`) live directly on this\n * slice rather than behind a `run-tracker` sub-knob.\n * Extraction earns its keep when two independent axes vary; here the lifecycle\n * has exactly one anchoring axis (this run), so a separate module would be a\n * premature seam. The two genuinely independent axes — the safety DEADLINE and\n * the periodic SNAPSHOT — ARE extracted (`../deadline`, `../snapshot`), and the\n * stage list is a `../work-queue` slice (`QueueItem<Stage>[]`) whose every\n * status flip is DELEGATED to the blessed `../work-queue/ops` (`enqueueOp` /\n * `claimNextOp` / `markDoneOp`) — never re-rolled here. The lifecycle is the\n * glue between them.\n *\n * ## The two non-negotiables (canon)\n *\n * - **Durable** — the slice is plain data (the stage queue is a\n * JSON-serializable `QueueItem<Stage>[]`, the snapshot slice is plain\n * numbers), so it survives DO eviction / reload.\n * - **Replayable** — every transition is a pure verb returning new state +\n * Cmds; nothing here reads the clock or RNG. Time arrives as an `at` / `atMs`\n * parameter; the only clock read is `Date.now()` inside the snapshot\n * `handlers` port (the effect boundary), exactly as `../snapshot` stamps it.\n *\n * ## Typical wiring\n *\n * const run = createMonitoredRun<MyStage, MyState>({\n * stages: [\"plan\", \"test\", \"report\"],\n * deadlineMs: 10 * 60_000,\n * snapshotEvery: 25,\n * });\n *\n * init: (loaded) => loaded ? [loaded, []] : [{ run: run.init() }, []],\n * update: {\n * begin: (s, m) => lift(s, run.start(s.run, m.runId, m.at)),\n * advance: (s, m) => lift(s, run.advance(s.run, checkpoint(s), m.result, m.at)),\n * progress: (s, m) => lift(s, run.progress(s.run, checkpoint(s), m.at)),\n * run_deadline: (s, m) => lift(s, run.onDeadline(s.run, m)),\n * boot: (s, m) => lift(s, run.boot(s.run, m.at)),\n * },\n * subscriptions: (s) => run.subs(s.run),\n * subscribe: { deadline: subscribeDeadline },\n * interpret: run.handlers({ store: r2 }),\n */\n\nimport {\n type DeadlineExceeded,\n type DeadlineSub,\n deadlineSub,\n subscribeDeadline,\n} from \"../deadline\";\nimport type { Cmd, Interpret } from \"../index\";\nimport {\n createSnapshot,\n type SnapshotFailedMsg,\n type SnapshotLoadCmd,\n type SnapshotLoadedMsg,\n type SnapshotLoadFailedMsg,\n type SnapshotSavedMsg,\n type SnapshotState,\n type SnapshotStore,\n type SnapshotWriteCmd,\n} from \"../snapshot\";\nimport type { QueueItem } from \"../work-queue\";\nimport { queueAdapter } from \"../work-queue/adapter\";\n\n// ===========================================================================\n// Config — the knob. Every field optional; omit a field → omit its gate.\n// ===========================================================================\n\n/**\n * The monitored-run knob. EVERY field is optional:\n * - omit `stages` → single-shot run (no pipeline; one `advance` finishes).\n * - omit `deadlineMs` → no no-progress watchdog (the run is never auto-failed).\n * - omit `snapshotEvery`→ no periodic checkpoint (no `snapshot_write` Cmds).\n *\n * An empty config `{}` is a valid single-shot, unwatched, uncheckpointed run.\n *\n * `Stage` is the consumer's stage identifier — any JSON-serializable value\n * (a string id, a `{ kind, ... }` record). It rides through the stage queue\n * unchanged so the consumer's reducer can branch on the current stage to emit\n * that stage's outstanding effect (the audit machine's `outstandingEffect`).\n */\nexport interface MonitoredRunConfig<Stage> {\n /**\n * The ordered stages of the pipeline. Position survives eviction (lives in\n * the Model's stage queue). Omit for a single-shot run. An empty array is\n * treated as single-shot too — there is nothing to pipeline through.\n */\n readonly stages?: readonly Stage[];\n /**\n * No-progress watchdog budget, in ms. The safety deadline is armed at\n * `lastProgressAt + deadlineMs` and re-armed on every progress event. Omit\n * for no watchdog.\n */\n readonly deadlineMs?: number;\n /**\n * Checkpoint cadence — write a durable snapshot once this many progress\n * units have accumulated. Threaded straight into `../snapshot`'s `every`.\n * Omit for no checkpointing.\n */\n readonly snapshotEvery?: number;\n /**\n * Store key the rolling checkpoint is written under. Forwarded to\n * `../snapshot`'s `key`; defaults to that module's `@@snapshot`. Ignored\n * when `snapshotEvery` is omitted.\n */\n readonly snapshotKey?: string;\n}\n\n// ===========================================================================\n// Slice — the Model field this knob owns. Run lifecycle INLINE + composed bricks.\n// ===========================================================================\n\n/**\n * Why a run terminated as `failed`. Discriminated so each reason carries only\n * its own data (pattern 11):\n * - `deadline` — the no-progress watchdog fired (`onDeadline`).\n * - `stage` — the consumer reported a stage failure via `advance(..., fail)`.\n *\n * `stale` is a SEPARATE phase (not a fail reason): a run is `stale` only by an\n * explicit consumer signal that progress has wedged WITHOUT the deadline having\n * fired — a soft, recoverable observation. The deadline firing is a hard\n * terminal (`failed { reason: \"deadline\" }`).\n *\n * The `stage` variant carries the FAILING STAGE VALUE (the `Stage` itself), not\n * a positional index: done stages leave the queue (`markDoneOp` filters them),\n * so a residual index drifts as the pipeline advances. The stage value is the\n * stable, meaningful identifier — `undefined` for a single-shot run (no stage).\n */\nexport type RunFailure<Stage> =\n | { readonly reason: \"deadline\"; readonly at: number }\n | {\n readonly reason: \"stage\";\n readonly stage: Stage | undefined;\n readonly error: unknown;\n };\n\n/**\n * The run bookkeeping every phase carries in common, split out so each arm of\n * the `MonitoredRunState` discriminated union names only the data it ADDS\n * (`runId` on started phases, `failure` on `failed`) — the same discipline the\n * sibling `PaginatorState` uses for its shared `seen` / `pages` counters. Plain\n * data end to end so the whole slice survives DO eviction / reload:\n *\n * - `stepStates` — the stage pipeline as a `work-queue` of stage items.\n * Empty for a single-shot run. The ONE `running` item is\n * the current stage; `done` items are retired stages\n * (they leave the queue via `markDoneOp`, so the queue\n * shrinks as the pipeline advances and is empty at\n * `done`). POSITION survives eviction because this list\n * is durable.\n * - `startedAt` — `at` of `start`. Observability; never compared in a verb.\n * - `lastProgressAt` — `at` of the most recent progress event; the watchdog\n * base. Re-armed by `progress` / `advance`.\n * - `progressSeq` — monotonic progress counter; keys the safety alarm Sub\n * id so every bump retires the old alarm and arms a new\n * one (the audit subs' `safety:{progressSeq}` discipline).\n * - `snapshot` — the `../snapshot` slice (cadence counter + durable\n * watermark). Always present (uniform shape); the gate\n * is simply never driven when `snapshotEvery` is omitted.\n */\ninterface RunCore<Stage> {\n readonly stepStates: readonly QueueItem<Stage>[];\n readonly startedAt: number;\n readonly lastProgressAt: number;\n readonly progressSeq: number;\n readonly snapshot: SnapshotState;\n}\n\n/**\n * The slice — a discriminated union on `phase` so each phase carries ONLY its\n * own data and impossible combinations are unrepresentable (pattern 11), the\n * same idiom the sibling `CircuitState` / `PaginatorState` / `RunFailure` follow:\n *\n * - `idle` — created but never started. Carries no `runId` (identity is\n * host-minted at `start`) and no `failure`: \"not started\" is its\n * OWN phase, not a `running` run wearing an empty-string `runId`\n * sentinel. `start` is the only transition out, so no verb has to\n * guard \"has this actually started?\" — `phase === \"idle\"` says it.\n * - `running` — the run is live. Carries the host-minted `runId` and the\n * `RunCore` pipeline position + watchdog bookkeeping.\n * - `stale` — a soft \"no progress observed\" mark the consumer can set and\n * clear; still resumable (a `progress` clears it back to\n * `running`). Distinct from a fired deadline, which is terminal.\n * - `done` — every stage retired (or the single-shot `advance` landed).\n * - `failed` — terminal failure; the typed `failure` reason rides ONLY this\n * arm, so it is present iff `phase === \"failed\"` BY CONSTRUCTION —\n * there is no nullable side field to keep in sync.\n *\n * `runId` is the host-minted run identity, stamped at `start` (the machine never\n * mints — see the audit seed's \"run identity is host-minted\"). It rides every\n * started phase, but not `idle`.\n */\nexport type MonitoredRunState<Stage> =\n | (RunCore<Stage> & { readonly phase: \"idle\" })\n | (RunCore<Stage> & { readonly phase: \"running\"; readonly runId: string })\n | (RunCore<Stage> & { readonly phase: \"stale\"; readonly runId: string })\n | (RunCore<Stage> & { readonly phase: \"done\"; readonly runId: string })\n | (RunCore<Stage> & {\n readonly phase: \"failed\";\n readonly runId: string;\n readonly failure: RunFailure<Stage>;\n });\n\n/**\n * The live (still-timed) phases — `running` or `stale`. The verbs narrow to this\n * before touching the watchdog bookkeeping (`runId` / `progressSeq`), so a\n * settled (`done` / `failed`) or never-started (`idle`) slice can never be bumped\n * — the narrow replaces the old `runId === \"\"` born-live guard.\n */\ntype LiveRun<Stage> = Extract<\n MonitoredRunState<Stage>,\n { readonly phase: \"running\" | \"stale\" }\n>;\n\n/**\n * The outcome a consumer reports to `advance`: the current stage either\n * succeeded (retire it, claim the next) or failed (terminate the run). Plain\n * data — the consumer parses its stage result at its own boundary and hands a\n * settled outcome here.\n */\nexport type StageResult =\n | { readonly kind: \"ok\" }\n | { readonly kind: \"fail\"; readonly error: unknown };\n\n// ===========================================================================\n// Cmd + Msg — re-exported from the composed bricks so consumers wire one import.\n// ===========================================================================\n\n/** The checkpoint-write Cmd, generic over the consumer's checkpoint value `V`. */\nexport type MonitoredRunCmd<V> = SnapshotWriteCmd<V>;\n\n/** The deadline Msg the safety alarm dispatches when the watchdog fires. */\nexport type MonitoredRunTimerMsg = DeadlineExceeded;\n\n/** Ports the consumer supplies to `handlers` — just the checkpoint store. */\nexport interface MonitoredRunPorts<V> {\n /** Where periodic checkpoints are written (R2 / KV / DO sub-key / Map). */\n readonly store: SnapshotStore<V>;\n}\n\n// ===========================================================================\n// Sub-id family. The safety alarm is keyed on `progressSeq` so a progress\n// event retires the old alarm and arms a new one (single-shot, re-armed by id\n// bump — the audit subs discipline).\n// ===========================================================================\n\nfunction safetyTimerId(runId: string, progressSeq: number): string {\n return `monitored:safety:${runId}:${progressSeq}`;\n}\n\n// ===========================================================================\n// The knob factory.\n// ===========================================================================\n\n/**\n * Build a monitored-run knob from `config`. `Stage` is the consumer's stage\n * identifier type; `V` is the consumer's checkpoint value type (what\n * `snapshotEvery` checkpoints). Returns the uniform L2 knob contract.\n *\n * No `rng` parameter: unlike `resilient-call`, nothing here jitters — the only\n * external inputs are time (an `at` / `atMs` parameter) and the consumer's\n * stage outcomes. The clock is read only inside the snapshot `handlers` port.\n */\nexport function createMonitoredRun<Stage, V = unknown>(\n config: MonitoredRunConfig<Stage> = {},\n) {\n const stages = config.stages ?? [];\n const isPipeline = stages.length > 0;\n\n // The stage-queue seam, bound once. Every stage status flip delegates to the\n // `QueueAdapter` verbs (`wq.enqueue` / `wq.claim` / `wq.markDone`), so the\n // `QueueItem<Stage>` shape stays insulated behind them.\n const wq = queueAdapter<Stage>();\n\n // The snapshot sub-knob is built only when `snapshotEvery` is configured;\n // absence is `null` so the progress gate short-circuits to \"no checkpoint\".\n // When present, the slice still carries a default SnapshotState so the shape\n // is uniform across configs — the gate simply never records into it.\n const snap =\n config.snapshotEvery !== undefined\n ? createSnapshot<V>({\n every: config.snapshotEvery,\n ...(config.snapshotKey !== undefined\n ? { key: config.snapshotKey }\n : {}),\n })\n : null;\n\n // The snapshot knob to actually drive: `snap` when configured, else a single\n // inert default (`every: 1`) bound ONCE here. Its `record` is never invoked\n // (the progress gate short-circuits when `snap` is null), so the cadence value\n // is immaterial — one shared stateless knob of pure functions stands in for\n // the disabled-checkpoint case across init/boot/handlers.\n const activeSnap = snap ?? createSnapshot<V>({ every: 1 });\n\n /** The starting slice for a never-started run: the `idle` phase, no `runId`. */\n function init(): MonitoredRunState<Stage> {\n return {\n phase: \"idle\",\n stepStates: [],\n startedAt: 0,\n lastProgressAt: 0,\n progressSeq: 0,\n // A default snapshot slice even without the brick → uniform shape. When\n // `snap` is null the inert `activeSnap` default supplies the slice; its\n // `record` is never called, so the cadence value is immaterial.\n snapshot: activeSnap.init(),\n };\n }\n\n /**\n * Seed the stage queue from the configured stages: every stage enqueued\n * `pending`, then the first claimed `running`. `at` stamps the claim. PURE —\n * the work-queue ops are pure array transforms; ids are positional (`stage:i`)\n * so they are deterministic and replayable (no `crypto`, no `Math.random`).\n */\n function seedStages(at: number): readonly QueueItem<Stage>[] {\n let queue: readonly QueueItem<Stage>[] = [];\n stages.forEach((stage, i) => {\n queue = wq.enqueue(queue, stage, at, `stage:${i}`).next;\n });\n // Claim the first stage as the running position. A non-empty pipeline always\n // has a pending head here, so the claim succeeds.\n const claimed = wq.claim(queue, at);\n return claimed ? claimed.next : queue;\n }\n\n /** The current (running) stage item, or `undefined` for a single-shot run. */\n function currentStage(\n s: MonitoredRunState<Stage>,\n ): QueueItem<Stage> | undefined {\n return s.stepStates.find((i) => i.status === \"running\");\n }\n\n // === Verb: start =========================================================\n\n /**\n * Start (or restart) the run for `runId` at time `at`. Seeds the stage queue\n * (if a pipeline) and enters `running` with the watchdog clock primed. Stamps\n * `runId` from the host — the machine never mints (invariant: identity is\n * host-minted, parsed at the boundary). PURE.\n */\n function start(\n s: MonitoredRunState<Stage>,\n runId: string,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n // Build the `running` arm explicitly rather than spreading `...s`: a restart\n // from `failed` must NOT carry the old `failure` field forward (the `running`\n // arm has none), and a start from `idle` has no `runId` to preserve. Only the\n // snapshot slice threads through.\n const next: MonitoredRunState<Stage> = {\n phase: \"running\",\n runId,\n stepStates: isPipeline ? seedStages(at) : [],\n startedAt: at,\n lastProgressAt: at,\n progressSeq: 0,\n snapshot: s.snapshot,\n };\n return [next, []];\n }\n\n /**\n * Bump the watchdog clock: set `lastProgressAt`/`progressSeq` so the safety\n * alarm Sub id changes and the substrate re-arms a fresh deadline. Shared by\n * `progress` and `advance`. PURE.\n */\n function bumpProgress(s: LiveRun<Stage>, at: number): LiveRun<Stage> {\n return { ...s, lastProgressAt: at, progressSeq: s.progressSeq + 1 };\n }\n\n /**\n * Account one progress unit to the snapshot slice (when the brick is\n * configured), returning the new slice + any checkpoint Cmd. `payload` is the\n * consumer's checkpoint value to persist. PURE — `snap.record` is pure, the\n * write is realized only when the runtime performs the emitted Cmd.\n */\n function recordSnapshot(\n s: MonitoredRunState<Stage>,\n payload: V,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (snap === null) return [s, []];\n const [snapshot, cmds] = snap.record(s.snapshot, payload, at);\n // `snap.record` only ever emits `SnapshotWriteCmd<V>` (its sole Cmd), but\n // its signature widens to `readonly Cmd[]`. Narrow back to this knob's\n // precise Cmd union so a consumer's host Cmd type can stay exact.\n return [{ ...s, snapshot }, cmds as readonly MonitoredRunCmd<V>[]];\n }\n\n // === Verb: progress ======================================================\n\n /**\n * Record forward progress: bump the watchdog (re-arming the safety alarm),\n * clear any `stale` mark back to `running`, and account a snapshot unit\n * (emitting a checkpoint Cmd on a cadence hit). A no-op unless the run is live\n * (`running` / `stale`) — a late progress event on a settled (`done` /\n * `failed`) or never-started (`idle`) run is ignored. PURE.\n */\n function progress(\n s: MonitoredRunState<Stage>,\n payload: V,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (s.phase !== \"running\" && s.phase !== \"stale\") return [s, []];\n const bumped: MonitoredRunState<Stage> = {\n ...bumpProgress(s, at),\n phase: \"running\",\n };\n return recordSnapshot(bumped, payload, at);\n }\n\n // === Verb: markStale =====================================================\n\n /**\n * Mark the run `stale` — a soft \"no progress observed\" signal the consumer\n * raises WITHOUT the deadline having fired (e.g. an upstream heartbeat gap).\n * Recoverable: the next `progress` flips it back to `running`. A no-op unless\n * the run is currently `running` (a settled run cannot go stale). Does NOT\n * touch `progressSeq` — going stale is not progress, so the armed deadline\n * keeps counting toward the hard terminal. PURE.\n */\n function markStale(\n s: MonitoredRunState<Stage>,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (s.phase !== \"running\") return [s, []];\n return [{ ...s, phase: \"stale\" }, []];\n }\n\n // === Verb: advance =======================================================\n\n /**\n * Advance the pipeline on a stage outcome:\n *\n * - `result.kind === \"fail\"` → terminate `failed { reason: \"stage\" }`,\n * carrying the failing stage index + error. No further stages run.\n * - `result.kind === \"ok\"`:\n * - single-shot run → finish to `done` (and force a final checkpoint\n * when checkpointing, so the last durable snapshot is the terminal\n * state).\n * - pipeline → retire the current stage (`markDoneOp`) and claim the\n * next (`claimNextOp`). If none remain, finish to `done`. The new\n * position survives eviction.\n *\n * An advance is itself a progress event, so it bumps the watchdog (re-arming\n * the alarm at the new stage) and accounts a snapshot unit. A no-op on a\n * settled (`done` / `failed`) or never-started (`idle`) run. PURE — `at` is the\n * only clock, ids are positional.\n */\n function advance(\n s: MonitoredRunState<Stage>,\n payload: V,\n result: StageResult,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (s.phase !== \"running\" && s.phase !== \"stale\") return [s, []];\n\n // Stage failure → hard terminal. Carry the failing stage VALUE (stable),\n // `undefined` for a single-shot run with no stage.\n if (result.kind === \"fail\") {\n const failed: MonitoredRunState<Stage> = {\n ...s,\n phase: \"failed\",\n failure: {\n reason: \"stage\",\n stage: currentStage(s)?.input,\n error: result.error,\n },\n };\n return [failed, []];\n }\n\n // Success: bump the watchdog first (an advance is progress), then move the\n // pipeline position.\n const bumped = bumpProgress(s, at);\n\n // Single-shot: one advance finishes the run.\n if (!isPipeline) {\n const done: MonitoredRunState<Stage> = { ...bumped, phase: \"done\" };\n // Force a terminal checkpoint so the last durable snapshot is `done`,\n // not whatever the cadence happened to land on.\n return finishWithSnapshot(done, payload, at);\n }\n\n // Pipeline: retire the running stage, claim the next.\n const current = currentStage(s);\n const afterDone =\n current !== undefined\n ? wq.markDone(s.stepStates, current.id).next\n : s.stepStates;\n const claimed = wq.claim(afterDone, at);\n\n if (claimed === null) {\n // No more stages — the pipeline is complete.\n const done: MonitoredRunState<Stage> = {\n ...bumped,\n phase: \"done\",\n stepStates: afterDone,\n };\n return finishWithSnapshot(done, payload, at);\n }\n\n // Advance to the next stage — POSITION now points at the claimed item.\n const moved: MonitoredRunState<Stage> = {\n ...bumped,\n stepStates: claimed.next,\n };\n return recordSnapshot(moved, payload, at);\n }\n\n /**\n * Finish helper: force a terminal checkpoint when checkpointing is enabled\n * (so the durable snapshot is the terminal state), else just settle. Shared\n * by single-shot finish and pipeline completion. PURE.\n */\n function finishWithSnapshot(\n s: MonitoredRunState<Stage>,\n payload: V,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (snap === null) return [s, []];\n const [snapshot, cmds] = snap.force(s.snapshot, payload, at);\n // Same narrowing as `recordSnapshot`: `snap.force` emits only the write Cmd.\n return [{ ...s, snapshot }, cmds as readonly MonitoredRunCmd<V>[]];\n }\n\n // === Verb: onDeadline ====================================================\n\n /**\n * The safety alarm fired. Two outcomes by Sub-id match:\n *\n * - the alarm matches the CURRENT `progressSeq` (no progress since it was\n * armed) → the run is wedged → terminate `failed { reason: \"deadline\" }`.\n * - the alarm matches an OLDER seq → a stale fire racing a progress event\n * that already re-armed the alarm → no-op (the substrate retired this id;\n * tolerate a fire still in flight defensively).\n *\n * A no-op on a settled run (its alarm was reconciled away; tolerate a stale\n * fire) and on a NEVER-STARTED (`idle`) run (`subs` armed nothing for it, so\n * any alarm reaching it is a rogue fire that must not un-start it). PURE —\n * `msg.atMs` stamps the failure.\n */\n function onDeadline(\n s: MonitoredRunState<Stage>,\n msg: MonitoredRunTimerMsg,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n // Only a live (`running` / `stale`) run is watched. A settled (`done` /\n // `failed`) run's alarm was reconciled away, and a never-started (`idle`) run\n // — which has no host-minted `runId` and armed nothing — must not be\n // un-started by a born-live/rogue fire. The phase narrow IS the guard the old\n // `runId === \"\"` sentinel stood in for.\n if (s.phase !== \"running\" && s.phase !== \"stale\") return [s, []];\n // Only the alarm armed against the current progressSeq is live. A bumped\n // seq means progress re-armed it; an older fire is stale.\n if (msg.id !== safetyTimerId(s.runId, s.progressSeq)) return [s, []];\n const failed: MonitoredRunState<Stage> = {\n ...s,\n phase: \"failed\",\n failure: { reason: \"deadline\", at: msg.atMs },\n };\n return [failed, []];\n }\n\n // === Verb: boot ==========================================================\n\n /**\n * Resume after a reload (cold wake). Re-seeds the watchdog clock to `at` so\n * the safety alarm re-arms from NOW (the pre-crash `lastProgressAt` would\n * immediately fire a deadline that already elapsed during downtime — a cold\n * wake is not a no-progress wedge). Bumps `progressSeq` so the alarm gets a\n * fresh id, and resets the snapshot cadence counter (un-checkpointed\n * pre-crash progress is moot — recovery resumes from the last durable\n * checkpoint). The pipeline POSITION (`stepStates`) is preserved untouched —\n * that is the whole point of staging: resume at the same stage.\n *\n * A no-op on a settled (`done` / `failed`) or never-started (`idle`) run. Emits\n * NO Cmd — re-emitting the current stage's outstanding effect is the CONSUMER's\n * job (it knows the per-stage Cmd), the audit machine's `outstandingEffect(stage)`\n * pattern. PURE.\n */\n function boot(\n s: MonitoredRunState<Stage>,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (s.phase !== \"running\" && s.phase !== \"stale\") return [s, []];\n // `snap.boot` returns the reducer-cell shape `[SnapshotState, Cmd[]]`\n // (uniform across every sibling knob); a boot emits no Cmd, so take only the\n // slice. Assigning the tuple itself would put an ARRAY where a SnapshotState\n // belongs — a type error AND a non-round-trippable slice.\n const [bootedSnapshot] = activeSnap.boot(s.snapshot);\n const reseeded: MonitoredRunState<Stage> = {\n ...s,\n phase: \"running\",\n lastProgressAt: at,\n progressSeq: s.progressSeq + 1,\n snapshot: bootedSnapshot,\n // Any stage left `running` mid-flight stays the running position; the\n // queue is already correct (a single in-flight stage). resetRunningOp is\n // NOT applied — unlike a work pool, a monitored run has exactly one\n // in-flight stage and resuming it (not re-queuing it) is the correct\n // cold-wake behavior.\n };\n return [reseeded, []];\n }\n\n // === Subs ================================================================\n\n /**\n * Pre-wired subscriptions: the no-progress safety deadline, armed only while\n * the run is live (`running` / `stale`) AND a `deadlineMs` is configured. The\n * Sub id is keyed on `progressSeq`, so every progress event retires the old\n * alarm and the substrate arms a fresh one at the new `lastProgressAt +\n * deadlineMs` — a self-rearming watchdog with no manual `clearTimeout`. A\n * settled run desires no subs. Wire `subscribe: { deadline: subscribeDeadline }`.\n *\n * A NEVER-STARTED slice (`init()`) is its OWN `idle` phase, not a `running` run\n * with an empty `runId`. The `phase !== \"running\" && phase !== \"stale\"` narrow\n * therefore excludes it structurally — no separate `runId === \"\"` sentinel gate\n * is needed. Were `idle` armed, it would fire a deadline at `0 + deadlineMs`\n * (already in the past) and auto-fail a run that never began — the \"born live\"\n * defect. `start` is the only transition out of `idle`, so the watchdog exists\n * exactly once a real run is in flight.\n */\n function subs(s: MonitoredRunState<Stage>): readonly DeadlineSub[] {\n if (config.deadlineMs === undefined) return [];\n if (s.phase !== \"running\" && s.phase !== \"stale\") return [];\n return [\n deadlineSub(\n safetyTimerId(s.runId, s.progressSeq),\n s.lastProgressAt + config.deadlineMs,\n ),\n ];\n }\n\n // === Handlers ============================================================\n\n /**\n * Pre-wired interpret handler for the checkpoint write. Delegates wholesale to\n * `../snapshot`'s `handlers` (which performs the `put` and routes Ok →\n * `snapshot_saved` / Err → `snapshot_failed` via Railway — the write never\n * rejects into the runtime). When checkpointing is disabled (`snapshotEvery`\n * omitted), no `snapshot_write` Cmd is ever emitted, so the returned map is an\n * inert no-op handler — kept for a uniform `handlers(ports)` call shape.\n *\n * Fold the `snapshot_saved` Msg back through this knob's `confirmSnapshot`\n * verb to advance the durable watermark.\n */\n function handlers(\n ports: MonitoredRunPorts<V>,\n ): Interpret<\n | SnapshotSavedMsg\n | SnapshotFailedMsg\n | SnapshotLoadedMsg<V>\n | SnapshotLoadFailedMsg,\n SnapshotWriteCmd<V> | SnapshotLoadCmd,\n unknown\n > {\n return activeSnap.handlers({ store: ports.store });\n }\n\n /**\n * Fold a confirmed checkpoint write into the slice — advances the snapshot\n * watermark (forward-only). A no-op pass-through when checkpointing is\n * disabled. PURE. Fold the `snapshot_saved` Msg here.\n */\n function confirmSnapshot(\n s: MonitoredRunState<Stage>,\n msg: SnapshotSavedMsg,\n ): MonitoredRunState<Stage> {\n if (snap === null) return s;\n // `snap.confirm` returns the reducer-cell shape `[SnapshotState, Cmd[]]`\n // (an ack emits nothing). Take only the slice — folding the whole tuple into\n // `snapshot` would store an ARRAY where a SnapshotState belongs (a type error\n // and a non-round-trippable slice).\n const [confirmed] = snap.confirm(s.snapshot, msg);\n return { ...s, snapshot: confirmed };\n }\n\n return {\n init,\n start,\n progress,\n markStale,\n advance,\n onDeadline,\n boot,\n confirmSnapshot,\n subs,\n handlers,\n };\n}\n\n/**\n * Lift a knob result `[slice, cmds]` into a host `[State, cmds]` where the\n * slice lives at `state.run`. Convenience for the common single-slice host;\n * hosts with a differently-named field spread by hand. PURE — a thin record\n * rebuild, no clock / RNG.\n */\nexport function liftRun<\n S extends { run: MonitoredRunState<Stage> },\n Stage,\n C extends Cmd,\n>(\n state: S,\n [slice, cmds]: readonly [MonitoredRunState<Stage>, readonly C[]],\n): readonly [S, readonly C[]] {\n return [{ ...state, run: slice }, cmds];\n}\n\n/**\n * Re-export the deadline Sub primitives so consumers (and tests) wire one\n * import: `subscribeDeadline` is the `subscribe` cell, `deadlineSub` builds the\n * Sub literal this knob's `subs` emits.\n */\nexport { subscribeDeadline, deadlineSub };\nexport type {\n DeadlineSub,\n DeadlineExceeded,\n SnapshotSavedMsg,\n SnapshotWriteCmd,\n};\n"]}
1
+ {"version":3,"sources":["../src/monitored-run/index.ts"],"names":[],"mappings":"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","file":"chunk-WLGO64WC.js","sourcesContent":["/**\n * @demlik/tea/monitored-run — a long-running operation that is BOTH staged and\n * watched: an ordered stage pipeline whose POSITION survives eviction, wrapped\n * by a no-progress safety deadline and (optionally) a periodic durable\n * checkpoint.\n *\n * This is the merge of two field-guide concerns the audit-agent machine\n * hand-wired together (`seed/audit-agent-machine`): the OUTER pipeline position\n * (`RunStage` — planning → testing → summarizing → reporting, replayed on cold\n * wake by re-emitting the one outstanding effect) and the no-progress safety\n * alarm (`subs.ts`, keyed on `progressSeq` so every progress event retires the\n * old alarm and arms a fresh one). `monitored-run` lifts both into one config-\n * driven knob with the uniform L2 contract `resilient-call` established:\n * `init()`, the verbs (`start` / `advance` / `progress` / `onDeadline` /\n * `boot`), `subs(state)`, and `handlers(ports)`.\n *\n * - It is **staged, optionally**. Omit `stages` and the run is single-shot:\n * `start` enters `running`, `advance` finishes it to `done`. Provide\n * `stages` and it becomes an ordered pipeline — each `advance` retires the\n * current stage and claims the next, finishing to `done` only when the last\n * stage is retired. The pipeline POSITION lives in the Model (a\n * `work-queue` of stage items), so an eviction mid-pipeline resumes at the\n * same stage via `boot` — the audit machine's \"pipeline position survives\n * eviction\" property, generalized.\n * - It is **watched**. While `running`, a no-progress deadline is armed at\n * `lastProgressAt + deadlineMs` and keyed on `progressSeq`. Every\n * `progress` / `advance` bumps the seq, so the substrate retires the old\n * alarm and arms a new one — a self-rearming watchdog. If the alarm fires\n * (`onDeadline`) the run goes `failed` with a deadline reason. Omit\n * `deadlineMs` → no watchdog (the `stale` phase is never reachable).\n * - It **checkpoints, optionally**. Provide `snapshotEvery` and every\n * progress unit is accounted to a `../snapshot` slice; once the cadence is\n * reached a checkpoint Cmd is emitted (interpreted by the spliced snapshot\n * `handlers`), so a crashed run restarts from the last durable checkpoint\n * rather than from zero. Omit it → no checkpointing.\n *\n * ## Why the run lifecycle is INLINE (run-tracker not extracted)\n *\n * The phase union (`idle` / `running` / `stale` / `done` / `failed`, a\n * discriminated union carrying only each phase's own data) and its bookkeeping\n * (`runId`, `startedAt`, `lastProgressAt`, `progressSeq`) live directly on this\n * slice rather than behind a `run-tracker` sub-knob.\n * Extraction earns its keep when two independent axes vary; here the lifecycle\n * has exactly one anchoring axis (this run), so a separate module would be a\n * premature seam. The two genuinely independent axes — the safety DEADLINE and\n * the periodic SNAPSHOT — ARE extracted (`../deadline`, `../snapshot`), and the\n * stage list is a `../work-queue` slice (`QueueItem<Stage>[]`) whose every\n * status flip is DELEGATED to the blessed `../work-queue/ops` (`enqueueOp` /\n * `claimNextOp` / `markDoneOp`) — never re-rolled here. The lifecycle is the\n * glue between them.\n *\n * ## The two non-negotiables (canon)\n *\n * - **Durable** — the slice is plain data (the stage queue is a\n * JSON-serializable `QueueItem<Stage>[]`, the snapshot slice is plain\n * numbers), so it survives DO eviction / reload.\n * - **Replayable** — every transition is a pure verb returning new state +\n * Cmds; nothing here reads the clock or RNG. Time arrives as an `at` / `atMs`\n * parameter; the only clock read is `Date.now()` inside the snapshot\n * `handlers` port (the effect boundary), exactly as `../snapshot` stamps it.\n *\n * ## Typical wiring\n *\n * const run = createMonitoredRun<MyStage, MyState>({\n * stages: [\"plan\", \"test\", \"report\"],\n * deadlineMs: 10 * 60_000,\n * snapshotEvery: 25,\n * });\n *\n * init: (loaded) => loaded ? [loaded, []] : [{ run: run.init() }, []],\n * update: {\n * begin: (s, m) => lift(s, run.start(s.run, m.runId, m.at)),\n * advance: (s, m) => lift(s, run.advance(s.run, checkpoint(s), m.result, m.at)),\n * progress: (s, m) => lift(s, run.progress(s.run, checkpoint(s), m.at)),\n * run_deadline: (s, m) => lift(s, run.onDeadline(s.run, m)),\n * boot: (s, m) => lift(s, run.boot(s.run, m.at)),\n * },\n * subscriptions: (s) => run.subs(s.run),\n * subscribe: { deadline: subscribeDeadline },\n * interpret: run.handlers({ store: r2 }),\n */\n\nimport {\n type DeadlineExceeded,\n type DeadlineSub,\n deadlineSub,\n subscribeDeadline,\n} from \"../deadline\";\nimport type { Cmd, Interpret } from \"../index\";\nimport {\n createSnapshot,\n type SnapshotFailedMsg,\n type SnapshotLoadCmd,\n type SnapshotLoadedMsg,\n type SnapshotLoadFailedMsg,\n type SnapshotSavedMsg,\n type SnapshotState,\n type SnapshotStore,\n type SnapshotWriteCmd,\n} from \"../snapshot\";\nimport type { QueueItem } from \"../work-queue\";\nimport { queueAdapter } from \"../work-queue/adapter\";\n\n// ===========================================================================\n// Config — the knob. Every field optional; omit a field → omit its gate.\n// ===========================================================================\n\n/**\n * The monitored-run knob. EVERY field is optional:\n * - omit `stages` → single-shot run (no pipeline; one `advance` finishes).\n * - omit `deadlineMs` → no no-progress watchdog (the run is never auto-failed).\n * - omit `snapshotEvery`→ no periodic checkpoint (no `snapshot_write` Cmds).\n *\n * An empty config `{}` is a valid single-shot, unwatched, uncheckpointed run.\n *\n * `Stage` is the consumer's stage identifier — any JSON-serializable value\n * (a string id, a `{ kind, ... }` record). It rides through the stage queue\n * unchanged so the consumer's reducer can branch on the current stage to emit\n * that stage's outstanding effect (the audit machine's `outstandingEffect`).\n */\nexport interface MonitoredRunConfig<Stage> {\n /**\n * The ordered stages of the pipeline. Position survives eviction (lives in\n * the Model's stage queue). Omit for a single-shot run. An empty array is\n * treated as single-shot too — there is nothing to pipeline through.\n */\n readonly stages?: readonly Stage[];\n /**\n * No-progress watchdog budget, in ms. The safety deadline is armed at\n * `lastProgressAt + deadlineMs` and re-armed on every progress event. Omit\n * for no watchdog.\n */\n readonly deadlineMs?: number;\n /**\n * Checkpoint cadence — write a durable snapshot once this many progress\n * units have accumulated. Threaded straight into `../snapshot`'s `every`.\n * Omit for no checkpointing.\n */\n readonly snapshotEvery?: number;\n /**\n * Store key the rolling checkpoint is written under. Forwarded to\n * `../snapshot`'s `key`; defaults to that module's `@@snapshot`. Ignored\n * when `snapshotEvery` is omitted.\n */\n readonly snapshotKey?: string;\n}\n\n// ===========================================================================\n// Slice — the Model field this knob owns. Run lifecycle INLINE + composed bricks.\n// ===========================================================================\n\n/**\n * Why a run terminated as `failed`. Discriminated so each reason carries only\n * its own data (pattern 11):\n * - `deadline` — the no-progress watchdog fired (`onDeadline`).\n * - `stage` — the consumer reported a stage failure via `advance(..., fail)`.\n *\n * `stale` is a SEPARATE phase (not a fail reason): a run is `stale` only by an\n * explicit consumer signal that progress has wedged WITHOUT the deadline having\n * fired — a soft, recoverable observation. The deadline firing is a hard\n * terminal (`failed { reason: \"deadline\" }`).\n *\n * The `stage` variant carries the FAILING STAGE VALUE (the `Stage` itself), not\n * a positional index: done stages leave the queue (`markDoneOp` filters them),\n * so a residual index drifts as the pipeline advances. The stage value is the\n * stable, meaningful identifier — `undefined` for a single-shot run (no stage).\n */\nexport type RunFailure<Stage> =\n | { readonly reason: \"deadline\"; readonly at: number }\n | {\n readonly reason: \"stage\";\n readonly stage: Stage | undefined;\n readonly error: unknown;\n };\n\n/**\n * The run bookkeeping every phase carries in common, split out so each arm of\n * the `MonitoredRunState` discriminated union names only the data it ADDS\n * (`runId` on started phases, `failure` on `failed`) — the same discipline the\n * sibling `PaginatorState` uses for its shared `seen` / `pages` counters. Plain\n * data end to end so the whole slice survives DO eviction / reload:\n *\n * - `stepStates` — the stage pipeline as a `work-queue` of stage items.\n * Empty for a single-shot run. The ONE `running` item is\n * the current stage; `done` items are retired stages\n * (they leave the queue via `markDoneOp`, so the queue\n * shrinks as the pipeline advances and is empty at\n * `done`). POSITION survives eviction because this list\n * is durable.\n * - `startedAt` — `at` of `start`. Observability; never compared in a verb.\n * - `lastProgressAt` — `at` of the most recent progress event; the watchdog\n * base. Re-armed by `progress` / `advance`.\n * - `progressSeq` — monotonic progress counter; keys the safety alarm Sub\n * id so every bump retires the old alarm and arms a new\n * one (the audit subs' `safety:{progressSeq}` discipline).\n * - `snapshot` — the `../snapshot` slice (cadence counter + durable\n * watermark). Always present (uniform shape); the gate\n * is simply never driven when `snapshotEvery` is omitted.\n */\ninterface RunCore<Stage> {\n readonly stepStates: readonly QueueItem<Stage>[];\n readonly startedAt: number;\n readonly lastProgressAt: number;\n readonly progressSeq: number;\n readonly snapshot: SnapshotState;\n}\n\n/**\n * The slice — a discriminated union on `phase` so each phase carries ONLY its\n * own data and impossible combinations are unrepresentable (pattern 11), the\n * same idiom the sibling `CircuitState` / `PaginatorState` / `RunFailure` follow:\n *\n * - `idle` — created but never started. Carries no `runId` (identity is\n * host-minted at `start`) and no `failure`: \"not started\" is its\n * OWN phase, not a `running` run wearing an empty-string `runId`\n * sentinel. `start` is the only transition out, so no verb has to\n * guard \"has this actually started?\" — `phase === \"idle\"` says it.\n * - `running` — the run is live. Carries the host-minted `runId` and the\n * `RunCore` pipeline position + watchdog bookkeeping.\n * - `stale` — a soft \"no progress observed\" mark the consumer can set and\n * clear; still resumable (a `progress` clears it back to\n * `running`). Distinct from a fired deadline, which is terminal.\n * - `done` — every stage retired (or the single-shot `advance` landed).\n * - `failed` — terminal failure; the typed `failure` reason rides ONLY this\n * arm, so it is present iff `phase === \"failed\"` BY CONSTRUCTION —\n * there is no nullable side field to keep in sync.\n *\n * `runId` is the host-minted run identity, stamped at `start` (the machine never\n * mints — see the audit seed's \"run identity is host-minted\"). It rides every\n * started phase, but not `idle`.\n */\nexport type MonitoredRunState<Stage> =\n | (RunCore<Stage> & { readonly phase: \"idle\" })\n | (RunCore<Stage> & { readonly phase: \"running\"; readonly runId: string })\n | (RunCore<Stage> & { readonly phase: \"stale\"; readonly runId: string })\n | (RunCore<Stage> & { readonly phase: \"done\"; readonly runId: string })\n | (RunCore<Stage> & {\n readonly phase: \"failed\";\n readonly runId: string;\n readonly failure: RunFailure<Stage>;\n });\n\n/**\n * The live (still-timed) phases — `running` or `stale`. The verbs narrow to this\n * before touching the watchdog bookkeeping (`runId` / `progressSeq`), so a\n * settled (`done` / `failed`) or never-started (`idle`) slice can never be bumped\n * — the narrow replaces the old `runId === \"\"` born-live guard.\n */\ntype LiveRun<Stage> = Extract<\n MonitoredRunState<Stage>,\n { readonly phase: \"running\" | \"stale\" }\n>;\n\n/**\n * The outcome a consumer reports to `advance`: the current stage either\n * succeeded (retire it, claim the next) or failed (terminate the run). Plain\n * data — the consumer parses its stage result at its own boundary and hands a\n * settled outcome here.\n */\nexport type StageResult =\n | { readonly kind: \"ok\" }\n | { readonly kind: \"fail\"; readonly error: unknown };\n\n// ===========================================================================\n// Cmd + Msg — re-exported from the composed bricks so consumers wire one import.\n// ===========================================================================\n\n/** The checkpoint-write Cmd, generic over the consumer's checkpoint value `V`. */\nexport type MonitoredRunCmd<V> = SnapshotWriteCmd<V>;\n\n/** The deadline Msg the safety alarm dispatches when the watchdog fires. */\nexport type MonitoredRunTimerMsg = DeadlineExceeded;\n\n/** Ports the consumer supplies to `handlers` — just the checkpoint store. */\nexport interface MonitoredRunPorts<V> {\n /** Where periodic checkpoints are written (R2 / KV / DO sub-key / Map). */\n readonly store: SnapshotStore<V>;\n}\n\n// ===========================================================================\n// Sub-id family. The safety alarm is keyed on `progressSeq` so a progress\n// event retires the old alarm and arms a new one (single-shot, re-armed by id\n// bump — the audit subs discipline).\n// ===========================================================================\n\nfunction safetyTimerId(runId: string, progressSeq: number): string {\n return `monitored:safety:${runId}:${progressSeq}`;\n}\n\n// ===========================================================================\n// The knob factory.\n// ===========================================================================\n\n/**\n * Build a monitored-run knob from `config`. `Stage` is the consumer's stage\n * identifier type; `V` is the consumer's checkpoint value type (what\n * `snapshotEvery` checkpoints). Returns the uniform L2 knob contract.\n *\n * No `rng` parameter: unlike `resilient-call`, nothing here jitters — the only\n * external inputs are time (an `at` / `atMs` parameter) and the consumer's\n * stage outcomes. The clock is read only inside the snapshot `handlers` port.\n */\nexport function createMonitoredRun<Stage, V = unknown>(\n config: MonitoredRunConfig<Stage> = {},\n) {\n const stages = config.stages ?? [];\n const isPipeline = stages.length > 0;\n\n // The stage-queue seam, bound once. Every stage status flip delegates to the\n // `QueueAdapter` verbs (`wq.enqueue` / `wq.claim` / `wq.markDone`), so the\n // `QueueItem<Stage>` shape stays insulated behind them.\n const wq = queueAdapter<Stage>();\n\n // The snapshot sub-knob is built only when `snapshotEvery` is configured;\n // absence is `null` so the progress gate short-circuits to \"no checkpoint\".\n // When present, the slice still carries a default SnapshotState so the shape\n // is uniform across configs — the gate simply never records into it.\n const snap =\n config.snapshotEvery !== undefined\n ? createSnapshot<V>({\n every: config.snapshotEvery,\n ...(config.snapshotKey !== undefined\n ? { key: config.snapshotKey }\n : {}),\n })\n : null;\n\n // The snapshot knob to actually drive: `snap` when configured, else a single\n // inert default (`every: 1`) bound ONCE here. Its `record` is never invoked\n // (the progress gate short-circuits when `snap` is null), so the cadence value\n // is immaterial — one shared stateless knob of pure functions stands in for\n // the disabled-checkpoint case across init/boot/handlers.\n const activeSnap = snap ?? createSnapshot<V>({ every: 1 });\n\n /** The starting slice for a never-started run: the `idle` phase, no `runId`. */\n function init(): MonitoredRunState<Stage> {\n return {\n phase: \"idle\",\n stepStates: [],\n startedAt: 0,\n lastProgressAt: 0,\n progressSeq: 0,\n // A default snapshot slice even without the brick → uniform shape. When\n // `snap` is null the inert `activeSnap` default supplies the slice; its\n // `record` is never called, so the cadence value is immaterial.\n snapshot: activeSnap.init(),\n };\n }\n\n /**\n * Seed the stage queue from the configured stages: every stage enqueued\n * `pending`, then the first claimed `running`. `at` stamps the claim. PURE —\n * the work-queue ops are pure array transforms; ids are positional (`stage:i`)\n * so they are deterministic and replayable (no `crypto`, no `Math.random`).\n */\n function seedStages(at: number): readonly QueueItem<Stage>[] {\n let queue: readonly QueueItem<Stage>[] = [];\n stages.forEach((stage, i) => {\n queue = wq.enqueue(queue, stage, at, `stage:${i}`).next;\n });\n // Claim the first stage as the running position. A non-empty pipeline always\n // has a pending head here, so the claim succeeds.\n const claimed = wq.claim(queue, at);\n return claimed ? claimed.next : queue;\n }\n\n /** The current (running) stage item, or `undefined` for a single-shot run. */\n function currentStage(\n s: MonitoredRunState<Stage>,\n ): QueueItem<Stage> | undefined {\n return s.stepStates.find((i) => i.status === \"running\");\n }\n\n // === Verb: start =========================================================\n\n /**\n * Start (or restart) the run for `runId` at time `at`. Seeds the stage queue\n * (if a pipeline) and enters `running` with the watchdog clock primed. Stamps\n * `runId` from the host — the machine never mints (invariant: identity is\n * host-minted, parsed at the boundary). PURE.\n */\n function start(\n s: MonitoredRunState<Stage>,\n runId: string,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n // Build the `running` arm explicitly rather than spreading `...s`: a restart\n // from `failed` must NOT carry the old `failure` field forward (the `running`\n // arm has none), and a start from `idle` has no `runId` to preserve. Only the\n // snapshot slice threads through.\n const next: MonitoredRunState<Stage> = {\n phase: \"running\",\n runId,\n stepStates: isPipeline ? seedStages(at) : [],\n startedAt: at,\n lastProgressAt: at,\n progressSeq: 0,\n snapshot: s.snapshot,\n };\n return [next, []];\n }\n\n /**\n * Bump the watchdog clock: set `lastProgressAt`/`progressSeq` so the safety\n * alarm Sub id changes and the substrate re-arms a fresh deadline. Shared by\n * `progress` and `advance`. PURE.\n */\n function bumpProgress(s: LiveRun<Stage>, at: number): LiveRun<Stage> {\n return { ...s, lastProgressAt: at, progressSeq: s.progressSeq + 1 };\n }\n\n /**\n * Account one progress unit to the snapshot slice (when the brick is\n * configured), returning the new slice + any checkpoint Cmd. `payload` is the\n * consumer's checkpoint value to persist. PURE — `snap.record` is pure, the\n * write is realized only when the runtime performs the emitted Cmd.\n */\n function recordSnapshot(\n s: MonitoredRunState<Stage>,\n payload: V,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (snap === null) return [s, []];\n const [snapshot, cmds] = snap.record(s.snapshot, payload, at);\n // `snap.record` only ever emits `SnapshotWriteCmd<V>` (its sole Cmd), but\n // its signature widens to `readonly Cmd[]`. Narrow back to this knob's\n // precise Cmd union so a consumer's host Cmd type can stay exact.\n return [{ ...s, snapshot }, cmds as readonly MonitoredRunCmd<V>[]];\n }\n\n // === Verb: progress ======================================================\n\n /**\n * Record forward progress: bump the watchdog (re-arming the safety alarm),\n * clear any `stale` mark back to `running`, and account a snapshot unit\n * (emitting a checkpoint Cmd on a cadence hit). A no-op unless the run is live\n * (`running` / `stale`) — a late progress event on a settled (`done` /\n * `failed`) or never-started (`idle`) run is ignored. PURE.\n */\n function progress(\n s: MonitoredRunState<Stage>,\n payload: V,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (s.phase !== \"running\" && s.phase !== \"stale\") return [s, []];\n const bumped: MonitoredRunState<Stage> = {\n ...bumpProgress(s, at),\n phase: \"running\",\n };\n return recordSnapshot(bumped, payload, at);\n }\n\n // === Verb: markStale =====================================================\n\n /**\n * Mark the run `stale` — a soft \"no progress observed\" signal the consumer\n * raises WITHOUT the deadline having fired (e.g. an upstream heartbeat gap).\n * Recoverable: the next `progress` flips it back to `running`. A no-op unless\n * the run is currently `running` (a settled run cannot go stale). Does NOT\n * touch `progressSeq` — going stale is not progress, so the armed deadline\n * keeps counting toward the hard terminal. PURE.\n */\n function markStale(\n s: MonitoredRunState<Stage>,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (s.phase !== \"running\") return [s, []];\n return [{ ...s, phase: \"stale\" }, []];\n }\n\n // === Verb: advance =======================================================\n\n /**\n * Advance the pipeline on a stage outcome:\n *\n * - `result.kind === \"fail\"` → terminate `failed { reason: \"stage\" }`,\n * carrying the failing stage index + error. No further stages run.\n * - `result.kind === \"ok\"`:\n * - single-shot run → finish to `done` (and force a final checkpoint\n * when checkpointing, so the last durable snapshot is the terminal\n * state).\n * - pipeline → retire the current stage (`markDoneOp`) and claim the\n * next (`claimNextOp`). If none remain, finish to `done`. The new\n * position survives eviction.\n *\n * An advance is itself a progress event, so it bumps the watchdog (re-arming\n * the alarm at the new stage) and accounts a snapshot unit. A no-op on a\n * settled (`done` / `failed`) or never-started (`idle`) run. PURE — `at` is the\n * only clock, ids are positional.\n */\n function advance(\n s: MonitoredRunState<Stage>,\n payload: V,\n result: StageResult,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (s.phase !== \"running\" && s.phase !== \"stale\") return [s, []];\n\n // Stage failure → hard terminal. Carry the failing stage VALUE (stable),\n // `undefined` for a single-shot run with no stage.\n if (result.kind === \"fail\") {\n const failed: MonitoredRunState<Stage> = {\n ...s,\n phase: \"failed\",\n failure: {\n reason: \"stage\",\n stage: currentStage(s)?.input,\n error: result.error,\n },\n };\n return [failed, []];\n }\n\n // Success: bump the watchdog first (an advance is progress), then move the\n // pipeline position.\n const bumped = bumpProgress(s, at);\n\n // Single-shot: one advance finishes the run.\n if (!isPipeline) {\n const done: MonitoredRunState<Stage> = { ...bumped, phase: \"done\" };\n // Force a terminal checkpoint so the last durable snapshot is `done`,\n // not whatever the cadence happened to land on.\n return finishWithSnapshot(done, payload, at);\n }\n\n // Pipeline: retire the running stage, claim the next.\n const current = currentStage(s);\n const afterDone =\n current !== undefined\n ? wq.markDone(s.stepStates, current.id).next\n : s.stepStates;\n const claimed = wq.claim(afterDone, at);\n\n if (claimed === null) {\n // No more stages — the pipeline is complete.\n const done: MonitoredRunState<Stage> = {\n ...bumped,\n phase: \"done\",\n stepStates: afterDone,\n };\n return finishWithSnapshot(done, payload, at);\n }\n\n // Advance to the next stage — POSITION now points at the claimed item.\n const moved: MonitoredRunState<Stage> = {\n ...bumped,\n stepStates: claimed.next,\n };\n return recordSnapshot(moved, payload, at);\n }\n\n /**\n * Finish helper: force a terminal checkpoint when checkpointing is enabled\n * (so the durable snapshot is the terminal state), else just settle. Shared\n * by single-shot finish and pipeline completion. PURE.\n */\n function finishWithSnapshot(\n s: MonitoredRunState<Stage>,\n payload: V,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (snap === null) return [s, []];\n const [snapshot, cmds] = snap.force(s.snapshot, payload, at);\n // Same narrowing as `recordSnapshot`: `snap.force` emits only the write Cmd.\n return [{ ...s, snapshot }, cmds as readonly MonitoredRunCmd<V>[]];\n }\n\n // === Verb: onDeadline ====================================================\n\n /**\n * The safety alarm fired. Two outcomes by Sub-id match:\n *\n * - the alarm matches the CURRENT `progressSeq` (no progress since it was\n * armed) → the run is wedged → terminate `failed { reason: \"deadline\" }`.\n * - the alarm matches an OLDER seq → a stale fire racing a progress event\n * that already re-armed the alarm → no-op (the substrate retired this id;\n * tolerate a fire still in flight defensively).\n *\n * A no-op on a settled run (its alarm was reconciled away; tolerate a stale\n * fire) and on a NEVER-STARTED (`idle`) run (`subs` armed nothing for it, so\n * any alarm reaching it is a rogue fire that must not un-start it). PURE —\n * `msg.atMs` stamps the failure.\n */\n function onDeadline(\n s: MonitoredRunState<Stage>,\n msg: MonitoredRunTimerMsg,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n // Only a live (`running` / `stale`) run is watched. A settled (`done` /\n // `failed`) run's alarm was reconciled away, and a never-started (`idle`) run\n // — which has no host-minted `runId` and armed nothing — must not be\n // un-started by a born-live/rogue fire. The phase narrow IS the guard the old\n // `runId === \"\"` sentinel stood in for.\n if (s.phase !== \"running\" && s.phase !== \"stale\") return [s, []];\n // Only the alarm armed against the current progressSeq is live. A bumped\n // seq means progress re-armed it; an older fire is stale.\n if (msg.id !== safetyTimerId(s.runId, s.progressSeq)) return [s, []];\n const failed: MonitoredRunState<Stage> = {\n ...s,\n phase: \"failed\",\n failure: { reason: \"deadline\", at: msg.atMs },\n };\n return [failed, []];\n }\n\n // === Verb: boot ==========================================================\n\n /**\n * Resume after a reload (cold wake). Re-seeds the watchdog clock to `at` so\n * the safety alarm re-arms from NOW (the pre-crash `lastProgressAt` would\n * immediately fire a deadline that already elapsed during downtime — a cold\n * wake is not a no-progress wedge). Bumps `progressSeq` so the alarm gets a\n * fresh id, and resets the snapshot cadence counter (un-checkpointed\n * pre-crash progress is moot — recovery resumes from the last durable\n * checkpoint). The pipeline POSITION (`stepStates`) is preserved untouched —\n * that is the whole point of staging: resume at the same stage.\n *\n * A no-op on a settled (`done` / `failed`) or never-started (`idle`) run. Emits\n * NO Cmd — re-emitting the current stage's outstanding effect is the CONSUMER's\n * job (it knows the per-stage Cmd), the audit machine's `outstandingEffect(stage)`\n * pattern. PURE.\n */\n function boot(\n s: MonitoredRunState<Stage>,\n at: number,\n ): readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]] {\n if (s.phase !== \"running\" && s.phase !== \"stale\") return [s, []];\n // `snap.boot` returns the reducer-cell shape `[SnapshotState, Cmd[]]`\n // (uniform across every sibling knob); a boot emits no Cmd, so take only the\n // slice. Assigning the tuple itself would put an ARRAY where a SnapshotState\n // belongs — a type error AND a non-round-trippable slice.\n const [bootedSnapshot] = activeSnap.boot(s.snapshot);\n const reseeded: MonitoredRunState<Stage> = {\n ...s,\n phase: \"running\",\n lastProgressAt: at,\n progressSeq: s.progressSeq + 1,\n snapshot: bootedSnapshot,\n // Any stage left `running` mid-flight stays the running position; the\n // queue is already correct (a single in-flight stage). resetRunningOp is\n // NOT applied — unlike a work pool, a monitored run has exactly one\n // in-flight stage and resuming it (not re-queuing it) is the correct\n // cold-wake behavior.\n };\n return [reseeded, []];\n }\n\n // === Subs ================================================================\n\n /**\n * Pre-wired subscriptions: the no-progress safety deadline, armed only while\n * the run is live (`running` / `stale`) AND a `deadlineMs` is configured. The\n * Sub id is keyed on `progressSeq`, so every progress event retires the old\n * alarm and the substrate arms a fresh one at the new `lastProgressAt +\n * deadlineMs` — a self-rearming watchdog with no manual `clearTimeout`. A\n * settled run desires no subs. Wire `subscribe: { deadline: subscribeDeadline }`.\n *\n * A NEVER-STARTED slice (`init()`) is its OWN `idle` phase, not a `running` run\n * with an empty `runId`. The `phase !== \"running\" && phase !== \"stale\"` narrow\n * therefore excludes it structurally — no separate `runId === \"\"` sentinel gate\n * is needed. Were `idle` armed, it would fire a deadline at `0 + deadlineMs`\n * (already in the past) and auto-fail a run that never began — the \"born live\"\n * defect. `start` is the only transition out of `idle`, so the watchdog exists\n * exactly once a real run is in flight.\n */\n function subs(s: MonitoredRunState<Stage>): readonly DeadlineSub[] {\n if (config.deadlineMs === undefined) return [];\n if (s.phase !== \"running\" && s.phase !== \"stale\") return [];\n return [\n deadlineSub(\n safetyTimerId(s.runId, s.progressSeq),\n s.lastProgressAt + config.deadlineMs,\n ),\n ];\n }\n\n // === Handlers ============================================================\n\n /**\n * Pre-wired interpret handler for the checkpoint write. Delegates wholesale to\n * `../snapshot`'s `handlers` (which performs the `put` and routes Ok →\n * `snapshot_saved` / Err → `snapshot_failed` via Railway — the write never\n * rejects into the runtime). When checkpointing is disabled (`snapshotEvery`\n * omitted), no `snapshot_write` Cmd is ever emitted, so the returned map is an\n * inert no-op handler — kept for a uniform `handlers(ports)` call shape.\n *\n * Fold the `snapshot_saved` Msg back through this knob's `confirmSnapshot`\n * verb to advance the durable watermark.\n */\n function handlers(\n ports: MonitoredRunPorts<V>,\n ): Interpret<\n | SnapshotSavedMsg\n | SnapshotFailedMsg\n | SnapshotLoadedMsg<V>\n | SnapshotLoadFailedMsg,\n SnapshotWriteCmd<V> | SnapshotLoadCmd,\n unknown\n > {\n return activeSnap.handlers({ store: ports.store });\n }\n\n /**\n * Fold a confirmed checkpoint write into the slice — advances the snapshot\n * watermark (forward-only). A no-op pass-through when checkpointing is\n * disabled. PURE. Fold the `snapshot_saved` Msg here.\n */\n function confirmSnapshot(\n s: MonitoredRunState<Stage>,\n msg: SnapshotSavedMsg,\n ): MonitoredRunState<Stage> {\n if (snap === null) return s;\n // `snap.confirm` returns the reducer-cell shape `[SnapshotState, Cmd[]]`\n // (an ack emits nothing). Take only the slice — folding the whole tuple into\n // `snapshot` would store an ARRAY where a SnapshotState belongs (a type error\n // and a non-round-trippable slice).\n const [confirmed] = snap.confirm(s.snapshot, msg);\n return { ...s, snapshot: confirmed };\n }\n\n return {\n init,\n start,\n progress,\n markStale,\n advance,\n onDeadline,\n boot,\n confirmSnapshot,\n subs,\n handlers,\n };\n}\n\n/**\n * Lift a knob result `[slice, cmds]` into a host `[State, cmds]` where the\n * slice lives at `state.run`. Convenience for the common single-slice host;\n * hosts with a differently-named field spread by hand. PURE — a thin record\n * rebuild, no clock / RNG.\n */\nexport function liftRun<\n S extends { run: MonitoredRunState<Stage> },\n Stage,\n C extends Cmd,\n>(\n state: S,\n [slice, cmds]: readonly [MonitoredRunState<Stage>, readonly C[]],\n): readonly [S, readonly C[]] {\n return [{ ...state, run: slice }, cmds];\n}\n\n/**\n * Re-export the deadline Sub primitives so consumers (and tests) wire one\n * import: `subscribeDeadline` is the `subscribe` cell, `deadlineSub` builds the\n * Sub literal this knob's `subs` emits.\n */\nexport { subscribeDeadline, deadlineSub };\nexport type {\n DeadlineSub,\n DeadlineExceeded,\n SnapshotSavedMsg,\n SnapshotWriteCmd,\n};\n"]}
@@ -0,0 +1,209 @@
1
+ import { typeOf, indexPath, unionKeys, keyPath } from './chunk-EWJP3YYP.js';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { useRef, useEffect, useState, useCallback } from 'react';
4
+
5
+ function MsgLog({ history, className }) {
6
+ return /* @__PURE__ */ jsx("div", { className: `tea-dt-log${className ? ` ${className}` : ""}`, children: history.map((r, i) => /* @__PURE__ */ jsxs(
7
+ "div",
8
+ {
9
+ className: `tea-dt-row tea-dt-${r.kind ?? "msg"}`,
10
+ children: [
11
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-t", children: r.ts }),
12
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-m", children: r.text })
13
+ ]
14
+ },
15
+ `${r.ts}-${i}`
16
+ )) });
17
+ }
18
+
19
+ // src/devtools/state-diff.ts
20
+ function diffState(expected, actual) {
21
+ const out = [];
22
+ walk(expected, actual, "state", out);
23
+ return out;
24
+ }
25
+ function walk(expected, actual, path, out) {
26
+ if (Object.is(expected, actual)) return;
27
+ const te = typeOf(expected);
28
+ const ta = typeOf(actual);
29
+ if (te === "undefined") {
30
+ out.push({ path, kind: "added", expected, actual });
31
+ return;
32
+ }
33
+ if (ta === "undefined") {
34
+ out.push({ path, kind: "removed", expected, actual });
35
+ return;
36
+ }
37
+ if (te !== ta) {
38
+ out.push({ path, kind: "changed", expected, actual });
39
+ return;
40
+ }
41
+ if (te === "array") {
42
+ const ae = expected;
43
+ const aa = actual;
44
+ const len = Math.max(ae.length, aa.length);
45
+ for (let i = 0; i < len; i++) {
46
+ walk(ae[i], aa[i], indexPath(path, i), out);
47
+ }
48
+ return;
49
+ }
50
+ if (te === "object") {
51
+ const oe = expected;
52
+ const oa = actual;
53
+ for (const key of unionKeys(oe, oa)) {
54
+ walk(oe[key], oa[key], keyPath(path, key), out);
55
+ }
56
+ return;
57
+ }
58
+ out.push({ path, kind: "changed", expected, actual });
59
+ }
60
+ function fmtValue(v) {
61
+ if (v === void 0) return "undefined";
62
+ if (typeof v === "number" && Number.isNaN(v)) return "NaN";
63
+ if (typeof v === "bigint") return `${v}n`;
64
+ if (typeof v === "string") return JSON.stringify(v);
65
+ if (typeof v === "function" || typeof v === "symbol") return String(v);
66
+ try {
67
+ return JSON.stringify(v) ?? String(v);
68
+ } catch {
69
+ return String(v);
70
+ }
71
+ }
72
+ function formatDiff(changes) {
73
+ return changes.map(formatChange).join("\n");
74
+ }
75
+ function formatChange(c) {
76
+ switch (c.kind) {
77
+ case "changed":
78
+ return `~ ${c.path}: ${fmtValue(c.expected)} \u2192 ${fmtValue(c.actual)}`;
79
+ case "removed":
80
+ return `- ${c.path}: ${fmtValue(c.expected)}`;
81
+ case "added":
82
+ return `+ ${c.path}: ${fmtValue(c.actual)}`;
83
+ }
84
+ }
85
+ function changeRow(c) {
86
+ switch (c.kind) {
87
+ case "changed":
88
+ return /* @__PURE__ */ jsxs("div", { className: "tea-dt-diff-row tea-dt-diff-changed", children: [
89
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-diff-mark", children: "~" }),
90
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-diff-path", children: c.path }),
91
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-diff-old", children: fmtValue(c.expected) }),
92
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-diff-arrow", children: "\u2192" }),
93
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-diff-new", children: fmtValue(c.actual) })
94
+ ] }, c.path);
95
+ case "removed":
96
+ return /* @__PURE__ */ jsxs("div", { className: "tea-dt-diff-row tea-dt-diff-removed", children: [
97
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-diff-mark", children: "-" }),
98
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-diff-path", children: c.path }),
99
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-diff-old", children: fmtValue(c.expected) })
100
+ ] }, c.path);
101
+ case "added":
102
+ return /* @__PURE__ */ jsxs("div", { className: "tea-dt-diff-row tea-dt-diff-added", children: [
103
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-diff-mark", children: "+" }),
104
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-diff-path", children: c.path }),
105
+ /* @__PURE__ */ jsx("span", { className: "tea-dt-diff-new", children: fmtValue(c.actual) })
106
+ ] }, c.path);
107
+ }
108
+ }
109
+ function StateDiff({ expected, actual, className }) {
110
+ const changes = diffState(expected, actual);
111
+ return /* @__PURE__ */ jsx(
112
+ "div",
113
+ {
114
+ className: `tea-dt-diff${className ? ` ${className}` : ""}`,
115
+ title: changes.length === 0 ? "no changes" : formatDiff(changes),
116
+ children: changes.length === 0 ? /* @__PURE__ */ jsx("div", { className: "tea-dt-diff-empty", children: "no changes" }) : changes.map(changeRow)
117
+ }
118
+ );
119
+ }
120
+ function escapeHtml(s) {
121
+ return s.replace(
122
+ /[&<>"]/g,
123
+ (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]
124
+ );
125
+ }
126
+ function fmtJson(obj, indent = 0) {
127
+ if (obj === null) return '<span class="tea-dt-b">null</span>';
128
+ if (typeof obj === "number") return `<span class="tea-dt-n">${obj}</span>`;
129
+ if (typeof obj === "boolean") return `<span class="tea-dt-b">${obj}</span>`;
130
+ if (typeof obj === "string")
131
+ return `<span class="tea-dt-s">"${escapeHtml(obj)}"</span>`;
132
+ if (Array.isArray(obj)) {
133
+ if (obj.length === 0) return '<span class="tea-dt-br">[]</span>';
134
+ return '<span class="tea-dt-br">[</span>' + obj.map((v) => fmtJson(v, indent + 1)).join('<span class="tea-dt-br">, </span>') + '<span class="tea-dt-br">]</span>';
135
+ }
136
+ if (typeof obj === "object") {
137
+ const keys = Object.keys(obj);
138
+ if (keys.length === 0) return '<span class="tea-dt-br">{}</span>';
139
+ const pad = "&nbsp;&nbsp;".repeat(indent + 1);
140
+ const lines = keys.map((k, i) => {
141
+ const v = obj[k];
142
+ const comma = i < keys.length - 1 ? '<span class="tea-dt-br">,</span>' : "";
143
+ return `${pad}<span class="tea-dt-k">${escapeHtml(k)}</span><span class="tea-dt-br">:</span> ${fmtJson(v, indent + 1)}${comma}`;
144
+ });
145
+ return [
146
+ '<span class="tea-dt-br">{</span>',
147
+ ...lines,
148
+ `${"&nbsp;&nbsp;".repeat(indent)}<span class="tea-dt-br">}</span>`
149
+ ].join("<br/>");
150
+ }
151
+ return escapeHtml(String(obj));
152
+ }
153
+ function StateInspector({
154
+ state,
155
+ flashKey,
156
+ className
157
+ }) {
158
+ const boxRef = useRef(null);
159
+ useEffect(() => {
160
+ if (flashKey === void 0 || !boxRef.current) return;
161
+ boxRef.current.classList.remove("tea-dt-flash");
162
+ void boxRef.current.offsetWidth;
163
+ boxRef.current.classList.add("tea-dt-flash");
164
+ }, [flashKey]);
165
+ return /* @__PURE__ */ jsx(
166
+ "div",
167
+ {
168
+ ref: boxRef,
169
+ className: `tea-dt-state${className ? ` ${className}` : ""}`,
170
+ children: /* @__PURE__ */ jsx(
171
+ "div",
172
+ {
173
+ className: "tea-dt-json",
174
+ dangerouslySetInnerHTML: { __html: fmtJson(state) }
175
+ }
176
+ )
177
+ }
178
+ );
179
+ }
180
+ function nowTs() {
181
+ return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB", { hour12: false }).slice(3);
182
+ }
183
+ function useMsgHistory(dispatch, opts = {}) {
184
+ const max = opts.max ?? 60;
185
+ const now = opts.now ?? nowTs;
186
+ const derive = opts.derive ?? ((msg) => {
187
+ const tag = msg && typeof msg === "object" && "type" in msg ? String(msg.type) : String(msg);
188
+ return { ts: now(), kind: "msg", text: tag };
189
+ });
190
+ const [history, setHistory] = useState([]);
191
+ const dispatchRef = useRef(dispatch);
192
+ dispatchRef.current = dispatch;
193
+ const deriveRef = useRef(derive);
194
+ deriveRef.current = derive;
195
+ const wrapped = useCallback(
196
+ (msg) => {
197
+ const entry = deriveRef.current(msg);
198
+ setHistory((prev) => [entry, ...prev].slice(0, max));
199
+ void dispatchRef.current(msg);
200
+ },
201
+ [max]
202
+ );
203
+ const clear = useCallback(() => setHistory([]), []);
204
+ return [history, wrapped, clear];
205
+ }
206
+
207
+ export { MsgLog, StateDiff, StateInspector, diffState, formatDiff, useMsgHistory };
208
+ //# sourceMappingURL=chunk-WRAA7GVZ.js.map
209
+ //# sourceMappingURL=chunk-WRAA7GVZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/devtools/msg-log.tsx","../src/devtools/state-diff.ts","../src/devtools/state-diff-view.tsx","../src/devtools/state-inspector.tsx","../src/devtools/use-msg-history.ts"],"names":["jsxs","jsx","useRef"],"mappings":"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","file":"chunk-WRAA7GVZ.js","sourcesContent":["export type MsgLogEntry = {\n /** Display timestamp (whatever format you produced). */\n ts: string;\n /** Visual category — drives the row icon. Defaults to \"msg\" via CSS. */\n kind?: \"msg\" | \"sub\" | \"cmd\" | \"ok\" | \"fail\";\n /** Row text — usually the Msg tag plus a short detail. */\n text: string;\n};\n\nexport interface MsgLogProps {\n history: readonly MsgLogEntry[];\n className?: string;\n}\n\nexport function MsgLog({ history, className }: MsgLogProps) {\n return (\n <div className={`tea-dt-log${className ? ` ${className}` : \"\"}`}>\n {history.map((r, i) => (\n <div\n key={`${r.ts}-${i}`}\n className={`tea-dt-row tea-dt-${r.kind ?? \"msg\"}`}\n >\n <span className=\"tea-dt-t\">{r.ts}</span>\n <span className=\"tea-dt-m\">{r.text}</span>\n </div>\n ))}\n </div>\n );\n}\n","// ---------------------------------------------------------------------------\n// state-diff — the RICHER cousin of trace-replay's first-divergence walk.\n//\n// `../trace-replay` answers \"did the state drift, and where FIRST?\" — it stops\n// at the first differing cell because a regression check only needs to point at\n// one. This module answers the fuller devtools question: \"show me EVERY cell\n// that differs,\" classified as added / removed / changed, ready to render.\n//\n// Same substrate, same path grammar (root `state`, `foo[2]` for arrays,\n// `foo.bar` for objects, sorted-union key order, `Object.is` leaf equality) so\n// a path printed here is the SAME string trace-replay would print for that\n// cell. The two tools are deliberately consistent: a `divergence.path` from a\n// replay result indexes straight into this module's `StateChange[]`.\n//\n// Pure + zero-dep on purpose — runs in Node, in tests, in a Worker. The React\n// view (`./state-diff.tsx`) is the only React-touching layer.\n// ---------------------------------------------------------------------------\n\nimport {\n indexPath,\n keyPath,\n typeOf,\n unionKeys,\n} from \"../trace-replay/state-walk\";\n\n/**\n * A single differing cell between two states.\n *\n * - `kind: \"added\"` — the path exists in `actual` but not `expected`\n * (`expected` is `undefined`).\n * - `kind: \"removed\"` — the path exists in `expected` but not `actual`\n * (`actual` is `undefined`).\n * - `kind: \"changed\"` — the path exists on both sides with differing leaf\n * values (or differing kinds, e.g. object vs array).\n */\nexport interface StateChange {\n /** Access path from the state root, e.g. `state.items[1].status`. */\n path: string;\n /** How the cell differs between the two states. */\n kind: \"added\" | \"removed\" | \"changed\";\n /** The value on the `expected` side (`undefined` when `added`). */\n expected: unknown;\n /** The value on the `actual` side (`undefined` when `removed`). */\n actual: unknown;\n}\n\n/**\n * Deep-walk two states and return EVERY differing cell, in deterministic\n * pre-order (the node before its children; children in array-index order, then\n * sorted-union key order for objects). The empty array means the two states are\n * deeply equal.\n *\n * Equality + path grammar match `../trace-replay`'s `firstDivergence` exactly:\n * - `Object.is` leaf equality, so `NaN` equals `NaN` and `+0` ≠ `-0`.\n * - arrays index by `[i]`; a length mismatch is NOT reported at the array\n * node — instead each surplus/missing element surfaces as its own\n * added/removed child, which is what a devtools list wants to show.\n * - objects walk the sorted union of both sides' keys; a key on only one\n * side becomes an `added`/`removed` child.\n * - the root path is `\"state\"`.\n *\n * Scope is JSON-shaped state (the same substrate trace-replay targets):\n * primitives, null/undefined, arrays, plain objects. A differing kind at a node\n * (object vs array, number vs string) is reported as a single `changed` at that\n * node — the walk does not descend into mismatched kinds.\n */\nexport function diffState(expected: unknown, actual: unknown): StateChange[] {\n const out: StateChange[] = [];\n walk(expected, actual, \"state\", out);\n return out;\n}\n\nfunction walk(\n expected: unknown,\n actual: unknown,\n path: string,\n out: StateChange[],\n): void {\n // Identity / primitive fast path. Equal cells contribute nothing. `Object.is`\n // so NaN equals NaN and +0 ≠ -0 — same intent as trace-replay's leaf check.\n if (Object.is(expected, actual)) return;\n\n const te = typeOf(expected);\n const ta = typeOf(actual);\n\n // A present/absent pair at this node is an add or a remove — the structural\n // signal a viewer color-codes differently from a value change.\n if (te === \"undefined\") {\n out.push({ path, kind: \"added\", expected, actual });\n return;\n }\n if (ta === \"undefined\") {\n out.push({ path, kind: \"removed\", expected, actual });\n return;\n }\n\n // Different kinds at a present node (object vs array, number vs string,\n // value vs null) — one `changed` at the node; do not descend into mismatched\n // shapes, mirroring trace-replay's \"this node IS the divergence\".\n if (te !== ta) {\n out.push({ path, kind: \"changed\", expected, actual });\n return;\n }\n\n if (te === \"array\") {\n const ae = expected as unknown[];\n const aa = actual as unknown[];\n // Walk the union of indices so surplus elements on EITHER side surface as\n // their own added/removed children (vs trace-replay, which reports a length\n // mismatch at the array node and stops). A viewer wants the per-element\n // detail.\n const len = Math.max(ae.length, aa.length);\n for (let i = 0; i < len; i++) {\n walk(ae[i], aa[i], indexPath(path, i), out);\n }\n return;\n }\n\n if (te === \"object\") {\n const oe = expected as Record<string, unknown>;\n const oa = actual as Record<string, unknown>;\n // Sorted union of keys — deterministic regardless of insertion order, so\n // two states with the same data in different key order produce no changes,\n // and the reported order is reproducible run-to-run.\n for (const key of unionKeys(oe, oa)) {\n walk(oe[key], oa[key], keyPath(path, key), out);\n }\n return;\n }\n\n // Two present primitives (string/number/boolean/bigint/symbol) or two\n // differing nulls that failed the Object.is fast path — a leaf change.\n out.push({ path, kind: \"changed\", expected, actual });\n}\n\n/** Render a single value compactly for a diff cell. Strings get quotes so\n * `\"3\"` is visibly distinct from `3`; everything else is JSON-ish, with the\n * `undefined`/`NaN`/`BigInt` cases JSON.stringify mishandles spelled out.\n * Exported (though not re-exported from `./index`) so the React `<StateDiff>`\n * view renders each cell with this exact formatter rather than a copy. */\nexport function fmtValue(v: unknown): string {\n if (v === undefined) return \"undefined\";\n if (typeof v === \"number\" && Number.isNaN(v)) return \"NaN\";\n if (typeof v === \"bigint\") return `${v}n`;\n if (typeof v === \"string\") return JSON.stringify(v);\n if (typeof v === \"function\" || typeof v === \"symbol\") return String(v);\n try {\n return JSON.stringify(v) ?? String(v);\n } catch {\n // Circular / non-serializable — fall back to a coarse tag.\n return String(v);\n }\n}\n\n/**\n * Pretty multi-line text for a {@link diffState} result. One `±`-prefixed line\n * per change, in the array's (pre-order, deterministic) order:\n *\n * - `~ state.total: 121.5 → 135` (changed: old → new)\n * - `- state.coupon: \"X\"` (removed: only the old value)\n * - `+ state.tax: 8` (added: only the new value)\n *\n * Deterministic given a deterministic `changes` array. An empty array yields\n * the empty string.\n */\nexport function formatDiff(changes: StateChange[]): string {\n return changes.map(formatChange).join(\"\\n\");\n}\n\n/** One `±`-prefixed line for a single change. Split out so the `map` callback\n * is total (Biome's useIterableCallbackReturn) without a dead default branch. */\nfunction formatChange(c: StateChange): string {\n switch (c.kind) {\n case \"changed\":\n return `~ ${c.path}: ${fmtValue(c.expected)} → ${fmtValue(c.actual)}`;\n case \"removed\":\n return `- ${c.path}: ${fmtValue(c.expected)}`;\n case \"added\":\n return `+ ${c.path}: ${fmtValue(c.actual)}`;\n }\n}\n","import {\n diffState,\n fmtValue,\n formatDiff,\n type StateChange,\n} from \"./state-diff\";\n\nexport interface StateDiffProps {\n /**\n * The baseline / \"before\" state — what you expected. In a regression view\n * this is a trace's recorded `finalState`; in a time-travel view it's the\n * previous model.\n */\n expected: unknown;\n /**\n * The \"after\" state to compare against `expected` — the recomputed or\n * current model.\n */\n actual: unknown;\n /** Optional className appended to the container. Use to override layout. */\n className?: string;\n}\n\n/** Per-kind row glyph + value rendering. Lives here (not CSS ::before like the\n * MsgLog rows) because the diff prints the path AND the value(s), so the glyph\n * is part of structured content the screen reader should read in order. */\nfunction changeRow(c: StateChange) {\n // `path` is unique within one diff (each cell appears at most once), so it is\n // a stable React key without needing the array index.\n switch (c.kind) {\n case \"changed\":\n return (\n <div key={c.path} className=\"tea-dt-diff-row tea-dt-diff-changed\">\n <span className=\"tea-dt-diff-mark\">~</span>\n <span className=\"tea-dt-diff-path\">{c.path}</span>\n <span className=\"tea-dt-diff-old\">{fmtValue(c.expected)}</span>\n <span className=\"tea-dt-diff-arrow\">→</span>\n <span className=\"tea-dt-diff-new\">{fmtValue(c.actual)}</span>\n </div>\n );\n case \"removed\":\n return (\n <div key={c.path} className=\"tea-dt-diff-row tea-dt-diff-removed\">\n <span className=\"tea-dt-diff-mark\">-</span>\n <span className=\"tea-dt-diff-path\">{c.path}</span>\n <span className=\"tea-dt-diff-old\">{fmtValue(c.expected)}</span>\n </div>\n );\n case \"added\":\n return (\n <div key={c.path} className=\"tea-dt-diff-row tea-dt-diff-added\">\n <span className=\"tea-dt-diff-mark\">+</span>\n <span className=\"tea-dt-diff-path\">{c.path}</span>\n <span className=\"tea-dt-diff-new\">{fmtValue(c.actual)}</span>\n </div>\n );\n }\n}\n\n/**\n * Presentational diff of two states. Renders {@link diffState}'s output as a\n * color-coded list (added / removed / changed), reusing the devtools `tea-dt-*`\n * class convention so it inherits the same design tokens as `<StateInspector>`\n * and `<MsgLog>` (see ./styles.css). No state, no subscription — pass the two\n * states as props, exactly like the rest of /devtools.\n *\n * When the states are deeply equal the list is empty; a tiny \"no changes\" hint\n * renders so an operator can tell \"equal\" from \"not yet computed\".\n */\nexport function StateDiff({ expected, actual, className }: StateDiffProps) {\n const changes = diffState(expected, actual);\n return (\n <div\n className={`tea-dt-diff${className ? ` ${className}` : \"\"}`}\n // The plain-text diff is the accessible/copyable fallback for the\n // color-coded rows — same content `formatDiff` produces in Node/tests.\n title={changes.length === 0 ? \"no changes\" : formatDiff(changes)}\n >\n {changes.length === 0 ? (\n <div className=\"tea-dt-diff-empty\">no changes</div>\n ) : (\n changes.map(changeRow)\n )}\n </div>\n );\n}\n","import { useEffect, useRef } from \"react\";\n\nfunction escapeHtml(s: string): string {\n return s.replace(\n /[&<>\"]/g,\n (c) =>\n ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\" })[c] as string,\n );\n}\n\nfunction fmtJson(obj: unknown, indent = 0): string {\n if (obj === null) return '<span class=\"tea-dt-b\">null</span>';\n if (typeof obj === \"number\") return `<span class=\"tea-dt-n\">${obj}</span>`;\n if (typeof obj === \"boolean\") return `<span class=\"tea-dt-b\">${obj}</span>`;\n if (typeof obj === \"string\")\n return `<span class=\"tea-dt-s\">\"${escapeHtml(obj)}\"</span>`;\n if (Array.isArray(obj)) {\n if (obj.length === 0) return '<span class=\"tea-dt-br\">[]</span>';\n return (\n '<span class=\"tea-dt-br\">[</span>' +\n obj\n .map((v) => fmtJson(v, indent + 1))\n .join('<span class=\"tea-dt-br\">, </span>') +\n '<span class=\"tea-dt-br\">]</span>'\n );\n }\n if (typeof obj === \"object\") {\n const keys = Object.keys(obj as object);\n if (keys.length === 0) return '<span class=\"tea-dt-br\">{}</span>';\n const pad = \"&nbsp;&nbsp;\".repeat(indent + 1);\n const lines = keys.map((k, i) => {\n const v = (obj as Record<string, unknown>)[k];\n const comma =\n i < keys.length - 1 ? '<span class=\"tea-dt-br\">,</span>' : \"\";\n return `${pad}<span class=\"tea-dt-k\">${escapeHtml(k)}</span><span class=\"tea-dt-br\">:</span> ${fmtJson(v, indent + 1)}${comma}`;\n });\n return [\n '<span class=\"tea-dt-br\">{</span>',\n ...lines,\n `${\"&nbsp;&nbsp;\".repeat(indent)}<span class=\"tea-dt-br\">}</span>`,\n ].join(\"<br/>\");\n }\n return escapeHtml(String(obj));\n}\n\nexport interface StateInspectorProps {\n /**\n * The machine state to display. Render-derived shape — pass whatever you\n * want surfaced to the inspector (often a hand-picked subset of the model).\n */\n state: unknown;\n /**\n * When this number changes, the inspector flashes. Pass `model.msgCount` or\n * any other monotonic counter you already have. Omit to disable flashing.\n */\n flashKey?: number;\n /**\n * Optional className appended to the container. Use to override layout.\n */\n className?: string;\n}\n\nexport function StateInspector({\n state,\n flashKey,\n className,\n}: StateInspectorProps) {\n const boxRef = useRef<HTMLDivElement>(null);\n useEffect(() => {\n if (flashKey === undefined || !boxRef.current) return;\n boxRef.current.classList.remove(\"tea-dt-flash\");\n void boxRef.current.offsetWidth;\n boxRef.current.classList.add(\"tea-dt-flash\");\n }, [flashKey]);\n\n return (\n <div\n ref={boxRef}\n className={`tea-dt-state${className ? ` ${className}` : \"\"}`}\n >\n <div\n className=\"tea-dt-json\"\n // biome-ignore lint/security/noDangerouslySetInnerHtml: formatter output\n dangerouslySetInnerHTML={{ __html: fmtJson(state) }}\n />\n </div>\n );\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type { MsgLogEntry } from \"./msg-log\";\n\nfunction nowTs(): string {\n return new Date().toLocaleTimeString(\"en-GB\", { hour12: false }).slice(3);\n}\n\n/**\n * Wrap a `dispatch` so every Msg it sees lands in a bounded history buffer.\n *\n * `derive` lets you map a Msg to a richer entry (e.g. assign `kind: \"sub\"` to\n * subscription-triggered messages). Default: every msg becomes a `\"msg\"` row\n * tagged with `msg.type` (or `String(msg)` if it has no `type`). The substrate\n * uses `type` as the Msg discriminant — using `_tag` here would render every\n * Msg as `[object Object]` in the inspector.\n *\n * `max` caps the buffer — oldest entries fall off (LIFO, newest first).\n *\n * `now` is the timestamp source for the default `derive`. Defaults to\n * `nowTs` (real wall-clock). Tests inject a deterministic shim so\n * snapshots don't drift; production code never sets it.\n */\nexport function useMsgHistory<M>(\n dispatch: (msg: M) => void | Promise<void>,\n opts: {\n max?: number;\n derive?: (msg: M) => MsgLogEntry;\n now?: () => string;\n } = {},\n): readonly [readonly MsgLogEntry[], (msg: M) => void, () => void] {\n const max = opts.max ?? 60;\n const now = opts.now ?? nowTs;\n const derive =\n opts.derive ??\n ((msg: M): MsgLogEntry => {\n const tag =\n msg && typeof msg === \"object\" && \"type\" in msg\n ? String((msg as { type: unknown }).type)\n : String(msg);\n return { ts: now(), kind: \"msg\", text: tag };\n });\n\n const [history, setHistory] = useState<readonly MsgLogEntry[]>([]);\n // Stable ref to `dispatch` so the wrapper identity stays stable across\n // renders — important when this is passed to keyboard handlers and the like.\n const dispatchRef = useRef(dispatch);\n dispatchRef.current = dispatch;\n const deriveRef = useRef(derive);\n deriveRef.current = derive;\n\n const wrapped = useCallback(\n (msg: M) => {\n const entry = deriveRef.current(msg);\n setHistory((prev) => [entry, ...prev].slice(0, max));\n // Fire-and-forget — `dispatch` may return a Promise; we don't await it\n // here because callers historically treat dispatch as void.\n void dispatchRef.current(msg);\n },\n [max],\n );\n\n const clear = useCallback(() => setHistory([]), []);\n\n return [history, wrapped, clear] as const;\n}\n"]}