@demlik/tea 0.1.0 → 0.1.1

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 (238) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agent/index.d.ts +907 -0
  3. package/dist/agent/index.js +20 -0
  4. package/dist/agent/index.js.map +1 -0
  5. package/dist/authed-call/index.d.ts +119 -0
  6. package/dist/authed-call/index.js +148 -0
  7. package/dist/authed-call/index.js.map +1 -0
  8. package/dist/await-terminal/index.d.ts +99 -0
  9. package/dist/await-terminal/index.js +75 -0
  10. package/dist/await-terminal/index.js.map +1 -0
  11. package/dist/batch-window/index.d.ts +259 -0
  12. package/dist/batch-window/index.js +43 -0
  13. package/dist/batch-window/index.js.map +1 -0
  14. package/dist/cache/index.d.ts +2 -2
  15. package/dist/cache/index.js +2 -50
  16. package/dist/cache/index.js.map +1 -1
  17. package/dist/{chunk-CMG2KMWX.js → chunk-2WZHS4P2.js} +220 -177
  18. package/dist/chunk-2WZHS4P2.js.map +1 -0
  19. package/dist/chunk-2YOE5PIS.js +45 -0
  20. package/dist/chunk-2YOE5PIS.js.map +1 -0
  21. package/dist/chunk-3545BOYL.js +65 -0
  22. package/dist/chunk-3545BOYL.js.map +1 -0
  23. package/dist/chunk-5VNWZADP.js +49 -0
  24. package/dist/chunk-5VNWZADP.js.map +1 -0
  25. package/dist/{chunk-ZRUST6BK.js → chunk-7FYQGDMF.js} +2 -2
  26. package/dist/chunk-7FYQGDMF.js.map +1 -0
  27. package/dist/chunk-7VEG4KHB.js +202 -0
  28. package/dist/chunk-7VEG4KHB.js.map +1 -0
  29. package/dist/{chunk-PYBQREFU.js → chunk-A56GNPI7.js} +20 -6
  30. package/dist/chunk-A56GNPI7.js.map +1 -0
  31. package/dist/chunk-BNJYE4AU.js +157 -0
  32. package/dist/chunk-BNJYE4AU.js.map +1 -0
  33. package/dist/chunk-BYQUGDD3.js +16 -0
  34. package/dist/chunk-BYQUGDD3.js.map +1 -0
  35. package/dist/chunk-D5EQDLDV.js +186 -0
  36. package/dist/chunk-D5EQDLDV.js.map +1 -0
  37. package/dist/chunk-EDGDR5XX.js +60 -0
  38. package/dist/chunk-EDGDR5XX.js.map +1 -0
  39. package/dist/chunk-EIZJTYKR.js +93 -0
  40. package/dist/chunk-EIZJTYKR.js.map +1 -0
  41. package/dist/chunk-EQRUAP2A.js +56 -0
  42. package/dist/chunk-EQRUAP2A.js.map +1 -0
  43. package/dist/chunk-FH5RUIIL.js +61 -0
  44. package/dist/chunk-FH5RUIIL.js.map +1 -0
  45. package/dist/chunk-HJO6VNQI.js +15 -0
  46. package/dist/chunk-HJO6VNQI.js.map +1 -0
  47. package/dist/chunk-IFBQJYGI.js +132 -0
  48. package/dist/chunk-IFBQJYGI.js.map +1 -0
  49. package/dist/chunk-IN33Q4VJ.js +52 -0
  50. package/dist/chunk-IN33Q4VJ.js.map +1 -0
  51. package/dist/chunk-JELSZN25.js +47 -0
  52. package/dist/chunk-JELSZN25.js.map +1 -0
  53. package/dist/chunk-K575PT4S.js +58 -0
  54. package/dist/chunk-K575PT4S.js.map +1 -0
  55. package/dist/chunk-LEC4U6CX.js +91 -0
  56. package/dist/chunk-LEC4U6CX.js.map +1 -0
  57. package/dist/{chunk-Y5JM3UUH.js → chunk-NSDFA6M6.js} +75 -3
  58. package/dist/chunk-NSDFA6M6.js.map +1 -0
  59. package/dist/chunk-QGZW4W3E.js +20 -0
  60. package/dist/chunk-QGZW4W3E.js.map +1 -0
  61. package/dist/{chunk-PYOCUBQJ.js → chunk-RGTPPF4U.js} +22 -15
  62. package/dist/chunk-RGTPPF4U.js.map +1 -0
  63. package/dist/chunk-RVJFGRGK.js +222 -0
  64. package/dist/chunk-RVJFGRGK.js.map +1 -0
  65. package/dist/chunk-RVU3RBE6.js +31 -0
  66. package/dist/chunk-RVU3RBE6.js.map +1 -0
  67. package/dist/chunk-SC3FAFZN.js +53 -0
  68. package/dist/chunk-SC3FAFZN.js.map +1 -0
  69. package/dist/chunk-SINXE47Y.js +55 -0
  70. package/dist/chunk-SINXE47Y.js.map +1 -0
  71. package/dist/chunk-TAO4YOLO.js +87 -0
  72. package/dist/chunk-TAO4YOLO.js.map +1 -0
  73. package/dist/chunk-TC4HWABA.js +506 -0
  74. package/dist/chunk-TC4HWABA.js.map +1 -0
  75. package/dist/chunk-TGG26XMK.js +52 -0
  76. package/dist/chunk-TGG26XMK.js.map +1 -0
  77. package/dist/chunk-UV6UWGEO.js +147 -0
  78. package/dist/chunk-UV6UWGEO.js.map +1 -0
  79. package/dist/chunk-UYWUVEP6.js +158 -0
  80. package/dist/chunk-UYWUVEP6.js.map +1 -0
  81. package/dist/chunk-WUV3PSCZ.js +54 -0
  82. package/dist/chunk-WUV3PSCZ.js.map +1 -0
  83. package/dist/chunk-XRXKH7KA.js +268 -0
  84. package/dist/chunk-XRXKH7KA.js.map +1 -0
  85. package/dist/circuit-breaker/index.js +1 -51
  86. package/dist/circuit-breaker/index.js.map +1 -1
  87. package/dist/core-CymXZCeY.d.ts +178 -0
  88. package/dist/deadline/index.d.ts +24 -5
  89. package/dist/deadline/index.js +4 -18
  90. package/dist/deadline/index.js.map +1 -1
  91. package/dist/debounce/index.js +1 -54
  92. package/dist/debounce/index.js.map +1 -1
  93. package/dist/devtools/index.d.ts +1 -1
  94. package/dist/devtools/index.js +18 -6
  95. package/dist/devtools/index.js.map +1 -1
  96. package/dist/do/index.d.ts +1732 -92
  97. package/dist/do/index.js +677 -31
  98. package/dist/do/index.js.map +1 -1
  99. package/dist/durable-effects-2MDxDs45.d.ts +180 -0
  100. package/dist/extension/index.d.ts +31 -13
  101. package/dist/extension/index.js +5 -3
  102. package/dist/extension/index.js.map +1 -1
  103. package/dist/extension/react.js +8 -3
  104. package/dist/extension/react.js.map +1 -1
  105. package/dist/extension/subs/index.d.ts +2 -2
  106. package/dist/extension/subs/index.js.map +1 -1
  107. package/dist/extension/test-utils.js +11 -3
  108. package/dist/extension/test-utils.js.map +1 -1
  109. package/dist/fan-out/index.d.ts +239 -0
  110. package/dist/fan-out/index.js +5 -0
  111. package/dist/fan-out/index.js.map +1 -0
  112. package/dist/idempotency/adapter.d.ts +62 -0
  113. package/dist/idempotency/adapter.js +4 -0
  114. package/dist/idempotency/adapter.js.map +1 -0
  115. package/dist/idempotency/index.js +1 -56
  116. package/dist/idempotency/index.js.map +1 -1
  117. package/dist/idempotent-intake/index.d.ts +186 -0
  118. package/dist/idempotent-intake/index.js +7 -0
  119. package/dist/idempotent-intake/index.js.map +1 -0
  120. package/dist/index.d.ts +341 -191
  121. package/dist/index.js +2 -1
  122. package/dist/llm-call/index.d.ts +309 -0
  123. package/dist/llm-call/index.js +14 -0
  124. package/dist/llm-call/index.js.map +1 -0
  125. package/dist/machine-viz/index.d.ts +1 -1
  126. package/dist/machine-viz/index.js +1 -1
  127. package/dist/machine-viz/index.js.map +1 -1
  128. package/dist/mem/index.d.ts +1 -0
  129. package/dist/monitored-run/index.d.ts +253 -0
  130. package/dist/monitored-run/index.js +10 -0
  131. package/dist/monitored-run/index.js.map +1 -0
  132. package/dist/node/index.d.ts +5 -5
  133. package/dist/node/index.js.map +1 -1
  134. package/dist/paginated-walk/index.d.ts +234 -0
  135. package/dist/paginated-walk/index.js +15 -0
  136. package/dist/paginated-walk/index.js.map +1 -0
  137. package/dist/paginator/index.d.ts +271 -0
  138. package/dist/paginator/index.js +3 -0
  139. package/dist/paginator/index.js.map +1 -0
  140. package/dist/parity/index.d.ts +86 -0
  141. package/dist/parity/index.js +77 -0
  142. package/dist/parity/index.js.map +1 -0
  143. package/dist/pbt/arbitraries/index.d.ts +1 -1
  144. package/dist/pbt/arbitraries/index.js +1 -1
  145. package/dist/pbt/index.d.ts +1 -1
  146. package/dist/pbt/index.js +5 -4
  147. package/dist/pbt/index.js.map +1 -1
  148. package/dist/pbt/runners/index.d.ts +1 -1
  149. package/dist/pbt/runners/index.js +3 -2
  150. package/dist/poller/index.d.ts +288 -0
  151. package/dist/poller/index.js +79 -0
  152. package/dist/poller/index.js.map +1 -0
  153. package/dist/prediction/index.d.ts +109 -0
  154. package/dist/prediction/index.js +4 -0
  155. package/dist/prediction/index.js.map +1 -0
  156. package/dist/protocol-B5YmJr0d.d.ts +45 -0
  157. package/dist/pure/index.d.ts +2 -0
  158. package/dist/pure/index.js +4 -0
  159. package/dist/pure/index.js.map +1 -0
  160. package/dist/raft/demo.d.ts +105 -0
  161. package/dist/raft/demo.js +172 -0
  162. package/dist/raft/demo.js.map +1 -0
  163. package/dist/raft/index.d.ts +548 -0
  164. package/dist/raft/index.js +7 -0
  165. package/dist/raft/index.js.map +1 -0
  166. package/dist/raft/sim.d.ts +222 -0
  167. package/dist/raft/sim.js +8 -0
  168. package/dist/raft/sim.js.map +1 -0
  169. package/dist/raft/timeline.d.ts +46 -0
  170. package/dist/raft/timeline.js +62 -0
  171. package/dist/raft/timeline.js.map +1 -0
  172. package/dist/rate-limit/index.js +1 -43
  173. package/dist/rate-limit/index.js.map +1 -1
  174. package/dist/react/index.d.ts +2 -1
  175. package/dist/react/index.js +39 -20
  176. package/dist/react/index.js.map +1 -1
  177. package/dist/reconciler/index.d.ts +192 -0
  178. package/dist/reconciler/index.js +166 -0
  179. package/dist/reconciler/index.js.map +1 -0
  180. package/dist/recorder/index.d.ts +7 -3
  181. package/dist/recorder/index.js +1 -155
  182. package/dist/recorder/index.js.map +1 -1
  183. package/dist/resilient-call/index.d.ts +251 -0
  184. package/dist/resilient-call/index.js +13 -0
  185. package/dist/resilient-call/index.js.map +1 -0
  186. package/dist/retry-backoff/index.d.ts +42 -5
  187. package/dist/retry-backoff/index.js +1 -35
  188. package/dist/retry-backoff/index.js.map +1 -1
  189. package/dist/saga/index.d.ts +225 -0
  190. package/dist/saga/index.js +150 -0
  191. package/dist/saga/index.js.map +1 -0
  192. package/dist/snapshot/index.d.ts +334 -0
  193. package/dist/snapshot/index.js +5 -0
  194. package/dist/snapshot/index.js.map +1 -0
  195. package/dist/subs/index.d.ts +48 -10
  196. package/dist/subs/index.js +1 -1
  197. package/dist/testing/index.d.ts +107 -2
  198. package/dist/testing/index.js +204 -6
  199. package/dist/testing/index.js.map +1 -1
  200. package/dist/throttle/index.js +1 -52
  201. package/dist/throttle/index.js.map +1 -1
  202. package/dist/throttled-input/index.d.ts +372 -0
  203. package/dist/throttled-input/index.js +88 -0
  204. package/dist/throttled-input/index.js.map +1 -0
  205. package/dist/token-refresh/index.d.ts +215 -0
  206. package/dist/token-refresh/index.js +6 -0
  207. package/dist/token-refresh/index.js.map +1 -0
  208. package/dist/trace-replay/index.d.ts +11 -2
  209. package/dist/trace-replay/index.js +3 -55
  210. package/dist/trace-replay/index.js.map +1 -1
  211. package/dist/{types-CwBC5wMn.d.ts → types-N9LZbdnr.d.ts} +1 -1
  212. package/dist/with-deadline/index.d.ts +196 -0
  213. package/dist/with-deadline/index.js +130 -0
  214. package/dist/with-deadline/index.js.map +1 -0
  215. package/dist/with-resilience/index.d.ts +205 -0
  216. package/dist/with-resilience/index.js +274 -0
  217. package/dist/with-resilience/index.js.map +1 -0
  218. package/dist/with-telemetry/index.d.ts +149 -0
  219. package/dist/with-telemetry/index.js +95 -0
  220. package/dist/with-telemetry/index.js.map +1 -0
  221. package/dist/work-queue/adapter.d.ts +82 -0
  222. package/dist/work-queue/adapter.js +4 -0
  223. package/dist/work-queue/adapter.js.map +1 -0
  224. package/dist/work-queue/index.d.ts +25 -11
  225. package/dist/work-queue/index.js +1 -46
  226. package/dist/work-queue/index.js.map +1 -1
  227. package/dist/work-queue/ops.d.ts +69 -0
  228. package/dist/work-queue/ops.js +3 -0
  229. package/dist/work-queue/ops.js.map +1 -0
  230. package/dist/workflow/index.d.ts +444 -0
  231. package/dist/workflow/index.js +225 -0
  232. package/dist/workflow/index.js.map +1 -0
  233. package/package.json +162 -25
  234. package/dist/chunk-CMG2KMWX.js.map +0 -1
  235. package/dist/chunk-PYBQREFU.js.map +0 -1
  236. package/dist/chunk-PYOCUBQJ.js.map +0 -1
  237. package/dist/chunk-Y5JM3UUH.js.map +0 -1
  238. package/dist/chunk-ZRUST6BK.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,38 +1,6 @@
