@demlik/tea 0.1.0 → 0.1.2

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 (243) 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-27N2SPTN.js +222 -0
  18. package/dist/chunk-27N2SPTN.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-3XJWC7VW.js +61 -0
  24. package/dist/chunk-3XJWC7VW.js.map +1 -0
  25. package/dist/chunk-5DEQ5I3U.js +132 -0
  26. package/dist/chunk-5DEQ5I3U.js.map +1 -0
  27. package/dist/chunk-5VNWZADP.js +49 -0
  28. package/dist/chunk-5VNWZADP.js.map +1 -0
  29. package/dist/chunk-6RGBKODV.js +268 -0
  30. package/dist/chunk-6RGBKODV.js.map +1 -0
  31. package/dist/{chunk-ZRUST6BK.js → chunk-7FYQGDMF.js} +2 -2
  32. package/dist/chunk-7FYQGDMF.js.map +1 -0
  33. package/dist/chunk-7W4LMPUW.js +186 -0
  34. package/dist/chunk-7W4LMPUW.js.map +1 -0
  35. package/dist/chunk-BYQUGDD3.js +16 -0
  36. package/dist/chunk-BYQUGDD3.js.map +1 -0
  37. package/dist/{chunk-PYBQREFU.js → chunk-DILMKOZ7.js} +22 -26
  38. package/dist/chunk-DILMKOZ7.js.map +1 -0
  39. package/dist/chunk-EHIX7NYZ.js +93 -0
  40. package/dist/chunk-EHIX7NYZ.js.map +1 -0
  41. package/dist/chunk-HCEVHFJ7.js +54 -0
  42. package/dist/chunk-HCEVHFJ7.js.map +1 -0
  43. package/dist/chunk-HJO6VNQI.js +15 -0
  44. package/dist/chunk-HJO6VNQI.js.map +1 -0
  45. package/dist/chunk-IN33Q4VJ.js +52 -0
  46. package/dist/chunk-IN33Q4VJ.js.map +1 -0
  47. package/dist/chunk-J6527VRQ.js +56 -0
  48. package/dist/chunk-J6527VRQ.js.map +1 -0
  49. package/dist/chunk-JELSZN25.js +47 -0
  50. package/dist/chunk-JELSZN25.js.map +1 -0
  51. package/dist/chunk-K575PT4S.js +58 -0
  52. package/dist/chunk-K575PT4S.js.map +1 -0
  53. package/dist/chunk-K7Q23EQ4.js +20 -0
  54. package/dist/chunk-K7Q23EQ4.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-PYOCUBQJ.js → chunk-LP7VQBRG.js} +22 -15
  58. package/dist/chunk-LP7VQBRG.js.map +1 -0
  59. package/dist/{chunk-CMG2KMWX.js → chunk-MMH7CW5K.js} +220 -189
  60. package/dist/chunk-MMH7CW5K.js.map +1 -0
  61. package/dist/{chunk-Y5JM3UUH.js → chunk-NSDFA6M6.js} +75 -3
  62. package/dist/chunk-NSDFA6M6.js.map +1 -0
  63. package/dist/chunk-ON24RKSE.js +60 -0
  64. package/dist/chunk-ON24RKSE.js.map +1 -0
  65. package/dist/chunk-PS23H6KQ.js +87 -0
  66. package/dist/chunk-PS23H6KQ.js.map +1 -0
  67. package/dist/chunk-QLA76GIT.js +162 -0
  68. package/dist/chunk-QLA76GIT.js.map +1 -0
  69. package/dist/chunk-S3FQN4SL.js +506 -0
  70. package/dist/chunk-S3FQN4SL.js.map +1 -0
  71. package/dist/chunk-SC3FAFZN.js +53 -0
  72. package/dist/chunk-SC3FAFZN.js.map +1 -0
  73. package/dist/chunk-SINXE47Y.js +55 -0
  74. package/dist/chunk-SINXE47Y.js.map +1 -0
  75. package/dist/chunk-SQTCS44W.js +33 -0
  76. package/dist/chunk-SQTCS44W.js.map +1 -0
  77. package/dist/chunk-TGG26XMK.js +52 -0
  78. package/dist/chunk-TGG26XMK.js.map +1 -0
  79. package/dist/chunk-UV6UWGEO.js +147 -0
  80. package/dist/chunk-UV6UWGEO.js.map +1 -0
  81. package/dist/chunk-UYWUVEP6.js +158 -0
  82. package/dist/chunk-UYWUVEP6.js.map +1 -0
  83. package/dist/chunk-VLGARN6H.js +202 -0
  84. package/dist/chunk-VLGARN6H.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-BoB6t7dB.d.ts +188 -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.d.ts +6 -0
  92. package/dist/debounce/index.js +1 -54
  93. package/dist/debounce/index.js.map +1 -1
  94. package/dist/devtools/index.d.ts +1 -1
  95. package/dist/devtools/index.js +18 -6
  96. package/dist/devtools/index.js.map +1 -1
  97. package/dist/do/index.d.ts +1723 -92
  98. package/dist/do/index.js +673 -31
  99. package/dist/do/index.js.map +1 -1
  100. package/dist/durable-effects-CO9Uh971.d.ts +180 -0
  101. package/dist/extension/index.d.ts +31 -13
  102. package/dist/extension/index.js +5 -3
  103. package/dist/extension/index.js.map +1 -1
  104. package/dist/extension/react.js +8 -3
  105. package/dist/extension/react.js.map +1 -1
  106. package/dist/extension/subs/index.d.ts +2 -2
  107. package/dist/extension/subs/index.js.map +1 -1
  108. package/dist/extension/test-utils.js +11 -3
  109. package/dist/extension/test-utils.js.map +1 -1
  110. package/dist/fan-out/index.d.ts +239 -0
  111. package/dist/fan-out/index.js +5 -0
  112. package/dist/fan-out/index.js.map +1 -0
  113. package/dist/idempotency/adapter.d.ts +62 -0
  114. package/dist/idempotency/adapter.js +4 -0
  115. package/dist/idempotency/adapter.js.map +1 -0
  116. package/dist/idempotency/index.js +1 -56
  117. package/dist/idempotency/index.js.map +1 -1
  118. package/dist/idempotent-intake/index.d.ts +186 -0
  119. package/dist/idempotent-intake/index.js +7 -0
  120. package/dist/idempotent-intake/index.js.map +1 -0
  121. package/dist/index.d.ts +341 -191
  122. package/dist/index.js +2 -1
  123. package/dist/llm-call/index.d.ts +309 -0
  124. package/dist/llm-call/index.js +14 -0
  125. package/dist/llm-call/index.js.map +1 -0
  126. package/dist/machine-viz/index.d.ts +4 -4
  127. package/dist/machine-viz/index.js +5 -9
  128. package/dist/machine-viz/index.js.map +1 -1
  129. package/dist/mem/index.d.ts +1 -0
  130. package/dist/monitored-run/index.d.ts +253 -0
  131. package/dist/monitored-run/index.js +10 -0
  132. package/dist/monitored-run/index.js.map +1 -0
  133. package/dist/node/index.d.ts +5 -5
  134. package/dist/node/index.js.map +1 -1
  135. package/dist/paginated-walk/index.d.ts +234 -0
  136. package/dist/paginated-walk/index.js +15 -0
  137. package/dist/paginated-walk/index.js.map +1 -0
  138. package/dist/paginator/index.d.ts +271 -0
  139. package/dist/paginator/index.js +3 -0
  140. package/dist/paginator/index.js.map +1 -0
  141. package/dist/parity/index.d.ts +92 -0
  142. package/dist/parity/index.js +78 -0
  143. package/dist/parity/index.js.map +1 -0
  144. package/dist/pbt/arbitraries/index.d.ts +1 -1
  145. package/dist/pbt/arbitraries/index.js +1 -1
  146. package/dist/pbt/index.d.ts +15 -8
  147. package/dist/pbt/index.js +6 -11
  148. package/dist/pbt/index.js.map +1 -1
  149. package/dist/pbt/runners/index.d.ts +1 -1
  150. package/dist/pbt/runners/index.js +3 -2
  151. package/dist/poller/index.d.ts +288 -0
  152. package/dist/poller/index.js +79 -0
  153. package/dist/poller/index.js.map +1 -0
  154. package/dist/prediction/index.d.ts +130 -0
  155. package/dist/prediction/index.js +4 -0
  156. package/dist/prediction/index.js.map +1 -0
  157. package/dist/protocol-B5YmJr0d.d.ts +45 -0
  158. package/dist/pure/index.d.ts +2 -0
  159. package/dist/pure/index.js +4 -0
  160. package/dist/pure/index.js.map +1 -0
  161. package/dist/raft/demo.d.ts +105 -0
  162. package/dist/raft/demo.js +172 -0
  163. package/dist/raft/demo.js.map +1 -0
  164. package/dist/raft/index.d.ts +548 -0
  165. package/dist/raft/index.js +7 -0
  166. package/dist/raft/index.js.map +1 -0
  167. package/dist/raft/sim.d.ts +222 -0
  168. package/dist/raft/sim.js +8 -0
  169. package/dist/raft/sim.js.map +1 -0
  170. package/dist/raft/timeline.d.ts +46 -0
  171. package/dist/raft/timeline.js +62 -0
  172. package/dist/raft/timeline.js.map +1 -0
  173. package/dist/rate-limit/index.js +1 -43
  174. package/dist/rate-limit/index.js.map +1 -1
  175. package/dist/react/index.d.ts +2 -1
  176. package/dist/react/index.js +43 -20
  177. package/dist/react/index.js.map +1 -1
  178. package/dist/reconciler/index.d.ts +192 -0
  179. package/dist/reconciler/index.js +166 -0
  180. package/dist/reconciler/index.js.map +1 -0
  181. package/dist/recorder/index.d.ts +7 -3
  182. package/dist/recorder/index.js +1 -155
  183. package/dist/recorder/index.js.map +1 -1
  184. package/dist/resilient-call/index.d.ts +294 -0
  185. package/dist/resilient-call/index.js +13 -0
  186. package/dist/resilient-call/index.js.map +1 -0
  187. package/dist/retry-backoff/index.d.ts +42 -5
  188. package/dist/retry-backoff/index.js +1 -35
  189. package/dist/retry-backoff/index.js.map +1 -1
  190. package/dist/retry-to-success/index.d.ts +109 -0
  191. package/dist/retry-to-success/index.js +35 -0
  192. package/dist/retry-to-success/index.js.map +1 -0
  193. package/dist/saga/index.d.ts +238 -0
  194. package/dist/saga/index.js +150 -0
  195. package/dist/saga/index.js.map +1 -0
  196. package/dist/snapshot/index.d.ts +334 -0
  197. package/dist/snapshot/index.js +5 -0
  198. package/dist/snapshot/index.js.map +1 -0
  199. package/dist/subs/index.d.ts +48 -10
  200. package/dist/subs/index.js +1 -1
  201. package/dist/testing/index.d.ts +107 -2
  202. package/dist/testing/index.js +204 -6
  203. package/dist/testing/index.js.map +1 -1
  204. package/dist/throttle/index.d.ts +9 -0
  205. package/dist/throttle/index.js +1 -52
  206. package/dist/throttle/index.js.map +1 -1
  207. package/dist/throttled-input/index.d.ts +380 -0
  208. package/dist/throttled-input/index.js +88 -0
  209. package/dist/throttled-input/index.js.map +1 -0
  210. package/dist/token-refresh/index.d.ts +215 -0
  211. package/dist/token-refresh/index.js +6 -0
  212. package/dist/token-refresh/index.js.map +1 -0
  213. package/dist/trace-replay/index.d.ts +11 -2
  214. package/dist/trace-replay/index.js +3 -55
  215. package/dist/trace-replay/index.js.map +1 -1
  216. package/dist/{types-CwBC5wMn.d.ts → types-B-8bcAp8.d.ts} +1 -1
  217. package/dist/with-deadline/index.d.ts +196 -0
  218. package/dist/with-deadline/index.js +111 -0
  219. package/dist/with-deadline/index.js.map +1 -0
  220. package/dist/with-resilience/index.d.ts +205 -0
  221. package/dist/with-resilience/index.js +251 -0
  222. package/dist/with-resilience/index.js.map +1 -0
  223. package/dist/with-telemetry/index.d.ts +149 -0
  224. package/dist/with-telemetry/index.js +70 -0
  225. package/dist/with-telemetry/index.js.map +1 -0
  226. package/dist/work-queue/adapter.d.ts +82 -0
  227. package/dist/work-queue/adapter.js +4 -0
  228. package/dist/work-queue/adapter.js.map +1 -0
  229. package/dist/work-queue/index.d.ts +25 -11
  230. package/dist/work-queue/index.js +1 -46
  231. package/dist/work-queue/index.js.map +1 -1
  232. package/dist/work-queue/ops.d.ts +69 -0
  233. package/dist/work-queue/ops.js +3 -0
  234. package/dist/work-queue/ops.js.map +1 -0
  235. package/dist/workflow/index.d.ts +450 -0
  236. package/dist/workflow/index.js +225 -0
  237. package/dist/workflow/index.js.map +1 -0
  238. package/package.json +168 -25
  239. package/dist/chunk-CMG2KMWX.js.map +0 -1
  240. package/dist/chunk-PYBQREFU.js.map +0 -1
  241. package/dist/chunk-PYOCUBQJ.js.map +0 -1
  242. package/dist/chunk-Y5JM3UUH.js.map +0 -1
  243. package/dist/chunk-ZRUST6BK.js.map +0 -1
