@demlik/tea 0.14.0 → 0.16.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 (116) hide show
  1. package/README.md +30 -7
  2. package/dist/agent/index.d.ts +49 -36
  3. package/dist/agent/index.js +12 -8
  4. package/dist/chunk-4LPY67E4.js +18 -0
  5. package/dist/chunk-4LPY67E4.js.map +1 -0
  6. package/dist/chunk-5XU6ZJO2.js +533 -0
  7. package/dist/chunk-5XU6ZJO2.js.map +1 -0
  8. package/dist/{chunk-AQ6ZIMEA.js → chunk-6NG4V5SB.js} +89 -115
  9. package/dist/chunk-6NG4V5SB.js.map +1 -0
  10. package/dist/{chunk-HYDWG35Q.js → chunk-7MGXQZPW.js} +11 -9
  11. package/dist/chunk-7MGXQZPW.js.map +1 -0
  12. package/dist/{chunk-BLJ2JQSH.js → chunk-BCTEOVOU.js} +3 -3
  13. package/dist/{chunk-BLJ2JQSH.js.map → chunk-BCTEOVOU.js.map} +1 -1
  14. package/dist/chunk-CH5KTOEZ.js +710 -0
  15. package/dist/chunk-CH5KTOEZ.js.map +1 -0
  16. package/dist/chunk-CIHSHLQX.js +64 -0
  17. package/dist/chunk-CIHSHLQX.js.map +1 -0
  18. package/dist/{chunk-Z2L3LSNV.js → chunk-EHUDXAKC.js} +246 -210
  19. package/dist/chunk-EHUDXAKC.js.map +1 -0
  20. package/dist/chunk-GOX2CYIM.js +380 -0
  21. package/dist/chunk-GOX2CYIM.js.map +1 -0
  22. package/dist/chunk-IVJP3GKJ.js +104 -0
  23. package/dist/chunk-IVJP3GKJ.js.map +1 -0
  24. package/dist/{chunk-F7FMAWLA.js → chunk-MVZS5FDM.js} +13 -6
  25. package/dist/chunk-MVZS5FDM.js.map +1 -0
  26. package/dist/{chunk-LYLTKJWM.js → chunk-MYNLY3HP.js} +9 -8
  27. package/dist/chunk-MYNLY3HP.js.map +1 -0
  28. package/dist/{chunk-I5MQMJDW.js → chunk-NSIAORRY.js} +20 -30
  29. package/dist/chunk-NSIAORRY.js.map +1 -0
  30. package/dist/{chunk-MYXFNWAJ.js → chunk-QNGYQMZB.js} +4 -4
  31. package/dist/chunk-QNGYQMZB.js.map +1 -0
  32. package/dist/chunk-WT5PBPLB.js +98 -0
  33. package/dist/chunk-WT5PBPLB.js.map +1 -0
  34. package/dist/{core-CTzMJ82M.d.ts → core-BbgtiXdg.d.ts} +231 -139
  35. package/dist/do/index.d.ts +37 -32
  36. package/dist/do/index.js +21 -14
  37. package/dist/do/index.js.map +1 -1
  38. package/dist/{durable-effects-B0-qGQDy.d.ts → durable-effects-7MTK_jnj.d.ts} +1 -1
  39. package/dist/effect/index.d.ts +151 -0
  40. package/dist/effect/index.js +98 -0
  41. package/dist/effect/index.js.map +1 -0
  42. package/dist/extension/index.d.ts +6 -7
  43. package/dist/extension/index.js +3 -2
  44. package/dist/extension/index.js.map +1 -1
  45. package/dist/flow/index.d.ts +68 -70
  46. package/dist/flow/index.js +32 -27
  47. package/dist/flow/index.js.map +1 -1
  48. package/dist/idempotency/index.d.ts +6 -7
  49. package/dist/idempotency/index.js +4 -2
  50. package/dist/{index-DJWIER_3.d.ts → index-7vV8xmsD.d.ts} +1 -1
  51. package/dist/{index-NjrT3fL2.d.ts → index-Bz5Rh9Ga.d.ts} +19 -96
  52. package/dist/index-CjFDUBeq.d.ts +207 -0
  53. package/dist/{index-6A7dB8f6.d.ts → index-CkFYA9Y6.d.ts} +14 -16
  54. package/dist/{index-B5BZ-_cT.d.ts → index-Cou86-GX.d.ts} +189 -108
  55. package/dist/{index-TvRb1KVf.d.ts → index-DU6JpQ8G.d.ts} +1 -1
  56. package/dist/{index-q0hXFeZ0.d.ts → index-Xufl55W8.d.ts} +286 -215
  57. package/dist/{index-4vZzljCD.d.ts → index-_dOR3xqA.d.ts} +57 -45
  58. package/dist/index.d.ts +181 -385
  59. package/dist/index.js +2 -1
  60. package/dist/jev/index.d.ts +147 -99
  61. package/dist/jev/index.js +329 -233
  62. package/dist/jev/index.js.map +1 -1
  63. package/dist/{machine-CdIcN2Xm.d.ts → machine-BQzTV8On.d.ts} +171 -280
  64. package/dist/machine-viz/index.d.ts +5 -5
  65. package/dist/machine-viz/index.js +8 -4
  66. package/dist/machine-viz/index.js.map +1 -1
  67. package/dist/mem/index.d.ts +3 -4
  68. package/dist/mem/index.js +2 -1
  69. package/dist/mem/index.js.map +1 -1
  70. package/dist/node/index.d.ts +119 -78
  71. package/dist/node/index.js +52 -38
  72. package/dist/node/index.js.map +1 -1
  73. package/dist/paginate/index.d.ts +27 -33
  74. package/dist/paginate/index.js +6 -4
  75. package/dist/parity/index.d.ts +4 -5
  76. package/dist/parity/index.js +3 -2
  77. package/dist/parity/index.js.map +1 -1
  78. package/dist/pbt/index.d.ts +2 -2
  79. package/dist/pbt/index.js +2 -1
  80. package/dist/pbt/index.js.map +1 -1
  81. package/dist/persistence/index.d.ts +6 -7
  82. package/dist/persistence/index.js +5 -3
  83. package/dist/promise/index.d.ts +186 -0
  84. package/dist/promise/index.js +5 -0
  85. package/dist/promise/index.js.map +1 -0
  86. package/dist/protocol-ClRZBhHQ.d.ts +53 -0
  87. package/dist/react/index.d.ts +36 -17
  88. package/dist/react/index.js +27 -4
  89. package/dist/react/index.js.map +1 -1
  90. package/dist/resilience/index.d.ts +63 -455
  91. package/dist/resilience/index.js +37 -483
  92. package/dist/resilience/index.js.map +1 -1
  93. package/dist/{runtime-types-LWDoi943.d.ts → runtime-types-CiVpC0HN.d.ts} +131 -388
  94. package/dist/testing/index.d.ts +165 -15
  95. package/dist/testing/index.js +104 -3
  96. package/dist/testing/index.js.map +1 -1
  97. package/dist/timing/index.d.ts +25 -22
  98. package/dist/timing/index.js +4 -3
  99. package/dist/timing/index.js.map +1 -1
  100. package/dist/{types-DP3f5NIl.d.ts → types-LxZxGYda.d.ts} +2 -2
  101. package/dist/work-queue/index.d.ts +5 -6
  102. package/package.json +18 -5
  103. package/dist/chunk-AQ6ZIMEA.js.map +0 -1
  104. package/dist/chunk-F2US5BW6.js +0 -72
  105. package/dist/chunk-F2US5BW6.js.map +0 -1
  106. package/dist/chunk-F7FMAWLA.js.map +0 -1
  107. package/dist/chunk-HUKZQX6U.js +0 -97
  108. package/dist/chunk-HUKZQX6U.js.map +0 -1
  109. package/dist/chunk-HYDWG35Q.js.map +0 -1
  110. package/dist/chunk-I5MQMJDW.js.map +0 -1
  111. package/dist/chunk-LYLTKJWM.js.map +0 -1
  112. package/dist/chunk-MYXFNWAJ.js.map +0 -1
  113. package/dist/chunk-RQULHGYH.js +0 -1804
  114. package/dist/chunk-RQULHGYH.js.map +0 -1
  115. package/dist/chunk-Z2L3LSNV.js.map +0 -1
  116. package/dist/index-DMCeMPMJ.d.ts +0 -272
