@demlik/tea 0.2.0 → 0.3.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 (196) hide show
  1. package/README.md +49 -0
  2. package/dist/agent/index.d.ts +16 -770
  3. package/dist/agent/index.js +18 -16
  4. package/dist/authed-call/index.d.ts +3 -3
  5. package/dist/authed-call/index.js +22 -36
  6. package/dist/authed-call/index.js.map +1 -1
  7. package/dist/await-terminal/index.d.ts +2 -2
  8. package/dist/await-terminal/index.js +2 -2
  9. package/dist/batch-window/index.d.ts +3 -3
  10. package/dist/batch-window/index.js +3 -3
  11. package/dist/cache/index.d.ts +2 -2
  12. package/dist/cache/index.js +5 -2
  13. package/dist/{chunk-3545BOYL.js → chunk-25LBDCBC.js} +6 -5
  14. package/dist/chunk-25LBDCBC.js.map +1 -0
  15. package/dist/{chunk-IN33Q4VJ.js → chunk-2KNLKQVN.js} +3 -3
  16. package/dist/{chunk-IN33Q4VJ.js.map → chunk-2KNLKQVN.js.map} +1 -1
  17. package/dist/{chunk-6VDCTR7N.js → chunk-BP2R7TPP.js} +203 -141
  18. package/dist/chunk-BP2R7TPP.js.map +1 -0
  19. package/dist/chunk-C6QC273X.js +71 -0
  20. package/dist/chunk-C6QC273X.js.map +1 -0
  21. package/dist/{chunk-ZDRN3YCE.js → chunk-CAXMQJJ3.js} +17 -24
  22. package/dist/chunk-CAXMQJJ3.js.map +1 -0
  23. package/dist/{chunk-UV6UWGEO.js → chunk-D736QUGF.js} +6 -5
  24. package/dist/chunk-D736QUGF.js.map +1 -0
  25. package/dist/chunk-EWJP3YYP.js +21 -0
  26. package/dist/chunk-EWJP3YYP.js.map +1 -0
  27. package/dist/{chunk-TGG26XMK.js → chunk-FV3Z5MBE.js} +10 -5
  28. package/dist/chunk-FV3Z5MBE.js.map +1 -0
  29. package/dist/chunk-G5E74IJB.js +8 -0
  30. package/dist/chunk-G5E74IJB.js.map +1 -0
  31. package/dist/{chunk-6GIGQT6C.js → chunk-IXBKFQH5.js} +12 -11
  32. package/dist/chunk-IXBKFQH5.js.map +1 -0
  33. package/dist/{chunk-G2U7DJHL.js → chunk-JFFVBEDI.js} +3 -3
  34. package/dist/{chunk-G2U7DJHL.js.map → chunk-JFFVBEDI.js.map} +1 -1
  35. package/dist/chunk-LYLDCIPH.js +365 -0
  36. package/dist/chunk-LYLDCIPH.js.map +1 -0
  37. package/dist/{chunk-HJO6VNQI.js → chunk-MU6MCIAC.js} +3 -3
  38. package/dist/{chunk-HJO6VNQI.js.map → chunk-MU6MCIAC.js.map} +1 -1
  39. package/dist/{chunk-UYWUVEP6.js → chunk-MUUROGIH.js} +2 -2
  40. package/dist/chunk-MUUROGIH.js.map +1 -0
  41. package/dist/{chunk-5KVHID6A.js → chunk-MVY5DAMF.js} +4 -4
  42. package/dist/{chunk-5KVHID6A.js.map → chunk-MVY5DAMF.js.map} +1 -1
  43. package/dist/{chunk-EGWTT5XJ.js → chunk-NPERUUUE.js} +76 -95
  44. package/dist/chunk-NPERUUUE.js.map +1 -0
  45. package/dist/{chunk-DYRKDCJI.js → chunk-PBDYW2TU.js} +41 -7
  46. package/dist/chunk-PBDYW2TU.js.map +1 -0
  47. package/dist/{chunk-5VNWZADP.js → chunk-Q2UM7BXU.js} +18 -7
  48. package/dist/chunk-Q2UM7BXU.js.map +1 -0
  49. package/dist/chunk-RMX4EOQK.js +26 -0
  50. package/dist/chunk-RMX4EOQK.js.map +1 -0
  51. package/dist/{chunk-2YOE5PIS.js → chunk-S2EGRPCM.js} +8 -6
  52. package/dist/chunk-S2EGRPCM.js.map +1 -0
  53. package/dist/{chunk-IK4S5DJN.js → chunk-S4O26D7B.js} +7 -14
  54. package/dist/chunk-S4O26D7B.js.map +1 -0
  55. package/dist/{chunk-6OUDPSSG.js → chunk-SK5JGUY7.js} +3 -3
  56. package/dist/{chunk-6OUDPSSG.js.map → chunk-SK5JGUY7.js.map} +1 -1
  57. package/dist/{chunk-FOGL3TC7.js → chunk-TPGASTFF.js} +8 -10
  58. package/dist/chunk-TPGASTFF.js.map +1 -0
  59. package/dist/{chunk-SC3FAFZN.js → chunk-UF4MZSLT.js} +2 -2
  60. package/dist/chunk-UF4MZSLT.js.map +1 -0
  61. package/dist/{chunk-M6UZT7HS.js → chunk-VBUCHVCV.js} +6 -5
  62. package/dist/chunk-VBUCHVCV.js.map +1 -0
  63. package/dist/{chunk-VFGKTGGC.js → chunk-WGGAMW7Y.js} +191 -178
  64. package/dist/chunk-WGGAMW7Y.js.map +1 -0
  65. package/dist/chunk-ZJ5AOYBH.js +8 -0
  66. package/dist/chunk-ZJ5AOYBH.js.map +1 -0
  67. package/dist/{chunk-LEC4U6CX.js → chunk-ZVJZ72HY.js} +3 -3
  68. package/dist/{chunk-LEC4U6CX.js.map → chunk-ZVJZ72HY.js.map} +1 -1
  69. package/dist/circuit-breaker/index.d.ts +1 -0
  70. package/dist/circuit-breaker/index.js +1 -1
  71. package/dist/{core-BmlJOvxM.d.ts → core-42kapW0n.d.ts} +96 -6
  72. package/dist/deadline/index.d.ts +77 -11
  73. package/dist/deadline/index.js +3 -3
  74. package/dist/devtools/index.js +8 -28
  75. package/dist/devtools/index.js.map +1 -1
  76. package/dist/do/index.d.ts +52 -24
  77. package/dist/do/index.js +94 -92
  78. package/dist/do/index.js.map +1 -1
  79. package/dist/{durable-effects-9LxFpmh3.d.ts → durable-effects-CFRngTNA.d.ts} +3 -3
  80. package/dist/extension/index.d.ts +25 -7
  81. package/dist/extension/index.js +4 -3
  82. package/dist/extension/index.js.map +1 -1
  83. package/dist/extension/react.js +4 -3
  84. package/dist/extension/react.js.map +1 -1
  85. package/dist/extension/subs/index.d.ts +2 -2
  86. package/dist/extension/subs/index.js +7 -10
  87. package/dist/extension/subs/index.js.map +1 -1
  88. package/dist/fan-out/index.d.ts +10 -10
  89. package/dist/fan-out/index.js +3 -3
  90. package/dist/idempotent-intake/index.d.ts +2 -2
  91. package/dist/idempotent-intake/index.js +3 -3
  92. package/dist/index.d.ts +50 -489
  93. package/dist/index.js +2 -2
  94. package/dist/llm-call/index.d.ts +3 -2
  95. package/dist/llm-call/index.js +12 -10
  96. package/dist/machine-B_BF4jij.d.ts +804 -0
  97. package/dist/machine-viz/index.d.ts +1 -1
  98. package/dist/machine-viz/index.js +2 -2
  99. package/dist/mem/index.d.ts +3 -2
  100. package/dist/mem/index.js.map +1 -1
  101. package/dist/monitored-run/index.d.ts +54 -30
  102. package/dist/monitored-run/index.js +7 -7
  103. package/dist/node/index.d.ts +3 -2
  104. package/dist/node/index.js +2 -3
  105. package/dist/node/index.js.map +1 -1
  106. package/dist/paginated-walk/index.d.ts +2 -2
  107. package/dist/paginated-walk/index.js +11 -10
  108. package/dist/parity/index.d.ts +6 -5
  109. package/dist/parity/index.js +5 -4
  110. package/dist/parity/index.js.map +1 -1
  111. package/dist/pbt/arbitraries/index.d.ts +1 -1
  112. package/dist/pbt/index.d.ts +1 -1
  113. package/dist/pbt/index.js +3 -3
  114. package/dist/pbt/index.js.map +1 -1
  115. package/dist/pbt/runners/index.d.ts +1 -1
  116. package/dist/pbt/runners/index.js +3 -3
  117. package/dist/poller/index.d.ts +59 -17
  118. package/dist/poller/index.js +56 -20
  119. package/dist/poller/index.js.map +1 -1
  120. package/dist/prediction/index.d.ts +1 -1
  121. package/dist/prediction/index.js +2 -2
  122. package/dist/pure/index.d.ts +1 -1
  123. package/dist/pure/index.js +2 -2
  124. package/dist/rate-limit/index.d.ts +2 -2
  125. package/dist/rate-limit/index.js +1 -1
  126. package/dist/react/index.d.ts +3 -2
  127. package/dist/react/index.js +2 -2
  128. package/dist/react/index.js.map +1 -1
  129. package/dist/reconciler/index.d.ts +2 -2
  130. package/dist/reconciler/index.js +13 -15
  131. package/dist/reconciler/index.js.map +1 -1
  132. package/dist/recorder/index.d.ts +20 -20
  133. package/dist/recorder/index.js +1 -1
  134. package/dist/resilient-call/index.d.ts +31 -6
  135. package/dist/resilient-call/index.js +10 -9
  136. package/dist/retry-backoff/index.d.ts +191 -14
  137. package/dist/retry-backoff/index.js +1 -1
  138. package/dist/retry-to-success/index.js +1 -1
  139. package/dist/runtime-types-gsog27b6.d.ts +337 -0
  140. package/dist/saga/index.d.ts +3 -2
  141. package/dist/saga/index.js +2 -2
  142. package/dist/saga/index.js.map +1 -1
  143. package/dist/snapshot/index.d.ts +1 -1
  144. package/dist/snapshot/index.js +3 -3
  145. package/dist/subs/index.d.ts +372 -4
  146. package/dist/subs/index.js +4 -1
  147. package/dist/testing/index.d.ts +2 -2
  148. package/dist/testing/index.js +2 -2
  149. package/dist/throttled-input/index.d.ts +2 -2
  150. package/dist/throttled-input/index.js +14 -13
  151. package/dist/throttled-input/index.js.map +1 -1
  152. package/dist/token-refresh/index.d.ts +1 -1
  153. package/dist/token-refresh/index.js +3 -3
  154. package/dist/trace-replay/index.d.ts +12 -7
  155. package/dist/trace-replay/index.js +4 -3
  156. package/dist/{types-BoUWgwSm.d.ts → types-KbL3W1mj.d.ts} +1 -1
  157. package/dist/with-deadline/index.d.ts +1 -1
  158. package/dist/with-deadline/index.js +2 -2
  159. package/dist/with-resilience/index.d.ts +21 -2
  160. package/dist/with-resilience/index.js +12 -23
  161. package/dist/with-resilience/index.js.map +1 -1
  162. package/dist/with-telemetry/index.d.ts +1 -1
  163. package/dist/with-telemetry/index.js +2 -2
  164. package/dist/work-queue/adapter.d.ts +2 -2
  165. package/dist/work-queue/adapter.js +2 -2
  166. package/dist/work-queue/index.d.ts +3 -2
  167. package/dist/work-queue/index.js +1 -1
  168. package/dist/work-queue/index.js.map +1 -1
  169. package/dist/work-queue/ops.d.ts +2 -2
  170. package/dist/work-queue/ops.js +1 -1
  171. package/dist/workflow/index.d.ts +51 -19
  172. package/dist/workflow/index.js +71 -52
  173. package/dist/workflow/index.js.map +1 -1
  174. package/package.json +4 -1
  175. package/dist/chunk-2YOE5PIS.js.map +0 -1
  176. package/dist/chunk-3545BOYL.js.map +0 -1
  177. package/dist/chunk-5VNWZADP.js.map +0 -1
  178. package/dist/chunk-6GIGQT6C.js.map +0 -1
  179. package/dist/chunk-6VDCTR7N.js.map +0 -1
  180. package/dist/chunk-DYRKDCJI.js.map +0 -1
  181. package/dist/chunk-EGWTT5XJ.js.map +0 -1
  182. package/dist/chunk-EMJTPHI7.js +0 -91
  183. package/dist/chunk-EMJTPHI7.js.map +0 -1
  184. package/dist/chunk-FOGL3TC7.js.map +0 -1
  185. package/dist/chunk-IK4S5DJN.js.map +0 -1
  186. package/dist/chunk-M6UZT7HS.js.map +0 -1
  187. package/dist/chunk-NSDFA6M6.js +0 -198
  188. package/dist/chunk-NSDFA6M6.js.map +0 -1
  189. package/dist/chunk-SC3FAFZN.js.map +0 -1
  190. package/dist/chunk-SKAMZKTE.js +0 -20
  191. package/dist/chunk-SKAMZKTE.js.map +0 -1
  192. package/dist/chunk-TGG26XMK.js.map +0 -1
  193. package/dist/chunk-UV6UWGEO.js.map +0 -1
  194. package/dist/chunk-UYWUVEP6.js.map +0 -1
  195. package/dist/chunk-VFGKTGGC.js.map +0 -1
  196. package/dist/chunk-ZDRN3YCE.js.map +0 -1
