@demlik/tea 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +11 -2
  2. package/dist/agent/index.d.ts +17 -17
  3. package/dist/agent/index.js +10 -10
  4. package/dist/{chunk-FTCYY2DO.js → chunk-4I2LCQKB.js} +3 -3
  5. package/dist/{chunk-FTCYY2DO.js.map → chunk-4I2LCQKB.js.map} +1 -1
  6. package/dist/{chunk-EA47MI5Q.js → chunk-6NIQIUGR.js} +4 -4
  7. package/dist/{chunk-EA47MI5Q.js.map → chunk-6NIQIUGR.js.map} +1 -1
  8. package/dist/{chunk-HX3C6LDK.js → chunk-7Z2IDZ7R.js} +3 -3
  9. package/dist/{chunk-HX3C6LDK.js.map → chunk-7Z2IDZ7R.js.map} +1 -1
  10. package/dist/{chunk-XWIE5V7R.js → chunk-BFSZ4L6Y.js} +4 -4
  11. package/dist/{chunk-XWIE5V7R.js.map → chunk-BFSZ4L6Y.js.map} +1 -1
  12. package/dist/{chunk-JAKM53JF.js → chunk-EB5OMOXX.js} +4 -4
  13. package/dist/chunk-EB5OMOXX.js.map +1 -0
  14. package/dist/{chunk-RKVUVALV.js → chunk-GAVLND5C.js} +19 -4
  15. package/dist/chunk-GAVLND5C.js.map +1 -0
  16. package/dist/{chunk-AIXC33SE.js → chunk-HU3Y2AZY.js} +4 -4
  17. package/dist/chunk-HU3Y2AZY.js.map +1 -0
  18. package/dist/{chunk-72CN3VPY.js → chunk-MBQR3LFW.js} +5 -5
  19. package/dist/{chunk-72CN3VPY.js.map → chunk-MBQR3LFW.js.map} +1 -1
  20. package/dist/{chunk-TAKPC6SD.js → chunk-QIE4Y6BK.js} +4 -4
  21. package/dist/{chunk-TAKPC6SD.js.map → chunk-QIE4Y6BK.js.map} +1 -1
  22. package/dist/{chunk-TGJLXZAU.js → chunk-TMYUDZLE.js} +3 -3
  23. package/dist/{chunk-TGJLXZAU.js.map → chunk-TMYUDZLE.js.map} +1 -1
  24. package/dist/{chunk-DVNXEB7V.js → chunk-VHKDUBXC.js} +3 -3
  25. package/dist/chunk-VHKDUBXC.js.map +1 -0
  26. package/dist/{chunk-UHAQ4PI6.js → chunk-VM7PMFV5.js} +70 -47
  27. package/dist/chunk-VM7PMFV5.js.map +1 -0
  28. package/dist/{chunk-2774I242.js → chunk-W6XWI6QE.js} +20 -20
  29. package/dist/chunk-W6XWI6QE.js.map +1 -0
  30. package/dist/{chunk-NGYFBD5G.js → chunk-YCJPLPQQ.js} +3 -3
  31. package/dist/{chunk-NGYFBD5G.js.map → chunk-YCJPLPQQ.js.map} +1 -1
  32. package/dist/{chunk-2R62Q5UK.js → chunk-YDF6P74H.js} +3 -3
  33. package/dist/{chunk-2R62Q5UK.js.map → chunk-YDF6P74H.js.map} +1 -1
  34. package/dist/{chunk-A73IJ27X.js → chunk-Z7IL735U.js} +3 -3
  35. package/dist/{chunk-A73IJ27X.js.map → chunk-Z7IL735U.js.map} +1 -1
  36. package/dist/{chunk-5HVKDHY6.js → chunk-ZV4WGW74.js} +3 -3
  37. package/dist/{chunk-5HVKDHY6.js.map → chunk-ZV4WGW74.js.map} +1 -1
  38. package/dist/{core-CtvAyPpH.d.ts → core-BKJW1Kmq.d.ts} +2 -2
  39. package/dist/do/index.d.ts +17 -15
  40. package/dist/do/index.js +11 -11
  41. package/dist/do/index.js.map +1 -1
  42. package/dist/{drive-loop-Dw1sGSsW.d.ts → drive-loop-Dc6Z8Vbs.d.ts} +1 -1
  43. package/dist/{durable-effects-Ump5lSdO.d.ts → durable-effects-CgrLNccA.d.ts} +1 -1
  44. package/dist/effect/index.d.ts +74 -7
  45. package/dist/effect/index.js +44 -6
  46. package/dist/effect/index.js.map +1 -1
  47. package/dist/extension/index.d.ts +3 -3
  48. package/dist/extension/index.js +2 -2
  49. package/dist/extension/index.js.map +1 -1
  50. package/dist/flow/index.d.ts +17 -13
  51. package/dist/flow/index.js +13 -12
  52. package/dist/flow/index.js.map +1 -1
  53. package/dist/idempotency/index.d.ts +3 -3
  54. package/dist/idempotency/index.js +3 -3
  55. package/dist/{index-CrcoVfd_.d.ts → index-1hit3dJ9.d.ts} +2 -2
  56. package/dist/{index-CKtEKGyp.d.ts → index-B-VC4qjx.d.ts} +3 -3
  57. package/dist/{index-EIgfl83J.d.ts → index-BjwRcjBi.d.ts} +2 -2
  58. package/dist/{index-CZZ3HI-Z.d.ts → index-CYrUWi1c.d.ts} +4 -4
  59. package/dist/{index-D3NY3VCN.d.ts → index-Cuy1_xTh.d.ts} +1 -1
  60. package/dist/{index-Bk2hBUdK.d.ts → index-DB8Yn0-K.d.ts} +1 -1
  61. package/dist/{index-CVU7SK3a.d.ts → index-Diyo8WBO.d.ts} +2 -2
  62. package/dist/{index-Dl71V7kt.d.ts → index-Z6Uw7C9t.d.ts} +1 -1
  63. package/dist/index.d.ts +9 -9
  64. package/dist/index.js +2 -2
  65. package/dist/jev/index.d.ts +15 -15
  66. package/dist/jev/index.js +5 -5
  67. package/dist/jev/index.js.map +1 -1
  68. package/dist/{machine-Dtre2TmN.d.ts → machine-BuPTS1QH.d.ts} +7 -7
  69. package/dist/machine-viz/index.d.ts +1 -1
  70. package/dist/machine-viz/index.js +2 -2
  71. package/dist/mem/index.d.ts +2 -2
  72. package/dist/mem/index.js +2 -2
  73. package/dist/node/index.d.ts +2 -2
  74. package/dist/node/index.js +2 -2
  75. package/dist/node/index.js.map +1 -1
  76. package/dist/otel/index.d.ts +10 -10
  77. package/dist/otel/index.js +10 -10
  78. package/dist/paginate/index.d.ts +4 -4
  79. package/dist/paginate/index.js +5 -5
  80. package/dist/parity/index.d.ts +3 -3
  81. package/dist/parity/index.js +3 -3
  82. package/dist/parity/index.js.map +1 -1
  83. package/dist/pbt/index.d.ts +1 -1
  84. package/dist/pbt/index.js +2 -2
  85. package/dist/persistence/index.d.ts +5 -5
  86. package/dist/persistence/index.js +4 -4
  87. package/dist/promise/index.d.ts +25 -10
  88. package/dist/promise/index.js +4 -4
  89. package/dist/react/index.d.ts +7 -6
  90. package/dist/react/index.js.map +1 -1
  91. package/dist/resilience/index.d.ts +6 -6
  92. package/dist/resilience/index.js +5 -5
  93. package/dist/{run-fiBe7pOG.d.ts → run-D0sxbyno.d.ts} +22 -9
  94. package/dist/{runtime-types-DEluQgGO.d.ts → runtime-types-BPKp2bGo.d.ts} +48 -9
  95. package/dist/testing/effect/index.d.ts +5 -5
  96. package/dist/testing/effect/index.js +8 -8
  97. package/dist/testing/index.d.ts +2 -2
  98. package/dist/testing/index.js +3 -3
  99. package/dist/testing/promise/index.d.ts +4 -4
  100. package/dist/testing/promise/index.js +5 -5
  101. package/dist/timing/index.d.ts +4 -4
  102. package/dist/timing/index.js +4 -4
  103. package/dist/{types-CXNzlbAg.d.ts → types-BiXj7k_k.d.ts} +1 -1
  104. package/dist/work-queue/index.d.ts +4 -4
  105. package/dist/work-queue/index.js +2 -2
  106. package/package.json +4 -2
  107. package/dist/chunk-2774I242.js.map +0 -1
  108. package/dist/chunk-AIXC33SE.js.map +0 -1
  109. package/dist/chunk-DVNXEB7V.js.map +0 -1
  110. package/dist/chunk-JAKM53JF.js.map +0 -1
  111. package/dist/chunk-RKVUVALV.js.map +0 -1
  112. package/dist/chunk-UHAQ4PI6.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/index.ts"],"names":[],"mappings":";;AAqGO,SAAS,UAAA,CAOd,SACA,IAAA,EACgC;AAMhC,EAAA,MAAM,MAAM,IAAA,CAAK,GAAA;AACjB,EAAA,MAAM,MAAM,IAAA,CAAK,GAAA;AACjB,EAAA,MAAM,QAAQ,IAAA,CAAK,KAAA;AAInB,EAAA,MAAM,WAAA,GAAc,OAAkC,IAAI,CAAA;AAC1D,EAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,EAAA,MAAM,QAAA,GAAW,OAAA;AAAA,IACf,OAAO;AAAA,MACL,SAAA,EAAW,WAAA;AAAA,QACT,MAAO,YAAY,OAAA,CAAmC;AAAA,OACxD;AAAA,MACA,SAAA,EAAW,WAAA,CAAY,MAAM,WAAA,CAAY,QAAQ,SAAS;AAAA,KAG5D,CAAA;AAAA,IACA;AAAC,GACH;AAMA,EAAA,MAAM,OAAA,GAAU,OAAA;AAAA,IACd,MACE,IAAI,OAAA,EAAS;AAAA,MACX,GAAA;AAAA,MACA,KAAA;AAAA,MACA,GAAG;AAAA,KAC2B,CAAA;AAAA,IAClC,CAAC,OAAA,EAAS,GAAA,EAAK,GAAA,EAAK,OAAO,QAAQ;AAAA,GACrC;AAeA,EAAA,MAAM,QAAA,GAAW,OAAqC,IAAI,CAAA;AAC1D,EAAA,MAAM,GAAG,UAAU,CAAA,GAAI,WAAW,CAAC,IAAA,KAAiB,IAAA,GAAO,CAAA,EAAG,CAAC,CAAA;AAa/D,EAAA,MAAM,gBAAA,GAAmB,OAAA;AAAA,IACvB,MAAM,OAAA,CAAQ,IAAA,CAAK,IAAA,EAAM,GAAG,EAAE,CAAC,CAAA;AAAA,IAC/B,CAAC,SAAS,GAAG;AAAA,GACf;AAMA,EAAA,MAAM,WAAA,GAAc,MAClB,QAAA,CAAS,OAAA,KAAY,OAAO,QAAA,CAAS,OAAA,CAAQ,UAAS,GAAI,gBAAA;AAE5D,EAAA,MAAM,KAAA,GAAQ,oBAAA;AAAA,IACZ,OAAA,CAAQ,SAAA;AAAA,IACR,WAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,IAAA,GAAO,IAAA;AAMX,IAAA,OAAA,CAAQ,KAAA,CAAM,IAAA;AAAA,MACZ,CAAC,CAAA,KAAM;AACL,QAAA,IAAI,IAAA,EAAM;AACR,UAAA,QAAA,CAAS,OAAA,GAAU,CAAA;AACnB,UAAA,UAAA,EAAW;AAAA,QACb;AAAA,MACF,CAAA;AAAA,MACA,MAAM;AAAA,MAAC;AAAA,KACT;AACA,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,GAAO,KAAA;AACP,MAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAInB,MAAA,OAAA,CAAQ,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM;AAAA,MAAC,CAAC,CAAA;AAAA,IAC/B,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,OAAO,CAAC,KAAA,EAAO,OAAA,CAAQ,QAAQ,CAAA;AACjC;AAOA,SAAS,YAAY,OAAA,EAA2C;AAC9D,EAAA,OAAO,IAAI,KAAA;AAAA,IACT,EAAC;AAAA,IACD;AAAA,MACE,KAAK,CAAC,OAAA,EAAS,IAAA,KACZ,OAAA,KAAyD,IAAI;AAAA;AAClE,GACF;AACF;AAgBO,SAAS,WAId,OAAA,EAAmE;AACnE,EAAA,MAAM,KAAA,GAAQ,oBAAA;AAAA,IACZ,OAAA,CAAQ,SAAA;AAAA,IACR,OAAA,CAAQ,QAAA;AAAA,IACR,OAAA,CAAQ;AAAA,GACV;AACA,EAAA,OAAO,CAAC,KAAA,EAAO,OAAA,CAAQ,QAAQ,CAAA;AACjC","file":"index.js","sourcesContent":["/**\n * @packageDocumentation\n * @demlik/tea/react — React host adapter for `@demlik/tea`.\n *\n * The thinnest possible adapter that makes a tea machine consumable from a\n * React component. Three rules (PRD §\"React adapter\"):\n *\n * 1. The runtime is the source of truth, NOT React state. We subscribe via\n * `useSyncExternalStore(runtime.subscribe, runtime.getState,\n * runtime.getState)`. React renders are driven by runtime state changes;\n * React never owns the state.\n * 2. The runtime is memoized per component-mount, not per render. The deps\n * array is `[machine, opts.ctx, opts.store]` — IDENTITY, not value. See\n * `README.md` for the ctx-identity footgun + fix.\n * 3. Concurrent React is the design target. `useSyncExternalStore` is the\n * React-blessed primitive for tearing-free external state.\n *\n * Client-only in v1 — components that call `useMachine` MUST be `\"use client\"`.\n * Server Components consume data via Relay/RSC as today; tea machines own\n * client-side stateful flows. No RSC streaming, no server-side state\n * hydration. Crossing this boundary is a v2 concern.\n */\n\n\"use client\";\n\nimport {\n useEffect,\n useMemo,\n useReducer,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport type {\n BootedRunHandle,\n Cmd,\n EngineRun,\n Interpret,\n Machine,\n RunHandle,\n RunHandlers,\n RunOptions,\n Store,\n Sub,\n Subscribe,\n} from \"../index\";\n\n/**\n * Options passed to `useMachine`. `run` is the engine's `run` — `run` from\n * `@demlik/tea/promise`, or any engine's; the hook imports no engine. `ctx`\n * is required (every machine has one), `store` is optional (omit it for\n * volatile-state machines), and the handlers are the ones `run` takes beside\n * the machine: `interpret` (required once the machine emits a Cmd) and the\n * `subscribe` runners (required once the machine declares a Sub type other\n * than the built-in `timer`) — see {@link RunHandlers}. Same requiredness as\n * `run`, because it is the same type.\n *\n * **The handlers are read fresh, never memoized on.** A handler table written\n * inline in the component is a new object every render; keying the runtime on\n * it would reboot the machine on every render. So `useMachine` hands `run` a\n * table that looks each cell up on the latest render's `interpret` /\n * `subscribe` at the moment a Cmd runs or a Sub starts — a handler may close\n * over props and state freely.\n *\n * **Identity matters.** The runtime is memoized on `[machine, opts.run,\n * opts.ctx, opts.store]`. A new `ctx` reference rebuilds the runtime, and so\n * does a new `run` — pass the engine's own function, not an inline wrapper. Always `useMemo`\n * (or otherwise stabilize) the ctx at the call site. See README.\n *\n * Forgetting to is no longer silent: the replaced runtime's `stop()` reports\n * `RuntimeDiscardedError` under `phase: \"discard\"` when Cmds were still in\n * flight, which the substrate's default sink warns about (issue #365).\n */\nexport type UseMachineOpts<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n> = {\n run: EngineRun<S, M, C, U, Ctx>;\n ctx: Ctx;\n store?: Store<S>;\n} & RunHandlers<M, C, U, Ctx>;\n\n/**\n * Build and own a run of `machine` for the lifetime of the component mount, on\n * the engine whose `run` the caller hands in.\n *\n * - Builds via `useMemo(() => opts.run(machine, opts), [machine, opts.run,\n * opts.ctx, opts.store])` — the run is recreated when ANY of those\n * identities change.\n * - Subscribes via `useSyncExternalStore(runtime.subscribe, runtime.getState,\n * runtime.getState)` — tearing-free under React 18 concurrent rendering.\n * - On unmount (or any dep change), `useEffect` cleanup calls\n * `runtime.stop()` — drains the queue, runs every active sub cleanup,\n * flushes state, and — if Cmds were still in flight — reports\n * `RuntimeDiscardedError` (`phase: \"discard\"`) rather than dropping them\n * silently.\n *\n * Returns `[state, dispatch]` shaped like `useReducer`.\n */\nexport function useMachine<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: NoInfer<UseMachineOpts<S, M, C, U, Ctx>>,\n): [S, (msg: M) => Promise<void>] {\n // Deps are literal: machine, engine, ctx and store identity. NOT `[opts]`\n // (would rebuild every render — callers pass fresh objects). We pass\n // explicit fields to `run()` so the closure captures only the deps in the\n // array — keeps biome's `useExhaustiveDependencies` happy without lying\n // about what the memo actually depends on.\n const run = opts.run;\n const ctx = opts.ctx;\n const store = opts.store;\n // The latest render's handlers, read per call (see `UseMachineOpts`). Written\n // during render on purpose: a Cmd a transition emits runs after the render\n // that produced it, so the table it reads is never older than that render.\n const handlersRef = useRef<RunHandlers<M, C, U, Ctx>>(opts);\n handlersRef.current = opts;\n const handlers = useMemo(\n () => ({\n interpret: latestTable(\n () => (handlersRef.current as { interpret?: object }).interpret,\n ) as Interpret<M, C, Ctx>,\n subscribe: latestTable(() => handlersRef.current.subscribe) as Partial<\n Subscribe<M, U, Ctx>\n >,\n }),\n [],\n );\n // `run()` returns a `RunHandle<S, M>` SYNCHRONOUSLY (issue #45) — exactly\n // what `useMemo` needs. We never `await` here: awaiting would force an async\n // memo, a null first-commit state, and a resolved-flag dance. Instead we hold\n // the booting handle and capture the booted one (the only thing with a total\n // `getState`) once `ready` resolves.\n const booting = useMemo<RunHandle<S, M>>(\n () =>\n run(machine, {\n ctx,\n store,\n ...handlers,\n } as RunOptions<S, M, C, U, Ctx>),\n [machine, run, ctx, store, handlers],\n );\n\n // Captures the booted handle once `ready` resolves. Until then it is\n // `null` and `getSnapshot` serves `preliminaryState`. Reset whenever the\n // booting handle identity changes (a new machine/ctx/store mount).\n //\n // The boot race the paired `bootedTick` bump exists for: the substrate's\n // boot fanout fires BEFORE `ready` resolves (fireListeners runs inside the\n // boot step; `ready` chains off the boot promise), so at notification time\n // this ref is still null and `getSnapshot` returns the same\n // `preliminaryState` reference — React sees an unchanged snapshot and\n // skips the re-render. Nothing re-notifies after the ref is set, so a\n // store-backed mount would stay stuck on the preliminary state until the\n // next unrelated transition. Bumping a reducer when we capture the booted\n // runtime forces the render that swaps preliminary → real state.\n const readyRef = useRef<BootedRunHandle<S, M> | null>(null);\n const [, markBooted] = useReducer((tick: number) => tick + 1, 0);\n\n // Preliminary state computed sync from `machine.init(null, ctx)`. This is\n // the snapshot React sees BEFORE the runtime finishes booting (matters\n // only when `opts.store` is present — without a store, the substrate\n // already sets state synchronously inside `run()`, so the boot fanout that\n // captures `readyRef` lands on the same microtask as the first commit).\n //\n // We deliberately recompute on each render but cache by `[machine,\n // opts.ctx]` — `init` is pure by convention, so cost is negligible. The\n // cached snapshot is also identity-stable across rerenders when the deps\n // don't change, which `useSyncExternalStore` rewards (no spurious\n // re-renders).\n const preliminaryState = useMemo<S>(\n () => machine.init(null, ctx)[0],\n [machine, ctx],\n );\n\n // `getSnapshot` reads the booted runtime if `ready` has resolved (the ref is\n // populated), else falls back to the preliminary state. No try/catch dance\n // anymore — pre-boot there is simply no booted handle to read from, so\n // the fallback is structural, not exception-driven.\n const getSnapshot = (): S =>\n readyRef.current !== null ? readyRef.current.getState() : preliminaryState;\n\n const state = useSyncExternalStore(\n booting.subscribe,\n getSnapshot,\n getSnapshot,\n );\n\n useEffect(() => {\n let live = true;\n // Capture the booted runtime when `ready` resolves, then force the\n // render that swaps preliminary → real state (see the boot-race note on\n // `readyRef` — the boot fanout fires too early to do it for us). Boot\n // rejections are swallowed: a failed boot surfaces on every `dispatch`,\n // which is where the consumer handles it.\n booting.ready.then(\n (r) => {\n if (live) {\n readyRef.current = r;\n markBooted();\n }\n },\n () => {},\n );\n return () => {\n live = false;\n readyRef.current = null;\n // Fire-and-forget — `stop()` is async but `useEffect` cleanup is sync.\n // The runtime swallows in-flight rejections internally, so this never\n // rejects in practice; we still attach `.catch` for safety.\n booting.stop().catch(() => {});\n };\n }, [booting]);\n\n return [state, booting.dispatch];\n}\n\n/**\n * A handler table whose every cell is looked up on `current()` at the moment it\n * is read, so `run` — which reads a cell per Cmd / per Sub start — always sees\n * the latest render's handler without the runtime being rebuilt.\n */\nfunction latestTable(current: () => object | undefined): object {\n return new Proxy(\n {},\n {\n get: (_target, type) =>\n (current() as Record<PropertyKey, unknown> | undefined)?.[type],\n },\n );\n}\n\n/**\n * Lower-level escape hatch: consume an externally-built, booted run — any\n * engine's {@link BootedRunHandle} (the Promise engine's `Runtime` is one).\n *\n * Use this when the runtime is owned by something OTHER than the component —\n * a parent component, a test harness, a singleton. **The component does NOT\n * call `runtime.stop()` on unmount** — the caller owns the lifecycle.\n *\n * Primary use case in this repo: component tests. Build a runtime with\n * `@demlik/tea/mem` and pre-seed it via `replay`, then mount the component with\n * `useRuntime(testRuntime)` to render in any phase.\n *\n * Returns `[state, dispatch]` — same shape as `useMachine`.\n */\nexport function useRuntime<\n S,\n M extends { type: string },\n E extends { type: string } = never,\n>(runtime: BootedRunHandle<S, M, E>): [S, (msg: M) => Promise<void>] {\n const state = useSyncExternalStore(\n runtime.subscribe,\n runtime.getState,\n runtime.getState,\n );\n return [state, runtime.dispatch];\n}\n"]}