@@ -1,28 +1,23 @@
1
- import { MsgType, createResilientCall } from '../chunk-AQ6ZIMEA.js';
2
- export { canPass, createResilientCall, defaultCircuitPolicy, initBucket, initCircuit, initWindow, liftResilience, onFailure, onSuccess, record, refill, remaining, runCmdDef, tryConsume } from '../chunk-AQ6ZIMEA.js';
1
+ import { createResilientCall } from '../chunk-6NG4V5SB.js';
2
+ export { DEFAULT_RESILIENT_NAME, canPass, createResilientCall, defaultCircuitPolicy, initBucket, initCircuit, initWindow, liftResilience, onFailure, onSuccess, record, refill, remaining, runCmdDef, tryConsume } from '../chunk-6NG4V5SB.js';
3
3
  import { defaultRng, initRetry, recordFailure, shouldRetry, nextDelayMs } from '../chunk-VBDU4JL6.js';
4
- import { subscribeDeadline } from '../chunk-F2US5BW6.js';
5
- export { cacheEvictMsg, cacheEvictionSub, cacheEvictionSubscribe, deadlineExceeded, deadlineSub, evictExpired, get, has, initCache, remove, set, setTimeoutArmTimer, subscribeDeadline, subscribeWith } from '../chunk-F2US5BW6.js';
6
- import { Cmd, msgKeysOf, applyCell, fromTimeout, tryInterpret, cmdEdgeOf, subId, without, formOf } from '../chunk-RQULHGYH.js';
7
- import { z } from 'zod';
4
+ import { unchecked } from '../chunk-4LPY67E4.js';
5
+ export { cacheEvictMsg, cacheEvictionSub, cacheEvictionSubscribe, deadlineExceeded, deadlineMsgType, deadlineSub, deadlines, deadlinesSub, evictExpired, get, has, initCache, nextTimer, remove, set, setTimeoutArmTimer, subscribeDeadline, subscribeWith } from '../chunk-IVJP3GKJ.js';
6
+ import '../chunk-GOX2CYIM.js';
7
+ import { Cmd, without } from '../chunk-CH5KTOEZ.js';
8
8
 