@@ -1,6 +1,34 @@
1
- import { detectUpdateForm, foldUpdates, __DEV__, deepFreeze, applyCell, assertPureResult } from './chunk-DYRKDCJI.js';
1
+ import { detectUpdateForm, foldUpdates, structuralHash, applyCellChecked } from './chunk-PBDYW2TU.js';
2
2
  import { Result } from 'better-result';
3
3
 
4
+ // src/observability.ts
5
+ function historyTracker(runtime, size) {
6
+ const buffer = [];
7
+ let stopped = false;
8
+ const push = (entry) => {
9
+ buffer.push(entry);
10
+ if (buffer.length > size) buffer.shift();
11
+ };
12
+ const unobserve = size <= 0 ? () => {
13
+ } : runtime.observe((msg, state) => {
14
+ push({ msg, state });
15
+ });
16
+ const unboot = size <= 0 ? () => {
17
+ } : runtime.onBoot((state) => {
18
+ push({ msg: null, state });
19
+ });
20
+ return {
21
+ snapshot() {
22
+ return buffer.slice();
23
+ },
24
+ stop() {
25
+ if (stopped) return;
26
+ stopped = true;
27
+ unobserve();
28
+ unboot();
29
+ }
30
+ };
31
+ }
4
32
  function absurd(x) {
5
33
  throw new Error(`unreachable: ${JSON.stringify(x)}`);
6
34
  }