1
+ {"version":3,"sources":["../../src/react/index.ts"],"names":[],"mappings":";;AAsGO,SAAS,UAAA,CAOd,SACA,IAAA,EACgC;AAMhC,EAAA,MAAM,MAAM,IAAA,CAAK,GAAA;AACjB,EAAA,MAAM,MAAM,IAAA,CAAK,GAAA;AACjB,EAAA,MAAM,QAAQ,IAAA,CAAK,KAAA;AAInB,EAAA,MAAM,WAAA,GAAc,OAAkC,IAAI,CAAA;AAC1D,EAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,EAAA,MAAM,QAAA,GAAW,OAAA;AAAA,IACf,OAAO;AAAA,MACL,SAAA,EAAW,WAAA;AAAA,QACT,MAAO,YAAY,OAAA,CAAmC;AAAA,OACxD;AAAA,MACA,SAAA,EAAW,WAAA,CAAY,MAAM,WAAA,CAAY,QAAQ,SAAS;AAAA,KAG5D,CAAA;AAAA,IACA;AAAC,GACH;AAMA,EAAA,MAAM,OAAA,GAAU,OAAA;AAAA,IACd,MACE,IAAI,OAAA,EAAS;AAAA,MACX,GAAA;AAAA,MACA,KAAA;AAAA,MACA,GAAG;AAAA,KAC2B,CAAA;AAAA,IAClC,CAAC,OAAA,EAAS,GAAA,EAAK,GAAA,EAAK,OAAO,QAAQ;AAAA,GACrC;AAeA,EAAA,MAAM,QAAA,GAAW,OAAqC,IAAI,CAAA;AAC1D,EAAA,MAAM,GAAG,UAAU,CAAA,GAAI,WAAW,CAAC,IAAA,KAAiB,IAAA,GAAO,CAAA,EAAG,CAAC,CAAA;AAa/D,EAAA,MAAM,gBAAA,GAAmB,OAAA;AAAA,IACvB,MAAM,OAAA,CAAQ,IAAA,CAAK,IAAA,EAAM,GAAG,EAAE,CAAC,CAAA;AAAA,IAC/B,CAAC,SAAS,GAAG;AAAA,GACf;AAMA,EAAA,MAAM,WAAA,GAAc,MAClB,QAAA,CAAS,OAAA,KAAY,OAAO,QAAA,CAAS,OAAA,CAAQ,UAAS,GAAI,gBAAA;AAE5D,EAAA,MAAM,KAAA,GAAQ,oBAAA;AAAA,IACZ,OAAA,CAAQ,SAAA;AAAA,IACR,WAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,IAAA,GAAO,IAAA;AAMX,IAAA,OAAA,CAAQ,KAAA,CAAM,IAAA;AAAA,MACZ,CAAC,CAAA,KAAM;AACL,QAAA,IAAI,IAAA,EAAM;AACR,UAAA,QAAA,CAAS,OAAA,GAAU,CAAA;AACnB,UAAA,UAAA,EAAW;AAAA,QACb;AAAA,MACF,CAAA;AAAA,MACA,MAAM;AAAA,MAAC;AAAA,KACT;AACA,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,GAAO,KAAA;AACP,MAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAInB,MAAA,OAAA,CAAQ,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM;AAAA,MAAC,CAAC,CAAA;AAAA,IAC/B,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,OAAO,CAAC,KAAA,EAAO,OAAA,CAAQ,QAAQ,CAAA;AACjC;AAOA,SAAS,YAAY,OAAA,EAA2C;AAC9D,EAAA,OAAO,IAAI,KAAA;AAAA,IACT,EAAC;AAAA,IACD;AAAA,MACE,KAAK,CAAC,OAAA,EAAS,IAAA,KACZ,OAAA,KAAyD,IAAI;AAAA;AAClE,GACF;AACF;AAgBO,SAAS,WAId,OAAA,EAAmE;AACnE,EAAA,MAAM,KAAA,GAAQ,oBAAA;AAAA,IACZ,OAAA,CAAQ,SAAA;AAAA,IACR,OAAA,CAAQ,QAAA;AAAA,IACR,OAAA,CAAQ;AAAA,GACV;AACA,EAAA,OAAO,CAAC,KAAA,EAAO,OAAA,CAAQ,QAAQ,CAAA;AACjC","file":"index.js","sourcesContent":["/**\n * @packageDocumentation\n * @demlik/tea/react — React host adapter for `@demlik/tea`.\n *\n * The thinnest possible adapter that makes a tea machine consumable from a\n * React component. Three rules (PRD §\"React adapter\"):\n *\n * 1. The runtime is the source of truth, NOT React state. We subscribe via\n * `useSyncExternalStore(runtime.subscribe, runtime.getState,\n * runtime.getState)`. React renders are driven by runtime state changes;\n * React never owns the state.\n * 2. The runtime is memoized per component-mount, not per render. The deps\n * array is `[machine, opts.ctx, opts.store]` — IDENTITY, not value. See\n * `README.md` for the ctx-identity footgun + fix.\n * 3. Concurrent React is the design target. `useSyncExternalStore` is the\n * React-blessed primitive for tearing-free external state.\n *\n * Client-only in v1 — components that call `useMachine` MUST be `\"use client\"`.\n * Server Components consume data via Relay/RSC as today; tea machines own\n * client-side stateful flows. No RSC streaming, no server-side state\n * hydration. Crossing this boundary is a v2 concern.\n */\n\n\"use client\";\n\nimport {\n useEffect,\n useMemo,\n useReducer,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport type {\n BootedRunHandle,\n Cmd,\n EngineRun,\n Interpret,\n Machine,\n RunHandle,\n RunHandlers,\n RunOptions,\n Store,\n Sub,\n Subscribe,\n} from \"../index\";\n\n/**\n * Options passed to `useMachine`. `run` is the engine's `run` — `run` from\n * `@demlik/tea/promise`, or any engine's; the hook imports no engine. `ctx`\n * is required (every machine has one), `store` is optional (omit it for\n * volatile-state machines), and the handlers are the ones `run` takes beside\n * the machine: `interpret` (required once the machine emits a Cmd) and the\n * `subscribe` runners (required once the machine declares a Sub type other\n * than the built-in `timer`) — see {@link RunHandlers}. Same requiredness as\n * `run`, because it is the same type.\n *\n * **The handlers are read fresh, never memoized on.** A handler table written\n * inline in the component is a new object every render; keying the runtime on\n * it would reboot the machine on every render. So `useMachine` hands `run` a\n * table that looks each cell up on the latest render's `interpret` /\n * `subscribe` at the moment a Cmd runs or a Sub starts — a handler may close\n * over props and state freely.\n *\n * **Identity matters.** The runtime is memoized on `[machine, opts.run,\n * opts.ctx, opts.store]`. A new `ctx` reference rebuilds the runtime, and so\n * does a new `run` — pass the engine's own function, not an inline wrapper. Always `useMemo`\n * (or otherwise stabilize) the ctx at the call site. See README.\n *\n * Forgetting to is no longer silent: the replaced runtime's `stop()` reports\n * `RuntimeDiscardedError` under `phase: \"discard\"` when Cmds were still in\n * flight, which the substrate's default sink warns about (issue #365).\n */\nexport type UseMachineOpts<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n> = {\n run: EngineRun<S, M, C, U, Ctx>;\n ctx: Ctx;\n store?: Store<S>;\n} & RunHandlers<M, C, U, Ctx>;\n\n/**\n * Build and own a run of `machine` for the lifetime of the component mount, on\n * the engine whose `run` the caller hands in.\n *\n * - Builds via `useMemo(() => opts.run(machine, opts), [machine, opts.run,\n * opts.ctx, opts.store])` — the run is recreated when ANY of those\n * identities change.\n * - Subscribes via `useSyncExternalStore(runtime.subscribe, runtime.getState,\n * runtime.getState)` — tearing-free under React 18 concurrent rendering.\n * - On unmount (or any dep change), `useEffect` cleanup calls\n * `runtime.stop()` — drains the queue, up to `disposeTimeoutMs`, runs every\n * active sub cleanup, flushes state, and — if Cmds were still in flight —\n * reports `RuntimeDiscardedError` (`phase: \"discard\"`) rather than dropping\n * them silently. A handler that has not returned by the limit is given up\n * on and reported as a `DrainTimeoutNotice`.\n *\n * Returns `[state, dispatch]` shaped like `useReducer`.\n */\nexport function useMachine<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: NoInfer<UseMachineOpts<S, M, C, U, Ctx>>,\n): [S, (msg: M) => Promise<void>] {\n // Deps are literal: machine, engine, ctx and store identity. NOT `[opts]`\n // (would rebuild every render — callers pass fresh objects). We pass\n // explicit fields to `run()` so the closure captures only the deps in the\n // array — keeps biome's `useExhaustiveDependencies` happy without lying\n // about what the memo actually depends on.\n const run = opts.run;\n const ctx = opts.ctx;\n const store = opts.store;\n // The latest render's handlers, read per call (see `UseMachineOpts`). Written\n // during render on purpose: a Cmd a transition emits runs after the render\n // that produced it, so the table it reads is never older than that render.\n const handlersRef = useRef<RunHandlers<M, C, U, Ctx>>(opts);\n handlersRef.current = opts;\n const handlers = useMemo(\n () => ({\n interpret: latestTable(\n () => (handlersRef.current as { interpret?: object }).interpret,\n ) as Interpret<M, C, Ctx>,\n subscribe: latestTable(() => handlersRef.current.subscribe) as Partial<\n Subscribe<M, U, Ctx>\n >,\n }),\n [],\n );\n // `run()` returns a `RunHandle<S, M>` SYNCHRONOUSLY (issue #45) — exactly\n // what `useMemo` needs. We never `await` here: awaiting would force an async\n // memo, a null first-commit state, and a resolved-flag dance. Instead we hold\n // the booting handle and capture the booted one (the only thing with a total\n // `getState`) once `ready` resolves.\n const booting = useMemo<RunHandle<S, M>>(\n () =>\n run(machine, {\n ctx,\n store,\n ...handlers,\n } as RunOptions<S, M, C, U, Ctx>),\n [machine, run, ctx, store, handlers],\n );\n\n // Captures the booted handle once `ready` resolves. Until then it is\n // `null` and `getSnapshot` serves `preliminaryState`. Reset whenever the\n // booting handle identity changes (a new machine/ctx/store mount).\n //\n // The boot race the paired `bootedTick` bump exists for: the substrate's\n // boot fanout fires BEFORE `ready` resolves (fireListeners runs inside the\n // boot step; `ready` chains off the boot promise), so at notification time\n // this ref is still null and `getSnapshot` returns the same\n // `preliminaryState` reference — React sees an unchanged snapshot and\n // skips the re-render. Nothing re-notifies after the ref is set, so a\n // store-backed mount would stay stuck on the preliminary state until the\n // next unrelated transition. Bumping a reducer when we capture the booted\n // runtime forces the render that swaps preliminary → real state.\n const readyRef = useRef<BootedRunHandle<S, M> | null>(null);\n const [, markBooted] = useReducer((tick: number) => tick + 1, 0);\n\n // Preliminary state computed sync from `machine.init(null, ctx)`. This is\n // the snapshot React sees BEFORE the runtime finishes booting (matters\n // only when `opts.store` is present — without a store, the substrate\n // already sets state synchronously inside `run()`, so the boot fanout that\n // captures `readyRef` lands on the same microtask as the first commit).\n //\n // We deliberately recompute on each render but cache by `[machine,\n // opts.ctx]` — `init` is pure by convention, so cost is negligible. The\n // cached snapshot is also identity-stable across rerenders when the deps\n // don't change, which `useSyncExternalStore` rewards (no spurious\n // re-renders).\n const preliminaryState = useMemo<S>(\n () => machine.init(null, ctx)[0],\n [machine, ctx],\n );\n\n // `getSnapshot` reads the booted runtime if `ready` has resolved (the ref is\n // populated), else falls back to the preliminary state. No try/catch dance\n // anymore — pre-boot there is simply no booted handle to read from, so\n // the fallback is structural, not exception-driven.\n const getSnapshot = (): S =>\n readyRef.current !== null ? readyRef.current.getState() : preliminaryState;\n\n const state = useSyncExternalStore(\n booting.subscribe,\n getSnapshot,\n getSnapshot,\n );\n\n useEffect(() => {\n let live = true;\n // Capture the booted runtime when `ready` resolves, then force the\n // render that swaps preliminary → real state (see the boot-race note on\n // `readyRef` — the boot fanout fires too early to do it for us). Boot\n // rejections are swallowed: a failed boot surfaces on every `dispatch`,\n // which is where the consumer handles it.\n booting.ready.then(\n (r) => {\n if (live) {\n readyRef.current = r;\n markBooted();\n }\n },\n () => {},\n );\n return () => {\n live = false;\n readyRef.current = null;\n // Fire-and-forget — `stop()` is async but `useEffect` cleanup is sync.\n // The runtime swallows in-flight rejections internally, so this never\n // rejects in practice; we still attach `.catch` for safety.\n booting.stop().catch(() => {});\n };\n }, [booting]);\n\n return [state, booting.dispatch];\n}\n\n/**\n * A handler table whose every cell is looked up on `current()` at the moment it\n * is read, so `run` — which reads a cell per Cmd / per Sub start — always sees\n * the latest render's handler without the runtime being rebuilt.\n */\nfunction latestTable(current: () => object | undefined): object {\n return new Proxy(\n {},\n {\n get: (_target, type) =>\n (current() as Record<PropertyKey, unknown> | undefined)?.[type],\n },\n );\n}\n\n/**\n * Lower-level escape hatch: consume an externally-built, booted run — any\n * engine's {@link BootedRunHandle} (the Promise engine's `Runtime` is one).\n *\n * Use this when the runtime is owned by something OTHER than the component —\n * a parent component, a test harness, a singleton. **The component does NOT\n * call `runtime.stop()` on unmount** — the caller owns the lifecycle.\n *\n * Primary use case in this repo: component tests. Build a runtime with\n * `@demlik/tea/mem` and pre-seed it via `replay`, then mount the component with\n * `useRuntime(testRuntime)` to render in any phase.\n *\n * Returns `[state, dispatch]` — same shape as `useMachine`.\n */\nexport function useRuntime<\n S,\n M extends { type: string },\n E extends { type: string } = never,\n>(runtime: BootedRunHandle<S, M, E>): [S, (msg: M) => Promise<void>] {\n const state = useSyncExternalStore(\n runtime.subscribe,\n runtime.getState,\n runtime.getState,\n );\n return [state, runtime.dispatch];\n}\n"]}
@@ -1,12 +1,12 @@
1
- import { a as CmdOf, b as CmdDef, e as SettledErr, f as TaggedError, d as SettledOk, T as TimerDeps, C as Cmd } from '../core-CtvAyPpH.js';
1
+ import { a as CmdOf, b as CmdDef, e as SettledErr, f as TaggedError, d as SettledOk, T as TimerDeps, C as Cmd } from '../core-BKJW1Kmq.js';
2
2
  import { M as MsgType } from '../protocol-ClRZBhHQ.js';
