@demlik/tea 0.16.0 → 0.17.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 (84) hide show
  1. package/dist/agent/index.d.ts +13 -13
  2. package/dist/agent/index.js +9 -9
  3. package/dist/{chunk-CIHSHLQX.js → chunk-4NTCPTNQ.js} +3 -3
  4. package/dist/{chunk-CIHSHLQX.js.map → chunk-4NTCPTNQ.js.map} +1 -1
  5. package/dist/{chunk-7MGXQZPW.js → chunk-4PXNZ5UY.js} +4 -4
  6. package/dist/{chunk-7MGXQZPW.js.map → chunk-4PXNZ5UY.js.map} +1 -1
  7. package/dist/{chunk-6NG4V5SB.js → chunk-6XBEYZME.js} +5 -5
  8. package/dist/{chunk-6NG4V5SB.js.map → chunk-6XBEYZME.js.map} +1 -1
  9. package/dist/{chunk-GOX2CYIM.js → chunk-C4U5D5HP.js} +3 -3
  10. package/dist/{chunk-GOX2CYIM.js.map → chunk-C4U5D5HP.js.map} +1 -1
  11. package/dist/{chunk-MVZS5FDM.js → chunk-DFUEAA6L.js} +3 -3
  12. package/dist/{chunk-MVZS5FDM.js.map → chunk-DFUEAA6L.js.map} +1 -1
  13. package/dist/{chunk-IVJP3GKJ.js → chunk-GWWCCSDH.js} +4 -4
  14. package/dist/{chunk-IVJP3GKJ.js.map → chunk-GWWCCSDH.js.map} +1 -1
  15. package/dist/{chunk-CH5KTOEZ.js → chunk-K4ETBNKS.js} +32 -9
  16. package/dist/chunk-K4ETBNKS.js.map +1 -0
  17. package/dist/{chunk-5XU6ZJO2.js → chunk-KWNCM6XM.js} +19 -13
  18. package/dist/chunk-KWNCM6XM.js.map +1 -0
  19. package/dist/{chunk-BCTEOVOU.js → chunk-LYVOGOBC.js} +3 -3
  20. package/dist/{chunk-BCTEOVOU.js.map → chunk-LYVOGOBC.js.map} +1 -1
  21. package/dist/{chunk-MYNLY3HP.js → chunk-OPH6Y4UT.js} +3 -3
  22. package/dist/{chunk-MYNLY3HP.js.map → chunk-OPH6Y4UT.js.map} +1 -1
  23. package/dist/{chunk-NSIAORRY.js → chunk-OYR7ENKU.js} +3 -3
  24. package/dist/{chunk-NSIAORRY.js.map → chunk-OYR7ENKU.js.map} +1 -1
  25. package/dist/{chunk-WT5PBPLB.js → chunk-QWIPH4YH.js} +4 -4
  26. package/dist/{chunk-WT5PBPLB.js.map → chunk-QWIPH4YH.js.map} +1 -1
  27. package/dist/{chunk-EHUDXAKC.js → chunk-XBZQT2TD.js} +7 -7
  28. package/dist/chunk-XBZQT2TD.js.map +1 -0
  29. package/dist/{core-BbgtiXdg.d.ts → core-rDbOaJTN.d.ts} +7 -3
  30. package/dist/do/index.d.ts +12 -12
  31. package/dist/do/index.js +10 -10
  32. package/dist/{durable-effects-7MTK_jnj.d.ts → durable-effects-CNaw97Be.d.ts} +1 -1
  33. package/dist/effect/index.d.ts +2 -2
  34. package/dist/effect/index.js +2 -2
  35. package/dist/extension/index.d.ts +3 -3
  36. package/dist/extension/index.js +2 -2
  37. package/dist/flow/index.d.ts +13 -13
  38. package/dist/flow/index.js +10 -10
  39. package/dist/idempotency/index.d.ts +3 -3
  40. package/dist/idempotency/index.js +3 -3
  41. package/dist/{index-Bz5Rh9Ga.d.ts → index-BOrsbdAf.d.ts} +4 -4
  42. package/dist/{index-DU6JpQ8G.d.ts → index-B_BxplWi.d.ts} +1 -1
  43. package/dist/{index-CjFDUBeq.d.ts → index-CWmsXNdX.d.ts} +1 -1
  44. package/dist/{index-_dOR3xqA.d.ts → index-Cberb5Rr.d.ts} +2 -2
  45. package/dist/{index-7vV8xmsD.d.ts → index-CcrKLtse.d.ts} +1 -1
  46. package/dist/{index-CkFYA9Y6.d.ts → index-Cx0F2M34.d.ts} +2 -2
  47. package/dist/{index-Xufl55W8.d.ts → index-DaX_P35Q.d.ts} +2 -2
  48. package/dist/{index-Cou86-GX.d.ts → index-q1PoukOZ.d.ts} +2 -2
  49. package/dist/index.d.ts +5 -5
  50. package/dist/index.js +2 -2
  51. package/dist/jev/index.d.ts +8 -8
  52. package/dist/jev/index.js +5 -5
  53. package/dist/{machine-BQzTV8On.d.ts → machine-BcfCFMYF.d.ts} +7 -7
  54. package/dist/machine-viz/index.d.ts +1 -1
  55. package/dist/machine-viz/index.js +2 -2
  56. package/dist/mem/index.d.ts +2 -2
  57. package/dist/mem/index.js +2 -2
  58. package/dist/node/index.d.ts +2 -2
  59. package/dist/node/index.js +2 -2
  60. package/dist/paginate/index.d.ts +4 -4
  61. package/dist/paginate/index.js +5 -5
  62. package/dist/parity/index.d.ts +3 -3
  63. package/dist/parity/index.js +3 -3
  64. package/dist/pbt/index.d.ts +1 -1
  65. package/dist/pbt/index.js +2 -2
  66. package/dist/persistence/index.d.ts +9 -10
  67. package/dist/persistence/index.js +4 -4
  68. package/dist/promise/index.d.ts +2 -2
  69. package/dist/promise/index.js +3 -3
  70. package/dist/react/index.d.ts +2 -2
  71. package/dist/resilience/index.d.ts +6 -6
  72. package/dist/resilience/index.js +5 -5
  73. package/dist/{runtime-types-CiVpC0HN.d.ts → runtime-types-B62Ucnxr.d.ts} +2 -2
  74. package/dist/testing/index.d.ts +22 -108
  75. package/dist/testing/index.js +27 -177
  76. package/dist/testing/index.js.map +1 -1
  77. package/dist/timing/index.d.ts +4 -4
  78. package/dist/timing/index.js +4 -4
  79. package/dist/{types-LxZxGYda.d.ts → types-DPm5YCDQ.d.ts} +1 -1
  80. package/dist/work-queue/index.d.ts +4 -4
  81. package/package.json +3 -3
  82. package/dist/chunk-5XU6ZJO2.js.map +0 -1
  83. package/dist/chunk-CH5KTOEZ.js.map +0 -1
  84. package/dist/chunk-EHUDXAKC.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing/assert-wrapper-faithful.ts","../../src/testing/assertions.ts","../../src/testing/bind-machine.ts","../../src/testing/drive.ts","../../src/testing/noop-runtime.ts","../../src/testing/state-factory.ts"],"names":["step","expect"],"mappings":"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","file":"index.js","sourcesContent":["// ---------------------------------------------------------------------------\n// `assertWrapperFaithful` — the shared conformance gate for the wrapper tier.\n//\n// A `withX` wrapper bolts a cross-cutting concern (deadline, resilience) onto a base machine as an AUTHORING property — the base's source\n// never hand-wires it. The DANGER is the runtime betrayal: hiding wrapper state\n// in a closure, or silently swallowing / retiming a base Cmd, so the wrapper's\n// behavior is invisible to the reducer + Msg log. This helper is the binary\n// betrayal-detector every wrapper inherits. Pass = TEA-faithful, ship it.\n// Fail = betrayal, does not merge.\n//\n// It asserts, by `replay` (which composes `init` + `update` only — the\n// conformance path):\n//\n// 1. **Base behavior byte-identical.** `replay(wrapped).state.base` deep-\n// equals `replay(base).state`. The wrapper never alters base behavior.\n// 2. **Slice AND wrapper Cmds reconstruct under any ambient clock/RNG.**\n// Replaying the SAME msgs under a DIFFERENT global wall-clock + RNG yields\n// a deep-equal wrapper slice AND a deep-equal `$x:*` wrapper-Cmd subset —\n// BOTH halves of the wrapper's output (state slice + emitted Cmds) are a\n// pure function of the Msg log, NOT of `Date.now()` / `Math.random()`.\n// (`update` cells never receive `ctx`, so an impure update read reaches for\n// the GLOBAL ambient; stubbing two distinct ambients and comparing catches\n// a folded wall-clock read that two back-to-back replays under one ambient\n// would hide.) The slice comparison alone has a blind spot: a clock/RNG\n// read folded into a wrapper Cmd PAYLOAD (not the slice) leaves the slice\n// identical, so we ALSO compare the `$x:*` Cmds across the two ambients.\n// Legit time enters only as Msg `at` payloads, identical across replays.\n// 3. **Every wrapper decision is in the log.** At least one wrapper-tagged\n// Cmd (`$x:*`) or Msg appears in the replayed Cmd stream whenever there is\n// a transition — the wrapper's decisions are visible, not off-ledger.\n// PLUS a PER-TRANSITION check: at each step the bare machine's step-i Cmds\n// are an ordered subsequence of the wrapped machine's step-i Cmds, so a\n// base Cmd deferred to a LATER transition (intra-window retiming a\n// flattened subsequence would miss) surfaces as a step-level failure.\n// 4. **Slice is JSON-round-trip equal.** `$x` survives\n// `JSON.parse(JSON.stringify(...))` — durable across DO eviction / reload,\n// never a closure.\n//\n// Strengthens invariant 9 (the testing surface is named and small) and is the\n// mechanical CI form of the wrapper-tier conformance gate.\n// ---------------------------------------------------------------------------\n\nimport { expect, vi } from \"vitest\";\nimport { type Cmd, type Machine, replay, type Sub } from \"../index\";\n\n/**\n * The composed Model shape every `withX` wrapper produces: the base machine's\n * state nested under `base`, and the wrapper's own NAMED, serializable slice\n * under a single `$`-prefixed key. This helper does not care WHICH `$`-key the\n * wrapper uses — it discovers it structurally (the one non-`base` field).\n */\nexport type WrapperModel<S> = {\n readonly base: S;\n} & { readonly [K: `$${string}`]: unknown };\n\n/** Options for the conformance replay. Mirrors `replay`'s second argument. */\nexport interface AssertWrapperFaithfulOpts<S, WM, BaseM, BaseCtx, WrapperCtx> {\n /** The Msg sequence to fold through BOTH machines. Use the base's Msgs. */\n readonly msgs: readonly BaseM[];\n /** Ctx for the WRAPPED machine (base ctx + wrapper ports). */\n readonly ctx: WrapperCtx;\n /** Ctx for the BARE base machine. Defaults to `ctx` when omitted. */\n readonly baseCtx?: BaseCtx;\n /** Optional pre-`init` snapshot of the COMPOSED Model (wrapped rehydrate). */\n readonly loaded?: WM | null;\n /**\n * Optional pre-`init` snapshot of the BARE base state. Supply when the\n * wrapped `loaded` is non-null so the base-equivalence check rehydrates the\n * base machine from the matching nested slice. Defaults to `loaded?.base`.\n */\n readonly baseLoaded?: S | null;\n /**\n * Opt-in relaxation for an INTERCEPTING wrapper (e.g. a retry wrapper).\n *\n * DEFAULT (`undefined` / `false`) — the OBSERVE-ONLY contract that a\n * deadline wrapper satisfies: every bare base Cmd must appear\n * UNCHANGED, in order, as a subsequence of the wrapped Cmd stream (properties\n * 1 and 3b). The wrapper may APPEND its own Cmds but may NOT alter, drop, or\n * retime a base Cmd. This is the strict default and it is NOT weakened.\n *\n * WHEN SET — for a wrapper whose whole job is to INTERCEPT a chosen base Cmd\n * (retag it into a wrapper-owned carrier so the concern — cache / breaker /\n * rate-limit / retry — can gate it), a target base Cmd LEGITIMATELY does not\n * appear raw at the top level of the wrapped stream. The contract is still\n * **retag-and-re-emit, never swallow** (rule 2): the Cmd must be RECORDED, not\n * vanished. So when a bare base Cmd is ABSENT from the top-level wrapped\n * subsequence, this option accepts it IFF, in that SAME transition window, it\n * is recorded by the wrapper as one of:\n *\n * - the inner payload of a `$x:*` \"run\" carrier Cmd (`carrierCmdType` +\n * `innerField`) — the retag path; OR\n * - any other `$x:*` divergence Cmd whose `recordsField` deep-equals the\n * base Cmd — the gate/defer path (e.g. a \"fast-failed\" or \"deferred\"\n * record Cmd that carries the base Cmd it diverged on).\n *\n * A target base Cmd that is NEITHER present raw NOR recorded by any `$x:*`\n * Cmd in its window is a SWALLOW betrayal and still fails the gate.\n *\n * Provide the field names the carrier uses so the gate can unwrap it\n * structurally without hard-coding any one wrapper's vocabulary.\n */\n readonly intercepting?: InterceptingOpt;\n}\n\n/**\n * Configures the intercepting relaxation (see\n * `AssertWrapperFaithfulOpts.intercepting`). All fields name the carrier /\n * divergence shape so the gate can structurally find a retagged base Cmd\n * inside a `$x:*` Cmd. The gate only EVER inspects `$x:*`-prefixed wrapped\n * Cmds for a recorded base Cmd — it never relaxes the check for a base Cmd\n * that appears under any non-wrapper Cmd type.\n */\nexport interface InterceptingOpt {\n /**\n * The `Cmd.type` of the wrapper's run-carrier (e.g. `\"$resilience:run\"`).\n * A bare base Cmd that is retagged appears as `carrier[innerField]`.\n */\n readonly carrierCmdType: string;\n /** The field on the carrier Cmd that holds the original base Cmd. */\n readonly innerField: string;\n /**\n * OPTIONAL field on ANY OTHER `$x:*` Cmd that records a base Cmd the wrapper\n * gated/deferred without running it (e.g. a fast-fail record). When present\n * on a `$x:*` Cmd and deep-equal to the missing base Cmd, it counts as a\n * recorded divergence. Omit when the wrapper only ever retags via the\n * carrier.\n */\n readonly recordsField?: string;\n}\n\n/**\n * Assert a `withX` wrapper is TEA-faithful. Replays the wrapped machine and the\n * bare base machine over the same Msgs and checks the four conformance\n * properties (see file header).\n *\n * @param base the bare base `Machine`.\n * @param makeWrapped a thunk that wraps `base` and returns the composed machine\n * (e.g. `() => withTimeout(wired, cfg).machine`, where `wired.machine` is\n * `base`). A thunk — not the wrapped value — so a wrapper that registers\n * process-scoped ports (`definePort`) is constructed inside the assertion's\n * control, and so the same betrayal-detector reads as\n * `assertWrapperFaithful(wired.machine, () => withX(wired, cfg).machine, ...)`\n * at every wrapper's test site.\n * @param opts the conformance replay options.\n */\nexport function assertWrapperFaithful<\n S,\n BaseM extends { type: string },\n BaseC extends Cmd,\n BaseU extends Sub,\n BaseCtx,\n WM extends WrapperModel<S>,\n WrapperM extends { type: string },\n WrapperC extends Cmd,\n WrapperU extends Sub,\n WrapperCtx,\n>(\n base: Machine<S, BaseM, BaseC, BaseU, BaseCtx>,\n makeWrapped: () => Machine<WM, WrapperM, WrapperC, WrapperU, WrapperCtx>,\n opts: AssertWrapperFaithfulOpts<S, WM, BaseM, BaseCtx, WrapperCtx>,\n): void {\n const baseCtx = (opts.baseCtx ?? (opts.ctx as unknown as BaseCtx)) as BaseCtx;\n const wrappedMsgs = opts.msgs as unknown as readonly WrapperM[];\n const baseLoaded =\n opts.baseLoaded ?? (opts.loaded != null ? (opts.loaded.base as S) : null);\n\n // Two DISTINCT global ambients (wall-clock + RNG). `replay` composes only\n // `init` + `update`, and `update` cells are `(state, msg)` — they never\n // receive `ctx`. So an impure update read reaches for the GLOBAL `Date.now()`\n // / `Math.random()`, not an injected clock. Running the two determinism\n // replays under DIFFERENT ambients turns a folded wall-clock read into an\n // OBSERVABLE slice divergence (property 2) — a single fixed ambient (two\n // back-to-back replays) would return the same ms both times and hide it.\n const A0 = { now: 1_000_000, rng: 0.123 };\n const A1 = { now: 9_876_543, rng: 0.987 };\n\n // The bare baseline, `wrappedA`, and every per-transition baseline (props 3/4)\n // run under the SAME fixed ambient A0 so their cmds/state are deterministic.\n const bare = underAmbient(A0, () =>\n replay(base, { msgs: opts.msgs, ctx: baseCtx, loaded: baseLoaded }),\n );\n const wrapped = underAmbient(A0, () => makeWrapped());\n const wrappedA = underAmbient(A0, () =>\n replay(wrapped, {\n msgs: wrappedMsgs,\n ctx: opts.ctx,\n loaded: opts.loaded ?? null,\n }),\n );\n // The second determinism replay runs under a DISTINCT ambient A1. A faithful\n // wrapper's slice (a pure function of the Msg log) is identical under A0 and\n // A1; an impure update that folds `Date.now()` / `Math.random()` diverges.\n const wrappedB = underAmbient(A1, () =>\n replay(makeWrapped(), {\n msgs: wrappedMsgs,\n ctx: opts.ctx,\n loaded: opts.loaded ?? null,\n }),\n );\n\n // --- Property 1: base behavior byte-identical -----------------------------\n // `state.base` after wrapping === state after running the base alone. The\n // wrapper observes; it does not alter base behavior.\n expect(wrappedA.state.base).toEqual(bare.state);\n // The base's OWN Cmds appear in the wrapped Cmd stream unchanged: every bare\n // Cmd is present, in order, among the wrapped Cmds (with wrapper Cmds\n // interleaved). We assert the bare Cmds are a subsequence of the wrapped ones.\n // For an INTERCEPTING wrapper (`opts.intercepting` set) a target base Cmd is\n // accepted when it appears RETAGGED inside a `$x:*` carrier / recorded by a\n // `$x:*` divergence Cmd instead of raw — retag, not swallow.\n expectCmdSubsequence(bare.cmds, wrappedA.cmds, undefined, opts.intercepting);\n\n // --- Property 2: slice reconstructs under any ambient clock/RNG -----------\n // Replaying the same Msgs under a DIFFERENT global ambient yields a deep-equal\n // wrapper SLICE — the slice is a pure function of the Msg log, never of\n // `Date.now()` / `Math.random()`. We compare the single `$`-slice (not the\n // whole composed state) because comparing whole state would FALSE-POSITIVE\n // when the BASE machine reads the clock — that is the base's behavior, not the\n // wrapper's fault, and the wrapper's faithfulness is precisely what its own\n // slice records.\n const sliceKey = wrapperSliceKey(wrappedA.state);\n expect(\n sliceOf(wrappedB.state, sliceKey),\n `wrapper slice changed under a different ambient clock/RNG → update is ` +\n `reading Date.now()/Math.random(); time must enter only as Msg \\`at\\` data`,\n ).toEqual(sliceOf(wrappedA.state, sliceKey));\n\n // ...AND the wrapper-tagged Cmds reconstruct under any ambient too. The slice\n // is only HALF the wrapper's output — the other half is its emitted Cmds. A\n // wall-clock / RNG read folded into a wrapper Cmd PAYLOAD (not the state\n // slice) escapes the slice comparison above: the slice stays identical while\n // the Cmd carries a leaked `Date.now()` / `Math.random()`. So mirror the\n // slice check on the `$x:*` subset of the replayed Cmd stream — a faithful\n // wrapper emits Cmds that are a pure function of the Msg log, identical under\n // A0 and A1. Only Msg `at` payloads (identical across both replays) may carry\n // time; if a wrapper Cmd diverges between ambients, the wrapper read the\n // ambient clock/RNG into a Cmd. (Base Cmds are EXCLUDED — a base machine that\n // reads the clock is the base's behavior, not the wrapper's, exactly as the\n // slice comparison excludes whole-state.)\n const wrapperFamily = `${sliceKey}:`;\n const wrapperCmdsA = wrappedA.cmds.filter((c) =>\n c.type.startsWith(wrapperFamily),\n );\n const wrapperCmdsB = wrappedB.cmds.filter((c) =>\n c.type.startsWith(wrapperFamily),\n );\n expect(\n wrapperCmdsB,\n `wrapper Cmds (\"${wrapperFamily}*\") changed under a different ambient ` +\n `clock/RNG → a wrapper Cmd PAYLOAD is reading Date.now()/Math.random(); ` +\n `wrapper Cmds must be a pure function of the Msg log, time entering only ` +\n `as Msg \\`at\\` data`,\n ).toEqual(wrapperCmdsA);\n\n // --- Property 3: every wrapper decision appears as an XMsg/XCmd in the log -\n // The composed Model's wrapper slice is the single non-`base` `$`-key. We\n // discover its key family (`$deadline` → `$deadline:*`) and require that,\n // whenever there is at least one transition, the replayed Cmd stream carries\n // at least one wrapper-tagged Cmd. (Wrappers whose decisions are Msgs route\n // them through the SAME log; for a Cmd-emitting wrapper the decision is its\n // `$x:*` Cmd.) A wrapper that performed its work off-ledger\n // would have an EMPTY wrapper-Cmd set here despite real transitions — the\n // exact betrayal this gate exists to catch.\n if (opts.msgs.length > 0) {\n expect(\n wrapperCmdsA.length,\n `expected at least one \"${wrapperFamily}*\" wrapper-decision Cmd in the replay log ` +\n `(found none across ${opts.msgs.length} transition(s)) — a wrapper whose ` +\n `decisions are invisible to the Msg/Cmd log has betrayed total visibility`,\n ).toBeGreaterThan(0);\n }\n\n // --- Property 3b: per-transition Cmd subsequence --------------------------\n // The flattened subsequence (property 1) misses INTRA-WINDOW retiming — a\n // base Cmd that a saga-style wrapper DEFERS to a later transition still drains\n // within the replay, so the flattened bare-Cmd set stays an ordered\n // subsequence and slips through. Pin it per step: for each transition i, the\n // bare machine's step-i Cmds must be an ordered subsequence of the wrapped\n // machine's step-i Cmds. A deferred base Cmd is then visible as a step-i\n // subsequence failure (it shows up at i+1 in the wrapped run but at i in the\n // bare run). Prefix replays run under the fixed ambient A0 for determinism.\n for (let i = 0; i < opts.msgs.length; i++) {\n const bareStep = stepCmds(\n () =>\n underAmbient(A0, () =>\n replay(base, {\n msgs: opts.msgs.slice(0, i),\n ctx: baseCtx,\n loaded: baseLoaded,\n }),\n ).cmds,\n () =>\n underAmbient(A0, () =>\n replay(base, {\n msgs: opts.msgs.slice(0, i + 1),\n ctx: baseCtx,\n loaded: baseLoaded,\n }),\n ).cmds,\n );\n const wrappedStep = stepCmds(\n () =>\n underAmbient(A0, () =>\n replay(wrapped, {\n msgs: wrappedMsgs.slice(0, i),\n ctx: opts.ctx,\n loaded: opts.loaded ?? null,\n }),\n ).cmds,\n () =>\n underAmbient(A0, () =>\n replay(wrapped, {\n msgs: wrappedMsgs.slice(0, i + 1),\n ctx: opts.ctx,\n loaded: opts.loaded ?? null,\n }),\n ).cmds,\n );\n expectCmdSubsequence(bareStep, wrappedStep, i, opts.intercepting);\n }\n\n // --- Property 4: slice is JSON-round-trip equal ---------------------------\n // The wrapper slice survives JSON.parse(JSON.stringify(...)) — durable across\n // DO eviction / reload, never a closure.\n const slice = sliceOf(wrappedA.state, sliceKey);\n expect(JSON.parse(JSON.stringify(slice))).toEqual(slice);\n}\n\n/**\n * Run `fn` with the GLOBAL `Date.now` and `Math.random` stubbed to fixed\n * values, restoring them in a `finally` so the stub never leaks across\n * assertions or tests. `update` cells receive no `ctx`, so an impure read\n * inside one resolves to these globals — the lever this gate pulls to make a\n * folded wall-clock / RNG read observable in the wrapper slice.\n */\nfunction underAmbient<T>(\n ambient: { readonly now: number; readonly rng: number },\n fn: () => T,\n): T {\n const nowSpy = vi.spyOn(Date, \"now\").mockReturnValue(ambient.now);\n const rngSpy = vi.spyOn(Math, \"random\").mockReturnValue(ambient.rng);\n try {\n return fn();\n } finally {\n nowSpy.mockRestore();\n rngSpy.mockRestore();\n }\n}\n\n/** The wrapper slice value (the single `$`-key) on a composed Model. */\nfunction sliceOf(model: object, sliceKey: string): unknown {\n return (model as Record<string, unknown>)[sliceKey];\n}\n\n/**\n * The Cmds a single transition `i` emitted: the suffix of the cmds after\n * `msgs.slice(0, i+1)` past the cmds after `msgs.slice(0, i)`. Cmds accumulate\n * in order across `replay`, so the longer run is a prefix-extension of the\n * shorter one and the suffix is exactly step `i`'s contribution.\n */\nfunction stepCmds(\n prefixCmds: () => readonly Cmd[],\n throughCmds: () => readonly Cmd[],\n): readonly Cmd[] {\n const before = prefixCmds();\n const after = throughCmds();\n return after.slice(before.length);\n}\n\n/**\n * The wrapper slice key on a composed Model — the single own enumerable field\n * whose name starts with `$`. Throws if zero or more than one is found, because\n * the wrapper-tier contract is exactly one named `$`-slice per wrapper.\n */\nfunction wrapperSliceKey(model: object): string {\n const dollarKeys = Object.keys(model).filter((k) => k.startsWith(\"$\"));\n if (dollarKeys.length !== 1) {\n throw new Error(\n `assertWrapperFaithful: expected exactly one \"$\"-prefixed wrapper slice ` +\n `on the composed Model, found ${dollarKeys.length} (${dollarKeys.join(\", \") || \"none\"}). ` +\n `Every withX wrapper owns exactly one named slice (e.g. \"$deadline\").`,\n );\n }\n // biome-ignore lint/style/noNonNullAssertion: length checked to be exactly 1 above\n return dollarKeys[0]!;\n}\n\n/**\n * Assert `needle` is an (order-preserving) subsequence of `haystack` by deep\n * equality — every base Cmd appears, in order, somewhere in the wrapped Cmd\n * stream (wrapper Cmds may be interleaved). This is the \"base Cmds pass through\n * UNCHANGED\" check that tolerates the wrapper's own appended Cmds without\n * requiring an exact-equal stream. When `step` is supplied the message names\n * the transition index, so a per-transition retiming failure points at the\n * exact step a base Cmd was deferred away from.\n *\n * `intercepting` (default `undefined` → the strict OBSERVE-ONLY check above)\n * relaxes the match for a base Cmd that an INTERCEPTING wrapper retags: when a\n * needle Cmd is NOT found raw in `haystack`, it is accepted IFF some `$x:*`\n * wrapper Cmd in `haystack` RECORDS it — as the carrier's `innerField` payload\n * (retag) or another `$x:*` Cmd's `recordsField` (recorded gate/defer). The\n * relaxation ONLY ever looks inside `$x:*` Cmds, so a base Cmd that vanished\n * with no `$x:*` record still fails. The order check is preserved: the carrier\n * / divergence Cmd must come at or after the cursor, exactly like a raw match.\n */\nfunction expectCmdSubsequence(\n needle: readonly Cmd[],\n haystack: readonly Cmd[],\n step?: number,\n intercepting?: InterceptingOpt,\n): void {\n let i = 0;\n for (const cmd of haystack) {\n if (i >= needle.length) break;\n if (__deepEqual(needle[i], cmd)) {\n i++;\n continue;\n }\n // Intercepting relaxation: the base Cmd at the cursor may legitimately be\n // absent raw because the wrapper RETAGGED it. Accept this haystack Cmd as\n // the matching slot IFF it is a `$x:*` wrapper Cmd that records `needle[i]`.\n if (\n intercepting !== undefined &&\n recordsBaseCmd(cmd, needle[i] as Cmd, intercepting)\n ) {\n i++;\n }\n }\n const where =\n step === undefined\n ? \"the wrapped Cmd stream\"\n : `the wrapped Cmd stream AT TRANSITION ${step} (a base Cmd deferred to a ` +\n `later transition is an intra-window retiming betrayal)`;\n const swallowHint =\n intercepting === undefined\n ? \"a missing/rewritten/retimed base Cmd is a swallow betrayal\"\n : `an intercepting wrapper must RETAG each target base Cmd into a ` +\n `\"${intercepting.carrierCmdType}\" carrier (inner field ` +\n `\"${intercepting.innerField}\") or record it on a \"$*:\" divergence Cmd ` +\n `— a base Cmd neither present raw NOR recorded by any \"$*:\" Cmd is a ` +\n `swallow betrayal`;\n expect(\n i,\n `expected every base Cmd to pass through unchanged (matched ${i} of ` +\n `${needle.length} base Cmds as an ordered subsequence of ${where}) — ` +\n `${swallowHint}`,\n ).toBe(needle.length);\n}\n\n/**\n * Does the wrapped Cmd `cmd` RECORD the base Cmd `baseCmd` under the\n * intercepting contract? True iff `cmd` is a `$`-prefixed wrapper Cmd AND\n * either:\n * - `cmd.type === carrierCmdType` and `cmd[innerField]` deep-equals\n * `baseCmd` (the retag carrier), OR\n * - `recordsField` is configured and `cmd[recordsField]` deep-equals\n * `baseCmd` (a recorded gate/defer divergence on any `$x:*` Cmd).\n *\n * The `$`-prefix guard is load-bearing: the relaxation must NEVER accept a base\n * Cmd hidden inside a NON-wrapper Cmd — that would let a wrapper launder a\n * swallowed base Cmd through an arbitrary re-emit. Only the wrapper's own\n * `$x:*` family may carry a retagged base Cmd.\n */\nfunction recordsBaseCmd(cmd: Cmd, baseCmd: Cmd, opt: InterceptingOpt): boolean {\n if (!cmd.type.startsWith(\"$\")) return false;\n const rec = cmd as unknown as Record<string, unknown>;\n if (\n cmd.type === opt.carrierCmdType &&\n __deepEqual(rec[opt.innerField], baseCmd)\n )\n return true;\n if (\n opt.recordsField !== undefined &&\n __deepEqual(rec[opt.recordsField], baseCmd)\n )\n return true;\n return false;\n}\n\n/**\n * Structural deep equality for plain Cmd data.\n *\n * NOT `JSON.stringify` equality: `JSON.stringify` DROPS undefined-valued fields,\n * so `{ type: \"x\" }` would falsely equal `{ type: \"x\", foo: undefined }` and the\n * gate could accept a wrapper that smuggles an extra (undefined) field into a\n * \"byte-identical\" base Cmd or a retag carrier. This walk distinguishes an\n * ABSENT key from an `undefined`-VALUED key by comparing the full own-enumerable\n * key SET (not just the defined values) in both directions, then recursing.\n *\n * Cmds are plain data — primitives, `null`, arrays, and plain objects — so this\n * intentionally does not model `Date`, `Map`, `Set`, cycles, or class instances.\n *\n * Exported under the `__` test-only convention so the conformance suite can\n * pin the absent-vs-undefined distinction directly. Not part of the public API.\n *\n * @internal test-only\n */\nexport function __deepEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n\n // Past the identity check, equality requires same kind on both sides. Anything\n // non-object (primitive, function, symbol) that wasn't `Object.is`-equal above\n // is unequal. `null` is `typeof \"object\"` but only equals `null` (caught by\n // `Object.is`), so screen it out explicitly.\n if (\n typeof a !== \"object\" ||\n typeof b !== \"object\" ||\n a === null ||\n b === null\n ) {\n return false;\n }\n\n const aIsArray = Array.isArray(a);\n if (aIsArray !== Array.isArray(b)) return false;\n\n if (aIsArray) {\n const bArr = b as unknown[];\n if (a.length !== bArr.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!__deepEqual(a[i], bArr[i])) return false;\n }\n return true;\n }\n\n // Plain objects: compare the own-enumerable key SET first so a key present on\n // one side with an `undefined` value (and absent on the other) is caught —\n // the betrayal `JSON.stringify` hid. Then recurse on every value.\n const aRec = a as Record<string, unknown>;\n const bRec = b as Record<string, unknown>;\n const aKeys = Object.keys(aRec);\n const bKeys = Object.keys(bRec);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n if (!Object.hasOwn(bRec, key)) return false;\n if (!__deepEqual(aRec[key], bRec[key])) return false;\n }\n return true;\n}\n","// ---------------------------------------------------------------------------\n// Test-side assertions over `replay`. Each helper composes the replay call\n// with a single `expect(...)` so consumers stop hand-rolling the 5-line\n// \"destructure { state, cmds } → expect.toBe / toEqual / toContainEqual\"\n// boilerplate at every test site.\n//\n// Each helper is a thin wrapper that:\n// 1. Calls `replay(machine, opts)` once.\n// 2. Pipes the result into a single vitest `expect(...)` matcher.\n//\n// All helpers return `void` — assertions throw on failure (vitest contract).\n// No mutation of the machine or opts. Pure test ergonomics.\n//\n// Type-inference note: each helper anchors its generic parameters to the\n// `machine` argument. The `expected` (and `opts.msgs`) arguments are wrapped\n// in `NoInfer<...>` so they do NOT participate in inference — otherwise a\n// `[]`-shaped `expected` would force `C = never` and reject a machine whose\n// real Cmd union is non-empty. Anchoring inference to the machine is the\n// same discipline `replay` uses (it has nothing but the machine and opts).\n//\n// Strengthens invariant 9 (the testing surface is named, small, and forks\n// nothing of the substrate's purity guarantees).\n// ---------------------------------------------------------------------------\n\nimport { expect } from \"vitest\";\nimport { type Cmd, type Machine, replay, type Sub } from \"../index\";\n\n// `NoInfer<T>` is the built-in TypeScript 5.4+ intrinsic that blocks\n// inference from a generic parameter at a position. We use it on `expected`\n// and `opts` so inference flows purely FROM the machine — `[]`-shaped\n// `expected` no longer drags `C` down to `never` and rejects the machine.\n\n/**\n * Shared options shape for every test assertion below. Mirrors the\n * second argument of `replay(machine, opts)` — `msgs` is the sequence to\n * fold over (may be empty for init-only assertions), `ctx` is the\n * substrate-required ctx value, `loaded` is the optional pre-`init`\n * snapshot (omit / pass `null` for fresh boot).\n */\nexport interface ReplayOpts<S, M, Ctx> {\n readonly msgs: readonly M[];\n readonly ctx: Ctx;\n readonly loaded?: S | null;\n}\n\n/**\n * Assert the final state after replaying `opts.msgs` equals `expected`.\n * Replaces the boilerplate:\n *\n * const { state } = replay(machine, opts);\n * expect(state).toEqual(expected);\n *\n * Uses `toEqual` (deep structural). For type-narrowing assertions that\n * branch on `state.type` afterwards, fall back to plain `replay` —\n * `expectFinalState` is the \"exact match\" tool.\n */\nexport function expectFinalState<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: NoInfer<ReplayOpts<S, M, Ctx>>,\n expected: NoInfer<S>,\n): void {\n const { state } = replay(machine, opts);\n expect(state).toEqual(expected);\n}\n\n/**\n * Assert that `cmd` appears at least once in the cmds array produced by\n * replaying `opts.msgs`. Uses `toContainEqual` (deep structural). Order\n * and surrounding cmds are not asserted — use `expectCmdSequence` for\n * exact ordered match.\n *\n * Replaces:\n *\n * const { cmds } = replay(machine, opts);\n * expect(cmds).toContainEqual(cmd);\n */\nexport function expectCmdEmitted<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: NoInfer<ReplayOpts<S, M, Ctx>>,\n cmd: NoInfer<C>,\n): void {\n const { cmds } = replay(machine, opts);\n expect(cmds).toContainEqual(cmd);\n}\n\n/**\n * Assert the exact ordered sequence of cmds emitted by replaying\n * `opts.msgs`. Uses `toEqual` against the full cmds array — extra cmds\n * fail the assertion, missing cmds fail the assertion, wrong order\n * fails the assertion.\n *\n * Replaces:\n *\n * const { cmds } = replay(machine, opts);\n * expect(cmds).toEqual(expected);\n */\nexport function expectCmdSequence<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: NoInfer<ReplayOpts<S, M, Ctx>>,\n expected: NoInfer<readonly C[]>,\n): void {\n const { cmds } = replay(machine, opts);\n expect(cmds).toEqual(expected);\n}\n\n/**\n * Assert the exact set of subs desired at the final state after\n * replaying `opts.msgs`. Uses `toEqual` against the full subs array.\n *\n * Replaces:\n *\n * const { subs } = replay(machine, opts);\n * expect(subs).toEqual(expected);\n */\nexport function expectActiveSubs<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: NoInfer<ReplayOpts<S, M, Ctx>>,\n expected: NoInfer<readonly U[]>,\n): void {\n const { subs } = replay(machine, opts);\n expect(subs).toEqual(expected);\n}\n\n/**\n * Single-msg step helper — feeds `loaded → msg → next state + cmds emitted by\n * that msg`. Returns a readonly tuple in the shape consumers used before the\n * substrate (`[state, cmds]`), so existing tests that destructure with\n * `[next, cmds]` can swap `reduce(state, msg)` for `step(machine, state, msg,\n * ctx)` without changing the assertion code.\n *\n * Internally a `replay` call with `loaded: prevState` and a single-element\n * `msgs` array. The cmds returned are exactly the cmds emitted by THIS msg\n * (init emits no cmds when `loaded` is provided, so the slice is the entire\n * array).\n *\n * Use when consumers want to walk a machine one msg at a time and assert per\n * step. For one-shot \"final state after N msgs\" assertions, prefer\n * `expectFinalState`.\n */\nexport function step<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n loaded: NoInfer<S>,\n msg: NoInfer<M>,\n ctx: NoInfer<Ctx>,\n): readonly [S, readonly C[]] {\n const { state, cmds } = replay(machine, { msgs: [msg], ctx, loaded });\n return [state, cmds] as const;\n}\n","// ---------------------------------------------------------------------------\n// `bindMachine(machine, ctx)` — bound test harness over the free-function\n// assertions in `./assertions.ts`. Closes over the `machine` and `ctx` once\n// so per-test callsites stop repeating both at every invocation.\n//\n// Why this exists: every consumer test file ends up hand-rolling a local\n// two-arg `step(state, msg)` wrapper around `step(machine, state, msg, ctx)`,\n// and the four `expect*` assertions repeat `machine` + `ctx` at every\n// callsite. The bound form pulls both into one place and gives every\n// per-test call its tightest possible shape (2-arg max).\n//\n// `bindMachine` is built ON TOP of the free helpers — it does not replace\n// them. Both forms coexist; pick the one that fits the call site:\n//\n// - Free form → one-off call inside a helper that isn't bound to one\n// machine (e.g. cross-machine integration test).\n// - Bound form → a `describe` block runs many assertions against one\n// machine + ctx. The bound form is the common case.\n//\n// Strengthens invariant 9 (the testing surface is named, small, and forks\n// nothing of the substrate's purity guarantees).\n// ---------------------------------------------------------------------------\n\nimport { expect } from \"vitest\";\nimport {\n type BuiltinSub,\n type Cmd,\n type Machine,\n replay,\n type Sub,\n} from \"../index\";\n\n/**\n * Replay options shape used by every bound method. `ctx` is captured by\n * `bindMachine`; the per-call opts only need `msgs` and an optional\n * `loaded` snapshot. (Compare `ReplayOpts` in `./assertions.ts`, which\n * still takes `ctx` because the free helpers cannot capture it.)\n */\ninterface BoundOpts<S, M> {\n readonly msgs: readonly M[];\n readonly loaded?: S | null;\n}\n\n/**\n * The bound testing surface. Every method is 2-arg max — `ctx` and\n * `machine` are captured by `bindMachine`.\n *\n * `NoInfer<...>` on `expected` / `cmd` arguments mirrors the discipline\n * the free helpers use (see `./assertions.ts`): inference flows purely\n * FROM the machine, so a `[]`-shaped expected does not drag `C` down to\n * `never` and reject the machine.\n */\nexport interface BoundMachine<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n // `Ctx` is a phantom-style parameter — present for consumers that want to\n // spell `BoundMachine<S, M, C, U, Ctx>` explicitly, mirroring `Machine`.\n // biome-ignore lint/correctness/noUnusedVariables: phantom type param (see above)\n Ctx,\n> {\n /**\n * Single-msg step — feeds `loaded → msg → [next state, cmds emitted by\n * that msg]`. Mirrors the free `step(machine, loaded, msg, ctx)`.\n */\n step(loaded: S, msg: M): readonly [S, readonly C[]];\n /**\n * Assert the final state after replaying `opts.msgs` deep-equals\n * `expected`. Mirrors the free `expectFinalState(machine, opts, expected)`.\n */\n expectFinalState(opts: BoundOpts<S, M>, expected: NoInfer<S>): void;\n /**\n * Assert `cmd` appears at least once in the emitted cmd list. Order is\n * not asserted. Mirrors the free `expectCmdEmitted(machine, opts, cmd)`.\n */\n expectCmdEmitted(opts: BoundOpts<S, M>, cmd: NoInfer<C>): void;\n /**\n * Assert the exact ordered sequence of cmds emitted by replaying\n * `opts.msgs`. Mirrors the free `expectCmdSequence(machine, opts, expected)`.\n */\n expectCmdSequence(\n opts: BoundOpts<S, M>,\n expected: readonly NoInfer<C>[],\n ): void;\n /**\n * Assert the exact set of subs desired at the final state. Mirrors the\n * free `expectActiveSubs(machine, opts, expected)`.\n */\n expectActiveSubs(\n opts: BoundOpts<S, M>,\n expected: readonly NoInfer<U | BuiltinSub<M>>[],\n ): void;\n /**\n * Bound `replay` — returns `{ state, cmds, subs }` for the given opts.\n * Use for the \"narrow-then-assert\" pattern where the test inspects a\n * specific field after `state.type === \"...\"` discrimination.\n */\n replay(opts: BoundOpts<S, M>): {\n state: S;\n cmds: readonly C[];\n subs: readonly (U | BuiltinSub<M>)[];\n };\n}\n\n/**\n * Bind a machine + ctx into a small bag of 2-arg helpers. Each method\n * delegates to `replay(machine, { ...opts, ctx })` and pipes the result\n * through a single `expect(...)` matcher (or returns the raw tuple, for\n * `step` and `replay`).\n *\n * Use one bind per `describe` block (or per file) when many assertions\n * share the same machine + ctx. For one-off cross-machine calls, stick\n * with the free helpers from `./assertions.ts`.\n */\nexport function bindMachine<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(machine: Machine<S, M, C, U, Ctx>, ctx: Ctx): BoundMachine<S, M, C, U, Ctx> {\n return {\n step(loaded, msg) {\n const { state, cmds } = replay(machine, { msgs: [msg], ctx, loaded });\n return [state, cmds] as const;\n },\n expectFinalState(opts, expected) {\n const { state } = replay(machine, { ...opts, ctx });\n expect(state).toEqual(expected);\n },\n expectCmdEmitted(opts, cmd) {\n const { cmds } = replay(machine, { ...opts, ctx });\n expect(cmds).toContainEqual(cmd);\n },\n expectCmdSequence(opts, expected) {\n const { cmds } = replay(machine, { ...opts, ctx });\n expect(cmds).toEqual(expected);\n },\n expectActiveSubs(opts, expected) {\n const { subs } = replay(machine, { ...opts, ctx });\n expect(subs).toEqual(expected);\n },\n replay(opts) {\n return replay(machine, { ...opts, ctx });\n },\n };\n}\n","// ---------------------------------------------------------------------------\n// `drive(machine, initial, msg, handlers, opts)` — the runtime's own loop,\n// said once, for a test.\n//\n// `bindMachine` hands back the Cmds a fold emitted and stops there; performing\n// them is the caller's. So every consumer test that exercises a Cmd-emitting\n// machine hand-writes the same fixture: step the Msg, await the real interpret\n// handler over each emitted Cmd, feed the settle Msg back, repeat until quiet,\n// with a hand-picked guard bound in the loop header. The guard count, the\n// accumulate-then-swap order and the \"did I drain this round before starting\n// the next\" question are re-decided at every copy site, and one of those copies\n// ships in `docs/how-to/ask-jev-a-typed-question.md`.\n//\n// `drive` is that fixture as one call. It composes `bindMachine`'s synchronous\n// 2-arg `step` and adds NO second reducer path — there is no `Runtime` here, no\n// observation, no clock, and nothing borrowed from the internal flow helpers.\n// The only impure thing it does is `await` the handler the caller handed it.\n//\n// **It returns the history, not just the endpoint.** `{ state, trace }`, where\n// `trace` is every Msg folded and every Cmd dispatched in order, is what makes\n// this worth a stable export rather than a snippet: a test asserts on the\n// SEQUENCE as well as the settled state — which handler ran, in which round,\n// and what came back — and the same trace's Msgs replayed through `replay` from\n// the same initial state reproduce the returned state exactly.\n//\n// Prior art converged on the same answer: Temporal's `TestWorkflowEnvironment`\n// hands a test the event history rather than the final result, and Effect's\n// `TestClock` runs make the effect log inspectable rather than opaque.\n//\n// **Exhausting `maxRounds` THROWS.** A driver that quietly returns a\n// half-driven state lets a test assert green on a machine that never settled —\n// the exact failure a hand-rolled `guard < 10` already risks.\n//\n// Strengthens invariant 9 (the testing surface is named and small).\n// ---------------------------------------------------------------------------\n\nimport {\n type Cmd,\n type CtxArg,\n type Interpret,\n type Machine,\n Outcome,\n type PortEmitter,\n type Sub,\n} from \"../index\";\nimport { cmdEdge, cmdEdgeOver } from \"../pure/core\";\nimport { bindMachine } from \"./bind-machine\";\n\n/**\n * The default round bound when `opts.maxRounds` is omitted: **100**.\n *\n * A round is one drain of the pending Msgs plus one drain of the Cmds those\n * folds emitted, so a settling machine costs a handful; 100 is loose enough\n * that a legitimate retry ladder finishes inside it and tight enough that a\n * genuine cycle stops rather than hangs.\n */\nexport const DEFAULT_MAX_ROUNDS = 100;\n\n/**\n * One entry of a driven run's history, in dispatch order.\n *\n * - `msg` — a Msg folded through the machine's reducer. The seed Msg is the\n * first entry of every trace; every later one is a settle Msg a handler\n * returned (or fired through its injected `dispatch`).\n * - `cmd` — a Cmd handed to an interpret handler. It is recorded BEFORE the\n * handler is awaited, so a trace recovered from a rejected handler names the\n * Cmd that rejected as its last entry.\n */\nexport type DriveTraceEntry<M, C> =\n | { readonly kind: \"msg\"; readonly msg: M }\n | { readonly kind: \"cmd\"; readonly cmd: C };\n\n/** What a settled {@link drive} hands back. */\nexport interface DriveResult<S, M, C> {\n /** The state after the machine went quiet — no Msg pending, no Cmd unperformed. */\n readonly state: S;\n /**\n * Every Msg folded and every Cmd dispatched, in order. Filtering it to its\n * `msg` entries and replaying those through `replay` from the same initial\n * state reproduces {@link DriveResult.state}.\n */\n readonly trace: readonly DriveTraceEntry<M, C>[];\n}\n\n/**\n * {@link drive}'s `ctx` field. `CtxArg` widened by exactly one case: a machine\n * declaring `ctx: undefined` in its `types` block — the pure shape every\n * how-to on this package writes — may omit it too. `CtxArg` alone keeps `ctx`\n * REQUIRED there, because `{}` is not assignable to `undefined`, which would\n * make the common pure call site spell `{ ctx: undefined }` to say nothing.\n * A machine whose `Ctx` carries a field a handler reads still requires it.\n */\nexport type DriveCtxArg<Ctx> = [undefined] extends [Ctx]\n ? { readonly ctx?: Ctx }\n : CtxArg<Ctx>;\n\n/**\n * {@link drive}'s options. `ctx` is conditionally optional (see\n * {@link DriveCtxArg}).\n */\nexport type DriveOptions<Ctx> = DriveCtxArg<Ctx> & {\n /**\n * The round bound. Exceeding it throws {@link DriveRoundsExceededError};\n * `drive` never returns a half-driven state. Defaults to\n * {@link DEFAULT_MAX_ROUNDS} (100).\n */\n readonly maxRounds?: number;\n /**\n * The clock that stamps `at` on a `Cmd.define`d Cmd's minted `_ok` / `_err`\n * Msg — the one `run` takes. Defaults to `Date.now`; pin it for a test that\n * asserts on `at`.\n */\n readonly clock?: () => number;\n};\n\n/**\n * Raised when a driven machine is still emitting work after `maxRounds` rounds.\n * Follows the tea error dialect (`override readonly name`) and carries a `_tag`\n * discriminant, so a test branches on it by `instanceof` or on the tag.\n *\n * It carries the round count it stopped at and the PARTIAL trace up to that\n * point, which is what a test needs to see the cycle — the bound alone says\n * only that one exists.\n */\nexport class DriveRoundsExceededError<M, C> extends Error {\n override readonly name = \"DriveRoundsExceededError\";\n readonly _tag = \"DriveRoundsExceededError\" as const;\n constructor(\n /** The bound that was exceeded. */\n public readonly maxRounds: number,\n /** The round the driver stopped at — always `maxRounds + 1`. */\n public readonly rounds: number,\n /** Every Msg folded and Cmd dispatched before the driver gave up. */\n public readonly trace: readonly DriveTraceEntry<M, C>[],\n ) {\n super(\n `@demlik/tea: drive did not settle within ${maxRounds} round(s) — the ` +\n `machine was still emitting work at round ${rounds}. The partial ` +\n `trace (${trace.length} entr${trace.length === 1 ? \"y\" : \"ies\"}) is ` +\n `on the error's \\`trace\\` field.`,\n );\n }\n}\n\n/**\n * Raised when a Cmd reaches {@link drive} with no handler for its `type` in the\n * `handlers` record. `Interpret`'s mapped type makes that a compile error at a\n * well-typed call site; this is the runtime arm for a handler record assembled\n * dynamically or cast.\n */\nexport class DriveNoHandlerError<M, C> extends Error {\n override readonly name = \"DriveNoHandlerError\";\n readonly _tag = \"DriveNoHandlerError\" as const;\n constructor(\n /** The Cmd `type` nothing in the handler record answered. */\n public readonly cmdType: string,\n /** Every Msg folded and Cmd dispatched before the miss. */\n public readonly trace: readonly DriveTraceEntry<M, C>[],\n ) {\n super(\n `@demlik/tea: drive found no interpret handler for Cmd type ` +\n `\"${cmdType}\" — the handlers record must answer every Cmd the ` +\n `machine emits.`,\n );\n }\n}\n\n/** The key `drive` hangs a partial trace off a rejected handler's error under. */\nconst TRACE_KEY = \"driveTrace\";\n\n/**\n * Read back the partial trace `drive` attached to an error a handler rejected\n * with. Returns `undefined` for anything `drive` did not annotate.\n *\n * A rejecting handler's error propagates out of `drive` UNSWALLOWED — same\n * value, same `instanceof`, same `_tag` — so a test that already branches on\n * the handler's own error type keeps doing so. The trace rides along on a\n * non-enumerable property so it is recoverable without changing how the error\n * prints or serializes.\n */\nexport function driveTraceOf<M, C>(\n err: unknown,\n): readonly DriveTraceEntry<M, C>[] | undefined {\n if (typeof err !== \"object\" || err === null) return undefined;\n const carried = (err as Record<string, unknown>)[TRACE_KEY];\n return Array.isArray(carried)\n ? (carried as readonly DriveTraceEntry<M, C>[])\n : undefined;\n}\n\n/**\n * Drive `machine` from `initial` through `msg` against the REAL interpret\n * `handlers`, feeding every settle Msg back until the machine goes quiet, and\n * hand back the settled state together with the whole history.\n *\n * `handlers` is the same table a host hands `run` as its `interpret` (e.g.\n * a jev knob's `resilient_run` handler), so the test drives the real interpreter and\n * mocks only the port beneath it.\n *\n * One round is: fold every pending Msg, then await a handler for every Cmd\n * those folds emitted. A Msg a handler returns — or fires through its injected\n * `dispatch` — is pending for the next round. A machine that emits no Cmd for\n * its seed Msg therefore settles in ONE round and never awaits a handler at\n * all.\n *\n * The `ctx` handed to each handler is `opts.ctx` augmented with a NO-OP `emit`,\n * so a handler that emits to a `Port` runs without a live runtime; nothing here\n * observes ports. Supply your own `emit` on `ctx` to capture them.\n *\n * @throws {DriveRoundsExceededError} when the machine is still emitting work\n * after `opts.maxRounds` rounds. It never returns a half-driven state.\n * @throws whatever a handler rejects with, unchanged — annotated with the\n * partial trace, readable via {@link driveTraceOf}.\n */\nexport async function drive<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n initial: S,\n msg: M,\n handlers: Interpret<M, C, Ctx>,\n ...[opts]: Record<never, never> extends DriveOptions<Ctx>\n ? [opts?: DriveOptions<Ctx>]\n : [opts: DriveOptions<Ctx>]\n): Promise<DriveResult<S, M, C>> {\n const options = (opts ?? {}) as {\n ctx?: Ctx;\n maxRounds?: number;\n clock?: () => number;\n };\n const maxRounds = options.maxRounds ?? DEFAULT_MAX_ROUNDS;\n const bound = bindMachine(machine, options.ctx as Ctx);\n\n // The `Cmd.define` edge `run` applies (ADR 0021): a defined Cmd's handler\n // returns an outcome, and the edge mints it into the def's `_ok` / `_err`\n // Msg. A hand-written Cmd's return passes through untouched.\n const settle = cmdEdgeOver(machine.cmds ?? [], options.clock ?? Date.now);\n\n // A handler's ctx is `Ctx & PortEmitter`, plus the `ok` / `err` builders and\n // the edge a defined Cmd's handler is handed. The Ctx half is the caller's;\n // the rest is the kernel's, and a driven test has no kernel — so a no-op\n // `emit` stands in, placed FIRST so a caller's own `emit` wins.\n const noopEmit: PortEmitter = { emit: () => {} };\n const handlerCtx = {\n ...noopEmit,\n ok: Outcome.ok,\n err: Outcome.err,\n [cmdEdge]: settle,\n ...(options.ctx as unknown as Record<string, unknown> | undefined),\n };\n\n // Indexed dynamically: `Interpret`'s mapped type already proved every Cmd\n // variant has a cell, but `cmd.type` is a union at the value level and TS\n // cannot correlate the two sides of that index.\n const cells = handlers as unknown as Record<\n string,\n | ((\n cmd: C,\n ctx: unknown,\n dispatch?: (msg: M) => void,\n // biome-ignore lint/suspicious/noConfusingVoidType: mirrors `Interpret`'s own cell return — a follow-up Msg, or nothing\n ) => Promise<M | void>)\n | undefined\n >;\n\n const trace: DriveTraceEntry<M, C>[] = [];\n let state = initial;\n let pending: M[] = [msg];\n let round = 0;\n\n while (pending.length > 0) {\n round += 1;\n if (round > maxRounds) {\n throw new DriveRoundsExceededError<M, C>(maxRounds, round, trace);\n }\n\n // Drain the round's Msgs first, THEN its Cmds. Swapping the buffer before\n // the fold is what keeps a settle Msg out of the round that produced it —\n // the ordering question every hand-rolled copy of this loop re-answers.\n const folding = pending;\n pending = [];\n const emitted: C[] = [];\n for (const next of folding) {\n trace.push({ kind: \"msg\", msg: next });\n const [advanced, cmds] = bound.step(state, next);\n state = advanced;\n emitted.push(...cmds);\n }\n\n for (const cmd of emitted) {\n trace.push({ kind: \"cmd\", cmd });\n const cell = cells[cmd.type];\n if (cell === undefined) {\n throw new DriveNoHandlerError<M, C>(cmd.type, trace);\n }\n // biome-ignore lint/suspicious/noConfusingVoidType: mirrors `Interpret`'s own cell return — a follow-up Msg, or nothing\n let settled: M | void;\n try {\n settled = settle(\n cmd,\n await cell(cmd, handlerCtx, (fired) => {\n pending.push(fired);\n }),\n ) as M | undefined;\n } catch (err) {\n // Unswallowed: the handler's own error is what leaves `drive`, so a\n // test's `instanceof` / `_tag` branch still lands. The trace rides on\n // a non-enumerable property (see `driveTraceOf`).\n if (typeof err === \"object\" && err !== null) {\n Object.defineProperty(err, TRACE_KEY, {\n value: [...trace],\n enumerable: false,\n configurable: true,\n writable: true,\n });\n }\n throw err;\n }\n if (settled !== undefined) pending.push(settled);\n }\n }\n\n return { state, trace };\n}\n","// ---------------------------------------------------------------------------\n// noopRuntime — a Runtime<S, M>-shaped value whose every method is inert.\n//\n// The motivating callsite: a machine's `ctx` requires a sibling Runtime<S,\n// M> reference (e.g. the queue runtime in extension/lib/queue-machine\n// reaches into an `auditRuntime: Runtime<...>` slot on its ctx so its\n// interpret handlers can dispatch into the audit machine). When the test\n// replays such a machine, `replay` never actually invokes `interpret`, so\n// the runtime reference is never dereferenced — but the type system\n// still requires a value of `Runtime<S, M>` shape at the ctx slot. Tests\n// today work around this with a hand-rolled 12-line NOOP_AUDIT_RUNTIME\n// const cast through `as unknown as Runtime<...>`.\n//\n// `noopRuntime<S, M>()` is that hand-roll, named once. It returns a real\n// Runtime<S, M> value whose every method is a structurally-correct no-op:\n//\n// - `dispatch`: resolves immediately, never advances state.\n// - `getState`: returns the optional `initialState` argument (or a\n// deliberate `undefined` cast as S — the function lies about its\n// return type when no initial state is provided; this is the trade-off\n// we accept for an ergonomic test substrate).\n// - `subscribe` / `observe` / `subscribePort`: register the listener,\n// return a no-op unsubscribe. The listener is never fired because the\n// state never changes.\n// - `emitPort`: no-op.\n// - `ready`: a Promise that resolves to the no-op runtime itself (the #45\n// `ready: Promise<Runtime<S, M>>` shape — already booted, nothing to wait\n// for).\n// - `stop`: resolved Promise<void>.\n//\n// Strengthens invariant 9 (the testing surface is named and small).\n// ---------------------------------------------------------------------------\n\nimport type { Port, Runtime } from \"../index\";\n\n/**\n * Construct an inert `Runtime<S, M>` value. Use at ctx sites that require\n * a Runtime reference but where the test never invokes `interpret` (so\n * the runtime is never dereferenced in practice).\n *\n * @param opts.initialState Optional value returned by `getState()`. Omit\n * when the test never calls `getState()` on the no-op runtime — most\n * tests only need the type-level slot filled.\n */\nexport function noopRuntime<S, M extends { type: string }>(opts?: {\n initialState?: S;\n}): Runtime<S, M> {\n const initialState = opts?.initialState;\n // Single source of truth for the \"do-nothing\" unsubscribe so multiple\n // subscribe call sites share the same function instance — purely cosmetic\n // (the function would be a no-op either way).\n const noopUnsubscribe = (): void => {};\n const runtime: Runtime<S, M> = {\n async dispatch(_msg: M): Promise<void> {\n // Resolves immediately. State never advances; listeners never fire.\n // Quiescent by default (#50), but the no-op has no follow-up chain to\n // drain, so single-step and run-to-quiescence collapse to the same no-op.\n },\n async dispatchOnce(_msg: M): Promise<void> {\n // Single-step sibling of `dispatch` (#50). Same no-op for the no-op\n // runtime — there is no tail to leave in flight.\n },\n async idle(): Promise<void> {\n // No tail to drain — the no-op runtime never enqueues a follow-up.\n },\n result(): S | undefined {\n // The no-op runtime never advances, so it is never terminal (#46).\n return undefined;\n },\n done(): Promise<S> {\n // Never terminal → the awaitable never settles, matching `run()` with no\n // `terminal` predicate. Returned as a never-resolving promise.\n return new Promise<S>(() => {});\n },\n getState(): S {\n // Lie if no initial state was provided. The caller's contract is\n // \"you accept the lie because you know replay never invokes\n // interpret and therefore never reads this.\" When the caller DOES\n // pass an initialState the lie collapses to truth.\n return initialState as S;\n },\n subscribe(_listener: () => void): () => void {\n return noopUnsubscribe;\n },\n observe(_observer: (msg: M, state: S) => void): () => void {\n return noopUnsubscribe;\n },\n onBoot(_handler: (state: S) => void): () => void {\n // Boot never fires on the inert runtime — it is \"already booted\" with no\n // initial fanout, so the handler is registered and never called (#47).\n return noopUnsubscribe;\n },\n on<K extends never>(\n _type: K,\n _handler: (event: never) => void,\n ): () => void {\n // The no-op runtime's event surface is `never` (no `events` projector),\n // so `on` is structurally present but uncallable — there is no `K` to\n // pass. The body is unreachable; it returns the shared no-op unsubscribe.\n return noopUnsubscribe;\n },\n subscribePort<T>(\n _port: Port<T>,\n _listener: (value: T) => void,\n ): () => void {\n return noopUnsubscribe;\n },\n emitPort<T>(_port: Port<T>, _value: T): void {\n // No-op. No subscribers to fan out to; nothing to do.\n },\n // Assigned just below — `ready` must resolve to `runtime` itself, which\n // can't be referenced inside its own object literal.\n ready: undefined as unknown as Promise<Runtime<S, M>>,\n async stop(): Promise<void> {\n // Resolves immediately. No in-flight tail to drain, no subs to clean.\n },\n };\n // Already \"booted\" — `ready` hands back the same runtime synchronously.\n runtime.ready = Promise.resolve(runtime);\n return runtime;\n}\n","// ---------------------------------------------------------------------------\n// stateFactory — typed phase-constructor API for a discriminated-union State.\n//\n// The motivating callsite: every reducer test today opens with ~60–80 lines\n// of hand-rolled factory functions, one per phase, each one's defaults\n// duplicated across files:\n//\n// function idleState() { return { type: \"idle\" }; }\n// function creatingWindowState(overrides?: Partial<...>) {\n// return { type: \"creating_window\", auditId: ..., wsUrl: ..., ...overrides };\n// }\n// ...\n//\n// `stateFactory<BackgroundState>({...})` collapses the whole block to one\n// declaration: pass each phase's REQUIRED defaults once (the type system\n// enforces every phase has an entry), and the helper builds an API of\n// `factory.idle()`, `factory.creating_window({...})`, etc.\n//\n// Two type-level guarantees:\n//\n// 1. Every phase has a defaults entry — `StateFactoryDefaults<S>` is a\n// mapped type keyed by every `S[\"type\"]`. Missing an entry is a\n// compile error (the @ts-expect-error in testing.spec.ts pins this).\n//\n// 2. Each phase's defaults match that phase's required shape minus the\n// `type` discriminator. Passing wrong fields fails to compile.\n//\n// The runtime is pure data:\n//\n// api[k] = (overrides) => ({\n// type: k,\n// ...defaults[k],\n// ...stripUndefined(overrides ?? {}),\n// })\n//\n// The `stripUndefined` pass is what preserves `exactOptionalPropertyTypes`\n// assignability — without it, calling `factory.creating_window({\n// queueItemId: undefined })` would land an explicit `queueItemId: undefined`\n// on the object, which is NOT assignable to `queueItemId?: string` under\n// `exactOptionalPropertyTypes`. Stripping undefineds before spread keeps\n// the boundary clean.\n//\n// `stripUndefined` is inlined (not imported from a shared stdlib) so this\n// package can stay dep-free of stdlib.\n// ---------------------------------------------------------------------------\n\n/** A factory-constructed phase: `S` narrowed to the arm whose discriminator equals `K`. */\ntype StateOf<S, K> = Extract<S, { type: K }>;\n\n/** A factory-constructed phase with the `type` discriminator removed — the \"fields the caller cares about\" half. */\ntype RequiredFields<T> = Omit<T, \"type\">;\n\n/**\n * Defaults shape passed to `stateFactory`. Mapped type over the discriminant\n * set: every phase MUST have an entry, and that entry MUST match the phase's\n * required fields minus the `type` discriminator.\n */\nexport type StateFactoryDefaults<S extends { type: string }> = {\n readonly [K in S[\"type\"]]: RequiredFields<StateOf<S, K>>;\n};\n\n/**\n * The API returned by `stateFactory`. One method per phase; calling it with\n * no args returns the default snapshot, calling it with a partial overrides\n * object merges those overrides in (with undefineds stripped — see\n * stripUndefined below).\n */\nexport type StateFactoryAPI<S extends { type: string }> = {\n readonly [K in S[\"type\"]]: (\n overrides?: Partial<RequiredFields<StateOf<S, K>>>,\n ) => StateOf<S, K>;\n};\n\n/**\n * Strip own-enumerable properties whose value is `undefined`. Used inside\n * the factory's spread chain so callers can pass `{ queueItemId: undefined }`\n * without the explicit-undefined landing on the result object (which would\n * fail to assign to a `queueItemId?: string` field under\n * `exactOptionalPropertyTypes`).\n *\n * Inlined (not imported from `a shared stdlib`) so `@demlik/tea/testing` can stay\n * dependency-free of stdlib. The 8-line cost of duplication is cheaper than\n * a package-graph edge.\n */\nfunction stripUndefined<T extends object>(obj: T): Partial<T> {\n const out: Partial<T> = {};\n for (const key of Object.keys(obj) as (keyof T)[]) {\n const value = obj[key];\n if (value !== undefined) {\n out[key] = value;\n }\n }\n return out;\n}\n\n/**\n * Build a typed phase-constructor API from per-phase defaults. The defaults\n * object MUST have one entry per `S[\"type\"]` discriminant; the returned API\n * has one method per phase. Each method merges the defaults with optional\n * overrides (undefineds stripped), then stamps the `type` discriminator.\n *\n * @example\n * type S = { type: \"a\"; n: number } | { type: \"b\"; s: string };\n * const f = stateFactory<S>({ a: { n: 0 }, b: { s: \"\" } });\n * f.a(); // { type: \"a\", n: 0 }\n * f.a({ n: 42 }); // { type: \"a\", n: 42 }\n * f.b({ s: \"hi\" }); // { type: \"b\", s: \"hi\" }\n */\nexport function stateFactory<S extends { type: string }>(\n defaults: StateFactoryDefaults<S>,\n): StateFactoryAPI<S> {\n // Build the API by walking the defaults' own keys. Each key corresponds\n // to one phase discriminator; the constructor closes over both the key\n // (for the `type` stamp) and that phase's defaults snapshot.\n const api = {} as { [k: string]: (overrides?: object) => unknown };\n for (const key of Object.keys(\n defaults,\n ) as (keyof StateFactoryDefaults<S>)[]) {\n const phaseDefaults = defaults[key];\n api[key as string] = (overrides?: object) => {\n // The strip-undefined pass lives here, not at the caller. See the\n // module header for the `exactOptionalPropertyTypes` rationale.\n const cleanedOverrides = overrides ? stripUndefined(overrides) : {};\n return { type: key, ...phaseDefaults, ...cleanedOverrides };\n };\n }\n return api as StateFactoryAPI<S>;\n}\n"]}