@@ -8,25 +36,25 @@ var noop = (state, _msg) => [state, []];
8
36
  var definedPortNames = /* @__PURE__ */ new Set();
9
37
  var PortNameCollisionError = class extends Error {
10
38
  name = "PortNameCollisionError";
39
+ _tag = "PortNameCollisionError";
11
40
  constructor(portName) {
12
41
  super(
13
42
  `definePort: a port named "${portName}" was already defined. Each definePort call must use a unique name. If two modules need the same port, export it from one module and import it.`
14
43
  );
15
44
  }
16
45
  };
46
+ var SubIdCollisionError = class extends Error {
47
+ name = "SubIdCollisionError";
48
+ _tag = "SubIdCollisionError";
49
+ constructor(id, declaredType, conflictingType) {
50
+ super(
51
+ `@demlik/tea: Sub.id collision: id="${id}" declared as type="${declaredType}" and type="${conflictingType}"`
52
+ );
53
+ }
54
+ };
17
55
  function __resetPortRegistry() {
18
56
  definedPortNames.clear();
19
57
  }
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
58
  var QuiescenceTimeoutError = class extends Error {
31
59
  constructor(iterations) {
32
60
  super(
@@ -35,6 +63,7 @@ var QuiescenceTimeoutError = class extends Error {
35
63
  this.iterations = iterations;
36
64
  }
37
65
  name = "QuiescenceTimeoutError";
66
+ _tag = "QuiescenceTimeoutError";
38
67
  };
39
68
  function definePort(name) {
40
69
  let isHmr = false;
@@ -50,6 +79,18 @@ function definePort(name) {
50
79
  definedPortNames.add(name);
51
80
  return { __brand: "port", name };
52
81
  }
82
+ function schemaMigrate(schema, upcast = (raw) => raw) {
83
+ return (raw) => {
84
+ let migrated;
85
+ try {
86
+ migrated = upcast(raw);
87
+ } catch {
88
+ return null;
89
+ }
90
+ const parsed = schema.safeParse(migrated);
91
+ return parsed.success ? parsed.data : null;
92
+ };
93
+ }
53
94
  function defineMachine(m) {
54
95
  if (m.__form === void 0) {
55
96
  Object.defineProperty(m, "__form", {
@@ -64,6 +105,64 @@ function defineMachine(m) {
64
105
  function asReducer(reducer) {
65
106
  return reducer;
66
107
  }
108
+ function replay(machine, opts) {
109
+ const loaded = opts.loaded ?? null;
110
+ const [initialState, initCmds] = machine.init(loaded, opts.ctx);
111
+ if (loaded !== null && initCmds.length > 0) {
112
+ throw new Error(
113
+ "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)."
114
+ );
115
+ }
116
+ const { state, cmds: foldedCmds } = foldUpdates(
117
+ machine,
118
+ initialState,
119
+ opts.msgs
120
+ );
121
+ const cmds = [...initCmds, ...foldedCmds];
122
+ const subs = machine.subscriptions ? [...machine.subscriptions(state)] : [];
123
+ const depSubs = [];
124
+ if (machine.subs) {
125
+ for (const [index, entry] of machine.subs.entries()) {
126
+ const deps = entry.deps(state);
127
+ if (deps !== null) depSubs.push({ index, id: structuralHash(deps) });
128
+ }
129
+ }
130
+ return { state, cmds, subs, depSubs };
131
+ }
132
+ function wrapDetached(handler) {
133
+ return (cmd, ctx, dispatch) => {
134
+ if (dispatch === void 0) {
135
+ throw new Error(
136
+ "@demlik/tea: a wrapDetached handler was invoked without the injected dispatch. The kernel always supplies it; call the handler through the runtime, not directly."
137
+ );
138
+ }
139
+ return handler(cmd, ctx, dispatch);
140
+ };
141
+ }
142
+ function tryInterpret(work, onOk, onErr) {
143
+ return async (cmd, ctx) => {
144
+ const result = await Result.tryPromise({
145
+ try: () => work(cmd, ctx),
146
+ catch: (error) => error
147
+ });
148
+ return result.match({
149
+ ok: (value) => onOk(value, cmd),
150
+ err: (error) => onErr(error, cmd)
151
+ });
152
+ };
153
+ }
154
+
155
+ // src/run.ts
156
+ function defaultOnError(error, _context) {
157
+ setTimeout(() => {
158
+ throw error;
159
+ }, 0);
160
+ }
161
+ function normalizeSupervision(supervision) {
162
+ if (supervision === void 0) return { strategy: "stop" };
163
+ if (typeof supervision === "string") return { strategy: supervision };
164
+ return supervision;
165
+ }
67
166
  function run(machine, opts) {
68
167
  const { store } = opts;
69
168
  const ctx = opts.ctx ?? {};
@@ -78,11 +177,21 @@ function run(machine, opts) {
78
177
  defaultOnError(sinkError);
79
178
  }
80
179
  };
180
+ function fanout(items, phase, fn) {
181
+ for (const item of items) {
182
+ try {
183
+ fn(item);
184
+ } catch (err) {
185
+ reportError(err, { phase });
186
+ }
187
+ }
188
+ }
81
189
  const supervision = normalizeSupervision(opts.supervision);
82
190
  let state;
83
191
  let bootError = null;
84
192
  let stopped = false;
85
193
  const subRegistry = /* @__PURE__ */ new Map();
194
+ const depSubRegistry = /* @__PURE__ */ new Map();
86
195
  const listeners = /* @__PURE__ */ new Set();
87
196
  const observers = /* @__PURE__ */ new Set();
88
197
  const bootHandlers = /* @__PURE__ */ new Set();
@@ -93,48 +202,78 @@ function run(machine, opts) {
93
202
  function portEmit(port, value) {
94
203
  const subscribers = portRegistry.get(port);
95
204
  if (!subscribers || subscribers.size === 0) return;
96
- for (const listener of subscribers) {
205
+ fanout(subscribers, "port-emit", (listener) => listener(value));
206
+ }
207
+ const augmentedCtx = { ...ctx, emit: portEmit };
208
+ let tail = Promise.resolve();
209
+ function stopSubs(ids) {
210
+ for (const id of ids) {
211
+ const cleanup = subRegistry.get(id);
212
+ if (cleanup === void 0) continue;
97
213
  try {
98
- listener(value);
214
+ cleanup();
99
215
  } catch (err) {
100
- reportError(err, { phase: "port-emit" });
216
+ reportError(err, { phase: "sub-cleanup" });
101
217
  }
218
+ subRegistry.delete(id);
102
219
  }
103
220
  }
104
- const augmentedCtx = { ...ctx, emit: portEmit };
105
- function applyUpdate(state2, msg) {
106
- if (__DEV__) deepFreeze(state2);
107
- const result = applyCell(machine, state2, msg);
108
- if (__DEV__) assertPureResult(result, msg.type);
109
- return result;
221
+ function disposeDepSubs(indices) {
222
+ for (const index of indices) {
223
+ const running = depSubRegistry.get(index);
224
+ if (running === void 0) continue;
225
+ try {
226
+ running.dispose();
227
+ } catch (err) {
228
+ reportError(err, { phase: "sub-cleanup" });
229
+ }
230
+ depSubRegistry.delete(index);
231
+ }
232
+ }
233
+ function reconcileDepSubs() {
234
+ let firstError = null;
235
+ const subs = machine.subs;
236
+ if (!subs) return firstError;
237
+ for (const [i, entry] of subs.entries()) {
238
+ const deps = entry.deps(state);
239
+ if (deps === null) {
240
+ disposeDepSubs([i]);
241
+ continue;
242
+ }
243
+ const id = structuralHash(deps);
244
+ const running = depSubRegistry.get(i);
245
+ if (running !== void 0 && running.runningId === id) {
246
+ continue;
247
+ }
248
+ disposeDepSubs([i]);
249
+ try {
250
+ const dispose = entry.source(state, enqueueDispatch, ctx);
251
+ depSubRegistry.set(i, { runningId: id, dispose });
252
+ } catch (err) {
253
+ if (firstError === null) firstError = err;
254
+ }
255
+ }
256
+ return firstError;
110
257
  }
111
- let tail = Promise.resolve();
112
258
  function reconcileSubs() {
113
- if (!machine.subscriptions) return;
259
+ const depError = reconcileDepSubs();
260
+ if (!machine.subscriptions) {
261
+ if (depError !== null) throw depError;
262
+ return;
263
+ }
114
264
  const desired = machine.subscriptions(state);
115
265
  const desiredIds = /* @__PURE__ */ new Set();
116
266
  const desiredTypeById = /* @__PURE__ */ new Map();
117
267
  for (const sub of desired) {
118
268
  const existing = desiredTypeById.get(sub.id);
119
269
  if (existing !== void 0 && existing !== sub.type) {
120
- throw new Error(
121
- `@demlik/tea: Sub.id collision: id="${sub.id}" declared as type="${existing}" and type="${sub.type}"`
122
- );
270
+ throw new SubIdCollisionError(sub.id, existing, sub.type);
123
271
  }
124
272
  desiredTypeById.set(sub.id, sub.type);
125
273
  }
126
274
  for (const sub of desired) desiredIds.add(sub.id);
127
- for (const [id, cleanup] of subRegistry) {
128
- if (!desiredIds.has(id)) {
129
- try {
130
- cleanup();
131
- } catch (err) {
132
- reportError(err, { phase: "sub-cleanup" });
133
- }
134
- subRegistry.delete(id);
135
- }
136
- }
137
- let firstStartError = null;
275
+ stopSubs([...subRegistry.keys()].filter((id) => !desiredIds.has(id)));
276
+ let firstStartError = depError;
138
277
  for (const sub of desired) {
139
278
  if (subRegistry.has(sub.id)) continue;
140
279
  const handler = machine.subscribe?.[sub.type];
@@ -155,13 +294,19 @@ function run(machine, opts) {
155
294
  if (firstStartError !== null) throw firstStartError;
156
295
  }
157
296
  const interpretMap = machine.interpret ?? {};
297
+ const interpretDispatch = (msg) => {
298
+ enqueueDispatch(msg).catch((error) => {
299
+ reportError(error, { phase: "follow-up" });
300
+ });
301
+ };
158
302
  async function runInterpret(cmds) {
159
303
  for (const cmd of cmds) {
160
304
  const handler = interpretMap[cmd.type];
161
305
  if (!handler) continue;
162
306
  const follow = await handler(
163
307
  cmd,
164
- augmentedCtx
308
+ augmentedCtx,
309
+ interpretDispatch
165
310
  );
166
311
  if (follow !== void 0 && follow !== null) {
167
312
  enqueueDispatch(follow).catch((error) => {
@@ -171,24 +316,12 @@ function run(machine, opts) {
171
316
  }
172
317
  }
173
318
  function fireListeners() {
174
- for (const listener of listeners) {
175
- try {
176
- listener();
177
- } catch (err) {
178
- reportError(err, { phase: "listener" });
179
- }
180
- }
319
+ fanout(listeners, "listener", (listener) => listener());
181
320
  }
182
321
  function fireObservers(msg) {
183
322
  if (observers.size === 0 || state === void 0) return;
184
323
  const snapshot = state;
185
- for (const observer of observers) {
186
- try {
187
- observer(msg, snapshot);
188
- } catch (err) {
189
- reportError(err, { phase: "observer" });
190
- }
191
- }
324
+ fanout(observers, "observer", (observer) => observer(msg, snapshot));
192
325
  }
193
326
  function fireEvents(msg) {
194
327
  if (eventHandlers.size === 0 || state === void 0) return;
@@ -202,26 +335,14 @@ function run(machine, opts) {
202
335
  for (const event of events) {
203
336
  const bucket = eventHandlers.get(event.type);
204
337
  if (bucket === void 0) continue;
205
- for (const handler of bucket) {
206
- try {
207
- handler(event);
208
- } catch (err) {
209
- reportError(err, { phase: "event" });
210
- }
211
- }
338
+ fanout(bucket, "event", (handler) => handler(event));
212
339
  }
213
340
  }
214
341
  function fireBoot() {
215
342
  booted = true;
216
343
  if (bootHandlers.size === 0 || state === void 0) return;
217
344
  const snapshot = state;
218
- for (const handler of bootHandlers) {
219
- try {
220
- handler(snapshot);
221
- } catch (err) {
222
- reportError(err, { phase: "boot" });
223
- }
224
- }
345
+ fanout(bootHandlers, "boot", (handler) => handler(snapshot));
225
346
  }
226
347
  function settleDoneWaiters() {
227
348
  if (doneWaiters.size === 0 || state === void 0) return;
@@ -245,10 +366,19 @@ function run(machine, opts) {
245
366
  if (state === void 0) {
246
367
  throw new Error("@demlik/tea: runtime not booted");
247
368
  }
369
+ if (machine.identity !== void 0) {
370
+ const addressed = machine.identity.ofMsg(msg);
371
+ if (addressed !== void 0) {
372
+ const own = machine.identity.ofState(state);
373
+ if (own !== void 0 && structuralHash(addressed) !== structuralHash(own)) {
374
+ return;
375
+ }
376
+ }
377
+ }
248
378
  let next;
249
379
  let cmds;
250
380
  try {
251
- [next, cmds] = applyUpdate(state, msg);
381
+ [next, cmds] = applyCellChecked(machine, state, msg);
252
382
  } catch (reduceError) {
253
383
  reportError(reduceError, { phase: "reduce" });
254
384
  switch (supervision.strategy) {
@@ -337,11 +467,8 @@ function run(machine, opts) {
337
467
  },
338
468
  onBoot(handler) {
339
469
  if (booted && state !== void 0) {
340
- try {
341
- handler(state);
342
- } catch (err) {
343
- reportError(err, { phase: "boot" });
344
- }
470
+ const snapshot = state;
471
+ fanout([handler], "boot", (h) => h(snapshot));
345
472
  return () => {
346
473
  };
347
474
  }
@@ -384,9 +511,6 @@ function run(machine, opts) {
384
511
  emitPort(port, value) {
385
512
  portEmit(port, value);
386
513
  },
387
- // `ready` is assigned just below (it must resolve to `runtime` itself,
388
- // which can't be referenced inside its own object literal). See the
389
- // assignment + rationale after this block.
390
514
  ready: readyPromise,
391
515
  idle() {
392
516
  return drainToQuiescence();
@@ -405,14 +529,8 @@ function run(machine, opts) {
405
529
  async stop() {
406
530
  stopped = true;
407
531
  await tail;
408
- for (const [id, cleanup] of subRegistry) {
409
- try {
410
- cleanup();
411
- } catch (err) {
412
- reportError(err, { phase: "sub-cleanup" });
413
- }
414
- subRegistry.delete(id);
415
- }
532
+ stopSubs([...subRegistry.keys()]);
533
+ disposeDepSubs([...depSubRegistry.keys()]);
416
534
  if (store && state !== void 0 && bootError === null) {
417
535
  try {
418
536
  await store.save(state);
@@ -424,63 +542,7 @@ function run(machine, opts) {
424
542
  };
425
543
  return runtime;
426
544
  }
427
- function historyTracker(runtime, size) {
428
- const buffer = [];
429
- let stopped = false;
430
- const push = (entry) => {
431
- buffer.push(entry);
432
- if (buffer.length > size) buffer.shift();
433
- };
434
- const unobserve = size <= 0 ? () => {
435
- } : runtime.observe((msg, state) => {
436
- push({ msg, state });
437
- });
438
- const unboot = size <= 0 ? () => {
439
- } : runtime.onBoot((state) => {
440
- push({ msg: null, state });
441
- });
442
- return {
443
- snapshot() {
444
- return buffer.slice();
445
- },
446
- stop() {
447
- if (stopped) return;
448
- stopped = true;
449
- unobserve();
450
- unboot();
451
- }
452
- };
453
- }
454
- function replay(machine, opts) {
455
- const loaded = opts.loaded ?? null;
456
- const [initialState, initCmds] = machine.init(loaded, opts.ctx);
457
- if (loaded !== null && initCmds.length > 0) {
458
- throw new Error(
459
- "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)."
460
- );
461
- }
462
- const { state, cmds: foldedCmds } = foldUpdates(
463
- machine,
464
- initialState,
465
- opts.msgs
466
- );
467
- const cmds = [...initCmds, ...foldedCmds];
468
- const subs = machine.subscriptions ? [...machine.subscriptions(state)] : [];
469
- return { state, cmds, subs };
470
- }
471
- function tryInterpret(work, onOk, onErr) {
472
- return async (cmd, ctx) => {
473
- const result = await Result.tryPromise({
474
- try: () => work(cmd, ctx),
475
- catch: (error) => error
476
- });
477
- return result.match({
478
- ok: (value) => onOk(value, cmd),
479
- err: (error) => onErr(error, cmd)
480
- });
481
- };
482
- }
483
545
 
484
- export { PortNameCollisionError, QuiescenceTimeoutError, __resetPortRegistry, absurd, asReducer, defineMachine, definePort, historyTracker, noop, replay, run, tryInterpret };
485
- //# sourceMappingURL=chunk-6VDCTR7N.js.map
486
- //# sourceMappingURL=chunk-6VDCTR7N.js.map
546
+ export { PortNameCollisionError, QuiescenceTimeoutError, SubIdCollisionError, __resetPortRegistry, absurd, asReducer, defineMachine, definePort, historyTracker, noop, replay, run, schemaMigrate, tryInterpret, wrapDetached };
547
+ //# sourceMappingURL=chunk-BP2R7TPP.js.map
548
+ //# sourceMappingURL=chunk-BP2R7TPP.js.map