3
- import { a as DeadlineSub } from '../index-CVU7SK3a.js';
4
- export { A as ArmTimer, e as CacheEntry, c as CacheEvictMsg, f as CacheEvictionDeps, C as CacheEvictionSub, D as DeadlineExceeded, g as DeadlineMsgType, h as DeadlineOpts, b as DeadlinesSub, T as TtlCache, i as cacheEvictMsg, j as cacheEvictionSub, k as cacheEvictionSubscribe, d as deadlineExceeded, l as deadlineMsgType, m as deadlineSub, n as deadlines, o as deadlinesSub, p as evictExpired, q as get, r as has, t as initCache, u as nextTimer, v as remove, w as set, x as setTimeoutArmTimer, s as subscribeDeadline, y as subscribeWith } from '../index-CVU7SK3a.js';
5
- import { d as ResilientConfig, b as ResilientState, R as RunCmd, S as SucceedMsg, F as FailMsg, a as ResilientTimerMsg } from '../index-CKtEKGyp.js';
6
- export { C as CacheConfig, e as CallBudget, f as CallPhase, g as CircuitConfig, h as CircuitPolicy, i as CircuitState, j as DEFAULT_RESILIENT_NAME, k as DeadlineExceededError, c as DeadlineNameOf, D as DefaultResilientName, l as RateLimitConfig, m as ResilientCallDeadlineConfig, n as ResilientDeadlineType, o as ResilientErrType, p as ResilientOkType, q as ResilientRunType, r as RunCmdDef, s as RunErr, t as SettleMsg, u as SettleOutcome, v as SettleResult, w as SlidingWindow, T as TokenBucket, x as canPass, y as createResilientCall, z as defaultCircuitPolicy, A as initBucket, B as initCircuit, E as initWindow, G as liftResilience, H as onFailure, I as onSuccess, J as record, K as refill, L as remaining, M as runCmdDef, N as tryConsume } from '../index-CKtEKGyp.js';
3
+ import { a as DeadlineSub } from '../index-Diyo8WBO.js';
4
+ export { A as ArmTimer, e as CacheEntry, c as CacheEvictMsg, f as CacheEvictionDeps, C as CacheEvictionSub, D as DeadlineExceeded, g as DeadlineMsgType, h as DeadlineOpts, b as DeadlinesSub, T as TtlCache, i as cacheEvictMsg, j as cacheEvictionSub, k as cacheEvictionSubscribe, d as deadlineExceeded, l as deadlineMsgType, m as deadlineSub, n as deadlines, o as deadlinesSub, p as evictExpired, q as get, r as has, t as initCache, u as nextTimer, v as remove, w as set, x as setTimeoutArmTimer, s as subscribeDeadline, y as subscribeWith } from '../index-Diyo8WBO.js';
5
+ import { d as ResilientConfig, b as ResilientState, R as RunCmd, S as SucceedMsg, F as FailMsg, a as ResilientTimerMsg } from '../index-B-VC4qjx.js';
6
+ export { C as CacheConfig, e as CallBudget, f as CallPhase, g as CircuitConfig, h as CircuitPolicy, i as CircuitState, j as DEFAULT_RESILIENT_NAME, k as DeadlineExceededError, c as DeadlineNameOf, D as DefaultResilientName, l as RateLimitConfig, m as ResilientCallDeadlineConfig, n as ResilientDeadlineType, o as ResilientErrType, p as ResilientOkType, q as ResilientRunType, r as RunCmdDef, s as RunErr, t as SettleMsg, u as SettleOutcome, v as SettleResult, w as SlidingWindow, T as TokenBucket, x as canPass, y as createResilientCall, z as defaultCircuitPolicy, A as initBucket, B as initCircuit, E as initWindow, G as liftResilience, H as onFailure, I as onSuccess, J as record, K as refill, L as remaining, M as runCmdDef, N as tryConsume } from '../index-B-VC4qjx.js';
7
7
  import { Rng, RetryPolicy } from '../retry-backoff/index.js';