1
- type Reducer<S, M extends {
2
- type: string;
3
- }, C extends Cmd> = {
4
- [K in M["type"]]: (state: S, msg: Extract<M, {
5
- type: K;
6
- }>) => readonly [S, readonly C[]];
7
- };
8
- type Transitions<S extends {
9
- type: string;
10
- }, M extends {
11
- type: string;
12
- }, C extends Cmd> = {
13
- [P in S["type"]]: {
14
- [K in M["type"]]: (state: Extract<S, {
15
- type: P;
16
- }>, msg: Extract<M, {
17
- type: K;
18
- }>) => readonly [S, readonly C[]];
19
- };
20
- };
21
- /**
22
- * Compile-time exhaustiveness assertion. Use in default branches of
23
- * switches over discriminated unions where every case should be handled
24
- * explicitly — TS narrows the type to `never` only if every variant has
25
- * been covered. Strengthens invariant 7 (identity is explicit).
26
- *
27
- * @example
28
- * switch (msg.type) {
29
- * case "a": return ...;
30
- * case "b": return ...;
31
- * default: return absurd(msg); // compile error if a "c" is added
32
- * }
33
- */
34
- declare function absurd(x: never): never;
35
- declare const noop: <S, M, C extends Cmd>(state: S, _msg: M) => readonly [S, readonly C[]];
1
+ import { C as Cmd, P as Port, S as Sub, M as Machine, T as Transitions, R as Reducer } from './core-CymXZCeY.js';
2
+ export { b as ContextFree, I as Interpret, N as NoCtx, c as PortEmitter, a as SubId, d as Subscribe, e as SyncReturn, U as UpdateForm, f as detectUpdateForm, g as foldMsgs, h as formOf, s as subId } from './core-CymXZCeY.js';
3
+
36
4
  /**
37
5
  * @demlik/tea — TEA-faithful state machine substrate.
38
6
  *
@@ -46,84 +14,26 @@ declare const noop: <S, M, C extends Cmd>(state: S, _msg: M) => readonly [S, rea
46
14
  * `tryInterpret` is Railway sugar — wraps a fallible async function and routes
47
15
  * Ok/Err to two Msg constructors via better-result.
48
16
  */