@@ -1,126 +1,10 @@
1
+ import { detectUpdateForm, foldUpdates, __DEV__, deepFreeze, applyCell, assertPureResult } from './chunk-QLA76GIT.js';
1
2
  import { Result } from 'better-result';
2
3
 
3
- // src/index.ts
4
- var __DEV__ = (() => {
5
- try {
6
- const meta = import.meta;
7
- if (meta.env?.DEV !== void 0) {
8
- return meta.env.DEV;
9
- }
10
- } catch {
11
- }
12
- try {
13
- return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
14
- } catch {
15
- return false;
16
- }
17
- })();
18
- function deepFreeze(obj, seen) {
19
- if (obj === null || typeof obj !== "object") return obj;
20
- const proto = Object.getPrototypeOf(obj);
21
- if (proto !== Object.prototype && proto !== Array.prototype && proto !== null) return obj;
22
- const visited = seen ?? /* @__PURE__ */ new WeakSet();
23
- if (visited.has(obj)) return obj;
24
- visited.add(obj);
25
- Object.freeze(obj);
26
- for (const v of Object.values(obj)) deepFreeze(v, visited);
27
- return obj;
28
- }
29
- function hasFunctionValues(obj) {
30
- if (obj === null || typeof obj !== "object") return null;
31
- for (const [key, value] of Object.entries(obj)) {
32
- if (typeof value === "function") return key;
33
- }
34
- return null;
35
- }
36
- function assertPureResult(result, msgType) {
37
- if (result && typeof result.then === "function") {
38
- throw new Error(
39
- `@demlik/tea: update cell "${msgType}" returned a Promise. update must be synchronous. Move async work to interpret.`
40
- );
41
- }
42
- if (!Array.isArray(result) || result.length < 2 || !Array.isArray(result[1])) {
43
- throw new Error(
44
- `@demlik/tea: update cell "${msgType}" returned a non-tuple. Expected [State, Cmd[]], got ${typeof result}.`
45
- );
46
- }
47
- const cmds = result[1];
48
- for (const cmd of cmds) {
49
- const fnField = hasFunctionValues(cmd);
50
- if (fnField !== null) {
51
- throw new Error(
52
- `@demlik/tea: Cmd "${cmd.type}" has function field "${fnField}". Cmds must be plain data, not closures.`
53
- );
54
- }
55
- }
56
- }
57
4
  function absurd(x) {
58
5
  throw new Error(`unreachable: ${JSON.stringify(x)}`);
59
6
  }