8
8
  import '@standard-schema/spec';
9
- import '../types-CXNzlbAg.js';
9
+ import '../types-BiXj7k_k.js';
10
10
 
11
11
  /**
12
12
  * A minted credential: the opaque `value` to send on the wire, and the absolute
@@ -1,10 +1,10 @@
1
- import { createResilientCall } from '../chunk-72CN3VPY.js';
2
- export { DEFAULT_RESILIENT_NAME, canPass, createResilientCall, defaultCircuitPolicy, initBucket, initCircuit, initWindow, liftResilience, onFailure, onSuccess, record, refill, remaining, runCmdDef, tryConsume } from '../chunk-72CN3VPY.js';
3
- export { cacheEvictMsg, cacheEvictionSub, cacheEvictionSubscribe, deadlineExceeded, deadlineMsgType, deadlineSub, deadlines, deadlinesSub, evictExpired, get, has, initCache, nextTimer, remove, set, setTimeoutArmTimer, subscribeDeadline, subscribeWith } from '../chunk-TAKPC6SD.js';
1
+ import { createResilientCall } from '../chunk-MBQR3LFW.js';
2
+ export { DEFAULT_RESILIENT_NAME, canPass, createResilientCall, defaultCircuitPolicy, initBucket, initCircuit, initWindow, liftResilience, onFailure, onSuccess, record, refill, remaining, runCmdDef, tryConsume } from '../chunk-MBQR3LFW.js';
3
+ export { cacheEvictMsg, cacheEvictionSub, cacheEvictionSubscribe, deadlineExceeded, deadlineMsgType, deadlineSub, deadlines, deadlinesSub, evictExpired, get, has, initCache, nextTimer, remove, set, setTimeoutArmTimer, subscribeDeadline, subscribeWith } from '../chunk-QIE4Y6BK.js';
4
4
  import { defaultRng, initRetry, recordFailure, shouldRetry, nextDelayMs } from '../chunk-VBDU4JL6.js';
5
5
  import { unchecked } from '../chunk-4LPY67E4.js';
6
- import '../chunk-DVNXEB7V.js';
7
- import { Cmd, without } from '../chunk-RKVUVALV.js';
6
+ import '../chunk-VHKDUBXC.js';
7
+ import { Cmd, without } from '../chunk-GAVLND5C.js';
8
8
 
9
9
  // src/internal/resilience/token-refresh/index.ts
10
10
  function initTokenRefresh() {
@@ -1,6 +1,6 @@
1
1
  import { Effect, Stream, Scope } from 'effect';
2
- import { P as Port, C as Cmd, E as ErrorsOf, o as OkOfCmd, p as DeclaredErrorsOf, S as Sub, q as BuiltinSubType, B as BuiltinSub, M as Machine } from './core-CtvAyPpH.js';
3
- import { c as DispatchSettle, C as CtxArg, S as Store, O as OnError, a as Supervision, T as TelemetrySink } from './runtime-types-DEluQgGO.js';
2
+ import { P as Port, C as Cmd, E as ErrorsOf, p as OkOfCmd, q as DeclaredErrorsOf, c as Sub, r as BuiltinSubType, B as BuiltinSub, M as Machine } from './core-BKJW1Kmq.js';
3
+ import { c as DispatchSettle, C as CtxArg, S as Store, O as OnError, a as Supervision, T as TelemetrySink } from './runtime-types-BPKp2bGo.js';
4
4
  import * as effect_Cause from 'effect/Cause';
5
5
  import * as effect_Types from 'effect/Types';
6
6
 
@@ -90,11 +90,13 @@ interface EffectBootingRuntime<S, M extends {
90
90
  */