9
+ // src/internal/resilience/token-refresh/index.ts
9
10
  function initTokenRefresh() {
10
11
  return { token: null, stale: false };
11
12
  }
12
13
  var refreshToken = Cmd.define("refresh_token", {
13
- input: z.object({}),
14
- ok: z.custom(),
14
+ input: unchecked(),
15
+ ok: unchecked(),
15
16
  err: ["token_refresh_failed"]
16
17
  });
17
18
  function refreshTokenCmd() {
18
19
  return refreshToken({});
19
20
  }
20
- function tokenRefreshedMsg(token) {
21
- return { type: MsgType.TokenRefreshed, token };
22
- }
23
- function tokenRefreshFailedMsg(error) {
24
- return { type: MsgType.TokenRefreshFailed, error };
25
- }
26
21
  function createTokenRefresh(config = {}) {
27
22
  const skewMs = config.skewMs !== void 0 && config.skewMs > 0 ? config.skewMs : 0;
28
23
  function needsRefresh(state, at) {
@@ -45,28 +40,20 @@ function createTokenRefresh(config = {}) {
45
40
  }
46
41
  return [state, []];
47
42
  }
48
- function handlers(ports) {
49
- return {
50
- refresh_token: tryInterpret(
51
- () => ports.refresh(),
52
- (token) => tokenRefreshedMsg(token),
53
- (error) => tokenRefreshFailedMsg(error)
54
- )
55
- };
56
- }
57
43
  return {
44
+ /** The `refresh_token` Cmd def — list it in the machine's `cmds`. */
45
+ run: refreshToken,
58
46
  /** The slice initializer — spread into the consumer's `init`. */
59
47
  init: initTokenRefresh,
60
48
  needsRefresh,
61
49
  on401,
62
50
  refreshed,
63
- ensureFresh,
64
- handlers
51
+ ensureFresh
65
52
  };
66
53
  }
67
54
 
68
55
  // src/internal/resilience/authed-call/index.ts