1
+ {"version":3,"sources":["../../src/testing/assertions.ts","../../src/testing/bind-machine.ts","../../src/testing/drive.ts","../../src/testing/expect-replay-deterministic.ts","../../src/testing/noop-runtime.ts","../../src/testing/state-factory.ts"],"names":["expect"],"mappings":"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","file":"index.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Test-side assertions over `replay`. Each helper composes the replay call\n// with a single `expect(...)` so consumers stop hand-rolling the 5-line\n// \"destructure { state, cmds } → expect.toBe / toEqual / toContainEqual\"\n// boilerplate at every test site.\n//\n// Each helper is a thin wrapper that:\n// 1. Calls `replay(machine, opts)` once.\n// 2. Pipes the result into a single vitest `expect(...)` matcher.\n//\n// All helpers return `void` — assertions throw on failure (vitest contract).\n// No mutation of the machine or opts. Pure test ergonomics.\n//\n// Type-inference note: each helper anchors its generic parameters to the\n// `machine` argument. The `expected` (and `opts.msgs`) arguments are wrapped\n// in `NoInfer<...>` so they do NOT participate in inference — otherwise a\n// `[]`-shaped `expected` would force `C = never` and reject a machine whose\n// real Cmd union is non-empty. Anchoring inference to the machine is the\n// same discipline `replay` uses (it has nothing but the machine and opts).\n//\n// Strengthens invariant 9 (the testing surface is named, small, and forks\n// nothing of the substrate's purity guarantees).\n// ---------------------------------------------------------------------------\n\nimport { expect } from \"vitest\";\nimport { type Cmd, type Machine, replay, type Sub } from \"../index\";\n\n// `NoInfer<T>` is the built-in TypeScript 5.4+ intrinsic that blocks\n// inference from a generic parameter at a position. We use it on `expected`\n// and `opts` so inference flows purely FROM the machine — `[]`-shaped\n// `expected` no longer drags `C` down to `never` and rejects the machine.\n\n/**\n * Shared options shape for every test assertion below. Mirrors the\n * second argument of `replay(machine, opts)` — `msgs` is the sequence to\n * fold over (may be empty for init-only assertions), `ctx` is the\n * substrate-required ctx value, `loaded` is the optional pre-`init`\n * snapshot (omit / pass `null` for fresh boot).\n */\nexport interface ReplayOpts<S, M, Ctx> {\n readonly msgs: readonly M[];\n readonly ctx: Ctx;\n readonly loaded?: S | null;\n}\n\n/**\n * Assert the final state after replaying `opts.msgs` equals `expected`.\n * Replaces the boilerplate:\n *\n * const { state } = replay(machine, opts);\n * expect(state).toEqual(expected);\n *\n * Uses `toEqual` (deep structural). For type-narrowing assertions that\n * branch on `state.type` afterwards, fall back to plain `replay` —\n * `expectFinalState` is the \"exact match\" tool.\n */\nexport function expectFinalState<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: NoInfer<ReplayOpts<S, M, Ctx>>,\n expected: NoInfer<S>,\n): void {\n const { state } = replay(machine, opts);\n expect(state).toEqual(expected);\n}\n\n/**\n * Assert that `cmd` appears at least once in the cmds array produced by\n * replaying `opts.msgs`. Uses `toContainEqual` (deep structural). Order\n * and surrounding cmds are not asserted — use `expectCmdSequence` for\n * exact ordered match.\n *\n * Replaces:\n *\n * const { cmds } = replay(machine, opts);\n * expect(cmds).toContainEqual(cmd);\n */\nexport function expectCmdEmitted<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: NoInfer<ReplayOpts<S, M, Ctx>>,\n cmd: NoInfer<C>,\n): void {\n const { cmds } = replay(machine, opts);\n expect(cmds).toContainEqual(cmd);\n}\n\n/**\n * Assert the exact ordered sequence of cmds emitted by replaying\n * `opts.msgs`. Uses `toEqual` against the full cmds array — extra cmds\n * fail the assertion, missing cmds fail the assertion, wrong order\n * fails the assertion.\n *\n * Replaces:\n *\n * const { cmds } = replay(machine, opts);\n * expect(cmds).toEqual(expected);\n */\nexport function expectCmdSequence<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: NoInfer<ReplayOpts<S, M, Ctx>>,\n expected: NoInfer<readonly C[]>,\n): void {\n const { cmds } = replay(machine, opts);\n expect(cmds).toEqual(expected);\n}\n\n/**\n * Assert the exact set of subs desired at the final state after\n * replaying `opts.msgs`. Uses `toEqual` against the full subs array.\n *\n * Replaces:\n *\n * const { subs } = replay(machine, opts);\n * expect(subs).toEqual(expected);\n */\nexport function expectActiveSubs<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: NoInfer<ReplayOpts<S, M, Ctx>>,\n expected: NoInfer<readonly U[]>,\n): void {\n const { subs } = replay(machine, opts);\n expect(subs).toEqual(expected);\n}\n\n/**\n * Single-msg step helper — feeds `loaded → msg → next state + cmds emitted by\n * that msg`. Returns a readonly tuple in the shape consumers used before the\n * substrate (`[state, cmds]`), so existing tests that destructure with\n * `[next, cmds]` can swap `reduce(state, msg)` for `step(machine, state, msg,\n * ctx)` without changing the assertion code.\n *\n * Internally a `replay` call with `loaded: prevState` and a single-element\n * `msgs` array. The cmds returned are exactly the cmds emitted by THIS msg\n * (init emits no cmds when `loaded` is provided, so the slice is the entire\n * array).\n *\n * Use when consumers want to walk a machine one msg at a time and assert per\n * step. For one-shot \"final state after N msgs\" assertions, prefer\n * `expectFinalState`.\n */\nexport function step<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n loaded: NoInfer<S>,\n msg: NoInfer<M>,\n ctx: NoInfer<Ctx>,\n): readonly [S, readonly C[]] {\n const { state, cmds } = replay(machine, { msgs: [msg], ctx, loaded });\n return [state, cmds] as const;\n}\n","// ---------------------------------------------------------------------------\n// `bindMachine(machine, ctx)` — bound test harness over the free-function\n// assertions in `./assertions.ts`. Closes over the `machine` and `ctx` once\n// so per-test callsites stop repeating both at every invocation.\n//\n// Why this exists: every consumer test file ends up hand-rolling a local\n// two-arg `step(state, msg)` wrapper around `step(machine, state, msg, ctx)`,\n// and the four `expect*` assertions repeat `machine` + `ctx` at every\n// callsite. The bound form pulls both into one place and gives every\n// per-test call its tightest possible shape (2-arg max).\n//\n// `bindMachine` is built ON TOP of the free helpers — it does not replace\n// them. Both forms coexist; pick the one that fits the call site:\n//\n// - Free form → one-off call inside a helper that isn't bound to one\n// machine (e.g. cross-machine integration test).\n// - Bound form → a `describe` block runs many assertions against one\n// machine + ctx. The bound form is the common case.\n//\n// Strengthens invariant 9 (the testing surface is named, small, and forks\n// nothing of the substrate's purity guarantees).\n// ---------------------------------------------------------------------------\n\nimport { expect } from \"vitest\";\nimport {\n type BuiltinSub,\n type Cmd,\n type Machine,\n replay,\n type Sub,\n} from \"../index\";\n\n/**\n * Replay options shape used by every bound method. `ctx` is captured by\n * `bindMachine`; the per-call opts only need `msgs` and an optional\n * `loaded` snapshot. (Compare `ReplayOpts` in `./assertions.ts`, which\n * still takes `ctx` because the free helpers cannot capture it.)\n */\ninterface BoundOpts<S, M> {\n readonly msgs: readonly M[];\n readonly loaded?: S | null;\n}\n\n/**\n * The bound testing surface. Every method is 2-arg max — `ctx` and\n * `machine` are captured by `bindMachine`.\n *\n * `NoInfer<...>` on `expected` / `cmd` arguments mirrors the discipline\n * the free helpers use (see `./assertions.ts`): inference flows purely\n * FROM the machine, so a `[]`-shaped expected does not drag `C` down to\n * `never` and reject the machine.\n */\nexport interface BoundMachine<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n // `Ctx` is a phantom-style parameter — present for consumers that want to\n // spell `BoundMachine<S, M, C, U, Ctx>` explicitly, mirroring `Machine`.\n // biome-ignore lint/correctness/noUnusedVariables: phantom type param (see above)\n Ctx,\n> {\n /**\n * Single-msg step — feeds `loaded → msg → [next state, cmds emitted by\n * that msg]`. Mirrors the free `step(machine, loaded, msg, ctx)`.\n */\n step(loaded: S, msg: M): readonly [S, readonly C[]];\n /**\n * Assert the final state after replaying `opts.msgs` deep-equals\n * `expected`. Mirrors the free `expectFinalState(machine, opts, expected)`.\n */\n expectFinalState(opts: BoundOpts<S, M>, expected: NoInfer<S>): void;\n /**\n * Assert `cmd` appears at least once in the emitted cmd list. Order is\n * not asserted. Mirrors the free `expectCmdEmitted(machine, opts, cmd)`.\n */\n expectCmdEmitted(opts: BoundOpts<S, M>, cmd: NoInfer<C>): void;\n /**\n * Assert the exact ordered sequence of cmds emitted by replaying\n * `opts.msgs`. Mirrors the free `expectCmdSequence(machine, opts, expected)`.\n */\n expectCmdSequence(\n opts: BoundOpts<S, M>,\n expected: readonly NoInfer<C>[],\n ): void;\n /**\n * Assert the exact set of subs desired at the final state. Mirrors the\n * free `expectActiveSubs(machine, opts, expected)`.\n */\n expectActiveSubs(\n opts: BoundOpts<S, M>,\n expected: readonly NoInfer<U | BuiltinSub<M>>[],\n ): void;\n /**\n * Bound `replay` — returns `{ state, cmds, subs }` for the given opts.\n * Use for the \"narrow-then-assert\" pattern where the test inspects a\n * specific field after `state.type === \"...\"` discrimination.\n */\n replay(opts: BoundOpts<S, M>): {\n state: S;\n cmds: readonly C[];\n subs: readonly (U | BuiltinSub<M>)[];\n };\n}\n\n/**\n * Bind a machine + ctx into a small bag of 2-arg helpers. Each method\n * delegates to `replay(machine, { ...opts, ctx })` and pipes the result\n * through a single `expect(...)` matcher (or returns the raw tuple, for\n * `step` and `replay`).\n *\n * Use one bind per `describe` block (or per file) when many assertions\n * share the same machine + ctx. For one-off cross-machine calls, stick\n * with the free helpers from `./assertions.ts`.\n */\nexport function bindMachine<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(machine: Machine<S, M, C, U, Ctx>, ctx: Ctx): BoundMachine<S, M, C, U, Ctx> {\n return {\n step(loaded, msg) {\n const { state, cmds } = replay(machine, { msgs: [msg], ctx, loaded });\n return [state, cmds] as const;\n },\n expectFinalState(opts, expected) {\n const { state } = replay(machine, { ...opts, ctx });\n expect(state).toEqual(expected);\n },\n expectCmdEmitted(opts, cmd) {\n const { cmds } = replay(machine, { ...opts, ctx });\n expect(cmds).toContainEqual(cmd);\n },\n expectCmdSequence(opts, expected) {\n const { cmds } = replay(machine, { ...opts, ctx });\n expect(cmds).toEqual(expected);\n },\n expectActiveSubs(opts, expected) {\n const { subs } = replay(machine, { ...opts, ctx });\n expect(subs).toEqual(expected);\n },\n replay(opts) {\n return replay(machine, { ...opts, ctx });\n },\n };\n}\n","// ---------------------------------------------------------------------------\n// `drive(machine, initial, msg, handlers, opts)` — the runtime's own loop,\n// said once, for a test.\n//\n// `bindMachine` hands back the Cmds a fold emitted and stops there; performing\n// them is the caller's. So every consumer test that exercises a Cmd-emitting\n// machine hand-writes the same fixture: step the Msg, await the real interpret\n// handler over each emitted Cmd, feed the settle Msg back, repeat until quiet,\n// with a hand-picked guard bound in the loop header. The guard count, the\n// accumulate-then-swap order and the \"did I drain this round before starting\n// the next\" question are re-decided at every copy site, and one of those copies\n// ships in `docs/how-to/ask-jev-a-typed-question.md`.\n//\n// `drive` is that fixture as one call. It composes `bindMachine`'s synchronous\n// 2-arg `step` and adds NO second reducer path — there is no `Runtime` here, no\n// observation, no clock, and nothing borrowed from the internal flow helpers.\n// The only impure thing it does is `await` the handler the caller handed it.\n//\n// **It returns the history, not just the endpoint.** `{ state, trace }`, where\n// `trace` is every Msg folded and every Cmd dispatched in order, is what makes\n// this worth a stable export rather than a snippet: a test asserts on the\n// SEQUENCE as well as the settled state — which handler ran, in which round,\n// and what came back — and the same trace's Msgs replayed through `replay` from\n// the same initial state reproduce the returned state exactly.\n//\n// Prior art converged on the same answer: Temporal's `TestWorkflowEnvironment`\n// hands a test the event history rather than the final result, and Effect's\n// `TestClock` runs make the effect log inspectable rather than opaque.\n//\n// **Exhausting `maxRounds` THROWS.** A driver that quietly returns a\n// half-driven state lets a test assert green on a machine that never settled —\n// the exact failure a hand-rolled `guard < 10` already risks.\n//\n// Strengthens invariant 9 (the testing surface is named and small).\n// ---------------------------------------------------------------------------\n\nimport {\n type Cmd,\n type CtxArg,\n type Interpret,\n type Machine,\n Outcome,\n type PortEmitter,\n type Sub,\n} from \"../index\";\nimport { cmdEdge, cmdEdgeOver } from \"../pure/core\";\nimport { bindMachine } from \"./bind-machine\";\n\n/**\n * The default round bound when `opts.maxRounds` is omitted: **100**.\n *\n * A round is one drain of the pending Msgs plus one drain of the Cmds those\n * folds emitted, so a settling machine costs a handful; 100 is loose enough\n * that a legitimate retry ladder finishes inside it and tight enough that a\n * genuine cycle stops rather than hangs.\n */\nexport const DEFAULT_MAX_ROUNDS = 100;\n\n/**\n * One entry of a driven run's history, in dispatch order.\n *\n * - `msg` — a Msg folded through the machine's reducer. The seed Msg is the\n * first entry of every trace; every later one is a settle Msg a handler\n * returned (or fired through its injected `dispatch`).\n * - `cmd` — a Cmd handed to an interpret handler. It is recorded BEFORE the\n * handler is awaited, so a trace recovered from a rejected handler names the\n * Cmd that rejected as its last entry.\n */\nexport type DriveTraceEntry<M, C> =\n | { readonly kind: \"msg\"; readonly msg: M }\n | { readonly kind: \"cmd\"; readonly cmd: C };\n\n/** What a settled {@link drive} hands back. */\nexport interface DriveResult<S, M, C> {\n /** The state after the machine went quiet — no Msg pending, no Cmd unperformed. */\n readonly state: S;\n /**\n * Every Msg folded and every Cmd dispatched, in order. Filtering it to its\n * `msg` entries and replaying those through `replay` from the same initial\n * state reproduces {@link DriveResult.state}.\n */\n readonly trace: readonly DriveTraceEntry<M, C>[];\n}\n\n/**\n * {@link drive}'s `ctx` field. `CtxArg` widened by exactly one case: a machine\n * declaring `ctx: undefined` in its `types` block — the pure shape every\n * how-to on this package writes — may omit it too. `CtxArg` alone keeps `ctx`\n * REQUIRED there, because `{}` is not assignable to `undefined`, which would\n * make the common pure call site spell `{ ctx: undefined }` to say nothing.\n * A machine whose `Ctx` carries a field a handler reads still requires it.\n */\nexport type DriveCtxArg<Ctx> = [undefined] extends [Ctx]\n ? { readonly ctx?: Ctx }\n : CtxArg<Ctx>;\n\n/**\n * {@link drive}'s options. `ctx` is conditionally optional (see\n * {@link DriveCtxArg}).\n */\nexport type DriveOptions<Ctx> = DriveCtxArg<Ctx> & {\n /**\n * The round bound. Exceeding it throws {@link DriveRoundsExceededError};\n * `drive` never returns a half-driven state. Defaults to\n * {@link DEFAULT_MAX_ROUNDS} (100).\n */\n readonly maxRounds?: number;\n /**\n * The clock that stamps `at` on a `Cmd.define`d Cmd's minted `_ok` / `_err`\n * Msg — the one `run` takes. Defaults to `Date.now`; pin it for a test that\n * asserts on `at`.\n */\n readonly clock?: () => number;\n};\n\n/**\n * Raised when a driven machine is still emitting work after `maxRounds` rounds.\n * Follows the tea error dialect (`override readonly name`) and carries a `_tag`\n * discriminant, so a test branches on it by `instanceof` or on the tag.\n *\n * It carries the round count it stopped at and the PARTIAL trace up to that\n * point, which is what a test needs to see the cycle — the bound alone says\n * only that one exists.\n */\nexport class DriveRoundsExceededError<M, C> extends Error {\n override readonly name = \"DriveRoundsExceededError\";\n readonly _tag = \"DriveRoundsExceededError\" as const;\n constructor(\n /** The bound that was exceeded. */\n public readonly maxRounds: number,\n /** The round the driver stopped at — always `maxRounds + 1`. */\n public readonly rounds: number,\n /** Every Msg folded and Cmd dispatched before the driver gave up. */\n public readonly trace: readonly DriveTraceEntry<M, C>[],\n ) {\n super(\n `@demlik/tea: drive did not settle within ${maxRounds} round(s) — the ` +\n `machine was still emitting work at round ${rounds}. The partial ` +\n `trace (${trace.length} entr${trace.length === 1 ? \"y\" : \"ies\"}) is ` +\n `on the error's \\`trace\\` field.`,\n );\n }\n}\n\n/**\n * Raised when a Cmd reaches {@link drive} with no handler for its `type` in the\n * `handlers` record. `Interpret`'s mapped type makes that a compile error at a\n * well-typed call site; this is the runtime arm for a handler record assembled\n * dynamically or cast.\n */\nexport class DriveNoHandlerError<M, C> extends Error {\n override readonly name = \"DriveNoHandlerError\";\n readonly _tag = \"DriveNoHandlerError\" as const;\n constructor(\n /** The Cmd `type` nothing in the handler record answered. */\n public readonly cmdType: string,\n /** Every Msg folded and Cmd dispatched before the miss. */\n public readonly trace: readonly DriveTraceEntry<M, C>[],\n ) {\n super(\n `@demlik/tea: drive found no interpret handler for Cmd type ` +\n `\"${cmdType}\" — the handlers record must answer every Cmd the ` +\n `machine emits.`,\n );\n }\n}\n\n/** The key `drive` hangs a partial trace off a rejected handler's error under. */\nconst TRACE_KEY = \"driveTrace\";\n\n/**\n * Read back the partial trace `drive` attached to an error a handler rejected\n * with. Returns `undefined` for anything `drive` did not annotate.\n *\n * A rejecting handler's error propagates out of `drive` UNSWALLOWED — same\n * value, same `instanceof`, same `_tag` — so a test that already branches on\n * the handler's own error type keeps doing so. The trace rides along on a\n * non-enumerable property so it is recoverable without changing how the error\n * prints or serializes.\n */\nexport function driveTraceOf<M, C>(\n err: unknown,\n): readonly DriveTraceEntry<M, C>[] | undefined {\n if (typeof err !== \"object\" || err === null) return undefined;\n const carried = (err as Record<string, unknown>)[TRACE_KEY];\n return Array.isArray(carried)\n ? (carried as readonly DriveTraceEntry<M, C>[])\n : undefined;\n}\n\n/**\n * Drive `machine` from `initial` through `msg` against the REAL interpret\n * `handlers`, feeding every settle Msg back until the machine goes quiet, and\n * hand back the settled state together with the whole history.\n *\n * `handlers` is the same table a host hands `run` as its `interpret` (e.g.\n * a jev knob's `resilient_run` handler), so the test drives the real interpreter and\n * mocks only the port beneath it.\n *\n * One round is: fold every pending Msg, then await a handler for every Cmd\n * those folds emitted. A Msg a handler returns — or fires through its injected\n * `dispatch` — is pending for the next round. A machine that emits no Cmd for\n * its seed Msg therefore settles in ONE round and never awaits a handler at\n * all.\n *\n * The `ctx` handed to each handler is `opts.ctx` augmented with a NO-OP `emit`,\n * so a handler that emits to a `Port` runs without a live runtime; nothing here\n * observes ports. Supply your own `emit` on `ctx` to capture them.\n *\n * @throws {DriveRoundsExceededError} when the machine is still emitting work\n * after `opts.maxRounds` rounds. It never returns a half-driven state.\n * @throws whatever a handler rejects with, unchanged — annotated with the\n * partial trace, readable via {@link driveTraceOf}.\n */\nexport async function drive<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n initial: S,\n msg: M,\n handlers: Interpret<M, C, Ctx>,\n ...[opts]: Record<never, never> extends DriveOptions<Ctx>\n ? [opts?: DriveOptions<Ctx>]\n : [opts: DriveOptions<Ctx>]\n): Promise<DriveResult<S, M, C>> {\n const options = (opts ?? {}) as {\n ctx?: Ctx;\n maxRounds?: number;\n clock?: () => number;\n };\n const maxRounds = options.maxRounds ?? DEFAULT_MAX_ROUNDS;\n const bound = bindMachine(machine, options.ctx as Ctx);\n\n // The `Cmd.define` edge `run` applies (ADR 0021): a defined Cmd's handler\n // returns an outcome, and the edge mints it into the def's `_ok` / `_err`\n // Msg. A hand-written Cmd's return passes through untouched.\n const settle = cmdEdgeOver(machine.cmds ?? [], options.clock ?? Date.now);\n\n // A handler's ctx is `Ctx & PortEmitter`, plus the `ok` / `err` builders and\n // the edge a defined Cmd's handler is handed. The Ctx half is the caller's;\n // the rest is the kernel's, and a driven test has no kernel — so a no-op\n // `emit` stands in, placed FIRST so a caller's own `emit` wins.\n const noopEmit: PortEmitter = { emit: () => {} };\n const handlerCtx = {\n ...noopEmit,\n ok: Outcome.ok,\n err: Outcome.err,\n [cmdEdge]: settle,\n ...(options.ctx as unknown as Record<string, unknown> | undefined),\n };\n\n // Indexed dynamically: `Interpret`'s mapped type already proved every Cmd\n // variant has a cell, but `cmd.type` is a union at the value level and TS\n // cannot correlate the two sides of that index.\n const cells = handlers as unknown as Record<\n string,\n | ((\n cmd: C,\n ctx: unknown,\n dispatch?: (msg: M) => void,\n // biome-ignore lint/suspicious/noConfusingVoidType: mirrors `Interpret`'s own cell return — a follow-up Msg, or nothing\n ) => Promise<M | void>)\n | undefined\n >;\n\n const trace: DriveTraceEntry<M, C>[] = [];\n let state = initial;\n let pending: M[] = [msg];\n let round = 0;\n\n while (pending.length > 0) {\n round += 1;\n if (round > maxRounds) {\n throw new DriveRoundsExceededError<M, C>(maxRounds, round, trace);\n }\n\n // Drain the round's Msgs first, THEN its Cmds. Swapping the buffer before\n // the fold is what keeps a settle Msg out of the round that produced it —\n // the ordering question every hand-rolled copy of this loop re-answers.\n const folding = pending;\n pending = [];\n const emitted: C[] = [];\n for (const next of folding) {\n trace.push({ kind: \"msg\", msg: next });\n const [advanced, cmds] = bound.step(state, next);\n state = advanced;\n emitted.push(...cmds);\n }\n\n for (const cmd of emitted) {\n trace.push({ kind: \"cmd\", cmd });\n const cell = cells[cmd.type];\n if (cell === undefined) {\n throw new DriveNoHandlerError<M, C>(cmd.type, trace);\n }\n // biome-ignore lint/suspicious/noConfusingVoidType: mirrors `Interpret`'s own cell return — a follow-up Msg, or nothing\n let settled: M | void;\n try {\n settled = settle(\n cmd,\n await cell(cmd, handlerCtx, (fired) => {\n pending.push(fired);\n }),\n ) as M | undefined;\n } catch (err) {\n // Unswallowed: the handler's own error is what leaves `drive`, so a\n // test's `instanceof` / `_tag` branch still lands. The trace rides on\n // a non-enumerable property (see `driveTraceOf`).\n if (typeof err === \"object\" && err !== null) {\n Object.defineProperty(err, TRACE_KEY, {\n value: [...trace],\n enumerable: false,\n configurable: true,\n writable: true,\n });\n }\n throw err;\n }\n if (settled !== undefined) pending.push(settled);\n }\n }\n\n return { state, trace };\n}\n","import { expect, vi } from \"vitest\";\nimport { type Cmd, type Machine, replay, type Sub } from \"../index\";\nimport type { ReplayOpts } from \"./assertions\";\n\ninterface Ambient {\n readonly now: number;\n readonly rng: number;\n}\n\nconst AMBIENT_A: Ambient = { now: 1_000_000, rng: 0.123 };\nconst AMBIENT_B: Ambient = { now: 9_876_543, rng: 0.987 };\n\n/**\n * Assert that replaying `opts.msgs` is a pure function of the Msg log: the\n * final state and every emitted Cmd come out the same under two different\n * global wall-clocks and RNG seeds.\n *\n * `init` and `update` never receive a clock, so a cell that reads\n * `Date.now()` or `Math.random()` reaches for the global one, and a replay of\n * the same log on another day or another host lands somewhere else. Two\n * back-to-back replays under one clock would hide that; this helper stubs\n * two distinct ambients and compares. Time belongs in the Msg (an `at`\n * field), where it is identical across replays.\n *\n * Subs are not compared: they are derived from the final state, so a state\n * that matches yields the same Subs.\n */\nexport function expectReplayDeterministic<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: NoInfer<ReplayOpts<S, M, Ctx>>,\n): void {\n const a = underAmbient(AMBIENT_A, () => replay(machine, opts));\n const b = underAmbient(AMBIENT_B, () => replay(machine, opts));\n expect(\n b.state,\n \"final state changed under a different clock/RNG: init or update reads \" +\n \"Date.now()/Math.random(); time must enter as Msg data\",\n ).toEqual(a.state);\n expect(\n b.cmds,\n \"emitted Cmds changed under a different clock/RNG: a Cmd payload reads \" +\n \"Date.now()/Math.random(); time must enter as Msg data\",\n ).toEqual(a.cmds);\n}\n\n/** Run `fn` with the global `Date.now` and `Math.random` pinned, then restore them. */\nfunction underAmbient<T>(ambient: Ambient, fn: () => T): T {\n const nowSpy = vi.spyOn(Date, \"now\").mockReturnValue(ambient.now);\n const rngSpy = vi.spyOn(Math, \"random\").mockReturnValue(ambient.rng);\n try {\n return fn();\n } finally {\n nowSpy.mockRestore();\n rngSpy.mockRestore();\n }\n}\n","// ---------------------------------------------------------------------------\n// noopRuntime — a Runtime<S, M>-shaped value whose every method is inert.\n//\n// The motivating callsite: a machine's `ctx` requires a sibling Runtime<S,\n// M> reference (e.g. the queue runtime in extension/lib/queue-machine\n// reaches into an `auditRuntime: Runtime<...>` slot on its ctx so its\n// interpret handlers can dispatch into the audit machine). When the test\n// replays such a machine, `replay` never actually invokes `interpret`, so\n// the runtime reference is never dereferenced — but the type system\n// still requires a value of `Runtime<S, M>` shape at the ctx slot. Tests\n// today work around this with a hand-rolled 12-line NOOP_AUDIT_RUNTIME\n// const cast through `as unknown as Runtime<...>`.\n//\n// `noopRuntime<S, M>()` is that hand-roll, named once. It returns a real\n// Runtime<S, M> value whose every method is a structurally-correct no-op:\n//\n// - `dispatch`: resolves immediately, never advances state.\n// - `getState`: returns the optional `initialState` argument (or a\n// deliberate `undefined` cast as S — the function lies about its\n// return type when no initial state is provided; this is the trade-off\n// we accept for an ergonomic test substrate).\n// - `subscribe` / `observe` / `subscribePort`: register the listener,\n// return a no-op unsubscribe. The listener is never fired because the\n// state never changes.\n// - `emitPort`: no-op.\n// - `ready`: a Promise that resolves to the no-op runtime itself (the #45\n// `ready: Promise<Runtime<S, M>>` shape — already booted, nothing to wait\n// for).\n// - `stop`: resolved Promise<void>.\n//\n// Strengthens invariant 9 (the testing surface is named and small).\n// ---------------------------------------------------------------------------\n\nimport type { Port, Runtime } from \"../index\";\n\n/**\n * Construct an inert `Runtime<S, M>` value. Use at ctx sites that require\n * a Runtime reference but where the test never invokes `interpret` (so\n * the runtime is never dereferenced in practice).\n *\n * @param opts.initialState Optional value returned by `getState()`. Omit\n * when the test never calls `getState()` on the no-op runtime — most\n * tests only need the type-level slot filled.\n */\nexport function noopRuntime<S, M extends { type: string }>(opts?: {\n initialState?: S;\n}): Runtime<S, M> {\n const initialState = opts?.initialState;\n // Single source of truth for the \"do-nothing\" unsubscribe so multiple\n // subscribe call sites share the same function instance — purely cosmetic\n // (the function would be a no-op either way).\n const noopUnsubscribe = (): void => {};\n const runtime: Runtime<S, M> = {\n async dispatch(_msg: M): Promise<void> {\n // Resolves immediately. State never advances; listeners never fire.\n // Quiescent by default (#50), but the no-op has no follow-up chain to\n // drain, so single-step and run-to-quiescence collapse to the same no-op.\n },\n async dispatchOnce(_msg: M): Promise<void> {\n // Single-step sibling of `dispatch` (#50). Same no-op for the no-op\n // runtime — there is no tail to leave in flight.\n },\n async idle(): Promise<void> {\n // No tail to drain — the no-op runtime never enqueues a follow-up.\n },\n result(): S | undefined {\n // The no-op runtime never advances, so it is never terminal (#46).\n return undefined;\n },\n done(): Promise<S> {\n // Never terminal → the awaitable never settles, matching `run()` with no\n // `terminal` predicate. Returned as a never-resolving promise.\n return new Promise<S>(() => {});\n },\n getState(): S {\n // Lie if no initial state was provided. The caller's contract is\n // \"you accept the lie because you know replay never invokes\n // interpret and therefore never reads this.\" When the caller DOES\n // pass an initialState the lie collapses to truth.\n return initialState as S;\n },\n subscribe(_listener: () => void): () => void {\n return noopUnsubscribe;\n },\n observe(_observer: (msg: M, state: S) => void): () => void {\n return noopUnsubscribe;\n },\n onBoot(_handler: (state: S) => void): () => void {\n // Boot never fires on the inert runtime — it is \"already booted\" with no\n // initial fanout, so the handler is registered and never called (#47).\n return noopUnsubscribe;\n },\n on<K extends never>(\n _type: K,\n _handler: (event: never) => void,\n ): () => void {\n // The no-op runtime's event surface is `never` (no `events` projector),\n // so `on` is structurally present but uncallable — there is no `K` to\n // pass. The body is unreachable; it returns the shared no-op unsubscribe.\n return noopUnsubscribe;\n },\n subscribePort<T>(\n _port: Port<T>,\n _listener: (value: T) => void,\n ): () => void {\n return noopUnsubscribe;\n },\n emitPort<T>(_port: Port<T>, _value: T): void {\n // No-op. No subscribers to fan out to; nothing to do.\n },\n // Assigned just below — `ready` must resolve to `runtime` itself, which\n // can't be referenced inside its own object literal.\n ready: undefined as unknown as Promise<Runtime<S, M>>,\n async stop(): Promise<void> {\n // Resolves immediately. No in-flight tail to drain, no subs to clean.\n },\n };\n // Already \"booted\" — `ready` hands back the same runtime synchronously.\n runtime.ready = Promise.resolve(runtime);\n return runtime;\n}\n","// ---------------------------------------------------------------------------\n// stateFactory — typed phase-constructor API for a discriminated-union State.\n//\n// The motivating callsite: every reducer test today opens with ~60–80 lines\n// of hand-rolled factory functions, one per phase, each one's defaults\n// duplicated across files:\n//\n// function idleState() { return { type: \"idle\" }; }\n// function creatingWindowState(overrides?: Partial<...>) {\n// return { type: \"creating_window\", auditId: ..., wsUrl: ..., ...overrides };\n// }\n// ...\n//\n// `stateFactory<BackgroundState>({...})` collapses the whole block to one\n// declaration: pass each phase's REQUIRED defaults once (the type system\n// enforces every phase has an entry), and the helper builds an API of\n// `factory.idle()`, `factory.creating_window({...})`, etc.\n//\n// Two type-level guarantees:\n//\n// 1. Every phase has a defaults entry — `StateFactoryDefaults<S>` is a\n// mapped type keyed by every `S[\"type\"]`. Missing an entry is a\n// compile error (the @ts-expect-error in testing.spec.ts pins this).\n//\n// 2. Each phase's defaults match that phase's required shape minus the\n// `type` discriminator. Passing wrong fields fails to compile.\n//\n// The runtime is pure data:\n//\n// api[k] = (overrides) => ({\n// type: k,\n// ...defaults[k],\n// ...stripUndefined(overrides ?? {}),\n// })\n//\n// The `stripUndefined` pass is what preserves `exactOptionalPropertyTypes`\n// assignability — without it, calling `factory.creating_window({\n// queueItemId: undefined })` would land an explicit `queueItemId: undefined`\n// on the object, which is NOT assignable to `queueItemId?: string` under\n// `exactOptionalPropertyTypes`. Stripping undefineds before spread keeps\n// the boundary clean.\n//\n// `stripUndefined` is inlined (not imported from a shared stdlib) so this\n// package can stay dep-free of stdlib.\n// ---------------------------------------------------------------------------\n\n/** A factory-constructed phase: `S` narrowed to the arm whose discriminator equals `K`. */\ntype StateOf<S, K> = Extract<S, { type: K }>;\n\n/** A factory-constructed phase with the `type` discriminator removed — the \"fields the caller cares about\" half. */\ntype RequiredFields<T> = Omit<T, \"type\">;\n\n/**\n * Defaults shape passed to `stateFactory`. Mapped type over the discriminant\n * set: every phase MUST have an entry, and that entry MUST match the phase's\n * required fields minus the `type` discriminator.\n */\nexport type StateFactoryDefaults<S extends { type: string }> = {\n readonly [K in S[\"type\"]]: RequiredFields<StateOf<S, K>>;\n};\n\n/**\n * The API returned by `stateFactory`. One method per phase; calling it with\n * no args returns the default snapshot, calling it with a partial overrides\n * object merges those overrides in (with undefineds stripped — see\n * stripUndefined below).\n */\nexport type StateFactoryAPI<S extends { type: string }> = {\n readonly [K in S[\"type\"]]: (\n overrides?: Partial<RequiredFields<StateOf<S, K>>>,\n ) => StateOf<S, K>;\n};\n\n/**\n * Strip own-enumerable properties whose value is `undefined`. Used inside\n * the factory's spread chain so callers can pass `{ queueItemId: undefined }`\n * without the explicit-undefined landing on the result object (which would\n * fail to assign to a `queueItemId?: string` field under\n * `exactOptionalPropertyTypes`).\n *\n * Inlined (not imported from `a shared stdlib`) so `@demlik/tea/testing` can stay\n * dependency-free of stdlib. The 8-line cost of duplication is cheaper than\n * a package-graph edge.\n */\nfunction stripUndefined<T extends object>(obj: T): Partial<T> {\n const out: Partial<T> = {};\n for (const key of Object.keys(obj) as (keyof T)[]) {\n const value = obj[key];\n if (value !== undefined) {\n out[key] = value;\n }\n }\n return out;\n}\n\n/**\n * Build a typed phase-constructor API from per-phase defaults. The defaults\n * object MUST have one entry per `S[\"type\"]` discriminant; the returned API\n * has one method per phase. Each method merges the defaults with optional\n * overrides (undefineds stripped), then stamps the `type` discriminator.\n *\n * @example\n * type S = { type: \"a\"; n: number } | { type: \"b\"; s: string };\n * const f = stateFactory<S>({ a: { n: 0 }, b: { s: \"\" } });\n * f.a(); // { type: \"a\", n: 0 }\n * f.a({ n: 42 }); // { type: \"a\", n: 42 }\n * f.b({ s: \"hi\" }); // { type: \"b\", s: \"hi\" }\n */\nexport function stateFactory<S extends { type: string }>(\n defaults: StateFactoryDefaults<S>,\n): StateFactoryAPI<S> {\n // Build the API by walking the defaults' own keys. Each key corresponds\n // to one phase discriminator; the constructor closes over both the key\n // (for the `type` stamp) and that phase's defaults snapshot.\n const api = {} as { [k: string]: (overrides?: object) => unknown };\n for (const key of Object.keys(\n defaults,\n ) as (keyof StateFactoryDefaults<S>)[]) {\n const phaseDefaults = defaults[key];\n api[key as string] = (overrides?: object) => {\n // The strip-undefined pass lives here, not at the caller. See the\n // module header for the `exactOptionalPropertyTypes` rationale.\n const cleanedOverrides = overrides ? stripUndefined(overrides) : {};\n return { type: key, ...phaseDefaults, ...cleanedOverrides };\n };\n }\n return api as StateFactoryAPI<S>;\n}\n"]}
@@ -1,9 +1,9 @@
1
1
  export { D as Debounced, d as debounce } from '../index-DHSUmQTR.js';