91
91
  readonly ready: Effect.Effect<EffectRuntime<S, M, E, Err>, Err | StoreFailed>;
92
92
  /**
93
- * Stop the run: drain in-flight work, stop its Subs, flush the final State.
94
- * Closing the run's Scope stops the run the same way, and `stop()` leaves
95
- * that Scope open. So for a child under `spawn` the two differ: `stop()`
96
- * does not take the child out of the host's table or tell the parent. Stop
97
- * such a child with the `stop` helper.
93
+ * Stop the run: drain in-flight work, up to `disposeTimeoutMs`, stop its
94
+ * Subs, flush the final State. `stop()` interrupts every handler still in
95
+ * flight, so the limit is reached only by a handler that cannot be
96
+ * interrupted. Closing the run's Scope stops the run the same way, and
97
+ * `stop()` leaves that Scope open. So for a child under `spawn` the two
98
+ * differ: `stop()` does not take the child out of the host's table or tell
99
+ * the parent. Stop such a child with the `stop` helper.
98
100
  */
99
101
  stop(): Effect.Effect<void>;
100
102
  }
@@ -240,7 +242,18 @@ type EffectRunOptions<S, M extends {
240
242
  readonly terminal?: (state: S) => boolean;
241
243
  /** A fire-and-forget sink for every applied transition. */
242
244
  readonly telemetry?: TelemetrySink;
243
- /** How long `stop()` waits for async teardown. Defaults to 5_000ms. */
245
+ /**
246
+ * How long `stop()` waits for the queue, and then for async teardown,
247
+ * before giving up on each. Defaults to 5_000ms. `stop()` interrupts every
248
+ * handler here, so the first wait ends early unless a handler cannot be
249
+ * interrupted.
250
+ *
251
+ * A store call is not interrupted. tea starts none after `stop()` has
252
+ * returned, but one already under way when `stop()` gave up can finish
253
+ * after it. On a plain store that late save can overwrite what a newer
254
+ * run saved; a fenced store refuses it. So a host that starts a new run on
255
+ * the same store after a `stop()` that gave up needs a fenced store.
256
+ */
244
257
  readonly disposeTimeoutMs?: number;
245
258
  };
