@demlik/tea 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.js +8 -8
- package/dist/authed-call/index.d.ts +2 -2
- package/dist/authed-call/index.js +5 -5
- package/dist/await-terminal/index.d.ts +1 -1
- package/dist/await-terminal/index.js +2 -2
- package/dist/batch-window/index.d.ts +2 -2
- package/dist/batch-window/index.js +3 -3
- package/dist/cache/index.d.ts +2 -2
- package/dist/{chunk-EHIX7NYZ.js → chunk-5KVHID6A.js} +4 -4
- package/dist/{chunk-EHIX7NYZ.js.map → chunk-5KVHID6A.js.map} +1 -1
- package/dist/{chunk-27N2SPTN.js → chunk-6GIGQT6C.js} +4 -4
- package/dist/{chunk-27N2SPTN.js.map → chunk-6GIGQT6C.js.map} +1 -1
- package/dist/{chunk-3XJWC7VW.js → chunk-6OUDPSSG.js} +3 -3
- package/dist/{chunk-3XJWC7VW.js.map → chunk-6OUDPSSG.js.map} +1 -1
- package/dist/{chunk-MMH7CW5K.js → chunk-6VDCTR7N.js} +24 -27
- package/dist/chunk-6VDCTR7N.js.map +1 -0
- package/dist/{chunk-QLA76GIT.js → chunk-DYRKDCJI.js} +30 -5
- package/dist/chunk-DYRKDCJI.js.map +1 -0
- package/dist/{chunk-LP7VQBRG.js → chunk-EGWTT5XJ.js} +3 -3
- package/dist/{chunk-LP7VQBRG.js.map → chunk-EGWTT5XJ.js.map} +1 -1
- package/dist/{chunk-DILMKOZ7.js → chunk-EMJTPHI7.js} +4 -4
- package/dist/{chunk-DILMKOZ7.js.map → chunk-EMJTPHI7.js.map} +1 -1
- package/dist/{chunk-5DEQ5I3U.js → chunk-FOGL3TC7.js} +3 -3
- package/dist/{chunk-5DEQ5I3U.js.map → chunk-FOGL3TC7.js.map} +1 -1
- package/dist/{chunk-SQTCS44W.js → chunk-G2U7DJHL.js} +3 -3
- package/dist/{chunk-SQTCS44W.js.map → chunk-G2U7DJHL.js.map} +1 -1
- package/dist/{chunk-ON24RKSE.js → chunk-IK4S5DJN.js} +3 -3
- package/dist/{chunk-ON24RKSE.js.map → chunk-IK4S5DJN.js.map} +1 -1
- package/dist/{chunk-PS23H6KQ.js → chunk-M6UZT7HS.js} +3 -3
- package/dist/{chunk-PS23H6KQ.js.map → chunk-M6UZT7HS.js.map} +1 -1
- package/dist/{chunk-K7Q23EQ4.js → chunk-SKAMZKTE.js} +3 -3
- package/dist/{chunk-K7Q23EQ4.js.map → chunk-SKAMZKTE.js.map} +1 -1
- package/dist/{chunk-S3FQN4SL.js → chunk-VFGKTGGC.js} +7 -7
- package/dist/{chunk-S3FQN4SL.js.map → chunk-VFGKTGGC.js.map} +1 -1
- package/dist/{chunk-7W4LMPUW.js → chunk-ZDRN3YCE.js} +4 -4
- package/dist/{chunk-7W4LMPUW.js.map → chunk-ZDRN3YCE.js.map} +1 -1
- package/dist/{core-BoB6t7dB.d.ts → core-BmlJOvxM.d.ts} +10 -1
- package/dist/deadline/index.d.ts +2 -2
- package/dist/deadline/index.js +3 -3
- package/dist/do/index.d.ts +451 -335
- package/dist/do/index.js +172 -144
- package/dist/do/index.js.map +1 -1
- package/dist/{durable-effects-CO9Uh971.d.ts → durable-effects-9LxFpmh3.d.ts} +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/extension/index.js +3 -3
- package/dist/extension/react.js +3 -3
- package/dist/extension/subs/index.d.ts +2 -2
- package/dist/fan-out/index.d.ts +1 -1
- package/dist/idempotent-intake/index.d.ts +1 -1
- package/dist/index.d.ts +20 -3
- package/dist/index.js +2 -2
- package/dist/llm-call/index.d.ts +2 -2
- package/dist/llm-call/index.js +5 -5
- package/dist/machine-viz/index.d.ts +1 -1
- package/dist/machine-viz/index.js +2 -2
- package/dist/mem/index.d.ts +1 -1
- package/dist/monitored-run/index.d.ts +2 -2
- package/dist/monitored-run/index.js +5 -5
- package/dist/node/index.d.ts +1 -1
- package/dist/paginated-walk/index.d.ts +2 -2
- package/dist/paginated-walk/index.js +5 -5
- package/dist/parity/index.d.ts +1 -1
- package/dist/parity/index.js +3 -3
- package/dist/pbt/arbitraries/index.d.ts +1 -1
- package/dist/pbt/index.d.ts +1 -1
- package/dist/pbt/index.js +3 -3
- package/dist/pbt/runners/index.d.ts +1 -1
- package/dist/pbt/runners/index.js +3 -3
- package/dist/poller/index.d.ts +2 -2
- package/dist/poller/index.js +3 -3
- package/dist/prediction/index.d.ts +1 -1
- package/dist/prediction/index.js +2 -2
- package/dist/pure/index.d.ts +1 -1
- package/dist/pure/index.js +2 -2
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +2 -2
- package/dist/reconciler/index.d.ts +2 -2
- package/dist/reconciler/index.js +5 -5
- package/dist/recorder/index.d.ts +1 -1
- package/dist/resilient-call/index.d.ts +2 -2
- package/dist/resilient-call/index.js +4 -4
- package/dist/saga/index.d.ts +1 -1
- package/dist/snapshot/index.d.ts +1 -1
- package/dist/snapshot/index.js +3 -3
- package/dist/subs/index.d.ts +2 -2
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +2 -2
- package/dist/throttled-input/index.d.ts +2 -2
- package/dist/throttled-input/index.js +4 -4
- package/dist/token-refresh/index.d.ts +1 -1
- package/dist/token-refresh/index.js +3 -3
- package/dist/trace-replay/index.d.ts +1 -1
- package/dist/trace-replay/index.js +3 -3
- package/dist/{types-B-8bcAp8.d.ts → types-BoUWgwSm.d.ts} +1 -1
- package/dist/with-deadline/index.d.ts +1 -1
- package/dist/with-deadline/index.js +2 -2
- package/dist/with-resilience/index.d.ts +2 -2
- package/dist/with-resilience/index.js +4 -4
- package/dist/with-telemetry/index.d.ts +1 -1
- package/dist/with-telemetry/index.js +2 -2
- package/dist/work-queue/adapter.d.ts +1 -1
- package/dist/work-queue/index.d.ts +1 -1
- package/dist/work-queue/ops.d.ts +1 -1
- package/dist/workflow/index.d.ts +3 -3
- package/package.json +1 -19
- package/dist/chunk-6RGBKODV.js +0 -268
- package/dist/chunk-6RGBKODV.js.map +0 -1
- package/dist/chunk-MMH7CW5K.js.map +0 -1
- package/dist/chunk-QLA76GIT.js.map +0 -1
- package/dist/chunk-VLGARN6H.js +0 -202
- package/dist/chunk-VLGARN6H.js.map +0 -1
- package/dist/raft/demo.d.ts +0 -105
- package/dist/raft/demo.js +0 -172
- package/dist/raft/demo.js.map +0 -1
- package/dist/raft/index.d.ts +0 -548
- package/dist/raft/index.js +0 -7
- package/dist/raft/index.js.map +0 -1
- package/dist/raft/sim.d.ts +0 -222
- package/dist/raft/sim.js +0 -8
- package/dist/raft/sim.js.map +0 -1
- package/dist/raft/timeline.d.ts +0 -46
- package/dist/raft/timeline.js +0 -62
- package/dist/raft/timeline.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["state","opts"],"mappings":"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","file":"chunk-MMH7CW5K.js","sourcesContent":["/**\n * @demlik/tea — TEA-faithful state machine substrate.\n *\n * This module exposes the full Locked API Surface from the PRD as types and\n * implements `defineMachine` (identity, task 1), `run` (task 2), and\n * `replay` + `tryInterpret` (task 3).\n *\n * `replay` is the pure unit-test tool: it composes `init` + `update` only,\n * never touches `Store`, never calls `interpret`, never starts subscriptions.\n *\n * `tryInterpret` is Railway sugar — wraps a fallible async function and routes\n * Ok/Err to two Msg constructors via better-result.\n */\n\nimport { Result } from \"better-result\";\nimport type {\n Machine,\n Port,\n PortEmitter,\n Reducer,\n Sub,\n Transitions,\n} from \"./pure/core\";\nimport {\n __DEV__,\n applyCell,\n assertPureResult,\n type Cmd,\n deepFreeze,\n detectUpdateForm,\n foldUpdates,\n} from \"./pure/core\";\n\nexport type {\n ContextFree,\n Interpret,\n Machine,\n NoCtx,\n Port,\n PortEmitter,\n Reducer,\n Sub,\n SubId,\n Subscribe,\n SyncReturn,\n Transitions,\n UpdateForm,\n} from \"./pure/core\";\n// Re-export the pure-core surface so the root `@demlik/tea` entry is unchanged\n// (additive; the runtime-free guarantee lives on `@demlik/tea/pure`).\nexport {\n applyCell,\n Cmd,\n detectUpdateForm,\n foldMsgs,\n formOf,\n msgKeysOf,\n subId,\n} from \"./pure/core\";\n\n// The nominal brand minted ONLY through the validated construction path\n// (`asReducer` / `defineMachine`). Mirrors `retry-backoff`'s `RngBrand`: a raw\n// record of handlers is a structural `Reducer<S, M, C>`, but a `Reducer` that\n// also carries this phantom brand is one that has passed through the single\n// minting boundary. The brand is an *optional* phantom so the structural\n// `Reducer<S, M, C>` annotation form (`const update: Reducer<...> = { ... }`)\n// keeps accepting plain object literals — the guard that actually rejects an\n// async reducer is `SyncReturn`'s non-thenable return, enforced on every cell.\ndeclare const ReducerBrand: unique symbol;\n\n// A `Reducer` (or `Transitions`) that has been minted through `asReducer` /\n// `defineMachine`. Carries the phantom brand; otherwise identical to its\n// structural counterpart.\nexport type Branded<T> = T & { readonly [ReducerBrand]: true };\n\n// === absurd: compile-time exhaustiveness assertion ===\n//\n// Use in default branches of switches over discriminated unions where every\n// case should be handled explicitly. TS narrows the operand to `never` only\n// if every variant has been covered upstream; adding a new variant produces\n// a compile error at the `absurd(x)` site.\n//\n// The substrate's `Reducer<S, M, C>` and `Transitions<S, M, C>` forms cover\n// the reducer-side exhaustiveness with mapped types — no `absurd` needed\n// there. This helper exists for the OTHER boundaries where a discriminated\n// union is narrowed by hand: Sub handlers mapping a wire-protocol\n// discriminant to flat Msg variants, message-bridge dispatchers, port\n// fanouts, etc. Without it, consumers redeclare the same 3-line helper\n// across files; with it, the substrate owns the one canonical version.\n//\n// Strengthens invariant 7 (identity is explicit).\n/**\n * Compile-time exhaustiveness assertion. Use in default branches of\n * switches over discriminated unions where every case should be handled\n * explicitly — TS narrows the type to `never` only if every variant has\n * been covered. Strengthens invariant 7 (identity is explicit).\n *\n * @example\n * switch (msg.type) {\n * case \"a\": return ...;\n * case \"b\": return ...;\n * default: return absurd(msg); // compile error if a \"c\" is added\n * }\n */\nexport function absurd(x: never): never {\n throw new Error(`unreachable: ${JSON.stringify(x)}`);\n}\n\n// === noop: the explicit \"this cell ignores this msg\" helper ===\n//\n// Elm's fallback for unhandled (state × msg) pairs is implicit:\n// `_ -> (model, Cmd.none)` in a case expression. We make it explicit because\n// implicit no-ops are silent regressions waiting to happen — a future Msg\n// variant that *should* be handled gets swallowed by the wildcard.\n//\n// Naming the no-op forces the author to *decide*: \"this cell does nothing\"\n// is a real decision, distinct from \"I forgot to write this cell.\" The\n// mapped type rejects the second case; `noop` names the first.\n//\n// Assignable to any Transitions cell regardless of S/M/C concrete types\n// (the type parameters are free) — drop it into any cell without per-type\n// casts.\nexport const noop = <S, M, C extends Cmd>(\n state: S,\n _msg: M,\n): readonly [S, readonly C[]] => [state, []];\n\n// Module-level registry — process-scope, identity-by-name. The set is the\n// runtime witness that every port name in this process is unique. Cleared\n// only by the test-only `__resetPortRegistry()` helper.\nconst definedPortNames = new Set<string>();\n\n/**\n * Thrown by `definePort` when a name has already been registered in the\n * current process. Symmetric with the `Sub.id` collision assert in\n * `reconcileSubs` — both mechanize invariant 7 (identity is explicit) at the\n * runtime layer the type system cannot reach (string names compared at\n * runtime, not at compile time).\n */\nexport class PortNameCollisionError extends Error {\n override readonly name = \"PortNameCollisionError\";\n constructor(portName: string) {\n super(\n `definePort: a port named \"${portName}\" was already defined. ` +\n `Each definePort call must use a unique name. ` +\n `If two modules need the same port, export it from one module and import it.`,\n );\n }\n}\n\n/**\n * Test-only escape hatch — clears the module-level port-name registry so the\n * collision assert in `definePort` doesn't false-positive across vitest's\n * non-isolated runs. Production code MUST NOT call this; the registry is the\n * invariant-7 witness for the process lifetime.\n *\n * @internal test-only\n */\nexport function __resetPortRegistry(): void {\n definedPortNames.clear();\n}\n\n// === Runtime error sink (invariant 6 — no silent failures) ===\n//\n// The runtime has points where a failure has no natural caller to reject at:\n// a follow-up Msg an interpret handler enqueued (the original dispatcher\n// already resolved), and the final `store.save` during `stop()` (whose\n// contract is a resolved Promise). Historically both were swallowed —\n// `.catch(() => {})` and a bare `console.error` — which violates invariant 6:\n// the runtime must be inspectable; no silent failures.\n//\n// `RuntimeErrorContext` tags WHICH swallowed path produced the error so a sink\n// can route by phase. `OnError` is the sink itself, configured at `run(...)`.\n// When no sink is provided the runtime uses `defaultOnError`, which RE-THROWS\n// on a fresh macrotask — surfacing to the host's unhandled-rejection / global\n// error handler rather than vanishing. The default surfaces; it never swallows.\n\n/**\n * Which otherwise-unattributable runtime path produced an error.\n *\n * - `\"follow-up\"` — a follow-up Msg an interpret handler returned rejected\n * when re-dispatched. The original dispatcher already resolved, so this\n * rejection has no caller; without the sink it was swallowed.\n * - `\"stop-save\"` — the final `store.save(state)` inside `stop()` threw.\n * `stop()` resolves regardless (its contract), so without the sink this was\n * silent loss of the last write.\n * - `\"reduce\"` — the pure `update` (reducer) threw synchronously while folding\n * a Msg. #71's reentrancy brand makes a reducer provably synchronous, so this\n * throw is catchable in the dispatch loop; the configured `Supervision`\n * strategy decides what the runtime does next (`stop` / `escalate` /\n * `restart`), but the failure is ALWAYS surfaced here as data first.\n */\nexport type RuntimeErrorPhase = \"follow-up\" | \"stop-save\" | \"reduce\";\n\n/** Context handed to an `OnError` sink alongside the error itself. */\nexport interface RuntimeErrorContext {\n readonly phase: RuntimeErrorPhase;\n}\n\n/**\n * Sink for runtime failures that have no caller to reject at. Configured via\n * `run(machine, { onError })`. Should be total — a throw inside the sink is\n * itself surfaced via `defaultOnError`, so the sink can never re-introduce a\n * silent failure.\n */\nexport type OnError = (error: unknown, context: RuntimeErrorContext) => void;\n\n/**\n * Default `onError` sink: re-throw on a fresh macrotask so the failure reaches\n * the host's global error / unhandledRejection handler instead of vanishing.\n * Used whenever `run(...)` is called without an explicit `onError`. The least\n * surprising default — it surfaces rather than swallows (invariant 6) without\n * forcing every caller to wire a sink.\n */\nfunction defaultOnError(error: unknown, _context: RuntimeErrorContext): void {\n setTimeout(() => {\n throw error;\n }, 0);\n}\n\n// === Supervision: declared policy for a reducer throw (ADR 0003 #4) ===\n//\n// The resilience kit (retry, circuit-breaker, …) covers *effect* failures —\n// throws inside `interpret`. A throw inside the pure `update` (the reducer) had\n// no declared policy: it simply rejected the dispatch promise and left the\n// runtime in an ambiguous \"did we halt or not?\" state. #71's reentrancy brand\n// makes a reducer provably synchronous, so a reducer throw is now a synchronous\n// throw catchable in the dispatch loop. This is the seam Akka supervisor\n// strategies / Erlang-OTP supervision trees occupy: \"let it crash\" becomes\n// CONFIG, not hope.\n//\n// Every strategy routes the failure to the `onError` sink with `phase:\n// \"reduce\"` FIRST (invariant 6 — errors are data, never silently swallowed),\n// then differs in what the runtime does next:\n//\n// - `stop` — halt the runtime. State is NOT advanced; the dispatch promise\n// rejects with the reducer error; every subsequent dispatch\n// rejects (\"runtime stopped\"). The SAFE DEFAULT: a reducer that\n// violated its own invariants does not get to keep folding.\n// Never a silent resume.\n// - `escalate`— surface via `onError` AND propagate: the dispatch promise\n// rejects with the reducer error so the failure bubbles to the\n// caller/parent supervisor. The runtime is NOT halted (a parent\n// may choose to keep dispatching) — escalation is \"tell someone\n// above me\", not \"die\".\n// - `restart` — re-initialize from a host-provided last-known-good state and\n// keep folding. The core does NOT own snapshot logic; the host\n// supplies `rehydrate()`, the core invokes it, installs its\n// result as the new state, and continues the transition\n// (save → reconcile → interpret → fire) from there. The\n// original failure is still surfaced as data via `onError`.\n//\n// The restart rehydrate is a host callback by design: the core has no opinion\n// on where last-known-good state lives (a snapshot Store, an in-memory cache, a\n// recomputed default). Mirrors how `Store` keeps persistence out of core.\n\n/** The three declared reducer-throw supervision strategies. */\nexport type SupervisionStrategy = \"stop\" | \"escalate\" | \"restart\";\n\n/**\n * Declared supervision policy for a reducer (`update`) throw, configured at\n * `run(machine, { supervision })`.\n *\n * - `{ strategy: \"stop\" }` — halt + surface (the safe default; also the bare\n * shorthand `\"stop\"`).\n * - `{ strategy: \"escalate\" }` — surface + propagate, runtime stays live (also\n * the bare shorthand `\"escalate\"`).\n * - `{ strategy: \"restart\", rehydrate }` — surface, then re-init from the\n * host-supplied last-known-good `S` and keep folding. `rehydrate` receives\n * the failing `(state, msg, error)` so the host can log / pick a snapshot;\n * it MUST return a valid `S` synchronously (the reducer is synchronous, so\n * recovery is too — no suspension across the single-writer slot).\n *\n * The bare-string shorthands (`\"stop\"` / `\"escalate\"`) are accepted for the two\n * strategies that need no host data; `restart` MUST be the object form because\n * it carries the `rehydrate` callback.\n */\nexport type Supervision<S, M extends { type: string }> =\n | \"stop\"\n | \"escalate\"\n | { readonly strategy: \"stop\" }\n | { readonly strategy: \"escalate\" }\n | {\n readonly strategy: \"restart\";\n /**\n * Host-provided rehydration to last-known-good state. Invoked by the core\n * when the reducer throws; its return value becomes the new state and the\n * transition continues from there. Receives the pre-throw `state`, the\n * `msg` that triggered the throw, and the thrown `error` so the host can\n * route by cause.\n */\n readonly rehydrate: (state: S, msg: M, error: unknown) => S;\n };\n\n/**\n * Normalize the `Supervision` config (bare string or object) into a single\n * object shape so the dispatch loop branches on `.strategy` once. The default\n * — applied when `run(...)` is called without `supervision` — is `stop`: the\n * explicit, safe choice. It surfaces via `onError` and halts; it never silently\n * resumes a machine whose reducer just violated an invariant (invariant 6).\n */\ntype NormalizedSupervision<S, M extends { type: string }> =\n | { readonly strategy: \"stop\" }\n | { readonly strategy: \"escalate\" }\n | {\n readonly strategy: \"restart\";\n readonly rehydrate: (state: S, msg: M, error: unknown) => S;\n };\n\nfunction normalizeSupervision<S, M extends { type: string }>(\n supervision: Supervision<S, M> | undefined,\n): NormalizedSupervision<S, M> {\n if (supervision === undefined) return { strategy: \"stop\" };\n if (typeof supervision === \"string\") return { strategy: supervision };\n return supervision;\n}\n\n/**\n * Raised by `idle()` when the quiescence wait hits its iteration cap without\n * the dispatch tail stabilizing. Replaces the old silent fall-through-resolve\n * (which made \"the loop quiesced\" indistinguishable from \"we gave up\"):\n * `idle()` now REJECTS with this so a livelocking machine surfaces instead of\n * masquerading as quiescent.\n *\n * Mechanizes invariant 6 (no silent failures) at the runtime layer — the same\n * role `PortNameCollisionError` plays for invariant 7.\n */\nexport class QuiescenceTimeoutError extends Error {\n override readonly name = \"QuiescenceTimeoutError\";\n constructor(public readonly iterations: number) {\n super(\n `@demlik/tea: idle() did not reach quiescence after ${iterations} ` +\n `iterations — the dispatch tail kept advancing. The machine is ` +\n `likely livelocking (an interpret handler enqueues a follow-up Msg ` +\n `that enqueues another, without end). Bound the follow-up chain in ` +\n `the reducer, or stop the runtime.`,\n );\n }\n}\n\n/**\n * Define a typed port. `name` is metadata (shown in devtools / debug logs);\n * port identity is by reference. Each definePort call must use a unique\n * `name` — a second call with the same name throws\n * `PortNameCollisionError`. If two modules need the same port, export it\n * from one module and import it from the other.\n *\n * Use ports for \"data leaving the runtime selectively\" — e.g. cursor\n * announcements, DOM-mutation broadcasts, telemetry events that should NOT be\n * folded into State and should NOT be observed by every transition listener.\n *\n * Strengthens invariant 7 (identity is explicit) — the runtime assert is the\n * Port-side counterpart of `SubId`'s id collision check in `reconcileSubs`.\n */\nexport function definePort<T>(name: string): Port<T> {\n // HMR skip — Vite / dev servers re-import the module on hot reload, which\n // would otherwise false-positive on every definePort across reloads. The\n // try/catch is load-bearing for service-worker bundles: Vite polyfills\n // `import.meta.url` via `document.baseURI`, which throws ReferenceError in\n // an MV3 service worker. A throw here would abort SW install (Chrome\n // surfaces it as \"registration failed status 15\"). Catching it = \"no HMR\n // detected\" = the collision check applies, which is the correct SW\n // behavior (SWs reload via chrome.runtime.reload, not module HMR).\n let isHmr = false;\n try {\n const meta = import.meta as { hot?: unknown };\n isHmr = meta?.hot !== undefined && meta?.hot !== null;\n } catch {\n isHmr = false;\n }\n if (!isHmr && definedPortNames.has(name)) {\n throw new PortNameCollisionError(name);\n }\n definedPortNames.add(name);\n return { __brand: \"port\", name } as Port<T>;\n}\n\n// === Store: pluggable persistence adapter ===\n//\n// `load()` returns `unknown` because storage genuinely doesn't know `S` —\n// chrome.storage returns whatever JSON lives at the key, DurableObjectStorage\n// returns whatever bytes were serialized. Casting raw storage to `S` at the\n// adapter boundary is the exact `as S` move forbidden by invariant 8\n// everywhere else in the substrate. `load()` returning `unknown` makes the\n// substrate stop pretending.\n//\n// `migrate(raw)` is the boundary parse. Returns `S` on a recognized shape —\n// the substrate passes it to `init` as `loaded`. Returns `null` on any\n// unrecognized shape — the substrate passes `null` to `init`, fresh-boot\n// path. Must NOT throw: if a future shape is unrecognizable, returning\n// `null` boots clean from a known-good path. Throwing here would\n// indistinguishably collapse \"storage corruption\" and \"schema migration not\n// yet written\" — both are runtime decisions, not panics.\n//\n// Strengthens invariant 8 (the boundary parses; the core trusts) — the\n// substrate now enforces the parse instead of relying on every adapter\n// remembering to.\nexport interface Store<S> {\n load(): Promise<unknown>;\n save(state: S): Promise<void>;\n migrate(raw: unknown): S | null;\n}\n\n// === DispatchSettle: how far a dispatch awaits ===\n//\n// `dispatch(msg)` settles ONE transition by default would be the dangerous\n// choice (issue #50): an `interpret` handler that returns a follow-up Msg\n// enqueues a FRESH transition on the tail, and that follow-up is fire-and-\n// forget relative to the original `dispatch`. Callers that forgot to also\n// `await idle()` saw their dispatch resolve \"early\", before the consequences\n// of the Msg had run — which is why the codebase grew a dispatch+`idle()`\n// wrapper and tests fell back to `for (i<200) sleep(5)` polls.\n//\n// So the default is the SAFE one: `\"quiescent\"` — `dispatch` resolves only\n// once the entire transitive follow-up chain has drained (the same drain\n// `idle()` performs). The rare single-step case (a caller that genuinely wants\n// one transition and will await the tail itself) opts in with `\"once\"`, or\n// uses the `dispatchOnce` convenience.\nexport type DispatchSettle = \"quiescent\" | \"once\";\n\n// === RuntimeRef<M>: typed sibling-runtime handle ===\n//\n// A `RuntimeRef<M>` exposes only the inbox of a Runtime — `dispatch(msg)` (and\n// its single-step sibling `dispatchOnce(msg)`).\n// Use it as the field type when one runtime holds a reference to a *sibling*\n// runtime (composition by reduction across orthogonal lifecycles — invariant\n// 5). The holder learns the Msg shape it can send, nothing about the\n// referenced runtime's State, Cmd, Sub, Ctx, observers, listeners, or Port\n// fanout. That asymmetry is the point: cross-runtime coupling collapses to\n// \"I can put a Msg in your inbox\", which is the smallest coordination\n// surface that preserves typed cross-runtime calls.\n//\n// `Runtime<S, M>` extends `RuntimeRef<M>` structurally — every Runtime is\n// trivially a RuntimeRef. The narrowing happens at the *consumer's* Ctx\n// type, not via a runtime cast.\n//\n// Strengthens invariant 5 (composition by reduction — sibling runtimes\n// coordinate through the narrowest typed surface) and invariant 6 (runtime\n// is small and inspectable — what a holder *can* do to a sibling runtime is\n// exactly one method, not the full surface).\nexport interface RuntimeRef<M extends { type: string }> {\n /**\n * Put a Msg in the runtime's inbox and resolve once it has been processed.\n *\n * Runs to QUIESCENCE by default (issue #50): the returned promise settles\n * only after the dispatched Msg AND every follow-up Msg its `interpret`\n * handlers (transitively) returned have drained off the serial tail. This is\n * the safe default — `await dispatch(msg)` means \"the consequences have run\",\n * not \"one transition fired and the rest are racing\". Pass\n * `{ settle: \"once\" }` (or call `dispatchOnce`) for the rare single-step\n * case where the caller will await the tail itself.\n *\n * Rejection ordering: the ONE dispatched transition's own failure (a reducer\n * / save / sub-start / interpret throw) surfaces first, on this promise. If\n * that transition succeeds but the follow-up chain never stabilizes, the\n * quiescent drain rejects with `QuiescenceTimeoutError` (same cap and same\n * no-silent-give-up contract as `idle()` — issue #51). Follow-up Msg\n * rejections themselves route to the `onError` sink, never here.\n */\n dispatch(msg: M, opts?: { readonly settle?: DispatchSettle }): Promise<void>;\n /**\n * Dispatch `msg` and resolve after exactly ONE transition's effects settle,\n * WITHOUT draining the follow-up chain. Equivalent to\n * `dispatch(msg, { settle: \"once\" })`.\n *\n * This is the old (pre-#50) `dispatch` behavior, kept as an explicit escape\n * hatch: a caller that wants to interleave its own work between the first\n * transition and its follow-ups, then `await runtime.idle()` itself. Prefer\n * plain `dispatch` (run-to-quiescence) unless you specifically need the\n * single step — the un-awaited follow-up chain is exactly the footgun #50\n * removed from the default.\n */\n dispatchOnce(msg: M): Promise<void>;\n}\n\n// === BootingRuntime: handle returned SYNCHRONOUSLY from run() ===\n//\n// `run()` returns a `BootingRuntime<S, M>` the instant it is called — boot\n// (store.load → init → save → reconcile → interpret-of-init-cmds → first\n// fanout) is still in flight. A BootingRuntime exposes exactly the surface\n// that is TOTAL before boot completes: you may queue a dispatch, subscribe a\n// listener, attach an observer, wire a Port, or stop the runtime — none of\n// those need the initial State to exist yet. What you may NOT do is read the\n// State (`getState`) or wait for quiescence (`idle`): there is no State to\n// read until boot has run `init`, and a half-booted runtime has no meaningful\n// \"quiescence\" to await. Those two are the difference between a BootingRuntime\n// and a `Runtime` — and that difference is enforced at the type level.\n//\n// `ready` resolves to the FULL `Runtime<S, M>` once boot completes (or rejects\n// with the boot error). It is the ONLY way to obtain a `Runtime`:\n//\n// const runtime = await run(machine, opts).ready;\n// runtime.getState(); // total — boot has run, State exists\n//\n// This closes issue #45: `getState(): S` used to be declared on the handle\n// `run()` returned synchronously, yet it THREW before boot under a store. The\n// type claimed total; the value was partial, and only JSDoc told you to await.\n// Splitting the booting handle from the ready runtime makes \"read State before\n// boot\" a COMPILE error rather than a runtime throw — the type no longer lies.\n//\n// `subscribe(listener)` is the React-shaped change notifier (zero-arg, paired\n// with `getState()` via `useSyncExternalStore` on the ready Runtime).\n// `observe(observer)` is the devtools-shaped trace hook: it receives `(msg,\n// state)` for every COMPLETED, APPLIED transition. The boot transition is NOT\n// an applied Msg — there is no event there, only the initial State — so it does\n// NOT flow through `observe`. It is delivered once via `onBoot(handler)`\n// instead, so `observe`'s `msg` is total (`M`, never `null`): a consumer no\n// longer carries a `| null` boot arm it then ignores (#47). Both `observe` and\n// `onBoot` fire from the same point in the dispatch loop (after save →\n// reconcile → interpret), so what they see is consistent with what a subscriber\n// sees via the ready Runtime's `getState()`.\n//\n// Observe is separate from subscribe because the contracts differ: React\n// consumers want \"something changed, re-read state\"; devtools consumers want\n// \"here is the exact (msg, state) pair, append it to a log\". Folding them\n// would force every React subscriber to type-check a `msg` arg they will never\n// use, and would couple `useSyncExternalStore` to a particular Msg type at the\n// substrate.\n//\n// `on(type, handler)` is the SEMANTIC event channel (#47): a typed subscription\n// to the machine-level `AgentEvent`-style union `E` a machine projects off its\n// transitions (via `run`'s `events` projector). Where `observe` hands every\n// consumer the raw `(M, S)` firehose to hand-filter — coupling a UI to the\n// machine's PRIVATE Msg vocabulary (e.g. a retry plumbing's `resilient_ok`) —\n// `on` delivers only the public, named events, narrowed to the requested\n// `type`. `E` defaults to `never`: a machine that wires no `events` projector\n// has an empty event surface and `on` is uncallable (no `E[\"type\"]` to pass).\nexport interface BootingRuntime<\n S,\n M extends { type: string },\n E extends { type: string } = never,\n> extends RuntimeRef<M> {\n dispatch(msg: M, opts?: { readonly settle?: DispatchSettle }): Promise<void>;\n dispatchOnce(msg: M): Promise<void>;\n subscribe(listener: () => void): () => void;\n observe(observer: (msg: M, state: S) => void): () => void;\n /**\n * Subscribe to the INITIAL State — the boot transition the old `observe`\n * `msg === null` arm carried (#47). Fires exactly once: immediately if boot\n * has already completed when `onBoot` is called (so a late subscriber never\n * misses it), otherwise on the boot fanout. Returns a cleanup that detaches\n * the handler (a no-op once it has fired).\n *\n * This is where the boot case the `observe` firehose used to fold in went\n * once `observe`'s `msg` became total: a consumer that needs to seed a view\n * from the initial State wires `onBoot`; a consumer that only folds events\n * (the common case) wires `observe` (or `on`) and is no longer handed a boot\n * arm to skip.\n */\n onBoot(handler: (state: S) => void): () => void;\n /**\n * Subscribe to a SEMANTIC event of `type` (#47). Typed narrowing: the handler\n * receives exactly the `E` member whose `type` matches `K`, so a consumer\n * built on `on(\"TurnSettled\", …)` never touches another event's shape and —\n * critically — never references the machine's PRIVATE Msg names. Returns a\n * cleanup that detaches the handler. Multiple handlers per type are\n * supported; fanout is synchronous and throw-isolated (same discipline as\n * `observe`). Fires only when the machine's `events` projector (wired on\n * `run`) emits an event of that type for a transition.\n */\n on<K extends E[\"type\"]>(\n type: K,\n handler: (event: Extract<E, { type: K }>) => void,\n ): () => void;\n /**\n * Subscribe to a typed Port. Returns a cleanup function that removes the\n * listener. Multiple listeners per port are supported; fanout is synchronous\n * (same shape as `observe`) and isolated — one listener throw does not strand\n * the others.\n *\n * Ports are for \"data leaving the runtime selectively\" — see `definePort`.\n * Distinct from `observe` (every transition) and from State (the world).\n */\n subscribePort<T>(port: Port<T>, listener: (value: T) => void): () => void;\n /**\n * Emit a value on a Port from OUTSIDE an interpret handler. Same synchronous\n * fanout discipline as `ctx.emit` inside interpret — every subscriber\n * receives the value immediately; emitting to a port with no subscribers is\n * a no-op; listener throws are isolated.\n *\n * Use this for `observe`-driven Port emission: the canonical case is one\n * runtime publishing a state-derived signal (e.g. \"am I idle?\") that\n * another runtime subscribes to via `subscribePort`. Without\n * `runtime.emitPort`, the only way to emit a Port is from inside an\n * `interpret` handler — which forces the reducer to spawn a \"tell the\n * outside world\" Cmd on every transition that should signal. With it,\n * `runtime.observe((msg, state) => runtime.emitPort(port, derive(state)))`\n * keeps the reducer free of Port-plumbing Cmds and matches the \"every\n * transition is observable\" contract this invariant set already commits to.\n *\n * Strengthens invariant 6 (runtime is small and inspectable — Port emission\n * is no longer privileged to interpret handlers; `observe`-driven emission\n * is structurally supported).\n */\n emitPort<T>(port: Port<T>, value: T): void;\n /**\n * Resolves to the booted `Runtime<S, M>` after boot completes — i.e. after\n * `store.load()` (if any), `init`, `store.save()` of the initial state,\n * `reconcileSubs`, `interpret` of any init-emitted cmds, and the initial\n * listener / observer fanout. Rejects with the boot error if boot fails (the\n * same error every subsequent `dispatch` call rejects with).\n *\n * This is the ONLY way to obtain a `Runtime<S, M>` — and a `Runtime` is the\n * only handle whose `getState()` is total. Awaiting `ready` is therefore the\n * single gate between \"boot in flight\" and \"State exists\":\n *\n * ```ts\n * const runtime = await run(machine, opts).ready;\n * runtime.getState(); // total — never throws-before-boot\n * ```\n *\n * Idempotent — subsequent reads return the same settled promise. The runtime\n * holds exactly one `ready` promise per `run()` call; it resolves to the same\n * `Runtime` object every time.\n *\n * Closes issue #45 (the `getState()` lie) and the v1 boot-await gap once\n * documented in `stepBootEffects`: components that need synchronous initial\n * state under a store (e.g. `useSyncExternalStore` consumers) await `ready`\n * for the booted runtime instead of dispatching a no-op Msg.\n *\n * Expresses canon §2.3 (the `init` contract — boot is a named, awaitable\n * moment). Strengthens invariant 6 (runtime is small and inspectable).\n */\n ready: Promise<Runtime<S, M, E>>;\n stop(): Promise<void>;\n}\n\n// === Runtime: the BOOTED handle `ready` resolves to ===\n//\n// A `Runtime<S, M>` is a `BootingRuntime<S, M>` whose boot has completed. It\n// adds the two members that are only meaningful once the initial State exists:\n// `getState()` (total — boot ran `init`, so State is always present) and\n// `idle()` (quiescence is only definable on a runtime that has booted). It\n// also narrows `ready` to `Promise<Runtime<S, M>>` resolving to itself — a\n// booted runtime awaited again hands back the same booted runtime, so consumer\n// code that re-awaits (e.g. a cached `getRuntime()` accessor) keeps a total\n// `getState`.\n//\n// You never construct a `Runtime` directly from `run()`; you obtain it via\n// `await bootingRuntime.ready`. That asymmetry is the whole fix for #45 —\n// \"read State before boot\" is now unrepresentable, not merely discouraged.\nexport interface Runtime<\n S,\n M extends { type: string },\n E extends { type: string } = never,\n> extends BootingRuntime<S, M, E> {\n /**\n * The current State. TOTAL — never throws. Obtaining a `Runtime` requires\n * awaiting `ready`, which only resolves AFTER boot has run `init` and set the\n * initial State; a `Runtime` therefore always has a State to return. (Before\n * #45 this was declared on the synchronously-returned handle and threw under\n * a store until boot finished — the type lied. It no longer does.)\n */\n getState(): S;\n /**\n * Resolves to this same `Runtime` once boot completes. Idempotent.\n */\n ready: Promise<Runtime<S, M, E>>;\n /**\n * Resolves once the runtime has reached QUIESCENCE — every dispatched Msg AND\n * every follow-up Msg an interpret handler returned has been processed, with\n * no further step pending on the serial tail.\n *\n * Since #50, plain `dispatch(msg)` already runs to quiescence (it awaits this\n * same drain after its own transition), so a caller rarely needs `idle()`\n * directly. `idle()` is still the right tool to (a) settle follow-ups left in\n * flight by a `dispatchOnce` / `dispatch(msg, { settle: \"once\" })` single\n * step, or (b) wait out a follow-up chain kicked off by a Sub or by boot\n * `init` cmds where there was no `dispatch` call to await. It chains onto the\n * current tail and re-checks until the tail stops advancing, draining the\n * entire transitive follow-up chain.\n *\n * Idempotent and re-entrant-safe: each call reads the live tail; a tail that\n * advanced during the await (a follow-up landed) is awaited again until\n * stable. Tail rejections are NOT surfaced here — a failing dispatch\n * surfaces on its OWN returned promise (and follow-up rejections route to\n * the `onError` sink). The ONE rejection `idle()` itself produces is a\n * `QuiescenceTimeoutError` when the wait hits its iteration cap without the\n * tail stabilizing: that replaces the old silent fall-through-resolve so a\n * livelocking machine is distinguishable from a genuinely quiesced one\n * (invariant 6 — no silent failures).\n *\n * Use `idle()` for runtime-internal follow-ups; a poll is still correct when\n * waiting on an EXTERNAL event (a WS reply) the runtime cannot enqueue itself.\n *\n * Strengthens invariant 6 (runtime is small and inspectable — quiescence is a\n * first-class awaitable moment, not a poll the consumer re-derives).\n */\n idle(): Promise<void>;\n /**\n * The terminal State of the run, or `undefined` while it is still in flight.\n * A run is \"terminal\" exactly when the `terminal` predicate passed to `run()`\n * returns `true` for the current State; with no predicate supplied a run is\n * never terminal and this always returns `undefined`.\n *\n * This is the FIRST-CLASS result read (issue #46): the outcome of running the\n * thing, off the State the machine already owns — NOT scraped off the\n * `observe` firehose by matching an internal Msg name (`resilient_ok`) and\n * racing a state-clear. The consumer reads the run's product (e.g. an agent's\n * `state.output`) off the returned `S`, with no coupling to the machine's\n * private retry / loop vocabulary.\n *\n * Total — never throws (a `Runtime` always has a State; see `getState`).\n */\n result(): S | undefined;\n /**\n * Resolves with the terminal State the first time the run reaches a terminal\n * State (per the `terminal` predicate passed to `run()`). If the run is\n * ALREADY terminal when `done()` is called, resolves immediately with the\n * current State; otherwise resolves on the transition that first makes\n * `terminal` hold.\n *\n * The awaitable companion to `result()` — \"wait for the run to finish, then\n * hand me what it produced\" in one call, instead of polling `result()` or\n * hand-rolling an `observe` loop that watches for a private terminal Msg.\n * With no `terminal` predicate supplied the run is never terminal, so this\n * promise never resolves (a non-terminating machine has no result to await).\n *\n * Idempotent and multi-caller safe: every call observes the live State, so a\n * call made after the run already terminated resolves at once, and concurrent\n * callers all settle on the same terminal transition.\n */\n done(): Promise<S>;\n}\n\n// === defineMachine: identity-typed pass-through ===\n//\n// Overloads enforce exactly one `update` shape per call so consumers see a\n// concrete type for `update`, not the substrate-internal union. Implementation\n// is identity at runtime — the runtime (`run`) branches on the structural\n// shape of `update` (Reducer vs Transitions) when dispatching.\n//\n// - Reducer-form overload: `update: Reducer<S, M, C>` — flat record keyed by\n// Msg.type. Exhaustiveness is enforced by the mapped type; adding a Msg\n// variant without a matching key is a compile error.\n// - Transitions-form overload: `update: Transitions<S, M, C>` — 2D table\n// keyed by State.type then Msg.type. Only available when `S extends {\n// type: string }`.\n//\n// Distinguishing the two: TS overload resolution checks the literal shape of\n// the argument. Reducer values are functions; Transitions values are records\n// (of functions). Structurally distinct, so overload resolution picks the\n// right branch.\n//\n// Strengthens invariant 2 (record forms have no fall-through default) and\n// invariant 7 (the Msg variant set — and, for Transitions, the State variant\n// set — is load-bearing at the type level).\n\n// Transitions-form overload — `update` is a 2D table keyed by State.type\n// then Msg.type. Declared FIRST (most specific): the value shape (record of\n// records of functions) is structurally distinct from Reducer (record of\n// functions), so TS overload resolution picks this branch only when the\n// literal matches the table shape.\n//\n// The S constraint is expressed via a conditional `S extends { type: string\n// } ? Transitions<S, M, C> : never` instead of a generic-parameter\n// constraint. The conditional approach keeps `S` unconstrained at the\n// overload signature, so `Parameters<typeof defineMachine<NonDiscriminatedS,\n// ...>>` (the public `satisfies` pattern) doesn't fail at the first\n// overload — it just skips to the next one because `update` becomes\n// `never`.\nexport function defineMachine<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n m: Omit<Machine<S, M, C, U, Ctx>, \"update\"> & {\n // `[S]` tuple-wrap disables distributive conditional behavior — see\n // the comment on `Machine.update` for why distribution is wrong here.\n update: [S] extends [{ type: string }] ? Transitions<S, M, C> : never;\n },\n): Machine<S, M, C, U, Ctx>;\n// Reducer-form overload — `update` is a flat record keyed by Msg.type.\n// Object literals where every property is a function match this shape.\nexport function defineMachine<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n m: Omit<Machine<S, M, C, U, Ctx>, \"update\"> & {\n update: Reducer<S, M, C>;\n },\n): Machine<S, M, C, U, Ctx>;\n// Implementation signature — accepts both via the union on `Machine.update`.\nexport function defineMachine<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(m: Machine<S, M, C, U, Ctx>): Machine<S, M, C, U, Ctx> {\n // Tag the update form ONCE, here at the typed construction boundary, so no\n // downstream reader (`run`, `replay`, the `withX` wrappers) re-derives it\n // structurally. Non-enumerable: it must not serialize, must not show up in\n // `Object.keys(machine)`, and must not collide with any Msg.type key on a\n // flattened surface. Idempotent under re-wrap (`defineMachine(defineMachine(m))`).\n if (m.__form === undefined) {\n Object.defineProperty(m, \"__form\", {\n value: detectUpdateForm(m.update as object),\n enumerable: false,\n writable: false,\n configurable: true,\n });\n }\n return m;\n}\n\n// === asReducer: the validated minting path for a reducer (ADR 0003 #5) ===\n//\n// The single entry point that turns a raw record of handlers into a branded\n// `Reducer<S, M, C>` — the exact role `asRng` plays for `Rng` in\n// `retry-backoff`. The parameter type is `Reducer<S, M, C>`, whose every cell\n// returns the non-thenable `SyncReturn<S, C>`, so the reentrancy guard fires\n// HERE, at construction: an `async` cell (or one that inline-`await`s) returns\n// `Promise<...>`, which is not assignable to `SyncReturn`, and `tsc` rejects the\n// call at the offending cell. A pure synchronous reducer passes through\n// untouched and gains the phantom `ReducerBrand`.\n//\n// PURE: identity at runtime (the brand is phantom). `defineMachine` brands its\n// `update` internally the same way, so callers that pass an object literal\n// straight to `defineMachine` never need to call `asReducer` — it exists for\n// the standalone `const update = asReducer<...>({ ... })` form, mirroring how a\n// caller can mint an `Rng` with `asRng` before wiring it.\nexport function asReducer<S, M extends { type: string }, C extends Cmd>(\n reducer: Reducer<S, M, C>,\n): Branded<Reducer<S, M, C>> {\n return reducer as Branded<Reducer<S, M, C>>;\n}\n\n// === run: implemented in task 2 ===\n//\n// Returns a `Runtime<S, M>` synchronously. Boot (`store?.load()` + `init`) runs\n// asynchronously as the FIRST entry on the serial dispatch tail; every public\n// runtime method (`dispatch`, `stop`, `getState`) awaits the boot phase\n// implicitly through the queue. Failures inside `store.load` propagate via the\n// boot promise — subsequent calls reject/throw with that error. There is no\n// \"synchronous load\" branch because `Store#load` is async by contract.\n//\n// Save-then-effects ordering is structural: every transition mutates state,\n// awaits `store.save(newState)`, then reconciles subscriptions, then runs\n// `interpret` for emitted cmds, then fires external listeners. A throw in any\n// effect phase leaves the persisted state ahead of the host's belief about\n// what executed — the Railway discipline (`tryInterpret` in handlers) makes\n// that safe in practice.\n\n// === CtxArg<Ctx>: the `ctx` field of `run`'s opts, conditionally optional ===\n//\n// A PURE machine reads nothing from `ctx` — its `Ctx` is `NoCtx`\n// (`Record<never, never>`), `Record<string, never>` (the vortex arena grain,\n// #182), or `unknown`. Forcing such a machine to write `ctx: {}` is ceremony\n// for a value the type already pins as empty — the same friction the\n// `Machine.interpret` field removed for cmdless machines. So `ctx` is\n// CONDITIONALLY optional: when the empty object satisfies `Ctx` it may be\n// OMITTED (the runtime defaults it to `{}`), so a pure reducer runs as\n// `run(machine)`; when `Ctx` carries a field a handler reads, `ctx` stays\n// REQUIRED so a context-bearing machine can't silently forget it.\n//\n// `[Record<never, never>] extends [Ctx]` reads as \"is `{}` assignable to\n// `Ctx`\" — true for the context-free shapes above, false for `{ db: … }`. The\n// tuple-wrap disables distributive conditional behavior, the same trick the\n// `Machine.interpret`/`update` fields use. Existing callers that pass `ctx`\n// are unaffected — an optional field still accepts a present value.\nexport type CtxArg<Ctx> = [Record<never, never>] extends [Ctx]\n ? { ctx?: Ctx }\n : { ctx: Ctx };\n\nexport function run<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n E extends { type: string } = never,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: CtxArg<Ctx> & {\n store?: Store<S>;\n onError?: OnError;\n /**\n * The SEMANTIC event projector (#47). Maps one APPLIED transition `(msg,\n * state)` to zero-or-more public events of the machine-level union `E`.\n * Returning `[]` skips the transition (it produced no event the consumer's\n * `on(type, …)` cares about). This is where a machine maps its PRIVATE Msg\n * vocabulary (a retry plumbing's `resilient_ok`, a tool loop's\n * `agent_tool_ok`) to NAMED events — the private names live only inside this\n * closure and never reach `on`'s `E` surface. Omit → the run has no event\n * surface (`E = never`) and `on` is uncallable.\n *\n * PURE — called inside the dispatch loop on the freshly-folded `(msg,\n * state)`; it must not read the clock, mutate, or throw (a throw is\n * isolated like an observer throw, never stranding the transition).\n */\n events?: (msg: M, state: S) => readonly E[];\n /**\n * Declared policy for when the pure `update` (reducer) throws. The reducer\n * throw is always surfaced via `onError` (`phase: \"reduce\"`); the strategy\n * decides what the runtime does next. Defaults to `\"stop\"` — the explicit,\n * safe choice (surface + halt, never a silent resume). See `Supervision`.\n */\n supervision?: Supervision<S, M>;\n /**\n * The run-terminality predicate — what makes the run's outcome first-class\n * (issue #46). Returns `true` for a State the run has finished in (e.g. an\n * agent's `state.run.phase === \"done\"`). The runtime feeds it to\n * `Runtime.result()` (the terminal State, or `undefined` in flight) and\n * `Runtime.done()` (resolves with the terminal State when it first holds).\n *\n * PURE — the runtime calls it inside the dispatch loop on the freshly-folded\n * State; it must not read the clock, mutate, or throw. Omit → the run is\n * never terminal: `result()` always returns `undefined` and `done()` never\n * resolves (the right behavior for a machine with no natural completion).\n */\n terminal?: (state: S) => boolean;\n /**\n * Iteration cap for `idle()`'s quiescence wait. Defaults to 100_000. Test\n * seam only — lets a livelock test trip the `QuiescenceTimeoutError` reject\n * path in a handful of iterations instead of 100k. Production code must not\n * set it.\n *\n * @internal test-only\n */\n __idleCap?: number;\n },\n): BootingRuntime<S, M, E> {\n const { store } = opts;\n // `ctx` is conditionally optional (see `CtxArg`): a pure machine omits it.\n // Default the absent/nullish case to an empty object so the augmented-ctx\n // spread and `init(loaded, ctx)` get a value, never `undefined`. Callers that\n // pass a real `ctx` (or `ctx: undefined` for a `Ctx = undefined` machine that\n // ignores it) are unchanged.\n const ctx = (opts.ctx ?? {}) as Ctx;\n const idleCap = opts.__idleCap ?? 100_000;\n // The semantic-event projector (#47). Defaults to \"no events\" — a machine\n // that wires none has an empty event surface (`E = never`), so `on` is\n // uncallable and this projector never runs.\n const projectEvents: (msg: M, state: S) => readonly E[] =\n opts.events ?? (() => []);\n // The run-terminality predicate (#46). Defaults to \"never terminal\" so a\n // machine with no natural completion has a sound `result()`/`done()` (always\n // undefined / never resolves) rather than a missing one.\n const isTerminal: (state: S) => boolean = opts.terminal ?? (() => false);\n // The error sink for paths with no caller to reject at (follow-up dispatch\n // rejections, the final stop-save). Optional on `run`; when absent we fall\n // back to `defaultOnError`, which re-throws on a macrotask so the failure\n // still surfaces. Invariant 6: no silent failures.\n const onError: OnError = opts.onError ?? defaultOnError;\n // The sink must itself be total. If a consumer's sink throws, route THAT\n // throw through `defaultOnError` so a buggy sink can't re-create a silent\n // failure (or strand a teardown loop).\n const reportError = (error: unknown, context: RuntimeErrorContext): void => {\n try {\n onError(error, context);\n } catch (sinkError) {\n defaultOnError(sinkError, context);\n }\n };\n // The declared policy for a reducer throw (ADR 0003 #4). Default `stop`:\n // surface via `onError` + halt, never a silent resume. Normalized once here\n // so `stepDispatch` branches on `.strategy` without re-parsing the shorthand.\n const supervision = normalizeSupervision<S, M>(opts.supervision);\n\n // Holders are intentionally late-initialized: `state` is set inside the boot\n // step, which runs as the head of the tail. `getState()` before boot throws.\n let state: S | undefined;\n let bootError: unknown = null;\n let stopped = false;\n\n const subRegistry = new Map<string, () => void>();\n const listeners = new Set<() => void>();\n // Observers receive (msg, state) for every completed, APPLIED transition. The\n // boot transition has no applied Msg — it is delivered via `onBoot`, not here\n // — so `msg` is total (`M`, never `null`) (#47). Same throw-isolation\n // contract as listeners.\n const observers = new Set<(msg: M, state: S) => void>();\n // `onBoot` handlers receive the initial State once (#47). The boot case the\n // old `observe(msg === null)` arm carried lives here now. `booted` flips on\n // the boot fanout so a handler registered AFTER boot fires immediately with\n // the captured initial State (never misses it).\n const bootHandlers = new Set<(state: S) => void>();\n let booted = false;\n // Semantic-event handlers, keyed by event `type` (#47). Each `on(type, fn)`\n // adds `fn` to its type's bucket; the dispatch loop projects each transition\n // to `E[]` via `projectEvents` and fans each event to its type's handlers.\n // Stored as `(event: { type: string }) => void` to keep the registry\n // monomorphic; per-type safety lives at the `on` call site (the handler's `K`\n // narrows the event via `Extract<E, { type: K }>`).\n const eventHandlers = new Map<string, Set<(event: E) => void>>();\n // `done()` waiters (#46): each open `done()` call parks its resolver here.\n // The first transition that makes `isTerminal(state)` hold drains the set,\n // resolving every parked promise with the terminal State. A `done()` call\n // made AFTER the run is already terminal resolves immediately and never\n // parks (so it can never miss the terminal transition).\n const doneWaiters = new Set<(state: S) => void>();\n\n // Port subscriber registry. Keyed by Port reference (NOT name) — port\n // identity is nominal/by-reference per Elm semantics. Each port can have\n // many listeners; emissions fan out synchronously to all of them.\n // `Set<(value: unknown) => void>` is the storage shape; per-port type\n // safety is enforced at the `subscribePort` / `emit` call sites via the\n // phantom `__t` field on `Port<T>`.\n const portRegistry = new Map<Port<unknown>, Set<(value: unknown) => void>>();\n\n // The `emit` function injected onto ctx. Synchronous fanout — same shape as\n // `observe`. No-op when the port has no subscribers. Listener throws are\n // isolated + logged so one bad subscriber does not strand the others.\n function portEmit<T>(port: Port<T>, value: T): void {\n const subscribers = portRegistry.get(port as Port<unknown>);\n if (!subscribers || subscribers.size === 0) return;\n for (const listener of subscribers) {\n try {\n listener(value);\n } catch (err) {\n console.error(err);\n }\n }\n }\n\n // Augmented ctx passed to every Cmd handler. `ctx` is intentionally spread\n // into a fresh object so handlers receive a Ctx & PortEmitter without\n // mutating the caller's ctx (which may be shared across runtimes / tests).\n const augmentedCtx: Ctx & PortEmitter = { ...ctx, emit: portEmit };\n\n // === update dispatch ===\n //\n // The substrate accepts two `update` shapes (see `defineMachine` overloads);\n // dispatch goes through `applyCell`, the single reducer-vs-transitions\n // primitive keyed on `formOf` (see `pure/core.ts`, #275).\n function applyUpdate(state: S, msg: M): readonly [S, readonly C[]] {\n if (__DEV__) deepFreeze(state);\n const result = applyCell<S, M, C>(machine, state, msg);\n if (__DEV__) assertPureResult(result, msg.type);\n return result;\n }\n\n // The serial dispatch queue: every step is chained onto `tail`. New dispatch\n // calls (and re-entrant ones from interpret follow-ups) wait for the\n // previous step to fully resolve before running their reducer. This is the\n // single concurrency gate.\n let tail: Promise<void> = Promise.resolve();\n\n /**\n * Reconcile subscriptions against `state`. Called after every save.\n *\n * - Same id present in old + new → leave running.\n * - Removed id → call cleanup. Swallow cleanup throws + `console.error`.\n * - New id → call subscribe[type]; on success store cleanup. On throw, the\n * sub is NOT registered; throw is remembered and re-thrown after the loop\n * so all other new subs still register.\n */\n function reconcileSubs(): void {\n if (!machine.subscriptions) return;\n const desired = machine.subscriptions(state as S);\n const desiredIds = new Set<string>();\n\n // Collision assert: within a single desired set, two subs sharing an id\n // but declaring different types is the silent class of bug that Elm's\n // sub diffing relies on the user to avoid — we catch it explicitly here.\n // (Same id + same type within the desired set is a redundant declaration\n // — harmless, and the `subRegistry.has` check below dedupes it on the\n // start path. Same id across transitions is the no-churn case the\n // reconcile pass is designed for and MUST NOT throw.)\n const desiredTypeById = new Map<string, string>();\n for (const sub of desired) {\n const existing = desiredTypeById.get(sub.id);\n if (existing !== undefined && existing !== sub.type) {\n throw new Error(\n `@demlik/tea: Sub.id collision: id=\"${sub.id}\" declared as type=\"${existing}\" and type=\"${sub.type}\"`,\n );\n }\n desiredTypeById.set(sub.id, sub.type);\n }\n\n // Removals first — anything in the registry not in `desired` should stop.\n for (const sub of desired) desiredIds.add(sub.id);\n for (const [id, cleanup] of subRegistry) {\n if (!desiredIds.has(id)) {\n try {\n cleanup();\n } catch (err) {\n console.error(err);\n }\n subRegistry.delete(id);\n }\n }\n\n // Additions — anything in `desired` not in the registry should start.\n let firstStartError: unknown = null;\n for (const sub of desired) {\n if (subRegistry.has(sub.id)) continue;\n const handler = machine.subscribe?.[sub.type as U[\"type\"]];\n if (!handler) {\n // No handler for this sub type — programmer error; skip. (TS would\n // normally forbid this at compile time when `subscribe` is provided\n // and U has variants, but a runtime guard is cheap.)\n continue;\n }\n try {\n const cleanup = handler(\n sub as Extract<U, { type: U[\"type\"] }>,\n ctx,\n enqueueDispatch,\n );\n subRegistry.set(sub.id, cleanup);\n } catch (err) {\n if (firstStartError === null) firstStartError = err;\n // Do NOT register; continue to next sub so other starts still run.\n }\n }\n if (firstStartError !== null) throw firstStartError;\n }\n\n // `interpret` is optional at the type level when `C extends Cmd<never>`\n // (see `Machine<...>`). The runtime safely defaults a missing map to `{}`\n // because such a machine never emits cmds — the cmd loop below never\n // dereferences a handler. The defensive default also guards against a\n // miswired non-Cmd<never> consumer reaching this code path; the per-cmd\n // `if (!handler) continue` then preserves invariant-6 forward progress\n // instead of throwing on `undefined.foo`.\n type InterpretMap = {\n [K in C[\"type\"]]: (\n cmd: Extract<C, { type: K }>,\n ctx: Ctx & PortEmitter,\n // biome-ignore lint/suspicious/noConfusingVoidType: an interpret handler returns a follow-up Msg or nothing; `void` permits no-return bodies that `M | undefined` would reject\n ) => Promise<M | void>;\n };\n const interpretMap: InterpretMap =\n (machine as { interpret?: InterpretMap }).interpret ?? ({} as InterpretMap);\n\n /**\n * Run `interpret` for each emitted cmd. Each handler may return a follow-up\n * Msg — that Msg is enqueued onto the tail (NOT dispatched re-entrantly\n * inside the current transition).\n *\n * Throws propagate to the caller of `step`. The first error stops further\n * handlers in this transition.\n */\n async function runInterpret(cmds: readonly C[]): Promise<void> {\n for (const cmd of cmds) {\n const handler = interpretMap[cmd.type as C[\"type\"]];\n if (!handler) continue;\n const follow = await handler(\n cmd as Extract<C, { type: C[\"type\"] }>,\n augmentedCtx,\n );\n if (follow !== undefined && follow !== null) {\n // Schedule follow-up Msg on the tail. The current step resolves\n // first; the follow-up runs after, as a fresh transition. The\n // returned rejection has no caller (the original dispatcher already\n // resolved), so route it to the error sink instead of swallowing it —\n // invariant 6: no silent failures. If you want this failure folded\n // back into state, name a failure Msg via `tryInterpret` (Railway);\n // the sink is the last-resort observability hook for the rest.\n enqueueDispatch(follow as M).catch((error: unknown) => {\n reportError(error, { phase: \"follow-up\" });\n });\n }\n }\n }\n\n /**\n * Fire every external listener. Listener throws are isolated — one bad\n * listener does not strand the others. This is the dual of cleanup\n * semantics: the runtime guarantees forward progress for unrelated\n * observers.\n */\n function fireListeners(): void {\n for (const listener of listeners) {\n try {\n listener();\n } catch (err) {\n console.error(err);\n }\n }\n }\n\n /**\n * Fire every observer with the just-applied msg + post-transition state.\n * Same throw-isolation contract as `fireListeners`. Called immediately\n * after `fireListeners` so observers see exactly what subscribers see. Only\n * called for APPLIED transitions — `msg` is total (the boot case routes to\n * `fireBoot`, not here) (#47).\n */\n function fireObservers(msg: M): void {\n if (observers.size === 0 || state === undefined) return;\n const snapshot = state;\n for (const observer of observers) {\n try {\n observer(msg, snapshot);\n } catch (err) {\n console.error(err);\n }\n }\n }\n\n /**\n * Project the just-applied transition to semantic events and fan each to its\n * type's `on(...)` handlers (#47). The projector maps the machine's private\n * Msg vocabulary to public events; a throw in the projector OR a handler is\n * isolated (errors-are-data) so it never strands the transition. Called right\n * after `fireObservers` so an `on` handler sees the same post-transition State\n * an observer would.\n */\n function fireEvents(msg: M): void {\n if (eventHandlers.size === 0 || state === undefined) return;\n let events: readonly E[];\n try {\n events = projectEvents(msg, state);\n } catch (err) {\n console.error(err);\n return;\n }\n for (const event of events) {\n const bucket = eventHandlers.get(event.type);\n if (bucket === undefined) continue;\n for (const handler of bucket) {\n try {\n handler(event);\n } catch (err) {\n console.error(err);\n }\n }\n }\n }\n\n /**\n * Fire every `onBoot` handler with the initial State, ONCE, and flip `booted`\n * (#47). This carries the case the old `observe(msg === null)` arm did. Same\n * throw-isolation contract. A handler registered after this runs fires\n * immediately at its `onBoot` call site instead (it reads `booted`).\n */\n function fireBoot(): void {\n booted = true;\n if (bootHandlers.size === 0 || state === undefined) return;\n const snapshot = state;\n for (const handler of bootHandlers) {\n try {\n handler(snapshot);\n } catch (err) {\n console.error(err);\n }\n }\n }\n\n /**\n * Resolve every parked `done()` waiter iff the just-folded State is terminal\n * (#46). Called once per completed transition (including boot), right after\n * the observer fanout, so a `done()` promise settles on the SAME transition a\n * synchronous `result()` would first return non-`undefined`. Drains the set\n * before resolving (a resolver enqueued anew during the drain belongs to the\n * next call, not this one — but a terminal State stays terminal, so the new\n * waiter's own `done()` call already resolved it immediately).\n */\n function settleDoneWaiters(): void {\n if (doneWaiters.size === 0 || state === undefined) return;\n if (!isTerminal(state)) return;\n const snapshot = state;\n const parked = [...doneWaiters];\n doneWaiters.clear();\n for (const resolve of parked) resolve(snapshot);\n }\n\n /**\n * One full transition: update → save → reconcile subs → interpret → fire\n * listeners. Save-before-effects is the hard ordering; tests pin it.\n *\n * Throws from `update` propagate immediately (state unchanged). Throws from\n * `save` propagate after the in-memory `state` is advanced (PRD row: \"state\n * advanced; persisted state did not\"). Throws from reconcile or interpret\n * propagate after `save` has succeeded (PRD row: \"state already saved\").\n */\n async function stepDispatch(msg: M): Promise<void> {\n if (state === undefined) {\n // This should never happen — boot always runs before any dispatch.\n throw new Error(\"@demlik/tea: runtime not booted\");\n }\n // The reducer is the only synchronous user code in the step, and #71's\n // brand guarantees it cannot suspend — so a throw here is a clean,\n // catchable synchronous throw with `state` still at its pre-transition\n // value. Route it to the declared supervision strategy (ADR 0003 #4).\n let next: S;\n let cmds: readonly C[];\n try {\n [next, cmds] = applyUpdate(state, msg);\n } catch (reduceError) {\n // Invariant 6 — surface the failure as data FIRST, for every strategy.\n reportError(reduceError, { phase: \"reduce\" });\n switch (supervision.strategy) {\n case \"restart\": {\n // Host supplies last-known-good state; core installs it and KEEPS\n // FOLDING from there. State did not advance to the (never-produced)\n // reducer result — it is replaced by the rehydrated value. The\n // transition continues below with no cmds, since the throwing reduce\n // produced none. A throw inside `rehydrate` itself is NOT caught here\n // — it propagates as a genuine recovery failure (the host's\n // last-good source is broken), surfacing to the dispatch caller.\n state = supervision.rehydrate(state, msg, reduceError);\n if (store) await store.save(state);\n reconcileSubs();\n // No cmds to interpret — the reducer never returned a result.\n fireListeners();\n fireObservers(msg);\n fireEvents(msg);\n settleDoneWaiters();\n return;\n }\n case \"escalate\":\n // Surface + propagate. The runtime stays live (a parent supervisor\n // may keep dispatching); the failure bubbles out the dispatch\n // promise so the caller above sees it.\n throw reduceError;\n default:\n // `stop` (the safe default): halt the runtime. State is NOT advanced;\n // every subsequent dispatch rejects (\"runtime stopped\"). Propagate so\n // THIS dispatch promise also rejects — the halt is observable, never\n // a silent resume.\n stopped = true;\n throw reduceError;\n }\n }\n state = next;\n if (store) await store.save(state);\n // Subscriptions reconcile against the new state; throws propagate AFTER\n // the entire diff pass completes (so other subs still register / clean\n // up correctly). Cleanup throws are swallowed inside reconcileSubs.\n reconcileSubs();\n await runInterpret(cmds);\n fireListeners();\n fireObservers(msg);\n fireEvents(msg);\n settleDoneWaiters();\n }\n\n /**\n * Boot is split into two phases.\n *\n * - **Synchronous (`run()` call):** when `store` is absent, we know `loaded\n * = null` without an await — run `init(null, ctx)` IMMEDIATELY so that\n * `getState()` is observable synchronously. This is what\n * `useSyncExternalStore` consumers (e.g. `@demlik/tea/react`) need to render\n * on the first commit without an undefined-state flicker.\n * - **Asynchronous (`stepBootEffects` on the tail):** save + sub reconcile\n * + interpret-of-init-cmds + listener fire. These run as the first tail\n * entry; subsequent dispatches queue behind them.\n *\n * When `store` IS present we cannot do `init` synchronously (we'd be\n * inventing a loaded value), so we keep the full async path. Components\n * that want synchronous initial state in that case should omit the store\n * for the React mount and persist via a different mechanism (or accept the\n * default-snapshot story in a future v2).\n *\n * `store.load` throws propagate via the boot promise; subsequent calls\n * reject with the load error. (The PRD row \"run() throws synchronously\" is\n * imprecise relative to Store#load being async — we honor the intent: no\n * usable runtime is ever observable on load failure.)\n */\n // Synchronous init when there is NO store: `getState()` is observable\n // synchronously by `useSyncExternalStore` consumers. When a store IS\n // present, `init` must run AFTER `store.load()` (the contract: `init`\n // receives the loaded snapshot), and callers must await boot before\n // calling `getState()` — they do so implicitly via the first `dispatch()`\n // (which queues behind the boot step).\n //\n // The asymmetry is the substrate being honest about a real constraint: a\n // store is, by definition, an async boundary. Components that need\n // synchronous initial state under a store should await the runtime's boot\n // before mounting — `await runtime.ready` (not provided in v1; use `await\n // runtime.dispatch(noopMsg)` if needed, or a parent-level loading state).\n let pendingInitCmds: readonly C[] = [];\n if (!store) {\n const [initialState, initCmds] = machine.init(null, ctx);\n state = initialState;\n pendingInitCmds = initCmds;\n }\n\n async function stepBootEffects(): Promise<void> {\n if (store) {\n // Boundary parse (invariant 8). `store.load()` returns `unknown`\n // because storage doesn't know `S`; `store.migrate(raw)` is the\n // substrate's required parse — `S` on recognized shape, `null` on\n // unrecognized (which boots fresh, a known-good path). `migrate` MUST\n // NOT throw per its contract; if it does we surface the error via the\n // boot promise (same as a `load` throw — `runtime.ready` rejects and\n // every subsequent `dispatch` surfaces `bootError`).\n const raw = await store.load();\n const parsed = store.migrate(raw);\n const [initialState, initCmds] = machine.init(parsed, ctx);\n state = initialState;\n pendingInitCmds = initCmds;\n await store.save(state);\n }\n reconcileSubs();\n await runInterpret(pendingInitCmds);\n fireListeners();\n // The boot transition has no applied Msg — deliver the initial State via\n // `onBoot`, not `observe` (#47). No `fireEvents` either: boot is not an\n // applied event, so it projects no semantic event.\n fireBoot();\n // A rehydrated boot can land in a terminal State (the run finished before\n // the last eviction) — settle any `done()` waiter parked before boot.\n settleDoneWaiters();\n }\n\n /**\n * Enqueue a dispatch on the tail. Re-entrant calls from interpret or\n * subscribe handlers go through this same gate — guaranteeing serial\n * execution across the entire system.\n *\n * `dispatch` rejects when:\n * - the runtime is stopped (next dispatch always rejects after stop);\n * - boot failed (every dispatch surfaces the boot error);\n * - the reducer, save, sub start, or interpret throws.\n */\n function enqueueDispatch(msg: M): Promise<void> {\n if (stopped)\n return Promise.reject(new Error(\"@demlik/tea: runtime stopped\"));\n const next = tail.then(() => {\n if (bootError !== null) throw bootError;\n return stepDispatch(msg);\n });\n // Swallow the rejection on the tail so a single failing dispatch does NOT\n // poison every subsequent one. The original `next` promise still rejects\n // for the dispatch caller.\n tail = next.catch(() => {});\n return next;\n }\n\n /**\n * Drain the serial tail to quiescence. Quiescence = the tail stopped\n * advancing: every interpret follow-up calls `enqueueDispatch`, which\n * reassigns `tail` SYNCHRONOUSLY (before the parent step resolves), so\n * awaiting the current `tail` and re-reading it catches every transitively\n * enqueued follow-up. We loop until the reference is stable across an await.\n *\n * Bounded by `idleCap` — on cap we REJECT with `QuiescenceTimeoutError`\n * (never a silent fall-through-resolve), so a livelocking machine stays\n * distinguishable from a genuinely quiesced one (invariant 6 — issue #51).\n *\n * Shared by `runtime.idle()` and the quiescent `dispatch` default (#50): one\n * drain definition, one cap, one reject contract.\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`: run-to-quiescence by default (#50). Enqueue the Msg\n * on the tail, await its OWN transition first (so a reducer / save / interpret\n * throw on THIS Msg surfaces here, before any drain), then — unless the caller\n * asked for a single step — drain the transitive follow-up chain.\n *\n * `{ settle: \"once\" }` (and the `dispatchOnce` alias) skip the drain: resolve\n * after the one transition and leave the follow-up chain racing on the tail\n * for a caller that will `await runtime.idle()` itself.\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 // Kick off boot-effects immediately. Boot rejections are remembered on\n // `bootError`; they surface on every subsequent dispatch call. Note: when\n // `store` is absent the synchronous-init step above already set `state` —\n // `stepBootEffects` only runs save/sub/interpret/listener-fire. When\n // `store` is present, the full async path runs here.\n //\n // `bootPromise` is the original (un-swallowed) promise — it is what\n // `runtime.ready` chains off so callers see the boot error directly. `tail`\n // gets the swallowed branch so a boot failure does NOT poison every\n // subsequent dispatch's `.then` chain (each dispatch surfaces `bootError`\n // explicitly inside `enqueueDispatch`).\n const bootPromise = stepBootEffects();\n tail = bootPromise.catch((err) => {\n bootError = err;\n });\n\n // `ready` resolves to the booted `Runtime` (issue #45). It chains off the\n // un-swallowed `bootPromise` so boot failures reject it directly — meaning a\n // failed boot never hands out a `Runtime`, which is what keeps `getState()`\n // total. The `runtime` reference resolves at `.then` time (a later\n // microtask), well after the object literal below finishes initializing, so\n // the forward reference is safe. Built once → idempotent: the same settled\n // promise comes back on every `ready` read.\n const readyPromise: Promise<Runtime<S, M, E>> = bootPromise.then(\n () => runtime,\n );\n\n const runtime: Runtime<S, M, E> = {\n dispatch: dispatchToQuiescence,\n dispatchOnce: enqueueDispatch,\n getState(): S {\n // TOTAL (issue #45). A `Runtime` is only obtainable by awaiting `ready`,\n // which resolves AFTER boot has run `init` and set `state` — so by the\n // time any holder of this object can call `getState()`, `state` is\n // defined. No `state === undefined` throw branch: that was the old lie\n // (declared total on the synchronously-returned handle, threw under a\n // store pre-boot). Boot failures never reach here either — `ready`\n // rejects, so no `Runtime` is ever handed out on a failed boot.\n //\n // The cast encodes that invariant. The lone way to reach it with `state`\n // still undefined would be to cast the BootingRuntime to Runtime and call\n // `getState()` before `ready` — which the type system now forbids without\n // an explicit, deliberate cast.\n return state as S;\n },\n subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n observe(observer: (msg: M, state: S) => void): () => void {\n observers.add(observer);\n return () => {\n observers.delete(observer);\n };\n },\n onBoot(handler: (state: S) => void): () => void {\n // Already booted → fire immediately with the captured initial State so a\n // late subscriber never misses the one-shot boot (#47). It does not park\n // (boot is a single past event), so the returned cleanup is a no-op.\n if (booted && state !== undefined) {\n try {\n handler(state);\n } catch (err) {\n console.error(err);\n }\n return () => {};\n }\n bootHandlers.add(handler);\n return () => {\n bootHandlers.delete(handler);\n };\n },\n on<K extends E[\"type\"]>(\n type: K,\n handler: (event: Extract<E, { type: K }>) => void,\n ): () => void {\n // The registry is monomorphic (`Set<(event: E) => void>`); the per-type\n // narrowing lives at THIS call site — `handler` accepts the `K`-narrowed\n // event, and `fireEvents` only ever routes an event to its own type's\n // bucket, so the erased call is sound.\n let bucket = eventHandlers.get(type);\n if (bucket === undefined) {\n bucket = new Set<(event: E) => void>();\n eventHandlers.set(type, bucket);\n }\n const erased = handler as (event: E) => void;\n bucket.add(erased);\n return () => {\n const set = eventHandlers.get(type);\n if (set === undefined) return;\n set.delete(erased);\n if (set.size === 0) eventHandlers.delete(type);\n };\n },\n subscribePort<T>(port: Port<T>, listener: (value: T) => void): () => void {\n const key = port as Port<unknown>;\n let bucket = portRegistry.get(key);\n if (!bucket) {\n bucket = new Set<(value: unknown) => void>();\n portRegistry.set(key, bucket);\n }\n // The bucket stores `(value: unknown) => void` to keep the registry\n // monomorphic; per-port type safety lives at this call site (the\n // listener's `T` is bound to the port's phantom `__t`).\n const erased = listener as (value: unknown) => void;\n bucket.add(erased);\n return () => {\n const set = portRegistry.get(key);\n if (!set) return;\n set.delete(erased);\n if (set.size === 0) portRegistry.delete(key);\n };\n },\n emitPort<T>(port: Port<T>, value: T): void {\n // Same closure as `ctx.emit` inside interpret — synchronous fanout,\n // no-op on no subscribers, listener-throw isolation. Exposing it on\n // the Runtime interface lets `observe`-driven Port emission work\n // without spawning a \"tell the outside world\" Cmd per transition.\n portEmit(port, value);\n },\n // `ready` is assigned just below (it must resolve to `runtime` itself,\n // which can't be referenced inside its own object literal). See the\n // assignment + rationale after this block.\n ready: readyPromise,\n idle(): Promise<void> {\n // Quiescence = the tail stopped advancing (full rationale on\n // `drainToQuiescence`). `idle()` is the direct await of that drain; the\n // quiescent `dispatch` default reuses the SAME helper after its own\n // transition. The cap + `QuiescenceTimeoutError` reject (no silent\n // fall-through-resolve) keeps \"quiesced\" and \"gave up\" distinguishable —\n // invariant 6: no silent failures.\n return drainToQuiescence();\n },\n result(): S | undefined {\n // TOTAL — `state` is defined for any holder of a `Runtime` (see\n // `getState`). The terminal predicate is the ONLY thing that decides\n // \"finished\"; with none supplied `isTerminal` is `() => false`, so a\n // non-terminating machine reads `undefined` forever (#46).\n const current = state as S;\n return isTerminal(current) ? current : undefined;\n },\n done(): Promise<S> {\n // Already terminal → resolve at once with the live State (never parks, so\n // a post-termination call can't miss the terminal transition).\n const current = state as S;\n if (isTerminal(current)) return Promise.resolve(current);\n // Otherwise park a resolver; `settleDoneWaiters` drains it on the\n // transition that first makes `isTerminal` hold. With no predicate this\n // promise never resolves — the documented \"no natural completion\" case.\n return new Promise<S>((resolve) => {\n doneWaiters.add(resolve);\n });\n },\n async stop(): Promise<void> {\n stopped = true;\n // Drain whatever is in flight before tearing down. Any rejections in\n // the tail were already swallowed at enqueue time, so this await\n // always resolves.\n await tail;\n // Run every active sub cleanup. Throws are swallowed + logged so one\n // bad cleanup does not strand the others.\n for (const [id, cleanup] of subRegistry) {\n try {\n cleanup();\n } catch (err) {\n console.error(err);\n }\n subRegistry.delete(id);\n }\n // Flush final state. A save throw here does not reject `stop()` — the\n // contract is \"returns a resolved Promise\" — but it IS the loss of the\n // last write, so route it to the error sink rather than swallowing it\n // with a bare console.error. Invariant 6: no silent failures.\n if (store && state !== undefined && bootError === null) {\n try {\n await store.save(state);\n } catch (error) {\n reportError(error, { phase: \"stop-save\" });\n }\n }\n },\n };\n\n return runtime;\n}\n\n// === historyTracker: composable observability helper ===\n//\n// A bounded ring buffer of recent `(msg, state)` transitions, built on the\n// Runtime's public `observe` API. The substrate itself stays minimal — it\n// has no notion of history, no opt to enable one, no method on the Runtime\n// interface to read one. Anything that wants \"recent transitions\" composes\n// via this helper. The substrate provides the primitive (`observe`); this\n// is the bookkeeping that builds on it.\n//\n// Use cases:\n// - Bridge backlog replay for late subscribers (see `@demlik/tea/extension`).\n// - Debug inspectors showing recent traffic on attach.\n// - Sentry breadcrumb collection.\n// - Property/contract tests that want to assert \"these N transitions\n// happened in this order.\"\n//\n// Discipline:\n// - One-line creation: `const tr = historyTracker(runtime, 100);`\n// - Snapshot at any time: `tr.snapshot()` — shallow copy, oldest first.\n// - Detach when done: `tr.stop()` — unsubscribes the observer; the buffer\n// freezes at its current contents (snapshot() still works, useful for\n// post-hoc inspection in tests).\n//\n// Why not put this on the Runtime interface as `runtime.history()`?\n// Because history is derivable. Putting it on the Runtime privileges one\n// use case (debug inspectors) at the substrate level and grows the\n// interface for everyone. The composition shape stays out of the dispatch\n// loop entirely — the substrate's hot path doesn't pay any cost for a\n// feature it doesn't need to provide.\n// =============================================================================\n\nexport interface HistoryTracker<S, M extends { type: string }> {\n /**\n * Snapshot of recorded transitions, oldest first. Each entry is\n * `{ msg, state }` exactly as the underlying `observe` callback received\n * it — `msg` is `null` for the boot transition, a Msg for every other.\n *\n * Returns a shallow copy: the array is fresh on every call (callers may\n * iterate, slice, or replay without affecting the tracker's buffer).\n * Entry values are references to the originals (TEA states/msgs are\n * conventionally immutable; do not mutate them if your S/M is not).\n */\n snapshot(): readonly { readonly msg: M | null; readonly state: S }[];\n /**\n * Detach the underlying observer. After `stop()`:\n * - No new transitions are recorded.\n * - `snapshot()` continues to work and returns the buffer's contents\n * at the moment of stop (useful for post-hoc inspection in tests).\n * - The tracker holds no references that would prevent GC of the\n * `runtime` argument other than the entries already buffered.\n *\n * Idempotent — subsequent calls are no-ops.\n */\n stop(): void;\n}\n\n/**\n * Create a bounded history tracker over a Runtime. The tracker subscribes\n * to the runtime via `observe(...)` and retains the last `size` `(msg,\n * state)` transitions in a FIFO ring buffer.\n *\n * @param runtime Any `BootingRuntime<S, M>` (a full `Runtime` satisfies it).\n * The tracker uses only `observe`, which is total before boot —\n * attach it to the synchronous `run()` handle to record the\n * boot transition.\n * @param size Buffer cap. `size <= 0` produces an inert no-op tracker:\n * no observer is attached to the runtime, `snapshot()` is\n * always `[]`, and `stop()` is a no-op. (Prefer skipping the\n * tracker entirely in that case — this arm just makes a\n * non-positive `size` harmless rather than an error.)\n *\n * Memory cost: O(size × avg(msg + state size)). For audit-extension's\n * inspector use case (size 100, audit state ~5KB), ~500KB max — held\n * in-memory only, no persistence.\n */\nexport function historyTracker<S, M extends { type: string }>(\n runtime: BootingRuntime<S, M>,\n size: number,\n): HistoryTracker<S, M> {\n const buffer: { msg: M | null; state: S }[] = [];\n let stopped = false;\n\n const push = (entry: { msg: M | null; state: S }): void => {\n buffer.push(entry);\n if (buffer.length > size) buffer.shift();\n };\n\n // The boot transition (the `{ msg: null, state }` head entry the old\n // `observe(msg === null)` arm recorded) now arrives via `onBoot` (#47);\n // applied transitions arrive via `observe` with a total `msg`. The public\n // snapshot shape (`msg: M | null`) is unchanged — boot is still `msg: null`.\n const unobserve =\n size <= 0\n ? (): void => {}\n : runtime.observe((msg, state) => {\n push({ msg, state });\n });\n const unboot =\n size <= 0\n ? (): void => {}\n : runtime.onBoot((state) => {\n push({ msg: null, state });\n });\n\n return {\n snapshot() {\n // Shallow copy — callers may freely iterate or mutate the returned\n // array. Entry values are references; see HistoryTracker docs.\n return buffer.slice();\n },\n stop() {\n if (stopped) return;\n stopped = true;\n unobserve();\n unboot();\n },\n };\n}\n\n// === replay: pure unit-test helper ===\n//\n// Composes `init(loaded ?? null, ctx)` then `update(state, msg)` for each msg.\n// Returns the final state plus the cmds that *would* have been emitted and the\n// subs that *would* have been desired at the final state.\n//\n// Crucially:\n// - does NOT call any `interpret[type]` handler\n// - does NOT touch `Store`\n// - does NOT start any subscription via `machine.subscribe?.[type]`\n//\n// `subscriptions` (the description function) IS called to derive the returned\n// `subs` array. That's intentional — it lets tests assert what would be wired\n// up without actually wiring it.\nexport function replay<\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: { msgs: readonly M[]; ctx: Ctx; loaded?: S | null },\n): { state: S; cmds: C[]; subs: U[] } {\n // `init` is `(loaded: S | null, ctx) => ...`; coerce undefined → null so the\n // AC \"`replay` with `loaded: undefined` calls `init(null, ctx)`\" holds.\n const loaded = opts.loaded ?? null;\n const [initialState, initCmds] = machine.init(loaded, opts.ctx);\n\n if (loaded !== null && initCmds.length > 0) {\n throw new Error(\n \"TEA contract violation: machine.init must return [state, []] (no Cmds) \" +\n \"when `loaded` is non-null. Init's rehydrate branch is a pure state \" +\n \"passthrough — the migration/parse boundary, not the boot-effect hook.\\n\\n\" +\n \"Routes for boot effects:\\n\" +\n \" - Stateless infrastructure (e.g. chrome.alarms ensure) → host module \" +\n \"top level, outside TEA.\\n\" +\n \" - State-conditional resume (e.g. re-attach a session) → a `boot` Msg \" +\n \"dispatched once from the host after `run(...)` returns, handled in update.\\n\\n\" +\n \"See .patterns/tea/tea-invariants.md (Invariant 2).\",\n );\n }\n\n // `replay` and `foldMsgs` share ONE internal fold (`foldUpdates`), keyed on\n // `formOf` — the same reader `run` uses — so all three agree on the\n // reducer-vs-transitions form by construction. `replay` wraps it with the\n // `init` entry + Cmd/Sub collection; `foldMsgs` calls it from a base state\n // and returns the state only.\n const { state, cmds: foldedCmds } = foldUpdates<S, M, C>(\n machine,\n initialState,\n opts.msgs,\n );\n const cmds: C[] = [...initCmds, ...foldedCmds];\n\n const subs: U[] = machine.subscriptions\n ? [...machine.subscriptions(state)]\n : [];\n\n return { state, cmds, subs };\n}\n\n// === tryInterpret: Railway sugar over `Result.tryPromise` ===\n//\n// Wraps a fallible `(cmd, ctx) => Promise<Ok>` and returns a handler suitable\n// for `interpret[type]`. On success the returned function resolves with\n// `onOk(value, cmd)`; on rejection it resolves with `onErr(error, cmd)`. It\n// NEVER rejects (assuming `onOk`/`onErr` are total — that's the caller's\n// contract).\n//\n// Implementation note: we use the `{try, catch: (e) => e}` form of\n// `Result.tryPromise` rather than the one-arg `(thunk)` form so the original\n// error passes through untouched. The one-arg form wraps errors in\n// `UnhandledException`, which would prevent callers from `instanceof`-checking\n// their own error types inside `onErr`.\n//\n// M is a Msg type (not a Cmd) — the helper routes Ok/Err to two Msg constructors.\nexport function tryInterpret<C extends Cmd, Ok, M, Ctx>(\n work: (cmd: C, ctx: Ctx) => Promise<Ok>,\n onOk: (value: Ok, cmd: C) => M,\n onErr: (error: unknown, cmd: C) => M,\n): (cmd: C, ctx: Ctx) => Promise<M> {\n return async (cmd, ctx) => {\n const result = await Result.tryPromise({\n try: () => work(cmd, ctx),\n catch: (error: unknown): unknown => error,\n });\n return result.match({\n ok: (value) => onOk(value, cmd),\n err: (error) => onErr(error, cmd),\n });\n };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pure/core.ts"],"names":[],"mappings":"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","file":"chunk-QLA76GIT.js","sourcesContent":["/**\n * @demlik/tea — pure-core leaf (ADR 0006).\n *\n * This is the runtime-free pure core of the substrate. It holds the\n * client-prediction fold seam (`foldMsgs`), the form reader\n * (`formOf`/`detectUpdateForm`), and the pure type vocabulary (`Machine`,\n * `Reducer`, `Transitions`, `Cmd`, `Sub`, `Port`, …).\n *\n * **Dependency direction (the actual decoupling):** this module imports\n * NOTHING from the runtime — no `better-result`, no `run`/host/`Store`. The\n * runtime (`run`, the host, interpret, `Store`, subscribe — all in\n * `../index.ts`) imports *from* here; never the reverse. The\n * `@demlik/tea/pure` subpath re-exports this surface, and\n * `pure/import-graph.test.ts` is the regression fence asserting the pure\n * entrypoint's import graph never reaches `run`.\n */\n\n// === Dev-mode invariant enforcement ===\n//\n// Three runtime guards that catch TEA invariant violations during development.\n// Dead-code-eliminated in production by any bundler that replaces\n// `import.meta.env.DEV` (Vite) or `process.env.NODE_ENV` (webpack/esbuild).\n//\n// 1. deepFreeze(state) before passing to update — catches mutation\n// 2. Thenable check on return value — catches async update\n// 3. Walk cmd fields for function types — catches closures-as-data\n//\n// These enforce invariants 1 and 2 at runtime where the type system cannot.\nexport const __DEV__: boolean = (() => {\n try {\n const meta = import.meta as unknown as { env?: { DEV?: boolean } };\n if (meta.env?.DEV !== undefined) {\n return meta.env.DEV;\n }\n } catch {\n // import.meta not available (Node CJS, service workers)\n }\n try {\n return (\n typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\"\n );\n } catch {\n return false;\n }\n})();\n\nexport function deepFreeze<T>(obj: T, seen?: WeakSet<object>): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const proto = Object.getPrototypeOf(obj);\n if (proto !== Object.prototype && proto !== Array.prototype && proto !== null)\n return obj;\n const visited = seen ?? new WeakSet();\n if (visited.has(obj as object)) return obj;\n visited.add(obj as object);\n Object.freeze(obj);\n for (const v of Object.values(obj as Record<string, unknown>))\n deepFreeze(v, visited);\n return obj;\n}\n\nexport function hasFunctionValues(obj: unknown): string | null {\n if (obj === null || typeof obj !== \"object\") return null;\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (typeof value === \"function\") return key;\n }\n return null;\n}\n\nexport function assertPureResult(result: unknown, msgType: string): void {\n if (result && typeof (result as { then?: unknown }).then === \"function\") {\n throw new Error(\n `@demlik/tea: update cell \"${msgType}\" returned a Promise. ` +\n `update must be synchronous. Move async work to interpret.`,\n );\n }\n if (\n !Array.isArray(result) ||\n result.length < 2 ||\n !Array.isArray(result[1])\n ) {\n throw new Error(\n `@demlik/tea: update cell \"${msgType}\" returned a non-tuple. ` +\n `Expected [State, Cmd[]], got ${typeof result}.`,\n );\n }\n const cmds = result[1] as readonly { type: string }[];\n for (const cmd of cmds) {\n const fnField = hasFunctionValues(cmd);\n if (fnField !== null) {\n throw new Error(\n `@demlik/tea: Cmd \"${cmd.type}\" has function field \"${fnField}\". ` +\n `Cmds must be plain data, not closures.`,\n );\n }\n }\n}\n\n// === Cmd: tagged-union, one-shot effect ===\nexport type Cmd<T extends string = string> = { type: T };\n\n// === SyncReturn<S, C>: the compile-time reentrancy guard (ADR 0003 #5) ===\n//\n// The canon rule (`.patterns/tea-do/reentrancy.md`,\n// `.patterns/tea-do/reentrancy-deadlock.md`): a reducer must never\n// inline-`await` a re-entrant result. \"Non-reentrant by default\" ≡ \"the reducer\n// is pure AND synchronous\": a reducer that returns a `Promise` (because it is\n// `async`, or inline-`await`s) is the suspended-but-still-blocking turn Orleans\n// serializes against — it occupies the single-writer slot across a suspension\n// point and can be re-entered mid-flight → deadlock.\n//\n// So the guard is purely type-level: every reducer/transition cell returns\n// `SyncReturn<S, C>` — the synchronous result tuple, intersected with `{ then?:\n// never }` to make it *non-thenable*. A `Promise<readonly [S, readonly C[]]>`\n// is an object with a `then` method, so it fails to satisfy `then?: never`; an\n// `async (s, m) => [...]` (or one that inline-`await`s) is therefore\n// unrepresentable at compile time — `tsc` rejects it *at the cell*, not at a\n// runtime hang. A plain synchronous tuple `[next, cmds]` has no `then` property,\n// so the optional-absent `then?: never` is satisfied and existing pure reducers\n// compile unchanged. `SyncReturn<S, C>` is also assignable *to* `readonly [S,\n// readonly C[]]` (the intersection is narrower), so the runtime dispatch in\n// `run`/`replay` consumes a cell result exactly as before.\n//\n// This mirrors `retry-backoff`'s `Rng` brand (#63): the obligation lives in the\n// type at the construction boundary (\"parse, don't validate\"), not in a comment.\nexport type SyncReturn<S, C extends Cmd> = readonly [S, readonly C[]] & {\n // A thenable carries a callable `then`; forbidding it rejects every Promise.\n // Optional + `never` means \"absent on a sync tuple, impossible on a Promise\".\n readonly then?: never;\n};\n\n// === update form: Reducer vs Transitions, tagged once ===\n//\n// The substrate accepts two `update` shapes (see the `Machine.update` union and\n// the `defineMachine` overloads):\n//\n// - \"reducer\" — flat record keyed by Msg.type, every cell a function.\n// - \"transitions\" — 2D table keyed by State.type then Msg.type, every cell a\n// record of functions.\n//\n// Which one a given machine is must be known at runtime by `run`, `replay`, and\n// the `withX` wrappers (their dispatch / key-enumeration / reserved-namespace\n// scans differ per form). Rather than re-derive it structurally at every reader\n// — a `typeof update[firstKey] === \"function\"` heuristic that breaks the day a\n// reducer cell is itself an object-with-a-call — `defineMachine` computes the\n// form ONCE at construction and stamps it on the machine as a non-enumerable\n// `__form` (so it never serializes, never collides with a Msg.type key, never\n// shows up in `Object.keys(machine)`). Every reader calls `formOf(machine)`.\nexport type UpdateForm = \"reducer\" | \"transitions\";\n\n// The single structural heuristic, defined ONCE. Used only by `defineMachine`\n// (and `formOf`'s fallback for machines built without it, e.g. a plain object\n// literal annotated as `Machine`). A Reducer's first own value is a function; a\n// Transitions table's first own value is a record (of functions). An empty\n// record (`M` is `never`) can never dispatch a Msg, so the form is irrelevant —\n// \"reducer\" is returned arbitrarily.\nexport function detectUpdateForm(update: object): UpdateForm {\n const firstKey = Object.keys(update)[0];\n if (firstKey === undefined) return \"reducer\";\n const firstValue = (update as Record<string, unknown>)[firstKey];\n return typeof firstValue === \"function\" ? \"reducer\" : \"transitions\";\n}\n\n// The single reader every form-sensitive site goes through. Prefers the\n// `__form` tag stamped by `defineMachine` (authoritative — computed once at the\n// typed construction boundary); falls back to `detectUpdateForm` only for a\n// machine that never passed through `defineMachine`. No reader re-implements the\n// heuristic.\nexport function formOf(machine: {\n update: object;\n __form?: UpdateForm;\n}): UpdateForm {\n return machine.__form ?? detectUpdateForm(machine.update);\n}\n\n// === applyCell: THE single reducer-vs-transitions dispatch primitive ===\n//\n// Applies the one update cell selected by `(formOf(machine), state, msg)` and\n// returns its `[nextState, cmds]` verbatim. Every site that steps a machine —\n// `run`'s applyUpdate, `foldUpdates` (replay/foldMsgs), the PBT fold runner,\n// and the withX wrappers — dispatches through THIS function, so production and\n// the verification tools agree on the update form by construction (#275).\n// Pure and dev-check-free: `deepFreeze`/`assertPureResult` stay at the call\n// sites that want them.\nexport function applyCell<S, M extends { type: string }, C extends Cmd>(\n machine: { update: object; __form?: UpdateForm },\n state: S,\n msg: M,\n): readonly [S, readonly C[]] {\n type CellFn = (state: S, msg: M) => readonly [S, readonly C[]];\n if (formOf(machine) === \"reducer\") {\n const record = machine.update as Record<string, CellFn>;\n // biome-ignore lint/style/noNonNullAssertion: the Reducer mapped type guarantees a cell for every dispatched Msg.type; `!` required under noUncheckedIndexedAccess and cannot miss for a Msg the machine accepts\n return record[msg.type]!(state, msg);\n }\n const table = machine.update as Record<string, Record<string, CellFn>>;\n // The ONE sanctioned `state as unknown as { type }` read: the Transitions\n // overload constrains S to `{ type: string }` at the `defineMachine`\n // boundary, but that constraint is erased on the runtime-facing `object`\n // here — every former per-site copy of this double-cast collapsed into\n // this line (#275).\n const stateKey = (state as unknown as { type: string }).type;\n // biome-ignore lint/style/noNonNullAssertion: the Transitions overload guarantees every (state.type × msg.type) cell exists; `!` required under noUncheckedIndexedAccess and cannot miss at runtime\n return table[stateKey]![msg.type]!(state, msg);\n}\n\n// === msgKeysOf: recover the Msg.type set from either update form ===\n//\n// A Reducer's own keys ARE the Msg.type set. A Transitions table's keys are\n// state.type; its INNER keys are the Msg.type set (uniform across phases by\n// the mapped-type contract), so the first phase's inner keys are read. An\n// empty update (`M` is `never`) yields `[]`. Keyed on `formOf` — the withX\n// wrappers and the PBT `msgTypeKeys` all read through this one helper (#275).\nexport function msgKeysOf(machine: {\n update: object;\n __form?: UpdateForm;\n}): readonly string[] {\n const keys = Object.keys(machine.update);\n const firstKey = keys[0];\n if (firstKey === undefined) return [];\n if (formOf(machine) === \"reducer\") return keys;\n const firstValue = (machine.update as Record<string, unknown>)[firstKey];\n return Object.keys(firstValue as object);\n}\n\n// === Reducer<S, M, C>: record-of-handlers form of `update` ===\n//\n// Flat dispatch table keyed by `Msg.type`. Each cell is a pure transition for\n// a single Msg variant, narrowed via `Extract<M, { type: K }>`. The mapped type\n// is *load-bearing*: adding a Msg variant without a matching key in the\n// Reducer record is a compile error — no `absurd()` helper at call sites, no\n// silent fall-through, no default branch hiding impurity.\n//\n// Strengthens invariant 2 (pure transitions — the record form has no\n// fall-through default to hide impurity behind) and invariant 7 (identity is\n// explicit — the Msg variant set is load-bearing at the type level).\n//\n// `defineMachine` accepts the Reducer record form via overload. The runtime\n// dispatches via `update[msg.type](state, msg)`.\nexport type Reducer<S, M extends { type: string }, C extends Cmd> = {\n [K in M[\"type\"]]: (\n state: S,\n msg: Extract<M, { type: K }>,\n // `SyncReturn<S, C>` (not `readonly [S, readonly C[]]`) is the reentrancy\n // guard: a non-thenable return type, so an `async`/inline-`await`ing cell —\n // which returns `Promise<...>` — fails to compile at the cell. See the\n // `SyncReturn` doc above.\n ) => SyncReturn<S, C>;\n};\n\n// === Transitions<S, M, C>: table form of `update` for state-machine-shaped machines ===\n//\n// When `State` is itself a discriminated union (`State.type` is the active\n// phase), the table form makes every (state.type × msg.type) cell explicit at\n// the type level. Missing a cell — e.g. forgetting how `phase: \"settling\"`\n// handles `msg.type: \"tab_stepped\"` — fails to compile. No nested switch, no\n// fall-through, no `_ -> (state, [])` default branch hiding regressions.\n//\n// Each cell receives the narrowed `State` for its phase and the narrowed\n// `Msg` for its variant via two `Extract` lookups. Phantom-narrow at the\n// type level; pure data at runtime.\n//\n// `defineMachine` accepts a third `update` form for `Transitions<S, M, C>` —\n// the runtime dispatches via `update[state.type][msg.type](state, msg)`.\n//\n// Strengthens invariant 2 (table form has no fall-through default),\n// invariant 6 (runtime walks the table predictably, no hidden dispatch\n// fallback), and invariant 7 (both state.type and msg.type are load-bearing\n// at the type level).\nexport type Transitions<\n S extends { type: string },\n M extends { type: string },\n C extends Cmd,\n> = {\n [P in S[\"type\"]]: {\n [K in M[\"type\"]]: (\n state: Extract<S, { type: P }>,\n msg: Extract<M, { type: K }>,\n // Same reentrancy guard as `Reducer`: a transition cell that returns a\n // Promise (async / inline-await) is the suspended-blocking turn the canon\n // forbids, so its return is the non-thenable `SyncReturn<S, C>`.\n ) => SyncReturn<S, C>;\n };\n};\n\n// === Cmd: namespace for conditional Cmd emission ===\n//\n// Two recurring frictions in reducer cells:\n//\n// 1. \"Emit cmd X only when condition Y holds.\"\n// Hand-rolled: `cond ? [{ type: \"x\", ... }] : []`\n// With Cmd: `Cmd.when(cond, { type: \"x\", ... })`\n//\n// 2. \"Emit cmd X carrying a value that may be undefined; if undefined,\n// emit nothing.\"\n// Hand-rolled: `value !== undefined ? [{ type: \"x\", value }] : []`\n// With Cmd: `Cmd.whenDefined(value, (v) => ({ type: \"x\", value: v }))`\n//\n// Both forms return `readonly C[]` so they spread cleanly into the cmds\n// array returned by a Transitions cell:\n//\n// return [next, [\n// ...Cmd.when(state.tabId !== undefined, { type: \"detach_debugger\", tabId: state.tabId }),\n// ...Cmd.whenDefined(state.queueItemId, (id) => ({\n// type: \"queue:complete\", queueItemId: id, status: \"done\",\n// })),\n// ]];\n//\n// `Cmd` as a value namespace coexists with `Cmd<T>` as a type — TypeScript's\n// declaration merging puts them in different name spaces (type vs. value).\n// The reader sees `Cmd<...>` in type position and `Cmd.when(...)` in\n// expression position; no ambiguity at use sites.\n//\n// Why a namespace and not top-level `when` / `whenDefined`: `when` at module\n// scope reads as a generic conditional, but these helpers are specifically\n// for *Cmd emission* (return `readonly C[]`, the cmds-array contract of\n// every Transitions cell). The namespace pins that intent at the call site.\nexport const Cmd = {\n /**\n * The empty Cmd array. Typed `readonly never[]` so it's assignable to any\n * `readonly C[]` for any `C extends Cmd`. Use in `init` returns and\n * Transitions cells that emit zero effects:\n *\n * init: (loaded) => [loaded ?? initial, Cmd.none],\n * tick: (state) => [state, Cmd.none],\n *\n * Elm's `Cmd.none` analogue. Cultural signal alongside the runtime help:\n * `[state, Cmd.none]` reads as intent (\"this transition emits nothing\"),\n * `[state, []]` reads as \"empty array of what\".\n *\n * Frozen at runtime so a downstream consumer can't `.push()` into the\n * shared reference.\n */\n none: Object.freeze([]) as readonly never[],\n\n /**\n * Flat concat of cmd arrays. Use when a cell composes effects from\n * multiple conditional sources:\n *\n * return [next, Cmd.batch(\n * Cmd.whenDefined(state.queueItemId, (id) => ({ type: \"complete\", id })),\n * Cmd.when(state.windowId !== undefined, { type: \"close_window\", windowId }),\n * [{ type: \"detach_debugger\", tabId: state.tabId }],\n * )];\n *\n * `<const C>` keeps inline cmd literals' discriminants narrow (same\n * rationale as `when` / `whenDefined` above).\n */\n batch: <const C extends Cmd>(\n ...arrs: readonly (readonly C[])[]\n ): readonly C[] => {\n const out: C[] = [];\n for (const arr of arrs) out.push(...arr);\n return out;\n },\n\n /**\n * Emit `cmd` wrapped in a single-element array iff `cond` is true.\n * Otherwise return the empty array. Spreads cleanly into the cmds array\n * returned by a Transitions cell.\n *\n * `const C` (TypeScript 5.0+) keeps the inferred `type:` discriminator\n * literal narrow when called with an object literal — so `Cmd.when(b,\n * { type: \"x\", ... })` infers as `{ type: \"x\", ... }`, not `{ type:\n * string, ... }`, and stays assignable to a discriminated-union arm\n * without `as const` at the call site.\n */\n when: <const C extends Cmd>(cond: boolean, cmd: C): readonly C[] =>\n cond ? [cmd] : [],\n\n /**\n * If `value` is defined, call `build(value)` and emit the resulting cmd\n * wrapped in a single-element array. If `value` is `undefined`, return\n * the empty array. `value` is narrowed to `T` inside `build`.\n *\n * `const C` on the return type keeps the callback's object-literal\n * `type:` field narrow (see `when` above for the full rationale).\n */\n whenDefined: <T, const C extends Cmd>(\n value: T | undefined,\n build: (value: T) => C,\n ): readonly C[] => (value !== undefined ? [build(value)] : []),\n} as const;\n\n// === SubId: branded string for Sub identity ===\n//\n// Subs are reconciled by id on every transition (canon §2.5, invariant 7).\n// Before branding, `Sub.id` was a raw `string` — a typo at one call site and a\n// different typo at another would silently produce two distinct subs that\n// looked identical to a human reader. The brand makes id construction explicit\n// (`subId(\"...\")`) and catches accidental string-where-SubId at the type\n// level.\n//\n// The brand is structural: `string & { __brand: \"SubId\" }`. The `subId(s)`\n// constructor is the ONE permitted cast in the substrate — every other call\n// site must go through it.\nexport type SubId = string & { readonly __brand: \"SubId\" };\n\n/**\n * Construct a `SubId` from a string. Use this everywhere a Sub literal is\n * built — including ids derived from runtime data (`subId(\\`ws:${auditId}\\`)`).\n * The brand is structural, not nominal, so dynamic ids are permitted; the\n * constructor's job is to make the identity decision explicit at every call\n * site so the type system can catch accidental raw-string drift.\n *\n * Strengthens invariant 7 (identity is explicit).\n */\nexport function subId(s: string): SubId {\n return s as SubId;\n}\n\n// === Sub: tagged-union, continuous source of msgs; stable id used for diff/reconcile ===\nexport type Sub<T extends string = string> = { id: SubId; type: T };\n\n// === Port<T>: typed escape hatch for \"data leaving the runtime\" ===\n//\n// A Port is a named, typed channel that Cmd handlers can `emit` to via the\n// augmented `ctx.emit(port, value)`. Subscribers attach via\n// `runtime.subscribePort(port, listener)` and receive every emitted value\n// synchronously (same fanout discipline as `runtime.observe`).\n//\n// Why Ports are a substrate primitive, distinct from State and Observe:\n// - **State** is \"the world as the program sees it.\" Folding outgoing\n// announcements into State turns ephemeral signals into persisted facts —\n// the classic anti-pattern that motivated this primitive.\n// - **Observe** sees every transition `(msg, state)`. That's the right channel\n// for devtools and logging, but it forces every consumer to filter the\n// entire firehose for a single signal.\n// - **Ports** are typed and selective: one channel per concept, subscribers\n// only see what the handler chose to emit. Mirrors Elm's outgoing-port\n// semantics — a typed declaration with `port` keyword whose values flow\n// out of the program to the host.\n//\n// Port identity is **by reference** — the returned object IS the identity.\n// Name is metadata for debugging. To prevent the \"two modules definePort the\n// same name expecting to share a channel, silently get distinct ports\" class\n// of bug, `definePort` asserts the name has not been seen in this process —\n// the symmetric runtime check to `SubId` (canon §2.12, invariant 7). Each\n// definePort call must use a unique name; if two modules need the same port,\n// one module exports it and the other imports it.\nexport interface Port<T> {\n readonly __brand: \"port\";\n readonly name: string;\n // Phantom field — never assigned, never read at runtime. Carries `T` for\n // inference at `ctx.emit` and `subscribePort` call sites. Using `?` so\n // `definePort` can produce a Port without constructing a value of `T`.\n readonly __t?: T;\n}\n\n/**\n * Augmentation injected onto `ctx` inside Cmd handlers. Handlers receive\n * `ctx & PortEmitter` so they can call `ctx.emit(port, value)` synchronously.\n * Subscribers registered via `runtime.subscribePort(port, listener)` receive\n * the value immediately.\n *\n * Emitting to a port with no subscribers is a no-op (does not throw).\n */\nexport interface PortEmitter {\n emit<T>(port: Port<T>, value: T): void;\n}\n\n// === NoCtx: the context-free marker ===\n//\n// A handler, Sub, or machine that reads NOTHING from `ctx` says so by using\n// `NoCtx` for its Ctx slot. This is the DELIBERATE absence of context — not\n// the accidental `unknown` looseness that erodes a boundary (see issue #64).\n//\n// `unknown` at a Ctx seam is ambiguous: it can mean \"I intentionally need no\n// context\" OR \"I gave up tightening this type.\" `NoCtx` resolves that\n// ambiguity at the type level. A reader (and the next agent) sees `NoCtx` and\n// knows the context-free-ness is a choice the author made, not a hole.\n//\n// Shape: an empty readonly record. A `Ctx & PortEmitter` is-a `NoCtx`, so a\n// context-free handler still composes at a richer call site. It carries no\n// own fields, so the name's claim (\"reads nothing from ctx\") is what callers\n// read at the seam.\n//\n// Strengthens invariant 6 (no silent looseness — a context-free seam is named,\n// not inferred) and invariant 8 (the boundary is legible; `unknown` stays\n// reserved for genuine wire-edge erasure, not for \"didn't bother\").\nexport type NoCtx = Readonly<Record<never, never>>;\n\n/** Spelled-out alias for `NoCtx`, for sites that prefer the long name. */\nexport type ContextFree = NoCtx;\n\n// === Interpret<M, C, Ctx>: record-of-handlers form of `interpret` ===\n//\n// Flat dispatch table keyed by `Cmd.type`. Each cell receives the narrowed Cmd\n// and the runtime-augmented Ctx (`Ctx & PortEmitter`) and resolves to a\n// follow-up Msg or `void` (fire-and-forget). The mapped type makes a missing\n// handler a compile error — `defineMachine` cannot accept the dictionary until\n// every Cmd variant has one.\n//\n// Hoisted out of `Machine.interpret` so consumers can type a free-standing\n// handler dictionary with `Interpret<MyMsg, MyCmd, MyCtx>` instead of\n// re-declaring the mapped type at every effects module.\n//\n// Strengthens invariant 2 (the record form has no fall-through default to\n// hide impurity behind) and invariant 7 (identity is explicit — the Cmd\n// variant set is load-bearing at the type level).\nexport type Interpret<M extends { type: string }, C extends Cmd, Ctx> = {\n [K in C[\"type\"]]: (\n cmd: Extract<C, { type: K }>,\n ctx: Ctx & PortEmitter,\n // biome-ignore lint/suspicious/noConfusingVoidType: an interpret handler returns a follow-up Msg or nothing; `void` permits no-return bodies that `M | undefined` would reject\n ) => Promise<M | void>;\n};\n\n// === Subscribe<M, U, Ctx>: record-of-handlers form of `subscribe` ===\n//\n// Flat dispatch table keyed by `Sub.type`. Each cell receives the narrowed\n// Sub, the Ctx, and a `dispatch` to fire follow-up Msgs. Returns a cleanup\n// function the substrate calls when the Sub is reconciled out (state\n// transitioned away). The mapped type guarantees every Sub variant has a\n// handler at the type level; runtime dispatch is a single property lookup.\n//\n// Hoisted out of `Machine.subscribe` so consumers can type a free-standing\n// handler dictionary with `Subscribe<MyMsg, MySub, MyCtx>` instead of\n// re-declaring the mapped type at every subs module.\n//\n// Strengthens invariant 7 (identity is explicit — the Sub variant set is\n// load-bearing at the type level).\nexport type Subscribe<M extends { type: string }, U extends Sub, Ctx> = {\n [K in U[\"type\"]]: (\n sub: Extract<U, { type: K }>,\n ctx: Ctx,\n dispatch: (msg: M) => void,\n ) => () => void;\n};\n\n// === Machine: pure data, host-agnostic ===\n//\n// `update` is stored as a union of the two record forms — the runtime branches\n// on shape at dispatch time. The public `defineMachine` overloads enforce one\n// shape per call so consumers never see the union themselves.\n//\n// - Reducer form: `Reducer<S, M, C>` — flat record keyed by Msg.type;\n// exhaustiveness enforced by the mapped type.\n// - Transitions form: `Transitions<S, M, C>` — 2D table keyed by\n// `state.type` then `msg.type`; only available when `S extends { type:\n// string }`. Conditional in the union so machines without a discriminated\n// State don't widen to include this branch.\n//\n// `M` is constrained to `{ type: string }` because both record forms require\n// a string discriminant.\n//\n// `interpret` is conditionally optional: when `C` is `Cmd<never>` the\n// interpret map is keyed by `never`, so the only valid value is `{}`. Forcing\n// every cmdless machine to write `interpret: {} as never` is ceremony for a\n// shape the type already pins. The tuple-wrap (`[C] extends [Cmd<never>]`)\n// disables distributive conditional so a real cmd union (e.g.\n// `Cmd<\"a\"> | Cmd<\"b\">`) doesn't degrade to optional just because one arm\n// happens to be `Cmd<never>`. Same trick the `update` field uses.\nexport type Machine<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n> = {\n /**\n * Boot the runtime. Called once by `run(...)` with whatever `Store.load()`\n * returned: `null` on fresh boot, the persisted state on rehydrate.\n *\n * **Contract:** when `loaded !== null`, init MUST return `[loaded, []]` —\n * no Cmds. Init's rehydrate branch is the migration / parse boundary, not\n * the boot-effect hook. See Invariant 2 in `.patterns/tea/tea-invariants.md`.\n *\n * Boot effects routes:\n * - Stateless infrastructure → host module top, outside TEA.\n * - State-conditional resume → a `boot` Msg the host dispatches once\n * after `run(...)` returns.\n *\n * Violations are caught at runtime by `replay` (which throws with a\n * pointer to the alternatives).\n */\n init: (loaded: S | null, ctx: Ctx) => readonly [S, readonly C[]];\n update:\n | Reducer<S, M, C>\n // Wrap in tuple to disable distributive conditional behavior. Without\n // `[S]`, TS distributes the conditional across `S`'s union members,\n // producing `Transitions<A,...> | Transitions<B,...>` instead of the\n // desired `Transitions<A|B,...>` — the table form needs a single\n // mapping over the full union of state types, not separate tables per\n // member.\n | ([S] extends [{ type: string }] ? Transitions<S, M, C> : never);\n subscriptions?: (state: S) => readonly U[];\n subscribe?: Subscribe<M, U, Ctx>;\n /**\n * The update form (\"reducer\" | \"transitions\"), stamped non-enumerably by\n * `defineMachine` at construction (see `UpdateForm` / `formOf`). Optional in\n * the type so the structural `Machine` annotation form keeps accepting plain\n * object literals; readers go through `formOf`, which falls back to\n * `detectUpdateForm` when the tag is absent. Never written by hand.\n */\n readonly __form?: UpdateForm;\n} & ([C] extends [Cmd<never>]\n ? { interpret?: Interpret<M, C, Ctx> }\n : { interpret: Interpret<M, C, Ctx> });\n\n// === foldUpdates: the single internal fold `replay` and `foldMsgs` share ===\n//\n// Folds `machine.update` over `msgs` from `initialState`, dispatching each\n// Msg through `applyCell` — the same primitive `run` uses — so every fold site\n// agrees on the reducer-vs-transitions form by construction (no second copy of\n// the dispatch to drift). Returns the final state plus the Cmds the cells emitted\n// along the way; the caller keeps them (`replay`) or discards them (`foldMsgs`).\n//\n// Touches no `Store`, no `interpret` handler, and starts no subscription — it\n// only calls `update` cells. The two public folds differ only in how they\n// enter and what they return: `replay` enters via `init` and returns\n// `{ state, cmds, subs }`; `foldMsgs` enters from a base state and returns `S`.\nexport function foldUpdates<S, M extends { type: string }, C extends Cmd>(\n machine: { update: object; __form?: UpdateForm },\n initialState: S,\n msgs: readonly M[],\n): { state: S; cmds: C[] } {\n let state: S = initialState;\n const cmds: C[] = [];\n\n for (const msg of msgs) {\n if (__DEV__) deepFreeze(state);\n\n const result = applyCell<S, M, C>(machine, state, msg);\n\n if (__DEV__) assertPureResult(result, msg.type);\n\n const [next, emitted] = result;\n state = next;\n cmds.push(...emitted);\n }\n\n return { state, cmds };\n}\n\n// === foldMsgs: runtime-free client-prediction fold seam (ADR 0006, #211) ===\n//\n// Folds `machine.update` over an ordered `Msg[]` starting from a caller-supplied\n// `base` state and returns the resulting state ONLY. This is the client-side\n// replay primitive a prediction loop needs (the Gambetta/Valve\n// authoritative-server reconcile step): re-simulate a queue of un-acked inputs\n// on top of an authoritative snapshot —\n// `foldMsgs(machine, snapshot, pendingInputs)`.\n//\n// Distinct from `replay` (the test idiom), by design (ADR 0006):\n// - Enters from a direct `base` parameter, NOT via `init`/`loaded` — so\n// reconciliation correctness never depends on the machine author's `init`\n// rehydrate discipline. Takes no `ctx` (the fold calls `update` only).\n// - Returns just the final `S` — not `{ state, cmds, subs }`. During prediction\n// the inputs' effects were already sent to the server; re-emitting their Cmds\n// on the client would double-fire, and `subs` are a runtime concern. Returning\n// only `S` makes \"replay fires no effects\" structural, not caller discipline.\n// A caller that wants the emitted Cmds for assertions uses `replay`.\n//\n// Invokes no `Store`, no `interpret` handler, and starts no subscription — it\n// shares `replay`'s `foldUpdates` fold, which only calls `update` cells, and\n// reads the reducer-vs-transitions form via the same `formOf(machine)` reader\n// `run`/`replay` use, so it agrees with them by construction.\n//\n// Per ADR 0006 the runtime-free *guarantee* lives on a dedicated\n// `@demlik/tea/pure` subpath whose module graph never reaches `run`; that\n// subpath export + import-graph guard are #213's scope. Here `foldMsgs` ships\n// as a reachable public API from root; #213 formalizes the boundary.\nexport function foldMsgs<\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>, base: S, msgs: readonly M[]): S {\n return foldUpdates<S, M, C>(machine, base, msgs).state;\n}\n"]}
|
package/dist/chunk-VLGARN6H.js
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { createRaftNode } from './chunk-6RGBKODV.js';
|
|
2
|
-
|
|
3
|
-
// src/raft/sim.ts
|
|
4
|
-
function clusterConfigs(n, over) {
|
|
5
|
-
const ids = Array.from({ length: n }, (_, i) => `n${i}`);
|
|
6
|
-
return ids.map((self) => ({
|
|
7
|
-
self,
|
|
8
|
-
peers: ids.filter((id) => id !== self),
|
|
9
|
-
electionTimeout: over?.electionTimeout ?? { minMs: 150, maxMs: 300 },
|
|
10
|
-
heartbeatMs: over?.heartbeatMs ?? 50
|
|
11
|
-
}));
|
|
12
|
-
}
|
|
13
|
-
function fixedRng(index, count) {
|
|
14
|
-
const value = count > 0 ? index / count : 0;
|
|
15
|
-
return () => value;
|
|
16
|
-
}
|
|
17
|
-
function cmdToInbound(sender, cmd) {
|
|
18
|
-
switch (cmd.type) {
|
|
19
|
-
case "raft:send_request_vote": {
|
|
20
|
-
const msg = {
|
|
21
|
-
_tag: "request_vote_request",
|
|
22
|
-
term: cmd.term,
|
|
23
|
-
candidateId: cmd.candidateId,
|
|
24
|
-
lastLogIndex: cmd.lastLogIndex,
|
|
25
|
-
lastLogTerm: cmd.lastLogTerm
|
|
26
|
-
};
|
|
27
|
-
return msg;
|
|
28
|
-
}
|
|
29
|
-
case "raft:send_request_vote_reply": {
|
|
30
|
-
const msg = {
|
|
31
|
-
_tag: "request_vote_reply",
|
|
32
|
-
term: cmd.term,
|
|
33
|
-
from: sender,
|
|
34
|
-
voteGranted: cmd.voteGranted
|
|
35
|
-
};
|
|
36
|
-
return msg;
|
|
37
|
-
}
|
|
38
|
-
case "raft:send_append_entries": {
|
|
39
|
-
const msg = {
|
|
40
|
-
_tag: "append_entries_request",
|
|
41
|
-
term: cmd.term,
|
|
42
|
-
leaderId: cmd.leaderId,
|
|
43
|
-
prevLogIndex: cmd.prevLogIndex,
|
|
44
|
-
prevLogTerm: cmd.prevLogTerm,
|
|
45
|
-
entries: cmd.entries,
|
|
46
|
-
leaderCommit: cmd.leaderCommit
|
|
47
|
-
};
|
|
48
|
-
return msg;
|
|
49
|
-
}
|
|
50
|
-
case "raft:send_append_entries_reply": {
|
|
51
|
-
const msg = {
|
|
52
|
-
_tag: "append_entries_reply",
|
|
53
|
-
term: cmd.term,
|
|
54
|
-
from: sender,
|
|
55
|
-
success: cmd.success,
|
|
56
|
-
matchIndex: cmd.matchIndex
|
|
57
|
-
};
|
|
58
|
-
return msg;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function applyInbound(node, state, msg, at) {
|
|
63
|
-
switch (msg._tag) {
|
|
64
|
-
case "election_timeout_fired":
|
|
65
|
-
return node.onElectionTimeout(state, at);
|
|
66
|
-
case "heartbeat_fired":
|
|
67
|
-
return node.onHeartbeat(state, at);
|
|
68
|
-
case "request_vote_request":
|
|
69
|
-
return node.onRequestVote(state, msg);
|
|
70
|
-
case "request_vote_reply":
|
|
71
|
-
return node.onRequestVoteReply(state, msg);
|
|
72
|
-
case "append_entries_request":
|
|
73
|
-
return node.onAppendEntries(state, msg);
|
|
74
|
-
case "append_entries_reply":
|
|
75
|
-
return node.onAppendEntriesReply(state, msg);
|
|
76
|
-
case "client_command":
|
|
77
|
-
return node.onClientCommand(state, msg);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
function park(world, actor, emitted) {
|
|
81
|
-
for (const cmd of emitted) {
|
|
82
|
-
world.pending.push({ from: actor, cmd });
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
function deliverOne(world, index) {
|
|
86
|
-
if (world.pending.length === 0) {
|
|
87
|
-
world.clock += 1;
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
world.clock += 1;
|
|
91
|
-
const at = world.clock;
|
|
92
|
-
const i = (index % world.pending.length + world.pending.length) % world.pending.length;
|
|
93
|
-
const [inFlight] = world.pending.splice(i, 1);
|
|
94
|
-
if (!inFlight) return null;
|
|
95
|
-
const target = inFlight.cmd.to;
|
|
96
|
-
if (world.down.has(target) || world.down.has(inFlight.from)) {
|
|
97
|
-
return inFlight;
|
|
98
|
-
}
|
|
99
|
-
const node = world.handles.get(target);
|
|
100
|
-
const state = world.states[target];
|
|
101
|
-
if (node && state) {
|
|
102
|
-
const inbound = cmdToInbound(inFlight.from, inFlight.cmd);
|
|
103
|
-
const [next, cmds] = applyInbound(node, state, inbound, at);
|
|
104
|
-
world.states[target] = next;
|
|
105
|
-
park(world, target, cmds);
|
|
106
|
-
}
|
|
107
|
-
return inFlight;
|
|
108
|
-
}
|
|
109
|
-
function deliveredOf(inFlight) {
|
|
110
|
-
return { from: inFlight.from, to: inFlight.cmd.to, kind: inFlight.cmd.type };
|
|
111
|
-
}
|
|
112
|
-
function applyEvent(world, event, steps) {
|
|
113
|
-
if (event.kind === "partition") {
|
|
114
|
-
world.clock += 1;
|
|
115
|
-
world.down = new Set(event.down);
|
|
116
|
-
steps.push({
|
|
117
|
-
event,
|
|
118
|
-
pending: world.pending.length,
|
|
119
|
-
states: snapshot(world.states)
|
|
120
|
-
});
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
if (event.kind === "settle") {
|
|
124
|
-
const bound = Math.max(0, event.bound);
|
|
125
|
-
for (let n = 0; n < bound && world.pending.length > 0; n++) {
|
|
126
|
-
const inFlight = deliverOne(world, 0);
|
|
127
|
-
steps.push({
|
|
128
|
-
event: { kind: "deliver", index: 0 },
|
|
129
|
-
pending: world.pending.length,
|
|
130
|
-
states: snapshot(world.states),
|
|
131
|
-
// Each drained delivery is its OWN step, so record that step's own
|
|
132
|
-
// delivered message (not "the last of the settle"). The pool was
|
|
133
|
-
// non-empty (loop guard), so `inFlight` is never null here.
|
|
134
|
-
...inFlight ? { delivered: deliveredOf(inFlight) } : {}
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
let delivered = null;
|
|
140
|
-
if (event.kind === "deliver") {
|
|
141
|
-
delivered = deliverOne(world, event.index);
|
|
142
|
-
} else {
|
|
143
|
-
world.clock += 1;
|
|
144
|
-
const at = world.clock;
|
|
145
|
-
const actor = event.node;
|
|
146
|
-
const node = world.handles.get(actor);
|
|
147
|
-
const state = world.states[actor];
|
|
148
|
-
if (node && state) {
|
|
149
|
-
const inbound = event.kind === "timer" ? event.timer === "election" ? { _tag: "election_timeout_fired", at } : { _tag: "heartbeat_fired", at } : { _tag: "client_command", command: event.command };
|
|
150
|
-
const [next, cmds] = applyInbound(node, state, inbound, at);
|
|
151
|
-
world.states[actor] = next;
|
|
152
|
-
park(world, actor, cmds);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
steps.push({
|
|
156
|
-
event,
|
|
157
|
-
pending: world.pending.length,
|
|
158
|
-
states: snapshot(world.states),
|
|
159
|
-
// Additive read-off: present only when a `deliver` actually consumed a
|
|
160
|
-
// message. Absent (key omitted) for timer/client and empty-pool delivers,
|
|
161
|
-
// so the step is structurally identical to a pre-#144 trace.
|
|
162
|
-
...delivered ? { delivered: deliveredOf(delivered) } : {}
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
function snapshot(states) {
|
|
166
|
-
return { ...states };
|
|
167
|
-
}
|
|
168
|
-
function runSchedule(configs, schedule) {
|
|
169
|
-
const handles = /* @__PURE__ */ new Map();
|
|
170
|
-
const states = {};
|
|
171
|
-
configs.forEach((config, i) => {
|
|
172
|
-
const node = createRaftNode(config, fixedRng(i, configs.length));
|
|
173
|
-
handles.set(config.self, node);
|
|
174
|
-
states[config.self] = node.init();
|
|
175
|
-
});
|
|
176
|
-
const world = {
|
|
177
|
-
handles,
|
|
178
|
-
states,
|
|
179
|
-
pending: [],
|
|
180
|
-
clock: 0,
|
|
181
|
-
down: /* @__PURE__ */ new Set()
|
|
182
|
-
};
|
|
183
|
-
const steps = [];
|
|
184
|
-
for (const event of schedule) {
|
|
185
|
-
applyEvent(world, event, steps);
|
|
186
|
-
}
|
|
187
|
-
return {
|
|
188
|
-
nodes: configs.map((c) => c.self),
|
|
189
|
-
steps,
|
|
190
|
-
final: snapshot(world.states)
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
function replaySchedule(configs, schedule) {
|
|
194
|
-
return runSchedule(configs, schedule);
|
|
195
|
-
}
|
|
196
|
-
function clusterStatesEqual(a, b) {
|
|
197
|
-
return JSON.stringify(a) === JSON.stringify(b);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export { clusterConfigs, clusterStatesEqual, replaySchedule, runSchedule };
|
|
201
|
-
//# sourceMappingURL=chunk-VLGARN6H.js.map
|
|
202
|
-
//# sourceMappingURL=chunk-VLGARN6H.js.map
|