2
- import { C as Cmd } from '../core-BbgtiXdg.js';
3
- import { T as TtlCache, a as DeadlineSub } from '../index-Xufl55W8.js';
4
- export { D as ThrottledInputSettled, s as subscribeThrottledInput, d as throttledInputSettled } from '../index-Xufl55W8.js';
2
+ import { C as Cmd } from '../core-rDbOaJTN.js';
3
+ import { T as TtlCache, a as DeadlineSub } from '../index-DaX_P35Q.js';
4
+ export { D as ThrottledInputSettled, s as subscribeThrottledInput, d as throttledInputSettled } from '../index-DaX_P35Q.js';
5
5
  import '@standard-schema/spec';
6
- import '../types-LxZxGYda.js';
6
+ import '../types-DPm5YCDQ.js';
7
7
 
8
8
  /**
9
9
  * internal/timing/throttle — a timer-based call transformer that caps invocation
@@ -1,8 +1,8 @@
1
1
  export { debounce } from '../chunk-LFZUK6J2.js';
2
- import { deadlineSub, initCache, get, set } from '../chunk-IVJP3GKJ.js';
3
- export { subscribeDeadline as subscribeThrottledInput, deadlineExceeded as throttledInputSettled } from '../chunk-IVJP3GKJ.js';
4
- import '../chunk-GOX2CYIM.js';
5
- import '../chunk-CH5KTOEZ.js';
2
+ import { deadlineSub, initCache, get, set } from '../chunk-GWWCCSDH.js';
3
+ export { subscribeDeadline as subscribeThrottledInput, deadlineExceeded as throttledInputSettled } from '../chunk-GWWCCSDH.js';
4
+ import '../chunk-C4U5D5HP.js';
5
+ import '../chunk-K4ETBNKS.js';
6
6
 
7
7
  // src/internal/timing/throttle/index.ts
8
8
  function throttle(fn, ms, opts) {
@@ -1,4 +1,4 @@
1
- import { S as Sub, h as Dispose } from './core-BbgtiXdg.js';
1
+ import { S as Sub, h as Dispose } from './core-rDbOaJTN.js';
2
2
 
3
3
  type SubscribeHandler<S extends Sub, M, Ctx> = (sub: S, ctx: Ctx, dispatch: (msg: M) => void) => Dispose;
4
4
 
@@ -1,7 +1,7 @@
1
- import { Q as QueueItem } from '../index-7vV8xmsD.js';
2
- export { E as EnqueueInput, a as QueueItemStatus, c as createQueue } from '../index-7vV8xmsD.js';
3
- import '../runtime-types-CiVpC0HN.js';
4
- import '../core-BbgtiXdg.js';
1
+ import { Q as QueueItem } from '../index-CcrKLtse.js';
2
+ export { E as EnqueueInput, a as QueueItemStatus, c as createQueue } from '../index-CcrKLtse.js';
3
+ import '../runtime-types-B62Ucnxr.js';
4
+ import '../core-rDbOaJTN.js';
5
5
  import '@standard-schema/spec';
6
6
 
7
7
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@demlik/tea",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "TEA-faithful state machine substrate — one package, every host adapter. Core + subs + testing, plus React, Durable Object, Chrome extension, Node, in-memory, devtools, and property-testing modules as subpaths.",
5
5
  "keywords": [
6
6
  "tea",
@@ -130,7 +130,7 @@
130
130
  "fast-check": "^3 || ^4",
131
131
  "react": "^18 || ^19",
132
132
  "react-dom": "^18 || ^19",
133
- "vitest": "^2 || ^3",
133
+ "vitest": "^2 || ^3 || ^4 || ^5",
134
134
  "ws": "^8"
135
135
  },
136
136
  "peerDependenciesMeta": {
@@ -174,7 +174,7 @@
174
174
  "typedoc": "0.28.20",
175
175
  "typescript": "5.7.3",
176
176
  "vite": "^8.2.1",
177
- "vitest": "~3.0.7",
177
+ "vitest": "~5.0.1",
178
178
  "ws": "8.18.1",
179
179
  "zod": "^4.5.4"
180
180
  },
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/internal/engine/builtins.ts","../src/internal/engine/loop.ts"],"names":[],"mappings":"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","file":"chunk-5XU6ZJO2.js","sourcesContent":["/**\n * tea's built-ins, each one an {@link Extension} of the core loop (`./loop`,\n * #280). None is a public plugin API (#268): each engine's `run` builds them\n * from its options through {@link builtinExtensions}, which fixes their order.\n */\n\nimport type { AnyCmdDef, Identity, Port } from \"../../pure/core\";\nimport {\n checkedStep,\n cmdEdge,\n cmdEdgeOver,\n Outcome,\n structuralHash,\n} from \"../../pure/core\";\nimport type {\n Store,\n Supervision,\n TelemetryEvent,\n TelemetrySink,\n} from \"../../runtime-types\";\nimport { IdentityDropNotice, isFencedStore } from \"../../runtime-types\";\nimport type { Extension, ExtensionFactory, LoopServices } from \"./loop\";\n\ntype AnyExtension<S, M, C> = ExtensionFactory<S, M, C>;\n\n// === update middleware ===\n\n/**\n * Supervision: a throw from the fold (the reducer, or any update middleware\n * inside this one) is reported under `\"reduce\"` for every strategy, then\n * `restart` commits the host's rehydrated State with no Cmds, `escalate`\n * rethrows with the runtime still live, and `stop` (the default) halts the gate\n * and rethrows, so THIS dispatch rejects too.\n */\nexport function supervision<S, M extends { type: string }, C>(\n declared: Supervision<S, M> | undefined,\n): AnyExtension<S, M, C> {\n const policy =\n declared === undefined\n ? ({ strategy: \"stop\" } as const)\n : typeof declared === \"string\"\n ? ({ strategy: declared } as const)\n : declared;\n return (loop) => ({\n update: (next) => (state, msg) => {\n try {\n return next(state, msg);\n } catch (reduceError) {\n loop.report(reduceError, \"reduce\");\n switch (policy.strategy) {\n case \"restart\":\n // A throw inside `rehydrate` is NOT caught — it reaches the caller.\n return [policy.rehydrate(state, msg, reduceError), []];\n case \"escalate\":\n throw reduceError;\n default:\n loop.halt();\n throw reduceError;\n }\n }\n },\n });\n}\n\n/**\n * The instance-identity filter: drop a Msg addressed to a DIFFERENT instance\n * than the one this State owns, before the reducer sees it. A Msg with no\n * identity, or a State that has none yet, always proceeds. The drop is no\n * transition, and it RESOLVES the dispatch — a mis-addressed Msg is not the\n * caller's error — so it is reported as an `IdentityDropNotice` (warn by\n * default) or the caller could not tell \"applied\" from \"discarded\".\n *\n * Sits INSIDE {@link supervision}, so a throwing `ofMsg` / `ofState` is reported\n * and supervised exactly like a reducer throw (spike #264 run 3).\n */\nexport function identityFilter<S, M extends { type: string }, C>(\n identity: Identity<S, M> | undefined,\n): AnyExtension<S, M, C> {\n return (loop) => {\n if (identity === undefined) return {};\n const misaddressed = (msg: M, state: S): boolean => {\n const addressed = identity.ofMsg(msg);\n if (addressed === undefined) return false;\n const own = identity.ofState(state);\n if (own === undefined) return false;\n return structuralHash(addressed) !== structuralHash(own);\n };\n return {\n update: (next) => (state, msg) => {\n if (!misaddressed(msg, state)) return next(state, msg);\n loop.report(new IdentityDropNotice(msg.type), \"identity-drop\");\n return null;\n },\n };\n };\n}\n\n/**\n * The dev pre/post pair around the reducer (`checkedStep`): freeze the input\n * State so an in-place mutation trips, and check the returned `[state, cmds]`\n * shape. Both compile out of production. Innermost, so it sees exactly what the\n * reducer saw.\n */\nexport function devChecks<S, M extends { type: string }, C>(): AnyExtension<\n S,\n M,\n C\n> {\n return () => ({\n update: (next) => (state, msg) => checkedStep(state, msg, next),\n });\n}\n\n// === interpret middleware ===\n\n/**\n * The `Cmd.define` edge (ADR 0021): a defined Cmd's handler return is minted\n * into its `_ok` / `_err` Msg, its `ok` value parsed against the def's schema\n * and stamped with `at` from `clock`. A defined Cmd's handler failing outside\n * its declared channel goes to the sink under `\"interpret\"`; a hand-written\n * Cmd's handler throw keeps rejecting the dispatch.\n *\n * The same edge rides on ctx under `cmdEdge`, beside the `ok` / `err` builders,\n * so a handler that runs a base handler inside its own settles the base's\n * outcome through this one edge (#66).\n */\nexport function cmdDefinitions<S, M extends { type: string }, C>(\n defs: readonly AnyCmdDef[],\n clock: () => number,\n): AnyExtension<S, M, C> {\n return (loop) => {\n const settle = cmdEdgeOver(defs, clock);\n const defined = new Set(defs.map((d) => d.cmdType));\n return {\n interpret: (next) => async (cmd) => {\n const type = (cmd as { type: string }).type;\n try {\n return settle(cmd as { type: string }, await next(cmd));\n } catch (err) {\n if (!defined.has(type)) throw err;\n loop.report(err, \"interpret\");\n return undefined;\n }\n },\n ctx: { ok: Outcome.ok, err: Outcome.err, [cmdEdge]: settle },\n };\n };\n}\n\n// === store wrapper ===\n\n/**\n * Fencing is a property of the store the caller handed `run`, never a flag\n * (#143). A fenced store is read with its version at boot and every save is a\n * compare-and-swap against the last version this run saw, so a second writer\n * that took over is refused with `StoreConflictError` at this run's next save —\n * before that transition's Cmds run. A plain store passes through unchanged.\n */\nexport function fencing<S, M, C>(): AnyExtension<S, M, C> {\n return () => ({\n store: (store: Store<S>): Store<S> => {\n if (!isFencedStore(store)) return store;\n let version = 0;\n return {\n async load() {\n const read = await store.loadFenced();\n version = read.version;\n return read.raw;\n },\n async save(state) {\n version = await store.saveFenced(state, version);\n },\n migrate: (raw) => store.migrate(raw),\n };\n },\n });\n}\n\n// === ctx + handle: ports ===\n\n/**\n * Ports (`definePort`): `ctx.emit(port, value)` from a handler and\n * `emitPort` / `subscribePort` on the handle. Fanout is synchronous and\n * throw-isolated under `\"port-emit\"`; no subscribers is a no-op.\n */\nexport function ports<S, M, C>(): AnyExtension<S, M, C> {\n return (loop) => {\n const registry = new Map<Port<unknown>, Set<(value: unknown) => void>>();\n function emit<T>(port: Port<T>, value: T): void {\n const subscribers = registry.get(port as Port<unknown>);\n if (!subscribers || subscribers.size === 0) return;\n loop.fanout(subscribers, \"port-emit\", (listener) => listener(value));\n }\n return {\n ctx: { emit },\n handle: {\n subscribePort<T>(\n port: Port<T>,\n listener: (value: T) => void,\n ): () => void {\n const key = port as Port<unknown>;\n const bucket = registry.get(key) ?? new Set();\n registry.set(key, bucket);\n const erased = listener as (value: unknown) => void;\n bucket.add(erased);\n return () => {\n bucket.delete(erased);\n if (bucket.size === 0 && registry.get(key) === bucket)\n registry.delete(key);\n };\n },\n emitPort: emit,\n },\n };\n };\n}\n\n// === commit observers ===\n\n/**\n * `subscribe` (zero-arg change notifiers), then `observe` (every applied\n * transition's `(msg, state)`) or, at boot, `onBoot` (the initial State, once;\n * a handler registered after boot fires at once). Each fanout is\n * throw-isolated under its own phase.\n */\nexport function observation<S, M, C>(): AnyExtension<S, M, C> {\n return (loop) => {\n const listeners = new Set<() => void>();\n const observers = new Set<(msg: M, state: S) => void>();\n const bootHandlers = new Set<(state: S) => void>();\n let booted = false;\n return {\n commit(msg, state) {\n loop.fanout(listeners, \"listener\", (listener) => listener());\n if (msg === undefined) {\n booted = true;\n loop.fanout(bootHandlers, \"boot\", (handler) => handler(state));\n return;\n }\n loop.fanout(observers, \"observer\", (observer) => observer(msg, state));\n },\n handle: {\n subscribe: subscribeTo(listeners),\n observe: subscribeTo(observers),\n onBoot(handler: (state: S) => void): () => void {\n const state = loop.state();\n if (booted && state !== undefined) {\n loop.fanout([handler], \"boot\", (h) => h(state));\n return () => {};\n }\n return subscribeTo(bootHandlers)(handler);\n },\n },\n };\n };\n}\n\nfunction subscribeTo<T>(set: Set<T>): (item: T) => () => void {\n return (item) => {\n set.add(item);\n return () => {\n set.delete(item);\n };\n };\n}\n\n/**\n * The semantic event channel: `project` maps each APPLIED transition to zero or\n * more public events, and `on(type, handler)` receives only its own type. The\n * machine's private Msg names never reach `on`. A throw in the projector or a\n * handler is isolated under `\"event\"`. Boot projects nothing.\n */\nexport function semanticEvents<S, M, C, E extends { type: string }>(\n project: ((msg: M, state: S) => readonly E[]) | undefined,\n): AnyExtension<S, M, C> {\n return (loop) => {\n const handlers = new Map<string, Set<(event: E) => void>>();\n return {\n commit(msg, state) {\n if (msg === undefined || project === undefined || handlers.size === 0)\n return;\n let events: readonly E[];\n try {\n events = project(msg, state);\n } catch (err) {\n loop.report(err, \"event\");\n return;\n }\n for (const event of events) {\n const bucket = handlers.get(event.type);\n if (bucket === undefined) continue;\n loop.fanout(bucket, \"event\", (handler) => handler(event));\n }\n },\n handle: {\n on(type: string, handler: (event: E) => void): () => void {\n const bucket = handlers.get(type) ?? new Set();\n handlers.set(type, bucket);\n bucket.add(handler);\n return () => {\n bucket.delete(handler);\n if (bucket.size === 0 && handlers.get(type) === bucket)\n handlers.delete(type);\n };\n },\n },\n };\n };\n}\n\n/**\n * Run terminality: `result()` reads the State when `isTerminal` holds for it,\n * and `done()` resolves on the first commit (boot included — a rehydrated run\n * can land terminal) where it does. No predicate → never terminal.\n */\nexport function terminality<S, M, C>(\n isTerminal: ((state: S) => boolean) | undefined,\n): AnyExtension<S, M, C> {\n const holds = isTerminal ?? (() => false);\n return (loop) => {\n const waiters = new Set<(state: S) => void>();\n return {\n commit(_msg, state) {\n if (waiters.size === 0 || !holds(state)) return;\n const parked = [...waiters];\n waiters.clear();\n for (const resolve of parked) resolve(state);\n },\n handle: {\n result(): S | undefined {\n const current = loop.state() as S;\n return holds(current) ? current : undefined;\n },\n done(): Promise<S> {\n const current = loop.state() as S;\n if (holds(current)) return Promise.resolve(current);\n return new Promise<S>((resolve) => {\n waiters.add(resolve);\n });\n },\n },\n };\n };\n}\n\n/**\n * Telemetry (#268: `withTelemetry` moved inside tea). Every applied transition\n * hands the sink `{ seq, msgType, at }` — `seq` counts this run's applied\n * transitions from 1, `at` reads `clock`. Fire-and-forget: the loop never waits\n * on the sink, and a sink that throws or rejects reaches `onError` under\n * `\"observer\"` without touching the run.\n */\nexport function telemetry<S, M extends { type: string }, C>(\n sink: TelemetrySink | undefined,\n clock: () => number,\n): AnyExtension<S, M, C> {\n return (loop: LoopServices<S>): Extension<S, M, C> => {\n if (sink === undefined) return {};\n let seq = 0;\n return {\n commit(msg) {\n if (msg === undefined) return;\n seq += 1;\n const event: TelemetryEvent = { seq, msgType: msg.type, at: clock() };\n try {\n Promise.resolve(sink(event)).catch((err: unknown) =>\n loop.report(err, \"observer\"),\n );\n } catch (err) {\n loop.report(err, \"observer\");\n }\n },\n };\n };\n}\n\n// === assembly: the one order both engines run the built-ins in ===\n\n/** The `run` options the built-ins read. Every engine's `run` accepts them. */\nexport interface BuiltinOptions<\n S,\n M extends { type: string },\n E extends { type: string },\n> {\n readonly clock?: () => number;\n readonly events?: (msg: M, state: S) => readonly E[];\n readonly supervision?: Supervision<S, M>;\n readonly terminal?: (state: S) => boolean;\n readonly telemetry?: TelemetrySink;\n}\n\n/**\n * The built-ins, in the one order that gives them their meaning. Every engine\n * builds its loop from this list, so a machine means the same thing on each.\n *\n * Update middleware nests first-outermost: supervision wraps the identity\n * filter (a throwing `ofMsg` is supervised like a reducer throw — spike #264\n * run 3), which wraps the dev checks around the reducer itself. Commit\n * callbacks run in order: change listeners, then `observe` / `onBoot`, then\n * semantic events, then `done()` waiters, then telemetry.\n */\nexport function builtinExtensions<\n S,\n M extends { type: string },\n C,\n E extends { type: string },\n>(\n machine: {\n readonly identity?: Identity<S, M>;\n readonly cmds?: readonly AnyCmdDef[];\n },\n opts: BuiltinOptions<S, M, E>,\n): readonly ExtensionFactory<S, M, C>[] {\n const clock = opts.clock ?? Date.now;\n return [\n supervision(opts.supervision),\n identityFilter(machine.identity),\n devChecks(),\n cmdDefinitions(machine.cmds ?? [], clock),\n fencing(),\n ports(),\n observation(),\n semanticEvents(opts.events),\n terminality(opts.terminal),\n telemetry(opts.telemetry, clock),\n ];\n}\n","/**\n * The core loop both engines run (#280, spike #264). It does five things and\n * nothing else: it keeps one serial tail, folds a Msg into a transition, saves\n * before any effect, reconciles the running Subs, and runs the transition's\n * Cmds, dispatching what they return. It has no special case for any built-in.\n *\n * One loop, not one per engine (#283). The loop calls a handler and a runner\n * through erased signatures and awaits a Promise either way, so an engine's\n * own types never pass through it. The Effect engine turns each handler and\n * runner into a fiber at its own edge, run with the services the caller\n * provided, so Effect services (`R`) reach every handler without the loop\n * carrying a type slot for them — the gap spike #260's generic loop left open.\n * Sharing the loop is also what makes the two engines' traces identical: the\n * fold, the save order and the built-ins are the same code.\n *\n * Everything else `run` offers is an {@link Extension} built on five points:\n *\n * 1. `update` — middleware over the fold. Returning `null` means \"no\n * transition\": no save, no effects, no commit.\n * 2. `interpret` — middleware over one Cmd's handler call; it maps what the\n * handler returned to the Msg to dispatch, if any.\n * 3. `store` — a wrapper over the store the caller handed `run`.\n * 4. `commit` — called after a transition's effects, with the Msg and the\n * committed State. Boot passes `undefined` for the Msg.\n * 5. `ctx` — keys added to the ctx every Cmd handler receives, so a\n * handler that runs another handler inside its own can reach\n * the same machinery the loop applies to it.\n *\n * An extension may also add methods to the run handle (`handle`). Array order\n * is nesting order: the first extension's middleware is the outermost, and\n * commit callbacks run first to last. `builtinExtensions` (`./builtins`) fixes\n * that order for every engine; it is not a user-facing plugin API (#268).\n *\n * `stop()` is the core's own lifecycle, because the gate it drives is what every\n * dispatch passes through: open → draining → closed, never backwards.\n */\n\nimport type { Dispose, Sub, SubEntry } from \"../../pure/core\";\nimport { desiredSub, detachWork } from \"../../pure/core\";\nimport type {\n DispatchSettle,\n OnError,\n RuntimeErrorContext,\n RuntimeErrorPhase,\n Store,\n} from \"../../runtime-types\";\nimport {\n DispatchDiscardedError,\n DisposeTimeoutNotice,\n QuiescenceTimeoutError,\n RuntimeDiscardedError,\n RuntimeDiscardNotice,\n} from \"../../runtime-types\";\n\n// === The extension points ===\n\n/** A fold's result: the next State and the Cmds it emits. */\nexport type Transition<S, C> = readonly [S, readonly C[]];\n\n/** One fold step. `null` means the Msg produced no transition. */\nexport type Step<S, M, C> = (state: S, msg: M) => Transition<S, C> | null;\n\n/** One Cmd's handler call; resolves to the Msg to dispatch, or nothing. */\nexport type InterpretStep<C> = (cmd: C) => Promise<unknown>;\n\n/** What the loop lends an extension. */\nexport interface LoopServices<S> {\n /** Route a failure with no caller to the `onError` sink. */\n report(error: unknown, phase: RuntimeErrorPhase): void;\n /** Call `fn` for each item, routing every throw to the sink under `phase`. */\n fanout<T>(\n items: Iterable<T>,\n phase: RuntimeErrorPhase,\n fn: (item: T) => void,\n ): void;\n /** Close the gate for good: every later dispatch is refused. */\n halt(): void;\n /** The committed State, or `undefined` before boot has produced one. */\n state(): S | undefined;\n}\n\n/** One built-in, as the loop sees it. Every member is optional. */\nexport interface Extension<S, M, C> {\n readonly update?: (next: Step<S, M, C>) => Step<S, M, C>;\n readonly interpret?: (next: InterpretStep<C>) => InterpretStep<C>;\n readonly store?: (store: Store<S>) => Store<S>;\n readonly commit?: (msg: M | undefined, state: S) => void;\n readonly ctx?: object;\n readonly handle?: object;\n}\n\n/** An extension is built once per run, from the loop's services. */\nexport type ExtensionFactory<S, M, C> = (\n loop: LoopServices<S>,\n) => Extension<S, M, C>;\n\n/** A Cmd handler as the loop calls it. */\nexport type LoopHandler<M> = (\n cmd: never,\n ctx: unknown,\n dispatch: (msg: M) => void,\n) => unknown;\n\n/** A Sub runner as the loop calls it. */\nexport type LoopRunner<Ctx, M> = (\n sub: Sub,\n ctx: Ctx,\n dispatch: (msg: M) => void,\n) => Dispose;\n\n/** Everything the loop needs from `run`. */\nexport interface LoopConfig<S, M extends { type: string }, C, Ctx> {\n readonly init: (loaded: S | null, ctx: Ctx) => Transition<S, C>;\n /** The innermost fold: the machine's own `update`. */\n readonly reduce: (state: S, msg: M) => Transition<S, C>;\n readonly subs: readonly SubEntry<S>[];\n readonly runnerFor: (type: string) => LoopRunner<Ctx, M> | undefined;\n readonly handlerFor: (type: string) => LoopHandler<M> | undefined;\n readonly store: Store<S> | undefined;\n readonly ctx: Ctx;\n readonly onError: OnError | undefined;\n readonly disposeTimeoutMs: number;\n readonly idleCap: number;\n readonly extensions: readonly ExtensionFactory<S, M, C>[];\n}\n\n// === liveWork: \"can anything still transition me without a dispatch?\" ===\n//\n// The one read `driveToDone` needs that the public `Runtime` does not carry:\n// after `start`'s chain quiesces, only a live Sub or an in-flight Cmd can still\n// enqueue a transition; with neither, waiting is a leak (#68). Module-private\n// and symbol-keyed so it reaches no export (the export map is the contract —\n// MAINTAINING.md), yet enumerable so it survives the spread a wrapper does over\n// the handle.\nexport const liveWork: unique symbol = Symbol(\"demlik-tea.liveWork\");\n\n/** The runtime's own sources of a caller-less transition, counted. */\nexport interface LiveWork {\n /** Live Subs — the running `machine.subs` entries. */\n readonly subs: number;\n /** Interpret handlers currently awaiting. */\n readonly cmds: number;\n}\n\nexport interface LiveWorkProbe {\n readonly [liveWork]: () => LiveWork;\n}\n\n/** The members the core itself puts on the run handle. */\nexport interface LoopHandle<S, M> extends LiveWorkProbe {\n dispatch(msg: M, opts?: { readonly settle?: DispatchSettle }): Promise<void>;\n dispatchOnce(msg: M): Promise<void>;\n getState(): S;\n ready: Promise<unknown>;\n idle(): Promise<void>;\n stop(): Promise<void>;\n}\n\n// Default `onError` sink: re-throw on a fresh macrotask so the failure reaches\n// the host's global error handler instead of vanishing — surface, not swallow\n// (invariant 6).\n//\n// A `RuntimeDiscardNotice` is NOT a failure of the runtime's own contract — a\n// host tearing a runtime down with Cmds in flight (a React ctx change, a\n// navigation) is legal, merely lossy. Rethrowing it would make every\n// unmount-during-fetch an uncaught error for consumers who never configured a\n// sink, so the default WARNS: loud enough that the silent discard #365\n// describes cannot happen again, never fatal. A configured `onError` sees\n// `\"discard\"` like any other phase and can route or ignore it.\n//\n// The branch keys on the ERROR CLASS, never on `context.phase`: `report`\n// hands a THROWING consumer sink's own error back here with the phase it was\n// handling, so a phase-keyed branch would warn away a broken sink and re-create\n// the very silent failure this exists to remove.\nfunction defaultOnError(error: unknown, _context: RuntimeErrorContext): void {\n if (error instanceof RuntimeDiscardNotice) {\n console.warn(error);\n return;\n }\n setTimeout(() => {\n throw error;\n }, 0);\n}\n\n/**\n * Start the loop. Returns the handle synchronously; boot runs as the FIRST entry\n * on the serial tail. Save-then-effects ordering is structural: every\n * transition installs State, awaits the save, reconciles Subs, runs the Cmds,\n * then calls the commit callbacks.\n */\nexport function startLoop<S, M extends { type: string }, C, Ctx>(\n config: LoopConfig<S, M, C, Ctx>,\n): LoopHandle<S, M> {\n const { idleCap, disposeTimeoutMs } = config;\n const onError: OnError = config.onError ?? defaultOnError;\n\n // A throwing sink routes THAT throw through `defaultOnError`, with the context\n // the sink was handling, so it cannot re-create a silent failure.\n function report(error: unknown, phase: RuntimeErrorPhase): void {\n const context: RuntimeErrorContext = { phase };\n try {\n onError(error, context);\n } catch (sinkError) {\n defaultOnError(sinkError, context);\n }\n }\n\n // Throw-isolated fanout: one bad consumer never strands its siblings\n // (invariant 6). The single home for that isolation discipline.\n function fanout<T>(\n items: Iterable<T>,\n phase: RuntimeErrorPhase,\n fn: (item: T) => void,\n ): void {\n for (const item of items) {\n try {\n fn(item);\n } catch (err) {\n report(err, phase);\n }\n }\n }\n\n // `state` is late-initialized: synchronously when there is no store to read,\n // otherwise inside the boot step (the head of the tail).\n let state: S | undefined;\n let bootError: unknown = null;\n // The dispatch gate. It only ever ADVANCES — `\"open\"` → `\"draining\"` →\n // `\"closed\"` — so the discard window can never re-open under a second `stop()`.\n //\n // \"open\" — normal operation; `enqueueDispatch` accepts.\n // \"draining\" — inside `stop()`, before the tail has settled. New work is\n // still refused (refusing is what makes the drain terminate),\n // but a Msg refused HERE was discarded BY the teardown: an\n // in-flight Cmd's follow-up, a detached handler's last Msg, or a\n // Sub still live because subs are torn down after the drain. So\n // the rejection is a `DispatchDiscardedError` reported under\n // `phase: \"discard\"` — lossy, legal, warn-only.\n // \"closed\" — halted by an extension, or `stop()` has returned. A dispatch\n // refused here is a consumer using a retired runtime — a real\n // error, and it stays loud.\n let gate: \"open\" | \"draining\" | \"closed\" = \"open\";\n // Interpret handlers currently awaiting. Written only by `trackInFlight`, read\n // by `stop()` to make a mid-flight teardown LOUD instead of silent (#365).\n let inFlightCmds = 0;\n\n const services: LoopServices<S> = {\n report,\n fanout,\n halt() {\n gate = \"closed\";\n },\n state: () => state,\n };\n const extensions = config.extensions.map((make) => make(services));\n\n // The fold, the handler call and the store, each wrapped by the extensions\n // that ask for it. `reduceRight` makes the first extension the outermost.\n const update: Step<S, M, C> = extensions.reduceRight<Step<S, M, C>>(\n (next, ext) => (ext.update ? ext.update(next) : next),\n config.reduce,\n );\n const interpret: InterpretStep<C> = extensions.reduceRight<InterpretStep<C>>(\n (next, ext) => (ext.interpret ? ext.interpret(next) : next),\n callHandler,\n );\n const store: Store<S> | undefined =\n config.store === undefined\n ? undefined\n : extensions.reduceRight<Store<S>>(\n (inner, ext) => (ext.store ? ext.store(inner) : inner),\n config.store,\n );\n const commitCallbacks = extensions.flatMap((ext) =>\n ext.commit ? [ext.commit] : [],\n );\n\n // Copied into a fresh object so handlers get the contributed keys without\n // mutating the caller's ctx (which may be shared across runtimes / tests).\n // `Object.assign`, not a spread: tsc cannot prove a generic `Ctx` is an\n // object type, and a spread refuses it.\n const handlerCtx: unknown = Object.assign(\n {},\n config.ctx,\n ...extensions.map((ext) => ext.ctx ?? {}),\n { [detachWork]: detachInFlight },\n );\n\n // Every step chains onto `tail` — the single concurrency gate.\n let tail: Promise<void> = Promise.resolve();\n\n // The running Subs, keyed by their derived id. An id present here is a runner\n // that started and has not been disposed.\n const subRegistry = new Map<string, { type: string; dispose: Dispose }>();\n\n // Tear down each named sub: run its cleanup (throws routed to the sink under\n // `\"sub-cleanup\"`), then drop it from the registry regardless.\n function stopSubs(ids: Iterable<string>): void {\n for (const id of ids) {\n const running = subRegistry.get(id);\n if (running === undefined) continue;\n try {\n trackDisposal(running.dispose());\n } catch (err) {\n report(err, \"sub-cleanup\");\n }\n subRegistry.delete(id);\n }\n }\n\n // Teardown work that returned a Promise and has not settled. Reconcile runs\n // inside the synchronous transition path (invariant 2) and cannot await a\n // cleanup, so every async disposal is remembered here the moment it starts,\n // and `stop()` drains the set before resolving: `await runtime.stop()` then\n // means \"teardown is done\", the only reading a host evicting an isolate can\n // act on. The rejection is attached HERE, once, so it can never become an\n // unhandled rejection.\n const pendingDisposals = new Set<Promise<void>>();\n\n function trackDisposal(result: void | Promise<void>): void {\n if (!(result instanceof Promise)) return;\n const settling: Promise<void> = result\n .catch((err) => {\n report(err, \"sub-cleanup\");\n })\n .finally(() => {\n pendingDisposals.delete(settling);\n });\n pendingDisposals.add(settling);\n }\n\n /**\n * Wait for the disposals started so far, bounded by `disposeTimeoutMs`. On\n * expiry report a `DisposeTimeoutNotice` and return anyway — `stop()` resolves\n * regardless (contract), and a release that never settles must not become a\n * host that never shuts down.\n */\n async function drainDisposals(): Promise<void> {\n if (pendingDisposals.size === 0) return;\n const outstanding = [...pendingDisposals];\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const outcome = await Promise.race([\n Promise.all(outstanding).then(() => \"settled\" as const),\n new Promise<\"timeout\">((resolve) => {\n timer = setTimeout(() => resolve(\"timeout\"), disposeTimeoutMs);\n }),\n ]);\n if (outcome === \"timeout\") {\n report(\n new DisposeTimeoutNotice(outstanding.length, disposeTimeoutMs),\n \"discard\",\n );\n }\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n }\n\n /**\n * Reconcile the running Subs against `state`, after every save. An id already\n * running is left alone, a running id no longer wanted is stopped, and a\n * wanted id not yet running is started through its runner.\n *\n * Every failure is collected, never thrown mid-pass, so one bad entry cannot\n * strand its siblings: a throwing `deps` leaves the running Subs of that type\n * untouched, and a throwing runner is not registered. The first error is\n * thrown after the pass. A Sub type with no runner is a failure too — nothing\n * would ever start it.\n */\n function reconcileSubs(): void {\n let firstError: unknown = null;\n const remember = (err: unknown): void => {\n if (firstError === null) firstError = err;\n };\n const desired = new Map<string, Sub>();\n // Types whose entry could not be read this pass: keep what is running.\n const unreadTypes = new Set<string>();\n for (const entry of config.subs) {\n try {\n const sub = desiredSub(entry, state as S);\n if (sub !== null) desired.set(sub.id, sub);\n } catch (err) {\n remember(err);\n unreadTypes.add(entry.type);\n }\n }\n\n stopSubs(\n [...subRegistry].flatMap(([id, running]) =>\n desired.has(id) || unreadTypes.has(running.type) ? [] : [id],\n ),\n );\n\n for (const [id, sub] of desired) {\n if (subRegistry.has(id)) continue;\n const runner = config.runnerFor(sub.type);\n if (runner === undefined) {\n remember(\n new Error(\n `@demlik/tea: no subscribe runner for Sub type \"${sub.type}\". ` +\n \"Pass one to run in `subscribe`.\",\n ),\n );\n continue;\n }\n try {\n // A runner's dispatch is unawaited by construction, so it gets the\n // wrapped form, which also queues the transition behind this step —\n // never on the runner's own stack (spike #260).\n const dispose = runner(sub, config.ctx, dispatchUnawaited);\n subRegistry.set(id, { type: sub.type, dispose });\n } catch (err) {\n remember(err);\n }\n }\n if (firstError !== null) throw firstError;\n }\n\n // The ONE `(msg) => void` handed to every producer that cannot await its own\n // dispatch: a Cmd handler's late tail, a Sub runner. Its rejection has no\n // caller, so it routes to the sink with the phase derived from the error\n // class (`reportUndelivered`). Handing those sites the raw `enqueueDispatch`\n // would turn a Sub firing during `stop()`'s drain into an unhandled rejection\n // the sink never sees.\n function dispatchUnawaited(msg: M): void {\n enqueueDispatch(msg).catch(reportUndelivered);\n }\n\n // A `DispatchDiscardedError` means the teardown refused the Msg (`\"discard\"`,\n // warn-only); anything else is a genuine failure of the follow-up itself.\n function reportUndelivered(error: unknown): void {\n report(\n error,\n error instanceof DispatchDiscardedError ? \"discard\" : \"follow-up\",\n );\n }\n\n /**\n * Count a handler's work as in flight for exactly the lifetime of its\n * promise. `finally`-balanced, so a rejecting handler can never strand the\n * count above zero. A handler that throws SYNCHRONOUSLY never reaches here,\n * which is correct — it was never in flight.\n */\n function trackInFlight<T>(work: T | Promise<T>): Promise<T> {\n inFlightCmds++;\n return Promise.resolve(work).finally(() => {\n inFlightCmds--;\n });\n }\n\n /**\n * Enlist work a handler will NOT return — the `detachWork` key on ctx. A\n * handler that fans out inside its own Cmd (ADR 0018) returns before its\n * effect finishes; handing the promise here puts it back on both accountings\n * a returned promise had: `trackInFlight`, so `stop()` still reports it, and\n * the serial `tail`, so `idle()` drains it. The tail extension is\n * SYNCHRONOUS, so a `drainToQuiescence` that already read `tail` sees the\n * reference change and loops again.\n */\n function detachInFlight(work: Promise<unknown>): void {\n const tracked = trackInFlight(work);\n tracked.catch(reportUndelivered);\n tail = tail\n .then(async () => {\n await tracked;\n })\n .catch(() => {});\n }\n\n // The innermost interpret step: call the Cmd's handler, if one is wired. A\n // missing handler returns nothing, which is invariant 6's forward progress\n // for a miswired consumer.\n function callHandler(cmd: C): Promise<unknown> {\n const handler = config.handlerFor((cmd as { type: string }).type);\n if (handler === undefined) return Promise.resolve(undefined);\n return trackInFlight(handler(cmd as never, handlerCtx, dispatchUnawaited));\n }\n\n /**\n * Run each emitted Cmd. A returned Msg is enqueued onto the tail, never\n * dispatched re-entrantly. The first error stops further handlers in this\n * transition.\n *\n * THIS LOOP IS SERIAL BY RULING, NOT BY OVERSIGHT (ADR 0018). It runs a\n * transition's Cmds one at a time and never interleaves two handlers, so the\n * Msgs it enqueues fold in Cmd-EMISSION order and a replayed log reproduces\n * the same fold — invariant 2's serializability. Overlap in time belongs\n * INSIDE one Cmd's handler, never across the Cmds of one transition.\n */\n async function runCmds(cmds: readonly C[]): Promise<void> {\n for (const cmd of cmds) {\n const follow = await interpret(cmd);\n if (follow !== undefined && follow !== null) {\n enqueueDispatch(follow as M).catch(reportUndelivered);\n }\n }\n }\n\n // Install State, then save → reconcile subs → run Cmds → commit callbacks.\n // Save-before-effects is the hard ordering; tests pin it. Boot and every\n // transition end here; boot passes no Msg.\n async function commit(\n next: S,\n msg: M | undefined,\n cmds: readonly C[],\n ): Promise<void> {\n state = next;\n if (store) await store.save(next);\n reconcileSubs();\n await runCmds(cmds);\n for (const callback of commitCallbacks) callback(msg, next);\n }\n\n // One full transition. A `null` from the fold is no transition at all, and\n // it RESOLVES the dispatch. A throw from the fold rejects it.\n async function stepDispatch(msg: M): Promise<void> {\n if (state === undefined) {\n throw new Error(\"@demlik/tea: runtime not booted\");\n }\n const transition = update(state, msg);\n if (transition === null) return;\n await commit(transition[0], msg, transition[1]);\n }\n\n // With NO store there is nothing to read, so `init` runs IMMEDIATELY and\n // `getState()` is observable synchronously (what `useSyncExternalStore`\n // consumers need to render the first commit without a flicker); an `init`\n // throw leaves `run` itself. With a store, the read is the first thing boot\n // awaits; a `load` or `migrate` throw rejects `ready`.\n const storeless: Transition<S, C> | null =\n store === undefined ? config.init(null, config.ctx) : null;\n if (storeless !== null) state = storeless[0];\n\n async function boot(): Promise<void> {\n // Boundary parse (invariant 8): `store.load()` returns `unknown`;\n // `store.migrate(raw)` is the required parse — `S` on a recognized shape,\n // `null` on an unrecognized one (boots fresh).\n const [initial, cmds] =\n storeless ??\n config.init(\n (store as Store<S>).migrate(await (store as Store<S>).load()),\n config.ctx,\n );\n await commit(initial, undefined, cmds);\n }\n\n /**\n * Enqueue a dispatch on the tail — the single gate re-entrant handler and\n * runner calls also go through. Rejects when the gate is not open (with a\n * `DispatchDiscardedError` while `stop()` drains, a plain stopped Error once\n * it has), boot failed, or the fold / save / sub start / handler throws.\n */\n function enqueueDispatch(msg: M): Promise<void> {\n if (gate !== \"open\") {\n return Promise.reject(\n gate === \"draining\"\n ? new DispatchDiscardedError(msg.type)\n : new Error(\"@demlik/tea: runtime stopped\"),\n );\n }\n const next = tail.then(() => {\n if (bootError !== null) throw bootError;\n return stepDispatch(msg);\n });\n // Swallow the rejection on the tail so one failing dispatch does NOT poison\n // every later one. The original `next` still rejects for the caller.\n tail = next.catch(() => {});\n return next;\n }\n\n /**\n * Drain the serial tail to quiescence. Every follow-up reassigns `tail`\n * SYNCHRONOUSLY before its parent step resolves, so awaiting the current\n * `tail` and re-reading it catches every transitively enqueued follow-up.\n * Bounded by `idleCap` — on cap REJECT with `QuiescenceTimeoutError`, keeping\n * a livelock distinguishable from quiescence (invariant 6).\n */\n async function drainToQuiescence(): Promise<void> {\n for (let i = 0; i < idleCap; i++) {\n const observed = tail;\n await observed;\n if (tail === observed) return;\n }\n throw new QuiescenceTimeoutError(idleCap);\n }\n\n /**\n * The public `dispatch`: await the Msg's OWN transition first (so its own\n * failure surfaces before any drain), then — unless `{ settle: \"once\" }` —\n * drain the transitive follow-up chain.\n */\n async function dispatchToQuiescence(\n msg: M,\n opts?: { readonly settle?: DispatchSettle },\n ): Promise<void> {\n await enqueueDispatch(msg);\n if (opts?.settle === \"once\") return;\n await drainToQuiescence();\n }\n\n // `bootPromise` is the un-swallowed promise `ready` chains off (so callers\n // see the boot error directly); `tail` gets the swallowed branch so a failed\n // boot does not poison every later dispatch's chain (each surfaces\n // `bootError` in `enqueueDispatch`).\n const bootPromise = boot();\n tail = bootPromise.catch((err) => {\n bootError = err;\n });\n\n const core: LoopHandle<S, M> = {\n [liveWork]: () => ({ subs: subRegistry.size, cmds: inFlightCmds }),\n dispatch: dispatchToQuiescence,\n dispatchOnce: enqueueDispatch,\n getState(): S {\n // TOTAL. A booted handle is only obtainable by awaiting `ready`, which\n // resolves AFTER boot set `state`.\n return state as S;\n },\n // A failed boot never hands out a handle. The forward reference resolves\n // at `.then` time, after `handle` below is built.\n ready: bootPromise.then(() => handle),\n idle: drainToQuiescence,\n async stop(): Promise<void> {\n // Open the discard window unless the gate is already `\"closed\"` (a halt,\n // or a redundant second `stop()`): the gate only advances.\n if (gate === \"open\") gate = \"draining\";\n // Report BEFORE the drain: after `await tail` the count is zero by\n // construction, and the fact worth surfacing is what was outstanding at\n // the moment the host let go (#365).\n if (inFlightCmds > 0) {\n report(new RuntimeDiscardedError(inFlightCmds), \"discard\");\n }\n // Drain in-flight work. Tail rejections were swallowed at enqueue time, so\n // this always resolves. It TERMINATES because the gate refuses the\n // follow-ups those handlers return.\n await tail;\n gate = \"closed\";\n stopSubs([...subRegistry.keys()]);\n // …then WAIT for the teardown still settling, bounded by\n // `disposeTimeoutMs` (see `drainDisposals`).\n await drainDisposals();\n // Flush final State. A save throw does not reject `stop()` (contract:\n // resolves regardless) but IS loss of the last write, so it reaches the\n // sink (invariant 6).\n if (store && state !== undefined && bootError === null) {\n try {\n await store.save(state);\n } catch (error) {\n report(error, \"stop-save\");\n }\n }\n },\n };\n const handle: LoopHandle<S, M> = Object.assign(\n core,\n ...extensions.map((ext) => ext.handle ?? {}),\n );\n return handle;\n}\n"]}