60
- var noop = (state, _msg) => [
61
- state,
62
- []
63
- ];
64
- var Cmd = {
65
- /**
66
- * The empty Cmd array. Typed `readonly never[]` so it's assignable to any
67
- * `readonly C[]` for any `C extends Cmd`. Use in `init` returns and
68
- * Transitions cells that emit zero effects:
69
- *
70
- * init: (loaded) => [loaded ?? initial, Cmd.none],
71
- * tick: (state) => [state, Cmd.none],
72
- *
73
- * Elm's `Cmd.none` analogue. Cultural signal alongside the runtime help:
74
- * `[state, Cmd.none]` reads as intent ("this transition emits nothing"),
75
- * `[state, []]` reads as "empty array of what".
76
- *
77
- * Frozen at runtime so a downstream consumer can't `.push()` into the
78
- * shared reference.
79
- */
80
- none: Object.freeze([]),
81
- /**
82
- * Flat concat of cmd arrays. Use when a cell composes effects from
83
- * multiple conditional sources:
84
- *
85
- * return [next, Cmd.batch(
86
- * Cmd.whenDefined(state.queueItemId, (id) => ({ type: "complete", id })),
87
- * Cmd.when(state.windowId !== undefined, { type: "close_window", windowId }),
88
- * [{ type: "detach_debugger", tabId: state.tabId }],
89
- * )];
90
- *
91
- * `<const C>` keeps inline cmd literals' discriminants narrow (same
92
- * rationale as `when` / `whenDefined` above).
93
- */
94
- batch: (...arrs) => {
95
- const out = [];
96
- for (const arr of arrs) out.push(...arr);
97
- return out;
98
- },
99
- /**
100
- * Emit `cmd` wrapped in a single-element array iff `cond` is true.
101
- * Otherwise return the empty array. Spreads cleanly into the cmds array
102
- * returned by a Transitions cell.
103
- *
104
- * `const C` (TypeScript 5.0+) keeps the inferred `type:` discriminator
105
- * literal narrow when called with an object literal — so `Cmd.when(b,
106
- * { type: "x", ... })` infers as `{ type: "x", ... }`, not `{ type:
107
- * string, ... }`, and stays assignable to a discriminated-union arm
108
- * without `as const` at the call site.
109
- */
110
- when: (cond, cmd) => cond ? [cmd] : [],
111
- /**
112
- * If `value` is defined, call `build(value)` and emit the resulting cmd
113
- * wrapped in a single-element array. If `value` is `undefined`, return
114
- * the empty array. `value` is narrowed to `T` inside `build`.
115
- *
116
- * `const C` on the return type keeps the callback's object-literal
117
- * `type:` field narrow (see `when` above for the full rationale).
118
- */
119
- whenDefined: (value, build) => value !== void 0 ? [build(value)] : []
120
- };
121
- function subId(s) {
122
- return s;
123
- }
7
+ var noop = (state, _msg) => [state, []];
124
8
  var definedPortNames = /* @__PURE__ */ new Set();