49
- type Cmd<T extends string = string> = {
50
- type: T;
51
- };
52
- declare const Cmd: {
53
- /**
54
- * The empty Cmd array. Typed `readonly never[]` so it's assignable to any
55
- * `readonly C[]` for any `C extends Cmd`. Use in `init` returns and
56
- * Transitions cells that emit zero effects:
57
- *
58
- * init: (loaded) => [loaded ?? initial, Cmd.none],
59
- * tick: (state) => [state, Cmd.none],
60
- *
61
- * Elm's `Cmd.none` analogue. Cultural signal alongside the runtime help:
62
- * `[state, Cmd.none]` reads as intent ("this transition emits nothing"),
63
- * `[state, []]` reads as "empty array of what".
64
- *
65
- * Frozen at runtime so a downstream consumer can't `.push()` into the
66
- * shared reference.
67
- */
68
- readonly none: readonly never[];
69
- /**
70
- * Flat concat of cmd arrays. Use when a cell composes effects from
71
- * multiple conditional sources:
72
- *
73
- * return [next, Cmd.batch(
74
- * Cmd.whenDefined(state.queueItemId, (id) => ({ type: "complete", id })),
75
- * Cmd.when(state.windowId !== undefined, { type: "close_window", windowId }),
76
- * [{ type: "detach_debugger", tabId: state.tabId }],
77
- * )];
78
- *
79
- * `<const C>` keeps inline cmd literals' discriminants narrow (same
80
- * rationale as `when` / `whenDefined` above).
81
- */
82
- readonly batch: <const C extends Cmd>(...arrs: readonly (readonly C[])[]) => readonly C[];
83
- /**
84
- * Emit `cmd` wrapped in a single-element array iff `cond` is true.
85
- * Otherwise return the empty array. Spreads cleanly into the cmds array
86
- * returned by a Transitions cell.
87
- *
88
- * `const C` (TypeScript 5.0+) keeps the inferred `type:` discriminator
89
- * literal narrow when called with an object literal — so `Cmd.when(b,
90
- * { type: "x", ... })` infers as `{ type: "x", ... }`, not `{ type:
91
- * string, ... }`, and stays assignable to a discriminated-union arm
92
- * without `as const` at the call site.
93
- */
94
- readonly when: <const C extends Cmd>(cond: boolean, cmd: C) => readonly C[];
95
- /**
96
- * If `value` is defined, call `build(value)` and emit the resulting cmd
97
- * wrapped in a single-element array. If `value` is `undefined`, return
98
- * the empty array. `value` is narrowed to `T` inside `build`.
99
- *
100
- * `const C` on the return type keeps the callback's object-literal
101
- * `type:` field narrow (see `when` above for the full rationale).
102
- */
103
- readonly whenDefined: <T, const C extends Cmd>(value: T | undefined, build: (value: T) => C) => readonly C[];
104
- };
105
- type SubId = string & {
106
- readonly __brand: "SubId";
17
+
18
+ declare const ReducerBrand: unique symbol;
19
+ type Branded<T> = T & {
20
+ readonly [ReducerBrand]: true;
107
21
  };
108
22
  /**
109
- * Construct a `SubId` from a string. Use this everywhere a Sub literal is
110
- * built — including ids derived from runtime data (`subId(\`ws:${auditId}\`)`).
111
- * The brand is structural, not nominal, so dynamic ids are permitted; the
112
- * constructor's job is to make the identity decision explicit at every call
113
- * site so the type system can catch accidental raw-string drift.
23
+ * Compile-time exhaustiveness assertion. Use in default branches of
24
+ * switches over discriminated unions where every case should be handled
25
+ * explicitly — TS narrows the type to `never` only if every variant has
26
+ * been covered. Strengthens invariant 7 (identity is explicit).
114
27
  *
115
- * Strengthens invariant 7 (identity is explicit).
28
+ * @example
29
+ * switch (msg.type) {
30
+ * case "a": return ...;
31
+ * case "b": return ...;
32
+ * default: return absurd(msg); // compile error if a "c" is added
33
+ * }
116
34
  */
117
- declare function subId(s: string): SubId;
118
- type Sub<T extends string = string> = {
119
- id: SubId;
120
- type: T;
121
- };
122
- interface Port<T> {
123
- readonly __brand: "port";
124
- readonly name: string;
125
- readonly __t?: T;
126
- }
35
+ declare function absurd(x: never): never;
36
+ declare const noop: <S, M, C extends Cmd>(state: S, _msg: M) => readonly [S, readonly C[]];
127
37
  /**
128
38
  * Thrown by `definePort` when a name has already been registered in the
129
39
  * current process. Symmetric with the `Sub.id` collision assert in
@@ -144,6 +54,85 @@ declare class PortNameCollisionError extends Error {
144
54
  * @internal test-only
145
55
  */
146
56
  declare function __resetPortRegistry(): void;
57
+ /**
58
+ * Which otherwise-unattributable runtime path produced an error.
59
+ *
60
+ * - `"follow-up"` — a follow-up Msg an interpret handler returned rejected
61
+ * when re-dispatched. The original dispatcher already resolved, so this
62
+ * rejection has no caller; without the sink it was swallowed.
63
+ * - `"stop-save"` — the final `store.save(state)` inside `stop()` threw.
64
+ * `stop()` resolves regardless (its contract), so without the sink this was
65
+ * silent loss of the last write.
66
+ * - `"reduce"` — the pure `update` (reducer) threw synchronously while folding
67
+ * a Msg. #71's reentrancy brand makes a reducer provably synchronous, so this
68
+ * throw is catchable in the dispatch loop; the configured `Supervision`
69
+ * strategy decides what the runtime does next (`stop` / `escalate` /
70
+ * `restart`), but the failure is ALWAYS surfaced here as data first.
71
+ */
72
+ type RuntimeErrorPhase = "follow-up" | "stop-save" | "reduce";
73
+ /** Context handed to an `OnError` sink alongside the error itself. */
74
+ interface RuntimeErrorContext {
75
+ readonly phase: RuntimeErrorPhase;
76
+ }
77
+ /**
78
+ * Sink for runtime failures that have no caller to reject at. Configured via
79
+ * `run(machine, { onError })`. Should be total — a throw inside the sink is
80
+ * itself surfaced via `defaultOnError`, so the sink can never re-introduce a
81
+ * silent failure.
82
+ */
83
+ type OnError = (error: unknown, context: RuntimeErrorContext) => void;
84
+ /** The three declared reducer-throw supervision strategies. */
85
+ type SupervisionStrategy = "stop" | "escalate" | "restart";
86
+ /**
87
+ * Declared supervision policy for a reducer (`update`) throw, configured at
88
+ * `run(machine, { supervision })`.
89
+ *
90
+ * - `{ strategy: "stop" }` — halt + surface (the safe default; also the bare
91
+ * shorthand `"stop"`).
92
+ * - `{ strategy: "escalate" }` — surface + propagate, runtime stays live (also
93
+ * the bare shorthand `"escalate"`).
94
+ * - `{ strategy: "restart", rehydrate }` — surface, then re-init from the
95
+ * host-supplied last-known-good `S` and keep folding. `rehydrate` receives
96
+ * the failing `(state, msg, error)` so the host can log / pick a snapshot;
97
+ * it MUST return a valid `S` synchronously (the reducer is synchronous, so
98
+ * recovery is too — no suspension across the single-writer slot).
99
+ *
100
+ * The bare-string shorthands (`"stop"` / `"escalate"`) are accepted for the two
101
+ * strategies that need no host data; `restart` MUST be the object form because
102
+ * it carries the `rehydrate` callback.
103
+ */
104
+ type Supervision<S, M extends {
105
+ type: string;
106
+ }> = "stop" | "escalate" | {
107
+ readonly strategy: "stop";
108
+ } | {
109
+ readonly strategy: "escalate";
110
+ } | {
111
+ readonly strategy: "restart";
112
+ /**
113
+ * Host-provided rehydration to last-known-good state. Invoked by the core
114
+ * when the reducer throws; its return value becomes the new state and the
115
+ * transition continues from there. Receives the pre-throw `state`, the
116
+ * `msg` that triggered the throw, and the thrown `error` so the host can
117
+ * route by cause.
118
+ */
119
+ readonly rehydrate: (state: S, msg: M, error: unknown) => S;
120
+ };
121
+ /**
122
+ * Raised by `idle()` when the quiescence wait hits its iteration cap without
123
+ * the dispatch tail stabilizing. Replaces the old silent fall-through-resolve
124
+ * (which made "the loop quiesced" indistinguishable from "we gave up"):
125
+ * `idle()` now REJECTS with this so a livelocking machine surfaces instead of
126
+ * masquerading as quiescent.
127
+ *
128
+ * Mechanizes invariant 6 (no silent failures) at the runtime layer — the same
129
+ * role `PortNameCollisionError` plays for invariant 7.
130
+ */
131
+ declare class QuiescenceTimeoutError extends Error {
132
+ readonly iterations: number;
133
+ readonly name = "QuiescenceTimeoutError";
134
+ constructor(iterations: number);
135
+ }
147
136
  /**
148
137
  * Define a typed port. `name` is metadata (shown in devtools / debug logs);
149
138
  * port identity is by reference. Each definePort call must use a unique
@@ -159,78 +148,88 @@ declare function __resetPortRegistry(): void;
159
148
  * Port-side counterpart of `SubId`'s id collision check in `reconcileSubs`.
160
149
  */
161
150
  declare function definePort<T>(name: string): Port<T>;
162
- /**
163
- * Augmentation injected onto `ctx` inside Cmd handlers. Handlers receive
164
- * `ctx & PortEmitter` so they can call `ctx.emit(port, value)` synchronously.
165
- * Subscribers registered via `runtime.subscribePort(port, listener)` receive
166
- * the value immediately.
167
- *
168
- * Emitting to a port with no subscribers is a no-op (does not throw).
169
- */
170
- interface PortEmitter {
171
- emit<T>(port: Port<T>, value: T): void;
172
- }
173
- type Interpret<M extends {
174
- type: string;
175
- }, C extends Cmd, Ctx> = {
176
- [K in C["type"]]: (cmd: Extract<C, {
177
- type: K;
178
- }>, ctx: Ctx & PortEmitter) => Promise<M | void>;
179
- };
180
- type Subscribe<M extends {
181
- type: string;
182
- }, U extends Sub, Ctx> = {
183
- [K in U["type"]]: (sub: Extract<U, {
184
- type: K;
185
- }>, ctx: Ctx, dispatch: (msg: M) => void) => () => void;
186
- };
187
- type Machine<S, M extends {
188
- type: string;
189
- }, C extends Cmd, U extends Sub, Ctx> = {
190
- /**
191
- * Boot the runtime. Called once by `run(...)` with whatever `Store.load()`
192
- * returned: `null` on fresh boot, the persisted state on rehydrate.
193
- *
194
- * **Contract:** when `loaded !== null`, init MUST return `[loaded, []]` —
195
- * no Cmds. Init's rehydrate branch is the migration / parse boundary, not
196
- * the boot-effect hook. See Invariant 2 in `.patterns/tea/tea-invariants.md`.
197
- *
198
- * Boot effects routes:
199
- * - Stateless infrastructure → host module top, outside TEA.
200
- * - State-conditional resume → a `boot` Msg the host dispatches once
201
- * after `run(...)` returns.
202
- *
203
- * Violations are caught at runtime by `replay` (which throws with a
204
- * pointer to the alternatives).
205
- */
206
- init: (loaded: S | null, ctx: Ctx) => readonly [S, readonly C[]];
207
- update: Reducer<S, M, C> | ([S] extends [{
208
- type: string;
209
- }] ? Transitions<S, M, C> : never);
210
- subscriptions?: (state: S) => readonly U[];
211
- subscribe?: Subscribe<M, U, Ctx>;
212
- } & ([C] extends [Cmd<never>] ? {
213
- interpret?: Interpret<M, C, Ctx>;
214
- } : {
215
- interpret: Interpret<M, C, Ctx>;
216
- });
217
151
  interface Store<S> {
218
152
  load(): Promise<unknown>;
219
153
  save(state: S): Promise<void>;
220
154
  migrate(raw: unknown): S | null;
221
155
  }
156
+ type DispatchSettle = "quiescent" | "once";
222
157
  interface RuntimeRef<M extends {
223
158
  type: string;
224
159
  }> {
225
- dispatch(msg: M): Promise<void>;
160
+ /**
161
+ * Put a Msg in the runtime's inbox and resolve once it has been processed.
162
+ *
163
+ * Runs to QUIESCENCE by default (issue #50): the returned promise settles
164
+ * only after the dispatched Msg AND every follow-up Msg its `interpret`
165
+ * handlers (transitively) returned have drained off the serial tail. This is
166
+ * the safe default — `await dispatch(msg)` means "the consequences have run",
167
+ * not "one transition fired and the rest are racing". Pass
168
+ * `{ settle: "once" }` (or call `dispatchOnce`) for the rare single-step
169
+ * case where the caller will await the tail itself.
170
+ *
171
+ * Rejection ordering: the ONE dispatched transition's own failure (a reducer
172
+ * / save / sub-start / interpret throw) surfaces first, on this promise. If
173
+ * that transition succeeds but the follow-up chain never stabilizes, the
174
+ * quiescent drain rejects with `QuiescenceTimeoutError` (same cap and same
175
+ * no-silent-give-up contract as `idle()` — issue #51). Follow-up Msg
176
+ * rejections themselves route to the `onError` sink, never here.
177
+ */
178
+ dispatch(msg: M, opts?: {
179
+ readonly settle?: DispatchSettle;
180
+ }): Promise<void>;
181
+ /**
182
+ * Dispatch `msg` and resolve after exactly ONE transition's effects settle,
183
+ * WITHOUT draining the follow-up chain. Equivalent to
184
+ * `dispatch(msg, { settle: "once" })`.
185
+ *
186
+ * This is the old (pre-#50) `dispatch` behavior, kept as an explicit escape
187
+ * hatch: a caller that wants to interleave its own work between the first
188
+ * transition and its follow-ups, then `await runtime.idle()` itself. Prefer
189
+ * plain `dispatch` (run-to-quiescence) unless you specifically need the
190
+ * single step — the un-awaited follow-up chain is exactly the footgun #50
191
+ * removed from the default.
192
+ */
193
+ dispatchOnce(msg: M): Promise<void>;
226
194
  }
227
- interface Runtime<S, M extends {
195
+ interface BootingRuntime<S, M extends {
228
196
  type: string;
229
- }> extends RuntimeRef<M> {
230
- dispatch(msg: M): Promise<void>;
231
- getState(): S;
197
+ }, E extends {
198
+ type: string;
199
+ } = never> extends RuntimeRef<M> {
200
+ dispatch(msg: M, opts?: {
201
+ readonly settle?: DispatchSettle;
202
+ }): Promise<void>;
203
+ dispatchOnce(msg: M): Promise<void>;
232
204
  subscribe(listener: () => void): () => void;
233
- observe(observer: (msg: M | null, state: S) => void): () => void;
205
+ observe(observer: (msg: M, state: S) => void): () => void;
206
+ /**
207
+ * Subscribe to the INITIAL State — the boot transition the old `observe`
208
+ * `msg === null` arm carried (#47). Fires exactly once: immediately if boot
209
+ * has already completed when `onBoot` is called (so a late subscriber never
210
+ * misses it), otherwise on the boot fanout. Returns a cleanup that detaches
211
+ * the handler (a no-op once it has fired).
212
+ *
213
+ * This is where the boot case the `observe` firehose used to fold in went
214
+ * once `observe`'s `msg` became total: a consumer that needs to seed a view
215
+ * from the initial State wires `onBoot`; a consumer that only folds events
216
+ * (the common case) wires `observe` (or `on`) and is no longer handed a boot
217
+ * arm to skip.
218
+ */
219
+ onBoot(handler: (state: S) => void): () => void;
220
+ /**
221
+ * Subscribe to a SEMANTIC event of `type` (#47). Typed narrowing: the handler
222
+ * receives exactly the `E` member whose `type` matches `K`, so a consumer
223
+ * built on `on("TurnSettled", …)` never touches another event's shape and —
224
+ * critically — never references the machine's PRIVATE Msg names. Returns a
225
+ * cleanup that detaches the handler. Multiple handlers per type are
226
+ * supported; fanout is synchronous and throw-isolated (same discipline as
227
+ * `observe`). Fires only when the machine's `events` projector (wired on
228
+ * `run`) emits an event of that type for a transition.
229
+ */
230
+ on<K extends E["type"]>(type: K, handler: (event: Extract<E, {
231
+ type: K;
232
+ }>) => void): () => void;
234
233
  /**
235
234
  * Subscribe to a typed Port. Returns a cleanup function that removes the
236
235
  * listener. Multiple listeners per port are supported; fanout is synchronous
@@ -263,26 +262,119 @@ interface Runtime<S, M extends {
263
262
  */
264
263
  emitPort<T>(port: Port<T>, value: T): void;
265
264
  /**
266
- * Resolves after boot completes — i.e. after `store.load()` (if any), `init`,
267
- * `store.save()` of the initial state, `reconcileSubs`, `interpret` of any
268
- * init-emitted cmds, and the initial listener / observer fanout. Rejects with
269
- * the boot error if boot fails (the same error every subsequent `dispatch`
270
- * call rejects with).
265
+ * Resolves to the booted `Runtime<S, M>` after boot completes — i.e. after
266
+ * `store.load()` (if any), `init`, `store.save()` of the initial state,
267
+ * `reconcileSubs`, `interpret` of any init-emitted cmds, and the initial
268
+ * listener / observer fanout. Rejects with the boot error if boot fails (the
269
+ * same error every subsequent `dispatch` call rejects with).
270
+ *
271
+ * This is the ONLY way to obtain a `Runtime<S, M>` — and a `Runtime` is the
272
+ * only handle whose `getState()` is total. Awaiting `ready` is therefore the
273
+ * single gate between "boot in flight" and "State exists":
274
+ *
275
+ * ```ts
276
+ * const runtime = await run(machine, opts).ready;
277
+ * runtime.getState(); // total — never throws-before-boot
278
+ * ```
271
279
  *
272
280
  * Idempotent — subsequent reads return the same settled promise. The runtime
273
- * holds exactly one `ready` promise per `run()` call.
281
+ * holds exactly one `ready` promise per `run()` call; it resolves to the same
282
+ * `Runtime` object every time.
274
283
  *
275
- * Closes the v1 boot-await gap previously documented in `stepBootEffects`:
276
- * components that need synchronous initial state under a store (e.g.,
277
- * `useSyncExternalStore` consumers) can `await runtime.ready` instead of
278
- * `runtime.dispatch(noopMsg)`.
284
+ * Closes issue #45 (the `getState()` lie) and the v1 boot-await gap once
285
+ * documented in `stepBootEffects`: components that need synchronous initial
286
+ * state under a store (e.g. `useSyncExternalStore` consumers) await `ready`
287
+ * for the booted runtime instead of dispatching a no-op Msg.
279
288
  *
280
289
  * Expresses canon §2.3 (the `init` contract — boot is a named, awaitable
281
290
  * moment). Strengthens invariant 6 (runtime is small and inspectable).
282
291
  */
283
- ready: Promise<void>;
292
+ ready: Promise<Runtime<S, M, E>>;
284
293
  stop(): Promise<void>;
285
294
  }
295
+ interface Runtime<S, M extends {
296
+ type: string;
297
+ }, E extends {
298
+ type: string;
299
+ } = never> extends BootingRuntime<S, M, E> {
300
+ /**
301
+ * The current State. TOTAL — never throws. Obtaining a `Runtime` requires
302
+ * awaiting `ready`, which only resolves AFTER boot has run `init` and set the
303
+ * initial State; a `Runtime` therefore always has a State to return. (Before
304
+ * #45 this was declared on the synchronously-returned handle and threw under
305
+ * a store until boot finished — the type lied. It no longer does.)
306
+ */
307
+ getState(): S;
308
+ /**
309
+ * Resolves to this same `Runtime` once boot completes. Idempotent.
310
+ */
311
+ ready: Promise<Runtime<S, M, E>>;
312
+ /**
313
+ * Resolves once the runtime has reached QUIESCENCE — every dispatched Msg AND
314
+ * every follow-up Msg an interpret handler returned has been processed, with
315
+ * no further step pending on the serial tail.
316
+ *
317
+ * Since #50, plain `dispatch(msg)` already runs to quiescence (it awaits this
318
+ * same drain after its own transition), so a caller rarely needs `idle()`
319
+ * directly. `idle()` is still the right tool to (a) settle follow-ups left in
320
+ * flight by a `dispatchOnce` / `dispatch(msg, { settle: "once" })` single
321
+ * step, or (b) wait out a follow-up chain kicked off by a Sub or by boot
322
+ * `init` cmds where there was no `dispatch` call to await. It chains onto the
323
+ * current tail and re-checks until the tail stops advancing, draining the
324
+ * entire transitive follow-up chain.
325
+ *
326
+ * Idempotent and re-entrant-safe: each call reads the live tail; a tail that
327
+ * advanced during the await (a follow-up landed) is awaited again until
328
+ * stable. Tail rejections are NOT surfaced here — a failing dispatch
329
+ * surfaces on its OWN returned promise (and follow-up rejections route to
330
+ * the `onError` sink). The ONE rejection `idle()` itself produces is a
331
+ * `QuiescenceTimeoutError` when the wait hits its iteration cap without the
332
+ * tail stabilizing: that replaces the old silent fall-through-resolve so a
333
+ * livelocking machine is distinguishable from a genuinely quiesced one
334
+ * (invariant 6 — no silent failures).
335
+ *
336
+ * Use `idle()` for runtime-internal follow-ups; a poll is still correct when
337
+ * waiting on an EXTERNAL event (a WS reply) the runtime cannot enqueue itself.
338
+ *
339
+ * Strengthens invariant 6 (runtime is small and inspectable — quiescence is a
340
+ * first-class awaitable moment, not a poll the consumer re-derives).
341
+ */
342
+ idle(): Promise<void>;
343
+ /**
344
+ * The terminal State of the run, or `undefined` while it is still in flight.
345
+ * A run is "terminal" exactly when the `terminal` predicate passed to `run()`
346
+ * returns `true` for the current State; with no predicate supplied a run is
347
+ * never terminal and this always returns `undefined`.
348
+ *
349
+ * This is the FIRST-CLASS result read (issue #46): the outcome of running the
350
+ * thing, off the State the machine already owns — NOT scraped off the
351
+ * `observe` firehose by matching an internal Msg name (`resilient_ok`) and
352
+ * racing a state-clear. The consumer reads the run's product (e.g. an agent's
353
+ * `state.output`) off the returned `S`, with no coupling to the machine's
354
+ * private retry / loop vocabulary.
355
+ *
356
+ * Total — never throws (a `Runtime` always has a State; see `getState`).
357
+ */
358
+ result(): S | undefined;
359
+ /**
360
+ * Resolves with the terminal State the first time the run reaches a terminal
361
+ * State (per the `terminal` predicate passed to `run()`). If the run is
362
+ * ALREADY terminal when `done()` is called, resolves immediately with the
363
+ * current State; otherwise resolves on the transition that first makes
364
+ * `terminal` hold.
365
+ *
366
+ * The awaitable companion to `result()` — "wait for the run to finish, then
367
+ * hand me what it produced" in one call, instead of polling `result()` or
368
+ * hand-rolling an `observe` loop that watches for a private terminal Msg.
369
+ * With no `terminal` predicate supplied the run is never terminal, so this
370
+ * promise never resolves (a non-terminating machine has no result to await).
371
+ *
372
+ * Idempotent and multi-caller safe: every call observes the live State, so a
373
+ * call made after the run already terminated resolves at once, and concurrent
374
+ * callers all settle on the same terminal transition.
375
+ */
376
+ done(): Promise<S>;
377
+ }
286
378
  declare function defineMachine<S, M extends {
287
379
  type: string;
288
380
  }, C extends Cmd, U extends Sub, Ctx>(m: Omit<Machine<S, M, C, U, Ctx>, "update"> & {
@@ -295,12 +387,66 @@ declare function defineMachine<S, M extends {
295
387
  }, C extends Cmd, U extends Sub, Ctx>(m: Omit<Machine<S, M, C, U, Ctx>, "update"> & {
296
388
  update: Reducer<S, M, C>;
297
389
  }): Machine<S, M, C, U, Ctx>;
298
- declare function run<S, M extends {
390
+ declare function asReducer<S, M extends {
299
391
  type: string;
300
- }, C extends Cmd, U extends Sub, Ctx>(machine: Machine<S, M, C, U, Ctx>, opts: {
392
+ }, C extends Cmd>(reducer: Reducer<S, M, C>): Branded<Reducer<S, M, C>>;
393
+ type CtxArg<Ctx> = [Record<never, never>] extends [Ctx] ? {
394
+ ctx?: Ctx;
395
+ } : {
301
396
  ctx: Ctx;
397
+ };
398
+ declare function run<S, M extends {
399
+ type: string;
400
+ }, C extends Cmd, U extends Sub, Ctx, E extends {
401
+ type: string;
402
+ } = never>(machine: Machine<S, M, C, U, Ctx>, opts: CtxArg<Ctx> & {
302
403
  store?: Store<S>;
303
- }): Runtime<S, M>;
404
+ onError?: OnError;
405
+ /**
406
+ * The SEMANTIC event projector (#47). Maps one APPLIED transition `(msg,
407
+ * state)` to zero-or-more public events of the machine-level union `E`.
408
+ * Returning `[]` skips the transition (it produced no event the consumer's
409
+ * `on(type, …)` cares about). This is where a machine maps its PRIVATE Msg
410
+ * vocabulary (a retry plumbing's `resilient_ok`, a tool loop's
411
+ * `agent_tool_ok`) to NAMED events — the private names live only inside this
412
+ * closure and never reach `on`'s `E` surface. Omit → the run has no event
413
+ * surface (`E = never`) and `on` is uncallable.
414
+ *
415
+ * PURE — called inside the dispatch loop on the freshly-folded `(msg,
416
+ * state)`; it must not read the clock, mutate, or throw (a throw is
417
+ * isolated like an observer throw, never stranding the transition).
418
+ */
419
+ events?: (msg: M, state: S) => readonly E[];
420
+ /**
421
+ * Declared policy for when the pure `update` (reducer) throws. The reducer
422
+ * throw is always surfaced via `onError` (`phase: "reduce"`); the strategy
423
+ * decides what the runtime does next. Defaults to `"stop"` — the explicit,
424
+ * safe choice (surface + halt, never a silent resume). See `Supervision`.
425
+ */
426
+ supervision?: Supervision<S, M>;
427
+ /**
428
+ * The run-terminality predicate — what makes the run's outcome first-class
429
+ * (issue #46). Returns `true` for a State the run has finished in (e.g. an
430
+ * agent's `state.run.phase === "done"`). The runtime feeds it to
431
+ * `Runtime.result()` (the terminal State, or `undefined` in flight) and
432
+ * `Runtime.done()` (resolves with the terminal State when it first holds).
433
+ *
434
+ * PURE — the runtime calls it inside the dispatch loop on the freshly-folded
435
+ * State; it must not read the clock, mutate, or throw. Omit → the run is
436
+ * never terminal: `result()` always returns `undefined` and `done()` never
437
+ * resolves (the right behavior for a machine with no natural completion).
438
+ */
439
+ terminal?: (state: S) => boolean;
440
+ /**
441
+ * Iteration cap for `idle()`'s quiescence wait. Defaults to 100_000. Test
442
+ * seam only — lets a livelock test trip the `QuiescenceTimeoutError` reject
443
+ * path in a handful of iterations instead of 100k. Production code must not
444
+ * set it.
445
+ *
446
+ * @internal test-only
447
+ */
448
+ __idleCap?: number;
449
+ }): BootingRuntime<S, M, E>;
304
450
  interface HistoryTracker<S, M extends {
305
451
  type: string;
306
452
  }> {
@@ -335,11 +481,15 @@ interface HistoryTracker<S, M extends {
335
481
  * to the runtime via `observe(...)` and retains the last `size` `(msg,
336
482
  * state)` transitions in a FIFO ring buffer.
337
483
  *
338
- * @param runtime Any `Runtime<S, M>`. The tracker uses only `observe`.
339
- * @param size Buffer cap. `size <= 0` produces a tracker whose
340
- * snapshot is always `[]` (the observer is still attached
341
- * but pushes a no-op; cheaper to skip the tracker entirely
342
- * in that case).
484
+ * @param runtime Any `BootingRuntime<S, M>` (a full `Runtime` satisfies it).
485
+ * The tracker uses only `observe`, which is total before boot —
486
+ * attach it to the synchronous `run()` handle to record the
487
+ * boot transition.
488
+ * @param size Buffer cap. `size <= 0` produces an inert no-op tracker:
489
+ * no observer is attached to the runtime, `snapshot()` is
490
+ * always `[]`, and `stop()` is a no-op. (Prefer skipping the
491
+ * tracker entirely in that case — this arm just makes a
492
+ * non-positive `size` harmless rather than an error.)
343
493
  *
344
494
  * Memory cost: O(size × avg(msg + state size)). For audit-extension's
345
495
  * inspector use case (size 100, audit state ~5KB), ~500KB max — held
@@ -347,7 +497,7 @@ interface HistoryTracker<S, M extends {
347
497
  */
348
498
  declare function historyTracker<S, M extends {
349
499
  type: string;
350
- }>(runtime: Runtime<S, M>, size: number): HistoryTracker<S, M>;
500
+ }>(runtime: BootingRuntime<S, M>, size: number): HistoryTracker<S, M>;
351
501
  declare function replay<S, M extends {
352
502
  type: string;
353
503
  }, C extends Cmd, U extends Sub, Ctx>(machine: Machine<S, M, C, U, Ctx>, opts: {
@@ -361,4 +511,4 @@ declare function replay<S, M extends {
361
511
  };
362
512
  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>;
363
513
 
364
- export { Cmd, type HistoryTracker, type Interpret, type Machine, type Port, type PortEmitter, PortNameCollisionError, type Reducer, type Runtime, type RuntimeRef, type Store, type Sub, type SubId, type Subscribe, type Transitions, __resetPortRegistry, absurd, defineMachine, definePort, historyTracker, noop, replay, run, subId, tryInterpret };
514
+ export { type BootingRuntime, type Branded, Cmd, type CtxArg, type DispatchSettle, type HistoryTracker, Machine, type OnError, Port, PortNameCollisionError, QuiescenceTimeoutError, Reducer, type Runtime, type RuntimeErrorContext, type RuntimeErrorPhase, type RuntimeRef, type Store, Sub, type Supervision, type SupervisionStrategy, Transitions, __resetPortRegistry, absurd, asReducer, defineMachine, definePort, historyTracker, noop, replay, run, tryInterpret };
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
- export { Cmd, PortNameCollisionError, __resetPortRegistry, absurd, defineMachine, definePort, historyTracker, noop, replay, run, subId, tryInterpret } from './chunk-CMG2KMWX.js';
1
+ export { PortNameCollisionError, QuiescenceTimeoutError, __resetPortRegistry, absurd, asReducer, defineMachine, definePort, historyTracker, noop, replay, run, tryInterpret } from './chunk-2WZHS4P2.js';
2
+ export { Cmd, detectUpdateForm, foldMsgs, formOf, subId } from './chunk-BNJYE4AU.js';
2
3
  //# sourceMappingURL=index.js.map
3
4
  //# sourceMappingURL=index.js.map