246
259
  /**
@@ -286,4 +299,4 @@ declare function run<S, M extends {
286
299
  type: string;
287
300
  } = never>(machine: Machine<S, M, C, U, Ctx>, opts: EffectRunOptions<S, M, C, U, Ctx, E, I, B>): Effect.Effect<EffectBootingRuntime<S, M, E, CellErrors<C, I>>, never, Scope.Scope | InterpretServices<I> | SubscribeServices<B>>;
288
301
 
289
- export { type CellErrors as C, type EffectRuntime as E, type InterpretServices as I, StoreFailed as S, type EffectBootingRuntime as a, type EffectInterpret as b, type EffectInterpretCell as c, type EffectRunOptions as d, type EffectRunner as e, type EffectSubscribe as f, Stopped as g, type SubscribeServices as h, type SubscribeOption as i, run as r };
302
+ export { type CellErrors as C, type EffectInterpret as E, type InterpretServices as I, type SubscribeOption as S, type EffectSubscribe as a, type SubscribeServices as b, type EffectRuntime as c, StoreFailed as d, type EffectBootingRuntime as e, type EffectInterpretCell as f, type EffectRunOptions as g, type EffectRunner as h, Stopped as i, run as r };
@@ -1,4 +1,4 @@
1
- import { C as Cmd, S as Sub, M as Machine, R as RunHandlers, P as Port, N as NoCellError, g as Reducer, A as AnyCmdDef, h as Settled, a as CmdOf, i as Transitions, B as BuiltinSub, U as UpdateForm, O as Outcome, j as InterpretDetached, H as HandlerCtx } from './core-CtvAyPpH.js';
1
+ import { C as Cmd, c as Sub, M as Machine, R as RunHandlers, P as Port, N as NoCellError, g as Reducer, A as AnyCmdDef, h as Settled, a as CmdOf, i as Transitions, B as BuiltinSub, U as UpdateForm, O as Outcome, j as InterpretDetached, H as HandlerCtx } from './core-BKJW1Kmq.js';
2
2
 
3
3
  /**
4
4
  * @demlik/tea runtime interface surface + pure helpers — the public types,
@@ -98,12 +98,14 @@ declare function __resetPortRegistry(): void;
98
98
  * run stops — on the Effect engine by closing its Scope with the failure. A
99
99
  * Sub that expects an error maps it to a Msg itself; there is no hook for it
100
100
  * here.
101
- * - `"discard"` — work lost to a teardown the host asked for. Two witnesses,
102
- * both carrying a `RuntimeDiscardNotice`: `stop()` was called while interpret
103
- * handlers were still awaiting (`RuntimeDiscardedError`), and a Msg that
104
- * arrived DURING `stop()`'s drain and so could not be delivered
105
- * (`DispatchDiscardedError`), and teardown work that did not settle inside
106
- * `disposeTimeoutMs` (`DisposeTimeoutNotice`). The one phase that is a
101
+ * - `"discard"` — work lost to a teardown the host asked for. Four witnesses,
102
+ * each carrying a `RuntimeDiscardNotice`: `stop()` was called while interpret
103
+ * handlers were still awaiting (`RuntimeDiscardedError`), a Msg that
104
+ * arrived DURING `stop()`'s drain, or was still queued when `stop()` gave up
105
+ * on it, and so could not be delivered (`DispatchDiscardedError`), a drain
106
+ * that did not finish inside `disposeTimeoutMs` (`DrainTimeoutNotice`), and
107
+ * teardown work that did not settle inside `disposeTimeoutMs`
108
+ * (`DisposeTimeoutNotice`). The one phase that is a
107
109
  * LIFECYCLE report rather than a failure: legal, merely lossy.
108
110
  * - `"identity-drop"` — the `Identity` filter dropped a message addressed to a
109
111
  * different instance (`IdentityDropNotice`). Its own phase rather than
@@ -237,7 +239,8 @@ declare abstract class RuntimeDiscardNotice extends Error {
237
239
  /**
238
240
  * Reported to the `OnError` sink under `phase: "discard"` when `stop()` is
239
241
  * called while `interpret` handlers are still awaiting. The host is dropping
240
- * its only handle on that work: `stop()` drains the tail, but the listeners,
242
+ * its only handle on that work: `stop()` drains the tail, up to
243
+ * `disposeTimeoutMs`, but the listeners,
241
244
  * observers and event handlers that would have consumed the resulting
242
245
  * transitions are gone, so the Cmds' results reach nobody.
243
246
  *
@@ -294,12 +297,48 @@ declare class DisposeTimeoutNotice extends RuntimeDiscardNotice {
294
297
  readonly _tag: "DisposeTimeoutNotice";
295
298
  constructor(pendingDisposals: number, timeoutMs: number);
296
299
  }
300
+ /**
301
+ * Reported to the `OnError` sink under `phase: "discard"` when `stop()`'s wait
302
+ * for the queue hits `disposeTimeoutMs` (ADR 0025): a Cmd handler, a save or
303
+ * detached work had not returned.
304
+ *
305
+ * `stop()` resolves by contract, so it gives up on that work instead of waiting
306
+ * for it without limit. It then stops the Subs and runs the final save as it
307
+ * always does. The work it gave up on is not recalled — a Promise cannot be
308
+ * cancelled — so a handler may still be running after `stop()` has returned.
309
+ * Whatever it returns later is dropped, and so are the Msgs queued behind it:
310
+ * each dropped Msg is a `DispatchDiscardedError`. The rest of its step's
311
+ * Cmds are skipped: no handler starts for them, and they get no notice of
312
+ * their own. None of it is saved.
313
+ *
314
+ * tea starts no store call after `stop()` has returned. A store call already
315
+ * under way when `stop()` gave up is not cancelled and can finish afterwards;
316
+ * on a plain store that late save can overwrite what a newer run saved, and a
317
+ * fenced store refuses it.
318
+ *
319
+ * `cmdTypes` names the Cmd of each handler call still awaiting, one entry per
320
+ * call. `pendingCmds` counts those plus any detached work, the same count
321
+ * `RuntimeDiscardedError` carries.
322
+ */
323
+ declare class DrainTimeoutNotice extends RuntimeDiscardNotice {
324
+ readonly cmdTypes: readonly string[];
325
+ readonly pendingCmds: number;
326
+ readonly timeoutMs: number;
327
+ readonly name = "DrainTimeoutNotice";
328
+ readonly _tag: "DrainTimeoutNotice";
329
+ constructor(cmdTypes: readonly string[], pendingCmds: number, timeoutMs: number);
330
+ }
297
331
  /**
298
332
  * The rejection of a dispatch that arrived DURING `stop()`'s drain — an in-flight
299
333
  * interpret handler's follow-up Msg, a detached handler's terminal Msg, or a Sub
300
334
  * that is still live because subs are torn down only after the drain. The stop
301
335
  * barrier is absolute (the Msg is refused, never folded), so this names the loss.
302
336
  *
337
+ * It is also the rejection of the work `stop()` gave up on (see
338
+ * `DrainTimeoutNotice`): a Msg a stuck handler produces after `stop()` has
339
+ * returned, and a Msg that was accepted before `stop()` and was still queued
340
+ * behind that handler.
341
+ *
303
342
  * It is the SECOND witness of `RuntimeDiscardedError`'s fact, and it is why the
304
343
  * drain window is a distinct runtime state rather than a plain `stopped` flag:
305
344
  * a Msg refused inside the window was discarded BY the teardown, while the same
@@ -884,4 +923,4 @@ declare function tryFoldMsgs<S, M extends {
884
923
  */