125
9
  var PortNameCollisionError = class extends Error {
126
10
  name = "PortNameCollisionError";
@@ -133,6 +17,25 @@ var PortNameCollisionError = class extends Error {
133
17
  function __resetPortRegistry() {
134
18
  definedPortNames.clear();
135
19
  }
20
+ function defaultOnError(error, _context) {
21
+ setTimeout(() => {
22
+ throw error;
23
+ }, 0);
24
+ }
25
+ function normalizeSupervision(supervision) {
26
+ if (supervision === void 0) return { strategy: "stop" };
27
+ if (typeof supervision === "string") return { strategy: supervision };
28
+ return supervision;
29
+ }
30
+ var QuiescenceTimeoutError = class extends Error {
31
+ constructor(iterations) {
32
+ super(
33
+ `@demlik/tea: idle() did not reach quiescence after ${iterations} iterations \u2014 the dispatch tail kept advancing. The machine is likely livelocking (an interpret handler enqueues a follow-up Msg that enqueues another, without end). Bound the follow-up chain in the reducer, or stop the runtime.`
34
+ );
35
+ this.iterations = iterations;
36
+ }
37
+ name = "QuiescenceTimeoutError";
38
+ };
136
39
  function definePort(name) {
137
40
  let isHmr = false;
138
41
  try {
@@ -148,16 +51,44 @@ function definePort(name) {
148
51
  return { __brand: "port", name };
149
52
  }
150
53
  function defineMachine(m) {
54
+ if (m.__form === void 0) {
55
+ Object.defineProperty(m, "__form", {
56
+ value: detectUpdateForm(m.update),
57
+ enumerable: false,
58
+ writable: false,
59
+ configurable: true
60
+ });
61
+ }
151
62
  return m;
152
63
  }
64
+ function asReducer(reducer) {
65
+ return reducer;
66
+ }
153
67
  function run(machine, opts) {
154
- const { ctx, store } = opts;
68
+ const { store } = opts;
69
+ const ctx = opts.ctx ?? {};
70
+ const idleCap = opts.__idleCap ?? 1e5;
71
+ const projectEvents = opts.events ?? (() => []);
72
+ const isTerminal = opts.terminal ?? (() => false);
73
+ const onError = opts.onError ?? defaultOnError;
74
+ const reportError = (error, context) => {
75
+ try {
76
+ onError(error, context);
77
+ } catch (sinkError) {
78
+ defaultOnError(sinkError);
79
+ }
80
+ };
81
+ const supervision = normalizeSupervision(opts.supervision);
155
82
  let state;
156
83
  let bootError = null;
157
84
  let stopped = false;
158
85
  const subRegistry = /* @__PURE__ */ new Map();
159
86
  const listeners = /* @__PURE__ */ new Set();
160
87
  const observers = /* @__PURE__ */ new Set();
88
+ const bootHandlers = /* @__PURE__ */ new Set();
89
+ let booted = false;
90
+ const eventHandlers = /* @__PURE__ */ new Map();
91
+ const doneWaiters = /* @__PURE__ */ new Set();
161
92
  const portRegistry = /* @__PURE__ */ new Map();
162
93
  function portEmit(port, value) {
163
94
  const subscribers = portRegistry.get(port);
@@ -171,28 +102,9 @@ function run(machine, opts) {
171
102
  }
172
103
  }
173
104
  const augmentedCtx = { ...ctx, emit: portEmit };
174
- const updateForm = machine.update;
175
- const updateMode = (() => {
176
- const firstKey = Object.keys(updateForm)[0];
177
- if (firstKey === void 0) {
178
- return "reducer";
179
- }
180
- const firstValue = updateForm[firstKey];
181
- return typeof firstValue === "function" ? "reducer" : "transitions";
182
- })();
183
105
  function applyUpdate(state2, msg) {
184
106
  if (__DEV__) deepFreeze(state2);
185
- let result;
186
- if (updateMode === "reducer") {
187
- const record = updateForm;
188
- const handler = record[msg.type];
189
- result = handler(state2, msg);
190
- } else {
191
- const table = updateForm;
192
- const stateKey = state2.type;
193
- const handler = table[stateKey][msg.type];
194
- result = handler(state2, msg);
195
- }
107
+ const result = applyCell(machine, state2, msg);
196
108
  if (__DEV__) assertPureResult(result, msg.type);
197
109
  return result;
198
110
  }
@@ -230,7 +142,11 @@ function run(machine, opts) {
230
142
  continue;
231
143
  }
232
144
  try {
233
- const cleanup = handler(sub, ctx, enqueueDispatch);
145
+ const cleanup = handler(
146
+ sub,
147
+ ctx,
148
+ enqueueDispatch
149
+ );
234
150
  subRegistry.set(sub.id, cleanup);
235
151
  } catch (err) {
236
152
  if (firstStartError === null) firstStartError = err;
@@ -243,9 +159,13 @@ function run(machine, opts) {
243
159
  for (const cmd of cmds) {
244
160
  const handler = interpretMap[cmd.type];
245
161
  if (!handler) continue;
246
- const follow = await handler(cmd, augmentedCtx);
162
+ const follow = await handler(
163
+ cmd,
164
+ augmentedCtx
165
+ );
247
166
  if (follow !== void 0 && follow !== null) {
248
- enqueueDispatch(follow).catch(() => {
167
+ enqueueDispatch(follow).catch((error) => {
168
+ reportError(error, { phase: "follow-up" });
249
169
  });
250
170
  }
251
171
  }
@@ -270,17 +190,83 @@ function run(machine, opts) {
270
190
  }
271
191
  }
272
192
  }
193
+ function fireEvents(msg) {
194
+ if (eventHandlers.size === 0 || state === void 0) return;
195
+ let events;
196
+ try {
197
+ events = projectEvents(msg, state);
198
+ } catch (err) {
199
+ console.error(err);
200
+ return;
201
+ }
202
+ for (const event of events) {
203
+ const bucket = eventHandlers.get(event.type);
204
+ if (bucket === void 0) continue;
205
+ for (const handler of bucket) {
206
+ try {
207
+ handler(event);
208
+ } catch (err) {
209
+ console.error(err);
210
+ }
211
+ }
212
+ }
213
+ }
214
+ function fireBoot() {
215
+ booted = true;
216
+ if (bootHandlers.size === 0 || state === void 0) return;
217
+ const snapshot = state;
218
+ for (const handler of bootHandlers) {
219
+ try {
220
+ handler(snapshot);
221
+ } catch (err) {
222
+ console.error(err);
223
+ }
224
+ }
225
+ }
226
+ function settleDoneWaiters() {
227
+ if (doneWaiters.size === 0 || state === void 0) return;
228
+ if (!isTerminal(state)) return;
229
+ const snapshot = state;
230
+ const parked = [...doneWaiters];
231
+ doneWaiters.clear();
232
+ for (const resolve of parked) resolve(snapshot);
233
+ }
273
234
  async function stepDispatch(msg) {
274
235
  if (state === void 0) {
275
236
  throw new Error("@demlik/tea: runtime not booted");
276
237
  }
277
- const [next, cmds] = applyUpdate(state, msg);
238
+ let next;
239
+ let cmds;
240
+ try {
241
+ [next, cmds] = applyUpdate(state, msg);
242
+ } catch (reduceError) {
243
+ reportError(reduceError, { phase: "reduce" });
244
+ switch (supervision.strategy) {
245
+ case "restart": {
246
+ state = supervision.rehydrate(state, msg, reduceError);
247
+ if (store) await store.save(state);
248
+ reconcileSubs();
249
+ fireListeners();
250
+ fireObservers(msg);
251
+ fireEvents(msg);
252
+ settleDoneWaiters();
253
+ return;
254
+ }
255
+ case "escalate":
256
+ throw reduceError;
257
+ default:
258
+ stopped = true;
259
+ throw reduceError;
260
+ }
261
+ }
278
262
  state = next;
279
263
  if (store) await store.save(state);
280
264
  reconcileSubs();
281
265
  await runInterpret(cmds);
282
266
  fireListeners();
283
267
  fireObservers(msg);
268
+ fireEvents(msg);
269
+ settleDoneWaiters();
284
270
  }
285
271
  let pendingInitCmds = [];
286
272
  if (!store) {
@@ -300,10 +286,12 @@ function run(machine, opts) {
300
286
  reconcileSubs();
301
287
  await runInterpret(pendingInitCmds);
302
288
  fireListeners();
303
- fireObservers(null);
289
+ fireBoot();
290
+ settleDoneWaiters();
304
291
  }
305
292
  function enqueueDispatch(msg) {
306
- if (stopped) return Promise.reject(new Error("@demlik/tea: runtime stopped"));
293
+ if (stopped)
294
+ return Promise.reject(new Error("@demlik/tea: runtime stopped"));
307
295
  const next = tail.then(() => {
308
296
  if (bootError !== null) throw bootError;
309
297
  return stepDispatch(msg);
@@ -312,17 +300,30 @@ function run(machine, opts) {
312
300
  });
313
301
  return next;
314
302
  }
303
+ async function drainToQuiescence() {
304
+ for (let i = 0; i < idleCap; i++) {
305
+ const observed = tail;
306
+ await observed;
307
+ if (tail === observed) return;
308
+ }
309
+ throw new QuiescenceTimeoutError(idleCap);
310
+ }
311
+ async function dispatchToQuiescence(msg, opts2) {
312
+ await enqueueDispatch(msg);
313
+ if (opts2?.settle === "once") return;
314
+ await drainToQuiescence();
315
+ }
315
316
  const bootPromise = stepBootEffects();
316
317
  tail = bootPromise.catch((err) => {
317
318
  bootError = err;
318
319
  });
320
+ const readyPromise = bootPromise.then(
321
+ () => runtime
322
+ );
319
323
  const runtime = {
320
- dispatch: enqueueDispatch,
324
+ dispatch: dispatchToQuiescence,
325
+ dispatchOnce: enqueueDispatch,
321
326
  getState() {
322
- if (bootError !== null) throw bootError;
323
- if (state === void 0) {
324
- throw new Error("@demlik/tea: getState() called before runtime booted");
325
- }
326
327
  return state;
327
328
  },
328
329
  subscribe(listener) {
@@ -337,6 +338,36 @@ function run(machine, opts) {
337
338
  observers.delete(observer);
338
339
  };
339
340
  },
341
+ onBoot(handler) {
342
+ if (booted && state !== void 0) {
343
+ try {
344
+ handler(state);
345
+ } catch (err) {
346
+ console.error(err);
347
+ }
348
+ return () => {
349
+ };
350
+ }
351
+ bootHandlers.add(handler);
352
+ return () => {
353
+ bootHandlers.delete(handler);
354
+ };
355
+ },
356
+ on(type, handler) {
357
+ let bucket = eventHandlers.get(type);
358
+ if (bucket === void 0) {
359
+ bucket = /* @__PURE__ */ new Set();
360
+ eventHandlers.set(type, bucket);
361
+ }
362
+ const erased = handler;
363
+ bucket.add(erased);
364
+ return () => {
365
+ const set = eventHandlers.get(type);
366
+ if (set === void 0) return;
367
+ set.delete(erased);
368
+ if (set.size === 0) eventHandlers.delete(type);
369
+ };
370
+ },
340
371
  subscribePort(port, listener) {
341
372
  const key = port;
342
373
  let bucket = portRegistry.get(key);
@@ -356,11 +387,24 @@ function run(machine, opts) {
356
387
  emitPort(port, value) {
357
388
  portEmit(port, value);
358
389
  },
359
- // `ready` is the original (un-swallowed) boot promise. Idempotency
360
- // comes for free — promises settle exactly once; every read returns the
361
- // same settled value. See the boot-promise note above for why this is
362
- // distinct from `tail`.
363
- ready: bootPromise,
390
+ // `ready` is assigned just below (it must resolve to `runtime` itself,
391
+ // which can't be referenced inside its own object literal). See the
392
+ // assignment + rationale after this block.
393
+ ready: readyPromise,
394
+ idle() {
395
+ return drainToQuiescence();
396
+ },
397
+ result() {
398
+ const current = state;
399
+ return isTerminal(current) ? current : void 0;
400
+ },
401
+ done() {
402
+ const current = state;
403
+ if (isTerminal(current)) return Promise.resolve(current);
404
+ return new Promise((resolve) => {
405
+ doneWaiters.add(resolve);
406
+ });
407
+ },
364
408
  async stop() {
365
409
  stopped = true;
366
410
  await tail;
@@ -375,8 +419,8 @@ function run(machine, opts) {
375
419
  if (store && state !== void 0 && bootError === null) {
376
420
  try {
377
421
  await store.save(state);
378
- } catch (err) {
379
- console.error(err);
422
+ } catch (error) {
423
+ reportError(error, { phase: "stop-save" });
380
424
  }
381
425
  }
382
426
  }
@@ -386,10 +430,17 @@ function run(machine, opts) {
386
430
  function historyTracker(runtime, size) {
387
431
  const buffer = [];
388
432
  let stopped = false;
433
+ const push = (entry) => {
434
+ buffer.push(entry);
435
+ if (buffer.length > size) buffer.shift();
436
+ };
389
437
  const unobserve = size <= 0 ? () => {
390
438
  } : runtime.observe((msg, state) => {
391
- buffer.push({ msg, state });
392
- if (buffer.length > size) buffer.shift();
439
+ push({ msg, state });
440
+ });
441
+ const unboot = size <= 0 ? () => {
442
+ } : runtime.onBoot((state) => {
443
+ push({ msg: null, state });
393
444
  });
394
445
  return {
395
446
  snapshot() {
@@ -399,6 +450,7 @@ function historyTracker(runtime, size) {
399
450
  if (stopped) return;
400
451
  stopped = true;
401
452
  unobserve();
453
+ unboot();
402
454
  }
403
455
  };
404
456
  }
@@ -410,33 +462,12 @@ function replay(machine, opts) {
410
462
  "TEA contract violation: machine.init must return [state, []] (no Cmds) when `loaded` is non-null. Init's rehydrate branch is a pure state passthrough \u2014 the migration/parse boundary, not the boot-effect hook.\n\nRoutes for boot effects:\n - Stateless infrastructure (e.g. chrome.alarms ensure) \u2192 host module top level, outside TEA.\n - State-conditional resume (e.g. re-attach a session) \u2192 a `boot` Msg dispatched once from the host after `run(...)` returns, handled in update.\n\nSee .patterns/tea/tea-invariants.md (Invariant 2)."
411
463
  );
412
464
  }
413
- let state = initialState;
414
- const cmds = [...initCmds];
415
- const updateForm = machine.update;
416
- const updateMode = (() => {
417
- const firstKey = Object.keys(updateForm)[0];
418
- if (firstKey === void 0) return "reducer";
419
- const firstValue = updateForm[firstKey];
420
- return typeof firstValue === "function" ? "reducer" : "transitions";
421
- })();
422
- for (const msg of opts.msgs) {
423
- if (__DEV__) deepFreeze(state);
424
- let result;
425
- if (updateMode === "reducer") {
426
- result = updateForm[msg.type](
427
- state,
428
- msg
429
- );
430
- } else {
431
- const table = updateForm;
432
- const stateKey = state.type;
433
- result = table[stateKey][msg.type](state, msg);
434
- }
435
- if (__DEV__) assertPureResult(result, msg.type);
436
- const [next, emitted] = result;
437
- state = next;
438
- cmds.push(...emitted);
439
- }
465
+ const { state, cmds: foldedCmds } = foldUpdates(
466
+ machine,
467
+ initialState,
468
+ opts.msgs
469
+ );
470
+ const cmds = [...initCmds, ...foldedCmds];
440
471
  const subs = machine.subscriptions ? [...machine.subscriptions(state)] : [];
441
472
  return { state, cmds, subs };
442
473
  }
@@ -453,6 +484,6 @@ function tryInterpret(work, onOk, onErr) {
453
484
  };
454
485
  }
455
486
 
456
- export { Cmd, PortNameCollisionError, __resetPortRegistry, absurd, defineMachine, definePort, historyTracker, noop, replay, run, subId, tryInterpret };
457
- //# sourceMappingURL=chunk-CMG2KMWX.js.map
458
- //# sourceMappingURL=chunk-CMG2KMWX.js.map
487
+ export { PortNameCollisionError, QuiescenceTimeoutError, __resetPortRegistry, absurd, asReducer, defineMachine, definePort, historyTracker, noop, replay, run, tryInterpret };
488
+ //# sourceMappingURL=chunk-MMH7CW5K.js.map
489
+ //# sourceMappingURL=chunk-MMH7CW5K.js.map