69
- function createAuthedCall(config, rng = Math.random) {
56
+ function createAuthedCall(config = {}, rng = Math.random) {
70
57
  const rc = createResilientCall(config, rng);
71
58
  const tr = createTokenRefresh({ skewMs: config.skewMs });
72
59
  function init() {
@@ -94,13 +81,18 @@ function createAuthedCall(config, rng = Math.random) {
94
81
  rc.attempt(cleared.resilience, key, input, at)
95
82
  );
96
83
  }
97
- function succeed(s, key, msg) {
98
- const settled = clearAuthKey(s, key);
99
- return liftResilient(settled, rc.succeed(settled.resilience, key, msg));
100
- }
101
- function fail(s, key, msg) {
102
- const [resilience, cmds] = rc.fail(s.resilience, key, msg);
103
- const next = settleAuthIfDone({ ...s, resilience }, key, resilience);
84
+ function succeed(s, msg) {
85
+ const settled = clearAuthKey(s, msg.cmd.key);
86
+ const { call, cmds } = rc.settle(settled.resilience, msg);
87
+ return liftResilient(settled, [call, cmds]);
88
+ }
89
+ function fail(s, msg) {
90
+ const { call: resilience, cmds } = rc.settle(s.resilience, msg);
91
+ const next = settleAuthIfDone(
92
+ { ...s, resilience },
93
+ msg.cmd.key,
94
+ resilience
95
+ );
104
96
  return [next, cmds];
105
97
  }
106
98
  function on401(s, key, at) {
@@ -109,7 +101,7 @@ function createAuthedCall(config, rng = Math.random) {
109
101
  const input = call?.phase === "running" || call?.phase === "waiting_retry" ? call.input : void 0;
110
102
  if (spent >= 1 || input === void 0) {
111
103
  const error = { _tag: "unauthorized", key };
112
- const [settled] = rc.settleFailed(s.resilience, key, error);
104
+ const [settled] = rc.settleFailed(s.resilience, key, error, at);
113
105
  return [{ ...clearAuthKey(s, key), resilience: settled }, []];
114
106
  }
115
107
  return [
@@ -153,17 +145,17 @@ function createAuthedCall(config, rng = Math.random) {
153
145
  }
154
146
  return next;
155
147
  }
156
- function subs(s) {
157
- return rc.subs(s.resilience);
148
+ function deadlines2(s) {
149
+ return rc.deadlines(s.resilience);
158
150
  }
159
- function handlers(ports) {
160
- const trPorts = { refresh: ports.refresh };
161
- return {
162
- ...rc.handlers({ run: ports.run }),
163
- ...tr.handlers(trPorts)
164
- };
151
+ function timer(s) {
152
+ return rc.timer(s.resilience);
165
153
  }
166
154
  return {
155
+ /** The call's `Cmd.define`d run Cmd — list it in `cmds`. */
156
+ run: rc.run,
157
+ /** token-refresh's `refresh_token` Cmd def — list it in `cmds`. */
158
+ refresh: tr.run,
167
159
  init,
168
160
  attempt,
169
161
  succeed,
@@ -174,8 +166,8 @@ function createAuthedCall(config, rng = Math.random) {
174
166
  onTimer,
175
167
  /** token-refresh's call-boundary verb, re-exposed for proactive expiry refresh. */
176
168
  needsRefresh: (s, at) => tr.needsRefresh(s.auth, at),
177
- subs,
178
- handlers
169
+ deadlines: deadlines2,
170
+ timer
179
171
  };
180
172
  }
181
173
  function liftAuthed(state, [slice, cmds]) {
@@ -213,445 +205,7 @@ async function retryToSuccess(port, policy, opts = {}) {
213
205
  }
214
206
  }
215
207
  }
216
- function deadlineExceededMsg(seq) {
217
- return { type: "$deadline:exceeded", seq };
218
- }
219
- var deadlineDecision = Cmd.define("$deadline:decision", {
220
- input: z.object({
221
- decision: z.enum(["rearm", "expire", "idle"]),
222
- /** The slice `seq` AFTER this transition (the re-arm generation now armed). */
223
- seq: z.number()
224
- }),
225
- ok: z.void(),
226
- err: []
227
- });
228
- function withDeadline(base, config) {
229
- const windowMs = config.ms;
230
- const isProgress = config.progress;
231
- const update = {};
232
- const baseMsgKeys = msgKeysOf(base);
233
- for (const key of baseMsgKeys) {
234
- if (key.startsWith("$deadline:")) {
235
- throw new Error(
236
- `withDeadline: the base machine declares a reserved "${key}" update cell \u2014 the wrapper owns the "$deadline:" Msg namespace. Rename the base Msg.`
237
- );
238
- }
239
- }
240
- for (const key of baseMsgKeys) {
241
- update[key] = (state, msg) => {
242
- const [nextBase, baseCmds] = applyCell(
243
- base,
244
- state.base,
245
- msg
246
- );
247
- const armed = state.$deadline.phase === "armed";
248
- const counts = armed && (isProgress === void 0 || isProgress(msg));
249
- const $deadline = counts ? { phase: "armed", seq: state.$deadline.seq + 1 } : state.$deadline;
250
- const next = { base: nextBase, $deadline };
251
- const decision = deadlineDecision({
252
- decision: counts ? "rearm" : "idle",
253
- seq: $deadline.seq
254
- });
255
- return [next, [...baseCmds, decision]];
256
- };
257
- }
258
- update["$deadline:exceeded"] = (state, msg) => {
259
- const m = msg;
260
- const fires = state.$deadline.phase === "armed" && m.seq === state.$deadline.seq;
261
- const $deadline = fires ? { phase: "exceeded", seq: state.$deadline.seq } : state.$deadline;
262
- const next = { base: state.base, $deadline };
263
- const decision = deadlineDecision({
264
- decision: fires ? "expire" : "idle",
265
- seq: $deadline.seq
266
- });
267
- return [next, [decision]];
268
- };
269
- const baseInterpret = base.interpret ?? {};
270
- if (Object.hasOwn(baseInterpret, "$deadline:decision")) {
271
- throw new Error(
272
- 'withDeadline: the base machine declares a reserved "$deadline:decision" interpret handler \u2014 the wrapper owns the "$deadline:" Cmd namespace. Rename the base handler.'
273
- );
274
- }
275
- const interpret = {
276
- ...baseInterpret,
277
- "$deadline:decision": async () => {
278
- }
279
- };
280
- const baseSubscriptions = base.subscriptions;
281
- const subscriptions = (state) => {
282
- const baseSubs = baseSubscriptions ? baseSubscriptions(state.base) : [];
283
- if (state.$deadline.phase !== "armed") return baseSubs;
284
- const timeout = {
285
- id: subId(`$deadline:timeout:${state.$deadline.seq}`),
286
- type: "$deadline:timeout",
287
- delayMs: windowMs
288
- };
289
- return [...baseSubs, timeout];
290
- };
291
- const baseSubscribe = base.subscribe ?? {};
292
- if (Object.hasOwn(baseSubscribe, "$deadline:timeout")) {
293
- throw new Error(
294
- 'withDeadline: the base machine declares a reserved "$deadline:timeout" subscribe handler \u2014 the wrapper owns the "$deadline:" Sub namespace. Rename the base handler.'
295
- );
296
- }
297
- const timeoutCell = fromTimeout(
298
- (sub) => {
299
- const seq = Number(sub.id.slice(sub.id.lastIndexOf(":") + 1));
300
- return deadlineExceededMsg(seq);
301
- }
302
- );
303
- const subscribe = {
304
- ...baseSubscribe,
305
- "$deadline:timeout": timeoutCell
306
- };
307
- return {
308
- init: (loaded, ctx) => {
309
- if (loaded !== null) {
310
- return [loaded, []];
311
- }
312
- const [base0, cmds] = base.init(null, ctx);
313
- const model = {
314
- base: base0,
315
- $deadline: { phase: "armed", seq: 0 }
316
- };
317
- return [model, cmds];
318
- },
319
- update,
320
- subscriptions,
321
- subscribe,
322
- interpret,
323
- // The base's `Cmd.define` list rides through so `run`'s interpret edge
324
- // still parses / stamps the base's settled Msgs behind the wrap (#66).
325
- ...base.cmds ? { cmds: base.cmds } : {}
326
- };
327
- }
328
- function resilienceRunCmdDef() {
329
- return Cmd.define("$resilience:run", {
330
- input: z.custom(),
331
- // The carried result is the base interpret handler's resolution (`M | void`).
332
- // biome-ignore lint/suspicious/noConfusingVoidType: mirrors `Interpret`'s own `Promise<M | void>`
333
- ok: z.custom(),
334
- err: ["base_rejected", "deadline_exceeded"]
335
- });
336
- }
337
- var RC_RETRY_PREFIX = "resilient:retry:";
338
- var RC_DEADLINE_PREFIX = "resilient:deadline:";
339
- var WX_RETRY_PREFIX = "$resilience:retry:";
340
- var WX_DEADLINE_PREFIX = "$resilience:deadline:";
341
- function toWrapperTimerId(rcId) {
342
- if (rcId.startsWith(RC_RETRY_PREFIX))
343
- return WX_RETRY_PREFIX + rcId.slice(RC_RETRY_PREFIX.length);
344
- if (rcId.startsWith(RC_DEADLINE_PREFIX))
345
- return WX_DEADLINE_PREFIX + rcId.slice(RC_DEADLINE_PREFIX.length);
346
- return rcId;
347
- }
348
- function toRcTimerId(wxId) {
349
- if (wxId.startsWith(WX_RETRY_PREFIX))
350
- return RC_RETRY_PREFIX + wxId.slice(WX_RETRY_PREFIX.length);
351
- if (wxId.startsWith(WX_DEADLINE_PREFIX))
352
- return RC_DEADLINE_PREFIX + wxId.slice(WX_DEADLINE_PREFIX.length);
353
- return wxId;
354
- }
355
- function updateDeclaresMsgKey(machine, key) {
356
- if (formOf(machine) === "reducer") {
357
- return Object.hasOwn(machine.update, key);
358
- }
359
- for (const innerTable of Object.values(machine.update)) {
360
- if (innerTable !== null && (typeof innerTable === "object" || typeof innerTable === "function") && Object.hasOwn(innerTable, key)) {
361
- return true;
362
- }
363
- }
364
- return false;
365
- }
366
- function withResilience(base, config, rng = Math.random) {
367
- const { target } = config;
368
- const keyOf = config.keyOf ?? (() => target);
369
- const runCmd = resilienceRunCmdDef();
370
- const timeSensitiveBrick = config.circuit !== void 0 || config.rateLimit !== void 0 || config.cache !== void 0 || config.deadline !== void 0;
371
- if (timeSensitiveBrick && config.at === void 0) {
372
- throw new Error(
373
- "withResilience: a time-sensitive brick (circuit / rateLimit / cache / deadline) is configured but `config.at` is missing. The cold-attempt gate needs the REAL wall time the triggering base Msg carries as data (time-as-data) \u2014 without it the gate would fabricate `at = 0`, so a cold attempt after a real-time breaker trip computes a negative cooldown and the breaker NEVER recovers (and a deadline brick fires immediately). Provide `at: (msg) => msg.<wallTimeField>`."
374
- );
375
- }
376
- const atOf = config.at ?? (() => 0);
377
- const rc = createResilientCall(config, rng);
378
- const update = {};
379
- function isBaseMsg(result) {
380
- return typeof result === "object" && result !== null && "type" in result;
381
- }
382
- function foldBase(state, msg, delivering = /* @__PURE__ */ new Set()) {
383
- const [nextBase, baseCmds] = applyCell(base, state.base, msg);
384
- let next = { base: nextBase, $resilience: state.$resilience };
385
- const outCmds = [];
386
- const at = atOf(msg);
387
- for (const cmd of baseCmds) {
388
- if (cmd.type !== target) {
389
- outCmds.push(cmd);
390
- continue;
391
- }
392
- const callKey = keyOf(cmd);
393
- const [nextSlice, runCmds] = rc.attempt(
394
- next.$resilience,
395
- callKey,
396
- cmd,
397
- at
398
- );
399
- next = { ...next, $resilience: nextSlice };
400
- outCmds.push(...renameRunCmds(runCmds));
401
- if (runCmds.length === 0 && !delivering.has(callKey)) {
402
- const call = nextSlice.calls[callKey];
403
- if (call?.phase === "succeeded" && isBaseMsg(call.result)) {
404
- const [delivered, moreCmds] = foldBase(
405
- next,
406
- call.result,
407
- /* @__PURE__ */ new Set([...delivering, callKey])
408
- );
409
- next = delivered;
410
- outCmds.push(...moreCmds);
411
- }
412
- }
413
- }
414
- return [next, outCmds];
415
- }
416
- for (const key of msgKeysOf(base)) {
417
- update[key] = (state, msg) => foldBase(state, msg);
418
- }
419
- update["$resilience:ok"] = (state, msg) => {
420
- const m = msg;
421
- const [slice, runCmds] = rc.succeed(state.$resilience, m.key, {
422
- type: MsgType.ResilientOk,
423
- key: m.key,
424
- result: m.result,
425
- at: m.at
426
- });
427
- const settled = { ...state, $resilience: slice };
428
- const settleCmds = renameRunCmds(runCmds);
429
- if (!isBaseMsg(m.result)) return [settled, settleCmds];
430
- const [next, followCmds] = foldBase(settled, m.result, /* @__PURE__ */ new Set([m.key]));
431
- return [next, [...settleCmds, ...followCmds]];
432
- };
433
- update["$resilience:err"] = (state, msg) => {
434
- const m = msg;
435
- const [slice, runCmds] = rc.fail(state.$resilience, m.key, {
436
- type: MsgType.ResilientErr,
437
- key: m.key,
438
- error: m.error,
439
- at: m.at
440
- });
441
- return [{ ...state, $resilience: slice }, renameRunCmds(runCmds)];
442
- };
443
- update["$resilience:timer"] = (state, msg) => {
444
- const m = msg;
445
- const [slice, runCmds] = rc.onTimer(state.$resilience, {
446
- type: "deadline_exceeded",
447
- id: toRcTimerId(m.id),
448
- atMs: m.atMs
449
- });
450
- return [{ ...state, $resilience: slice }, renameRunCmds(runCmds)];
451
- };
452
- function renameRunCmds(runCmds) {
453
- return runCmds.map((r) => runCmd({ key: r.key, input: r.input }));
454
- }
455
- const baseSubscriptions = base.subscriptions;
456
- const baseSubscribe = base.subscribe;
457
- function subscriptions(state) {
458
- const baseSubs = baseSubscriptions ? baseSubscriptions(state.base) : [];
459
- const rcSubs = rc.subs(state.$resilience);
460
- const timerSubs = rcSubs.map((s) => ({
461
- id: subId(toWrapperTimerId(s.id)),
462
- type: "$resilience:timer",
463
- atMs: s.atMs
464
- }));
465
- return [...baseSubs, ...timerSubs];
466
- }
467
- const baseInterpret = base.interpret ?? {};
468
- const RESERVED = [
469
- "$resilience:run",
470
- "$resilience:ok",
471
- "$resilience:err",
472
- "$resilience:timer"
473
- ];
474
- const surfaces = [
475
- {
476
- name: "update",
477
- declares: (reserved) => updateDeclaresMsgKey(base, reserved)
478
- },
479
- {
480
- name: "interpret",
481
- declares: (reserved) => Object.hasOwn(baseInterpret, reserved)
482
- },
483
- {
484
- name: "subscribe",
485
- declares: (reserved) => Object.hasOwn(baseSubscribe ?? {}, reserved)
486
- }
487
- ];
488
- for (const reserved of RESERVED) {
489
- for (const surface of surfaces) {
490
- if (surface.declares(reserved)) {
491
- throw new Error(
492
- `withResilience: the base machine declares a reserved "${reserved}" ${surface.name} key \u2014 the wrapper owns the "$resilience:" namespace ($resilience:run / :ok / :err / :timer across update, interpret, and subscribe). Rename the base key.`
493
- );
494
- }
495
- }
496
- }
497
- if (!Object.hasOwn(baseInterpret, target)) {
498
- throw new Error(
499
- `withResilience: no base interpret handler for target Cmd "${target}" \u2014 the resilient effect has nothing to perform. The base machine must declare an interpret handler for its target Cmd type.`
500
- );
501
- }
502
- const runHandler = tryInterpret(
503
- async (cmd, ctx) => {
504
- const inner = cmd.input;
505
- const handler = baseInterpret[inner.type];
506
- if (handler === void 0) {
507
- throw new Error(
508
- `withResilience: no base interpret handler for target Cmd "${inner.type}" \u2014 the resilient effect has nothing to perform.`
509
- );
510
- }
511
- return cmdEdgeOf(ctx)(
512
- inner,
513
- await handler(inner, ctx)
514
- );
515
- },
516
- (result, cmd) => ({
517
- type: "$resilience:ok",
518
- key: cmd.key,
519
- result,
520
- at: Date.now()
521
- }),
522
- (error, cmd) => ({
523
- type: "$resilience:err",
524
- key: cmd.key,
525
- error,
526
- at: Date.now()
527
- })
528
- );
529
- const interpret = {
530
- ...baseInterpret,
531
- "$resilience:run": runHandler
532
- };
533
- const subscribeMap = {
534
- ...baseSubscribe ?? {},
535
- "$resilience:timer": (sub, ctx, dispatch) => (
536
- // Reuse the deadline subscribe cell wholesale: it arms a one-shot timer
537
- // for `max(0, atMs - Date.now())` and fires when the wall clock crosses
538
- // `atMs`. We hand it a `DeadlineSub` shape and translate its
539
- // `deadline_exceeded` Msg into the wrapper's `$resilience:timer` Msg.
540
- subscribeDeadline(
541
- { id: sub.id, type: "deadline", atMs: sub.atMs },
542
- ctx,
543
- (dl) => dispatch({ type: "$resilience:timer", id: dl.id, atMs: dl.atMs })
544
- )
545
- )
546
- };
547
- return {
548
- init: (loaded, ctx) => {
549
- if (loaded !== null) {
550
- return [loaded, []];
551
- }
552
- const [base0, baseCmds] = base.init(null, ctx);
553
- let resilience = rc.init();
554
- const initCmds = [];
555
- for (const cmd of baseCmds) {
556
- if (cmd.type !== target) {
557
- initCmds.push(cmd);
558
- continue;
559
- }
560
- if (config.deadline !== void 0) {
561
- throw new Error(
562
- `withResilience: base.init emits the target Cmd "${target}" while a deadline brick is configured. The boot cold attempt has no real wall time (boot is gated at at=0), so the deadline would anchor at 0 + ${config.deadline.ms}ms and its $resilience:deadline Sub would fire IMMEDIATELY (max(0, atMs - now) === 0), failing the boot call instantly. Move the boot effect out of base.init and into a boot Msg dispatched after run() (where the triggering Msg carries a real at via config.at), so the deadline anchors at a real instant. (Boot effects belong in a boot Msg, not init Cmds.)`
563
- );
564
- }
565
- const callKey = keyOf(cmd);
566
- const [nextSlice, runCmds] = rc.attempt(resilience, callKey, cmd, 0);
567
- resilience = nextSlice;
568
- initCmds.push(...renameRunCmds(runCmds));
569
- }
570
- const model = { base: base0, $resilience: resilience };
571
- return [model, initCmds];
572
- },
573
- update,
574
- subscriptions,
575
- subscribe: subscribeMap,
576
- interpret,
577
- // The base's `Cmd.define` list rides through so `run`'s interpret edge
578
- // still parses / stamps the base's NON-target settled Msgs behind the
579
- // wrap; the target's settle through the carrier above (#66).
580
- ...base.cmds ? { cmds: base.cmds } : {}
581
- };
582
- }
583
- var telemetryEmit = Cmd.define("$telemetry:emit", {
584
- input: z.object({ event: z.custom() }),
585
- ok: z.void(),
586
- err: [],
587
- requirements: Cmd.requirements()
588
- });
589
- function withTelemetry(base, config = {}) {
590
- const project = config.event;
591
- const msgKeys = msgKeysOf(base);
592
- const update = {};
593
- for (const key of msgKeys) {
594
- update[key] = (state, msg) => {
595
- const [nextBase, baseCmds] = applyCell(base, state.base, msg);
596
- const $telemetry = { seq: state.$telemetry.seq + 1 };
597
- const next = { base: nextBase, $telemetry };
598
- const event = project ? project(msg, state, next) : { seq: $telemetry.seq, msgType: msg.type };
599
- const emit = telemetryEmit({ event });
600
- return [next, [...baseCmds, emit]];
601
- };
602
- }
603
- const baseInterpret = base.interpret ?? {};
604
- if (Object.hasOwn(baseInterpret, "$telemetry:emit")) {
605
- throw new Error(
606
- 'withTelemetry: the base machine declares a reserved "$telemetry:emit" interpret handler \u2014 the wrapper owns the "$telemetry:" Cmd namespace. Rename the base handler.'
607
- );
608
- }
609
- const interpret = {
610
- ...baseInterpret,
611
- // OBSERVE-ONLY at the boundary: the sink is fire-and-forget. The runtime
612
- // `await`s every interpret handler in its dispatch loop, so awaiting the
613
- // sink here would let a SLOW sink backpressure the wrapped machine (its
614
- // next dispatch waits) and a FAILING sink crash dispatch (the throw would
615
- // propagate out of the transition). Both violate the contract: telemetry
616
- // must never affect base behavior OR latency. So we DO NOT await — we hand
617
- // the event to the sink, normalize its result to a Promise, swallow any
618
- // rejection/throw, and return immediately. The handler resolves in the same
619
- // microtask the dispatch loop reaches it; the machine never waits on the
620
- // sink, and a rejected sink can never become an unhandled rejection.
621
- "$telemetry:emit": (cmd, ctx) => {
622
- const event = cmd.event.at === void 0 && ctx.clock !== void 0 ? { ...cmd.event, at: ctx.clock() } : cmd.event;
623
- try {
624
- Promise.resolve(ctx.telemetrySink(event)).catch(() => {
625
- });
626
- } catch {
627
- }
628
- }
629
- };
630
- const baseSubscriptions = base.subscriptions;
631
- const baseSubscribe = base.subscribe;
632
- return {
633
- init: (loaded, ctx) => {
634
- if (loaded !== null) {
635
- return [loaded, []];
636
- }
637
- const [base0, cmds] = base.init(null, ctx);
638
- const model = { base: base0, $telemetry: { seq: 0 } };
639
- return [model, cmds];
640
- },
641
- update,
642
- // Subs merge to the base's, verbatim — telemetry owns no timers. The base
643
- // subscribes against its own nested slice.
644
- ...baseSubscriptions ? {
645
- subscriptions: (state) => baseSubscriptions(state.base)
646
- } : {},
647
- ...baseSubscribe ? { subscribe: baseSubscribe } : {},
648
- interpret,
649
- // The base's `Cmd.define` list rides through so `run`'s interpret edge
650
- // still parses / stamps the base's settled Msgs behind the wrap (#66).
651
- ...base.cmds ? { cmds: base.cmds } : {}
652
- };
653
- }
654
208
 
655
- export { RetryExhaustedError, createAuthedCall, createTokenRefresh, deadlineDecision, deadlineExceededMsg, initTokenRefresh, liftAuthed, refreshToken, refreshTokenCmd, resilienceRunCmdDef, retryToSuccess, telemetryEmit, tokenRefreshFailedMsg, tokenRefreshedMsg, withDeadline, withResilience, withTelemetry };
209
+ export { RetryExhaustedError, createAuthedCall, createTokenRefresh, initTokenRefresh, liftAuthed, refreshToken, refreshTokenCmd, retryToSuccess };
656
210
  //# sourceMappingURL=index.js.map
657
211
  //# sourceMappingURL=index.js.map