885
924
  declare function tryInterpret<C extends Cmd, Ok, M, Ctx>(work: (cmd: C, ctx: Ctx) => Promise<Ok>, onOk: (value: Ok, cmd: C) => M, onErr: (error: unknown, cmd: C) => M): (cmd: C, ctx: Ctx) => Promise<M>;
886
925
 
887
- export { defineMachine as A, type BootingRuntime as B, type CtxArg as C, type DeletableStore as D, type EngineRun as E, type FencedStore as F, definePort as G, isFencedStore as H, IdentityDropNotice as I, noop as J, refuse as K, replay as L, type Migrated as M, schemaMigrate as N, type OnError as O, PortNameCollisionError as P, QuiescenceTimeoutError as Q, type Runtime as R, type Store as S, type TelemetrySink as T, tryApplyCell as U, tryFoldMsgs as V, tryInterpret as W, wrapDetached as X, __resetPortRegistry as _, type Supervision as a, type BootedRunHandle as b, type DispatchSettle as c, type RunHandle as d, type Branded as e, DispatchDiscardedError as f, DisposeTimeoutNotice as g, DriveFailedError as h, DriveStalledError as i, type FencedRead as j, type FoldRefusal as k, type MachineTypes as l, Refusal as m, type RunOptions as n, RuntimeDiscardNotice as o, RuntimeDiscardedError as p, type RuntimeErrorContext as q, type RuntimeErrorPhase as r, type RuntimeRef as s, type Schema as t, StoreConflictError as u, StoreRefusedError as v, type SupervisionStrategy as w, type TelemetryEvent as x, absurd as y, asReducer as z };
926
+ export { asReducer as A, type BootingRuntime as B, type CtxArg as C, type DeletableStore as D, type EngineRun as E, type FencedStore as F, defineMachine as G, definePort as H, IdentityDropNotice as I, isFencedStore as J, noop as K, refuse as L, type Migrated as M, replay as N, type OnError as O, PortNameCollisionError as P, QuiescenceTimeoutError as Q, type Runtime as R, type Store as S, type TelemetrySink as T, schemaMigrate as U, tryApplyCell as V, tryFoldMsgs as W, tryInterpret as X, wrapDetached as Y, __resetPortRegistry as _, type Supervision as a, type BootedRunHandle as b, type DispatchSettle as c, type RunHandle as d, type Branded as e, DispatchDiscardedError as f, DisposeTimeoutNotice as g, DrainTimeoutNotice as h, DriveFailedError as i, DriveStalledError as j, type FencedRead as k, type FoldRefusal as l, type MachineTypes as m, Refusal as n, type RunOptions as o, RuntimeDiscardNotice as p, RuntimeDiscardedError as q, type RuntimeErrorContext as r, type RuntimeErrorPhase as s, type RuntimeRef as t, type Schema as u, StoreConflictError as v, StoreRefusedError as w, type SupervisionStrategy as x, type TelemetryEvent as y, absurd as z };
@@ -1,9 +1,9 @@
1
- import { D as DriveRoundsExceededError, a as DriveNoHandlerError, b as DriveOptions, c as DriveResult } from '../../drive-loop-Dw1sGSsW.js';
2
- export { d as DEFAULT_MAX_ROUNDS, e as DriveCtxArg, f as DriveTraceEntry, g as driveTraceOf } from '../../drive-loop-Dw1sGSsW.js';
1
+ import { D as DriveRoundsExceededError, a as DriveNoHandlerError, b as DriveOptions, c as DriveResult } from '../../drive-loop-Dc6Z8Vbs.js';
2
+ export { d as DEFAULT_MAX_ROUNDS, e as DriveCtxArg, f as DriveTraceEntry, g as driveTraceOf } from '../../drive-loop-Dc6Z8Vbs.js';
3
3
  import { Effect } from 'effect';
4
- import { C as CellErrors, i as SubscribeOption, b as EffectInterpret, f as EffectSubscribe, I as InterpretServices, h as SubscribeServices } from '../../run-fiBe7pOG.js';
5
- import { C as Cmd, S as Sub, M as Machine } from '../../core-CtvAyPpH.js';
6
- import '../../runtime-types-DEluQgGO.js';
4
+ import { C as CellErrors, S as SubscribeOption, E as EffectInterpret, a as EffectSubscribe, I as InterpretServices, b as SubscribeServices } from '../../run-D0sxbyno.js';
5
+ import { C as Cmd, c as Sub, M as Machine } from '../../core-BKJW1Kmq.js';
6
+ import '../../runtime-types-BPKp2bGo.js';
7
7
  import 'effect/Cause';
8
8
  import 'effect/Types';
9
9
  import '@standard-schema/spec';
@@ -1,11 +1,11 @@
1
- import { CellFailure, builtinRunners, cellResult, settleCellExit } from '../../chunk-AIXC33SE.js';
2
- import { driveLoop, DEFAULT_MAX_ROUNDS, driveContractOf, DriveRoundsExceededError, DriveNoHandlerError, annotateTrace, driveTraceOf, desiredSubs } from '../../chunk-XWIE5V7R.js';
3
- export { DEFAULT_MAX_ROUNDS, DriveNoHandlerError, DriveRoundsExceededError, driveTraceOf } from '../../chunk-XWIE5V7R.js';
4
- import '../../chunk-HX3C6LDK.js';
5
- import '../../chunk-UHAQ4PI6.js';
6
- import '../../chunk-A73IJ27X.js';
7
- import '../../chunk-DVNXEB7V.js';
8
- import '../../chunk-RKVUVALV.js';
1
+ import { CellFailure, builtinRunners, cellResult, settleCellExit } from '../../chunk-HU3Y2AZY.js';
2
+ import { driveLoop, DEFAULT_MAX_ROUNDS, driveContractOf, DriveRoundsExceededError, DriveNoHandlerError, annotateTrace, driveTraceOf, desiredSubs } from '../../chunk-BFSZ4L6Y.js';
3
+ export { DEFAULT_MAX_ROUNDS, DriveNoHandlerError, DriveRoundsExceededError, driveTraceOf } from '../../chunk-BFSZ4L6Y.js';
4
+ import '../../chunk-7Z2IDZ7R.js';
5
+ import '../../chunk-VM7PMFV5.js';
6
+ import '../../chunk-Z7IL735U.js';
7
+ import '../../chunk-VHKDUBXC.js';
8
+ import '../../chunk-GAVLND5C.js';
9
9
  import { Effect, Stream, Exit, Cause, Fiber } from 'effect';
10
10
 
11
11
  var DriveInterrupted = class {
@@ -1,5 +1,5 @@
1
- import { C as Cmd, S as Sub, M as Machine, B as BuiltinSub } from '../core-CtvAyPpH.js';
2
- import { R as Runtime } from '../runtime-types-DEluQgGO.js';
1
+ import { C as Cmd, c as Sub, M as Machine, B as BuiltinSub } from '../core-BKJW1Kmq.js';
2
+ import { R as Runtime } from '../runtime-types-BPKp2bGo.js';
3
3
  import '@standard-schema/spec';
4
4
 
5
5
  /**
@@ -1,6 +1,6 @@
1
- export { bindMachine } from '../chunk-HX3C6LDK.js';
2
- import '../chunk-DVNXEB7V.js';
3
- import { replay } from '../chunk-RKVUVALV.js';
1
+ export { bindMachine } from '../chunk-7Z2IDZ7R.js';
2
+ import '../chunk-VHKDUBXC.js';
3
+ import { replay } from '../chunk-GAVLND5C.js';
4
4
  import { expect, vi } from 'vitest';
5
5
 
6
6
  function expectFinalState(machine, opts, expected) {
@@ -1,7 +1,7 @@
1
- import { b as DriveOptions, c as DriveResult } from '../../drive-loop-Dw1sGSsW.js';
2
- export { d as DEFAULT_MAX_ROUNDS, e as DriveCtxArg, a as DriveNoHandlerError, D as DriveRoundsExceededError, f as DriveTraceEntry, g as driveTraceOf } from '../../drive-loop-Dw1sGSsW.js';
3
- import { C as Cmd, S as Sub, M as Machine, I as Interpret } from '../../core-CtvAyPpH.js';
4
- import '../../runtime-types-DEluQgGO.js';
1
+ import { b as DriveOptions, c as DriveResult } from '../../drive-loop-Dc6Z8Vbs.js';
2
+ export { d as DEFAULT_MAX_ROUNDS, e as DriveCtxArg, a as DriveNoHandlerError, D as DriveRoundsExceededError, f as DriveTraceEntry, g as driveTraceOf } from '../../drive-loop-Dc6Z8Vbs.js';
3
+ import { C as Cmd, c as Sub, M as Machine, I as Interpret } from '../../core-BKJW1Kmq.js';
4
+ import '../../runtime-types-BPKp2bGo.js';
5
5
  import '@standard-schema/spec';
6
6
 
7
7
  /**
@@ -1,8 +1,8 @@
1
- import { driveContractOf, driveLoop, DEFAULT_MAX_ROUNDS } from '../../chunk-XWIE5V7R.js';
2
- export { DEFAULT_MAX_ROUNDS, DriveNoHandlerError, DriveRoundsExceededError, driveTraceOf } from '../../chunk-XWIE5V7R.js';
3
- import '../../chunk-HX3C6LDK.js';
4
- import '../../chunk-DVNXEB7V.js';
5
- import { cmdEdge, Outcome } from '../../chunk-RKVUVALV.js';
1
+ import { driveContractOf, driveLoop, DEFAULT_MAX_ROUNDS } from '../../chunk-BFSZ4L6Y.js';
2
+ export { DEFAULT_MAX_ROUNDS, DriveNoHandlerError, DriveRoundsExceededError, driveTraceOf } from '../../chunk-BFSZ4L6Y.js';
3
+ import '../../chunk-7Z2IDZ7R.js';
4
+ import '../../chunk-VHKDUBXC.js';
5
+ import { cmdEdge, Outcome } from '../../chunk-GAVLND5C.js';
6
6
 
7
7
  // src/testing/promise/drive.ts
8
8
  async function drive(machine, initial, msg, handlers, ...[opts]) {
@@ -1,9 +1,9 @@
1
1
  export { D as Debounced, d as debounce } from '../index-DHSUmQTR.js';
2
- import { C as Cmd } from '../core-CtvAyPpH.js';
3
- import { T as TtlCache, a as DeadlineSub } from '../index-CVU7SK3a.js';
4
- export { D as ThrottledInputSettled, s as subscribeThrottledInput, d as throttledInputSettled } from '../index-CVU7SK3a.js';
2
+ import { C as Cmd } from '../core-BKJW1Kmq.js';
3
+ import { T as TtlCache, a as DeadlineSub } from '../index-Diyo8WBO.js';
4
+ export { D as ThrottledInputSettled, s as subscribeThrottledInput, d as throttledInputSettled } from '../index-Diyo8WBO.js';
5
5
  import '@standard-schema/spec';
6
- import '../types-CXNzlbAg.js';
6
+ import '../types-BiXj7k_k.js';
7
7
 
8
8
  /**
9
9
  * internal/timing/throttle — a timer-based call transformer that caps invocation
@@ -1,8 +1,8 @@
1
1
  export { debounce } from '../chunk-LFZUK6J2.js';
2
- import { deadlineSub, initCache, get, set } from '../chunk-TAKPC6SD.js';
3
- export { subscribeDeadline as subscribeThrottledInput, deadlineExceeded as throttledInputSettled } from '../chunk-TAKPC6SD.js';
4
- import '../chunk-DVNXEB7V.js';
5
- import '../chunk-RKVUVALV.js';
2
+ import { deadlineSub, initCache, get, set } from '../chunk-QIE4Y6BK.js';
3
+ export { subscribeDeadline as subscribeThrottledInput, deadlineExceeded as throttledInputSettled } from '../chunk-QIE4Y6BK.js';
4
+ import '../chunk-VHKDUBXC.js';
5
+ import '../chunk-GAVLND5C.js';
6
6
 
7
7
  // src/internal/timing/throttle/index.ts
8
8
  function throttle(fn, ms, opts) {
@@ -1,4 +1,4 @@
1
- import { S as Sub, D as Dispose } from './core-CtvAyPpH.js';
1
+ import { c as Sub, D as Dispose } from './core-BKJW1Kmq.js';
2
2
 
3
3
  /**
4
4
  * A Sub runner: given the Sub, the `ctx` and a `dispatch`, it opens the
@@ -1,7 +1,7 @@
1
- import { Q as QueueItem } from '../index-D3NY3VCN.js';
2
- export { E as EnqueueInput, a as QueueItemStatus, c as createQueue } from '../index-D3NY3VCN.js';
3
- import '../runtime-types-DEluQgGO.js';
4
- import '../core-CtvAyPpH.js';
1
+ import { Q as QueueItem } from '../index-Cuy1_xTh.js';
2
+ export { E as EnqueueInput, a as QueueItemStatus, c as createQueue } from '../index-Cuy1_xTh.js';
3
+ import '../runtime-types-BPKp2bGo.js';
4
+ import '../core-BKJW1Kmq.js';
5
5
  import '@standard-schema/spec';
6
6
 
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import { enqueueOp, claimNextOp, markDoneOp, patchItemOp, resetRunningOp, removeOp } from '../chunk-TXN2KFHF.js';
2
2
  export { claimNextOp, enqueueOp, markDoneOp, patchItemOp, queueAdapter, removeOp, resetRunningOp } from '../chunk-TXN2KFHF.js';
3
- import { restore } from '../chunk-A73IJ27X.js';
4
- import '../chunk-RKVUVALV.js';
3
+ import { restore } from '../chunk-Z7IL735U.js';
4
+ import '../chunk-GAVLND5C.js';
5
5
 
6
6
  // src/internal/work-queue/index.ts
7
7
  function genId() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@demlik/tea",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "TEA-faithful state machine substrate — one package, every host adapter. Core + subs + testing, plus React, Durable Object, Chrome extension, Node, in-memory, devtools, and property-testing modules as subpaths.",
5
5
  "keywords": [
6
6
  "tea",
@@ -193,7 +193,8 @@
193
193
  "vite": "^8.2.1",
194
194
  "vitest": "~5.0.1",
195
195
  "ws": "^8.21.0",
196
- "zod": "^4.5.4"
196
+ "zod": "^4.5.4",
197
+ "@demlik/code-graph": "0.8.0"
197
198
  },
198
199
  "publishConfig": {
199
200
  "access": "public"
@@ -212,6 +213,7 @@
212
213
  "typecheck:consumers": "tsc -p tsconfig.consumers.json",
213
214
  "docs:reference": "TEA_DOCS_WRITE=1 vitest run src/docs/reference/generate-reference.test.ts",
214
215
  "docs:reference:strays": "vitest run src/docs/reference/generate-reference.test.ts -t 'holds nothing the generator does not write'",
216
+ "api:ratchet": "node scripts/api-ratchet.mjs",
215
217
  "docs:reference:check": "pnpm run docs:reference:strays && pnpm run docs:reference && git diff --exit-code -- docs/reference",
216
218
  "verify:exports": "node scripts/verify-exports.mjs",
217
219
  "verify-optional-peer-install": "node scripts/verify-optional-peer-install.mjs"