@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,4 +1,5 @@
1
1
  /**
2
+ * @packageDocumentation
2
3
  * @demlik/tea/retry-backoff — exponential backoff with jitter + cap, and the
3
4
  * retry-attempt state every fallible `interpret` handler folds over.
4
5
  *
@@ -20,6 +21,13 @@
20
21
  * const delayMs = nextDelayMs(retry, policy); // pure: rng injected at call site
21
22
  * return [{ ...state, retry }, [{ type: "ScheduleRetry", delayMs, into: TimerFiredRetry }]];
22
23
  *
24
+ * and the same wiring under a wall-clock outage budget, where the failure Msg
25
+ * carries the instant it was observed:
26
+ *
27
+ * // policy: { baseMs: 250, factor: 2, capMs: 4_000, maxElapsedMs: 1_050_000, jitter: "full" }
28
+ * const retry = recordFailure(state.retry, msg.error, msg.at);
29
+ * if (!shouldRetry(retry, policy, msg.at)) return [toGaveUp(state, retryElapsedMs(retry, msg.at)), []];
30
+ *
23
31
  * The reducer stays pure: `nextDelayMs` is called with the runtime's RNG only
24
32
  * at the emit boundary (or the caller defers the whole delay computation to the
25
33
  * `interpret` handler and passes `rng` there). Either way the substrate never
@@ -27,6 +35,42 @@
27
35
  *
28
36
  * NOT a substrate primitive: it depends on nothing from `../index`. It is a
29
37
  * leaf utility consumers reach via the `@demlik/tea/retry-backoff` subpath.
38
+ *
39
+ * ── THE BOUND MAY BE A DURATION, NOT ONLY A COUNT ──────────────────────────
40
+ * `maxAttempts` was the module's only terminal bound, and a count is the wrong
41
+ * bound whenever this ladder nests inside somebody else's ladder. A downstream
42
+ * consumer proved it in production: 4 restarts over a 250ms→4s ladder is ~3.75s
43
+ * of backoff, which nested under a client's own ~2.5s ladder came to roughly
44
+ * SIXTEEN SECONDS of total patience — against a peer that waits ~17.5 MINUTES
45
+ * before it gives up on the other side. A routine deploy outran the budget and
46
+ * killed 170 runs in 7 days that the peer would have resumed from a checkpoint.
47
+ *
48
+ * The failure a count cannot express is that the QUESTION is temporal. "How
49
+ * long do we tolerate an unreachable peer" has an answer in seconds, usually
50
+ * derived from the peer's own give-up window; what a count of attempts costs in
51
+ * seconds depends on how long each attempt takes, which is the carrier's
52
+ * business and not the policy's. So a policy may now declare `maxElapsedMs` —
53
+ * a wall-clock outage budget measured from the FIRST failure of the streak —
54
+ * instead of, or in addition to, `maxAttempts`. When both are declared, retry
55
+ * continues only while EVERY declared bound still permits it.
56
+ *
57
+ * TIME IS AN INPUT, NOT AN AMBIENT READ (invariant 2). The streak's origin is
58
+ * supplied by the caller to `recordFailure`, and `shouldRetry` is told `nowMs`;
59
+ * this module still never reads a clock, exactly as it never reads the RNG.
60
+ * The types make the wiring the only wiring that compiles: a duration-bounded
61
+ * policy only satisfies the `shouldRetry` overload that demands a
62
+ * {@link TimedRetryState} *and* a `nowMs`, so "declared an outage budget, never
63
+ * fed it a clock" is a type error rather than a bound that quietly never fires.
64
+ *
65
+ * A policy with NO bound at all is likewise unrepresentable by accident — the
66
+ * {@link RetryBudget} union has no "neither" arm; forever-retry must be spelled
67
+ * `unbounded: true` ({@link UnboundedRetryPolicy}).
68
+ *
69
+ * Strengthens invariant 2 (transitions are pure — the clock enters as an
70
+ * argument, never as a `Date.now()` inside the fold) and invariant 5
71
+ * (composition is by reduction — the new bound is an additive arm on the policy
72
+ * union and an optional argument on the verbs, so every existing count-bounded
73
+ * call site keeps compiling untouched).
30
74
  */
31
75
  /**
32
76
  * Jitter strategy applied to the computed backoff delay.
@@ -82,25 +126,107 @@ declare function asRng(fn: () => number, probe?: boolean): Rng;
82
126
  */
83
127
  declare const defaultRng: Rng;
84
128
  /**
85
- * A retry policy is pure configuration — the knobs of the backoff curve. It
86
- * carries no mutable state; `RetryState` does. One policy is shared across all
87
- * the attempts of one logical operation (often a module-scope const).
129
+ * The shape of the delay ladder, with no terminal bound attached — `baseMs`,
130
+ * `factor`, `capMs`, `jitter`. Split out from the policy because the curve and
131
+ * the bound answer different questions: the curve is "how fast do we come
132
+ * back", the bound is "when do we stop". `backoffDelay` needs only the curve.
88
133
  */
89
- interface RetryPolicy {
134
+ interface BackoffCurve {
90
135
  /** Delay for attempt 0, in milliseconds, before any jitter or cap. */
91
136
  readonly baseMs: number;
92
137
  /** Geometric growth factor. `2` doubles the delay each attempt. */
93
138
  readonly factor: number;
94
139
  /** Hard ceiling, in milliseconds. The capped exponential never exceeds this. */
95
140
  readonly capMs: number;
141
+ /** Jitter strategy applied to the capped exponential delay. */
142
+ readonly jitter: Jitter;
143
+ }
144
+ /**
145
+ * Bound by ATTEMPT COUNT — the original bound, and the right one when each
146
+ * attempt costs about the same and the caller is the outermost ladder.
147
+ *
148
+ * `maxElapsedMs: never` is what keeps this arm honest: a count-bounded policy
149
+ * is *declared* to have no outage budget, so it satisfies the clock-free
150
+ * `shouldRetry` overload. Add `maxElapsedMs` and it becomes a
151
+ * {@link DurationRetryPolicy}, which no longer does.
152
+ */
153
+ interface CountBound {
96
154
  /**
97
155
  * Maximum number of attempts. `shouldRetry` allows attempts `0 .. maxAttempts-1`,
98
156
  * so `maxAttempts: 3` means "try once, then retry at most twice" (attempts 0, 1, 2).
99
157
  */
100
158
  readonly maxAttempts: number;
101
- /** Jitter strategy applied to the capped exponential delay. */
102
- readonly jitter: Jitter;
159
+ readonly maxElapsedMs?: never;
160
+ readonly unbounded?: never;
161
+ }
162
+ /**
163
+ * Bound by WALL-CLOCK OUTAGE DURATION, optionally also by count.
164
+ *
165
+ * `maxElapsedMs` is measured from the FIRST failure of the current streak
166
+ * ({@link TimedRetryState.firstFailureAtMs}), not from the last attempt: the
167
+ * question it answers is "how long has the far side been unreachable", which is
168
+ * the question a nesting caller's own patience is denominated in. Derive it
169
+ * from the peer's give-up window rather than restating a guess — two sides
170
+ * holding different answers to one ordered fact is the defect this bound
171
+ * exists to prevent.
172
+ *
173
+ * `maxAttempts` stays available here, and when both are present retry continues
174
+ * only while BOTH still permit it (see {@link shouldRetry}). Adding a count to a
175
+ * duration budget is a belt-and-braces guard against a pathologically fast
176
+ * failure loop, not a substitute for the duration.
177
+ */
178
+ interface DurationBound {
179
+ /**
180
+ * Outage budget in wall-clock milliseconds. `shouldRetry` permits another
181
+ * attempt while `nowMs - firstFailureAtMs < maxElapsedMs`, however many
182
+ * attempts that takes.
183
+ */
184
+ readonly maxElapsedMs: number;
185
+ /** Optional secondary count bound; omitted means "no count bound". */
186
+ readonly maxAttempts?: number;
187
+ readonly unbounded?: never;
188
+ }
189
+ /**
190
+ * No bound at all — retry forever. Deliberately verbose and deliberately the
191
+ * only way to say it: {@link RetryBudget} has no "neither bound declared" arm,
192
+ * so an infinite retry loop is something a policy author must write down, never
193
+ * something a forgotten field produces.
194
+ */
195
+ interface Unbounded {
196
+ readonly unbounded: true;
197
+ readonly maxAttempts?: never;
198
+ readonly maxElapsedMs?: never;
103
199
  }
200
+ /**
201
+ * The terminal bound a policy declares: a count, a duration (optionally with a
202
+ * count), or an explicit opt-in to neither. Exactly one arm applies, and there
203
+ * is no arm for "I forgot".
204
+ */
205
+ type RetryBudget = CountBound | DurationBound | Unbounded;
206
+ /**
207
+ * A retry policy is pure configuration — the knobs of the backoff curve plus
208
+ * the bound that ends the retrying. It carries no mutable state; `RetryState`
209
+ * does. One policy is shared across all the attempts of one logical operation
210
+ * (often a module-scope const).
211
+ *
212
+ * `RetryPolicy` is the COUNT-bounded shape, unchanged since the module's first
213
+ * release: every existing `{ baseMs, factor, capMs, maxAttempts, jitter }`
214
+ * literal still satisfies it, `policy.maxAttempts` is still a `number` (not
215
+ * `number | undefined`), and `shouldRetry(state, policy)` still takes no clock.
216
+ * Reach for {@link DurationRetryPolicy} when the bound should be temporal, or
217
+ * {@link AnyRetryPolicy} when writing code generic over the bound.
218
+ */
219
+ type RetryPolicy = BackoffCurve & CountBound;
220
+ /** A curve bounded by wall-clock outage duration (and optionally by count). */
221
+ type DurationRetryPolicy = BackoffCurve & DurationBound;
222
+ /** A curve with the explicit opt-in to unbounded retry. */
223
+ type UnboundedRetryPolicy = BackoffCurve & Unbounded;
224
+ /**
225
+ * Any well-formed policy. The parameter type for code that must work whatever
226
+ * bound the caller declared — `shouldRetry`'s implementation, wrappers, and
227
+ * consumers that take a policy from configuration.
228
+ */
229
+ type AnyRetryPolicy = BackoffCurve & RetryBudget;
104
230
  /**
105
231
  * Sensible defaults: 100ms base, doubling, capped at 30s, up to 5 attempts,
106
232
  * full jitter (the herd-avoidance default). Tuned for network calls; override
@@ -128,7 +254,7 @@ declare const defaultRetryPolicy: RetryPolicy;
128
254
  * `baseMs`. `capMs` is also the floor of the clamp, so a misconfigured
129
255
  * `capMs < baseMs` still yields a delay within `[0, capMs]`.
130
256
  */
131
- declare function backoffDelay(attempt: number, policy: RetryPolicy, rng?: Rng): number;
257
+ declare function backoffDelay(attempt: number, policy: BackoffCurve, rng?: Rng): number;
132
258
  /**
133
259
  * Per-operation retry state: how many attempts have failed so far, and the most
134
260
  * recent error. `lastError` is `unknown` because the failure can be anything a
@@ -141,21 +267,72 @@ interface RetryState {
141
267
  /** The error from the most recent failure, if any. Carried, never interpreted. */
142
268
  readonly lastError?: unknown;
143
269
  }
144
- /** The starting state: zero attempts, no error yet. */
270
+ /**
271
+ * A retry state that also knows WHEN its failure streak began — the origin a
272
+ * duration bound is measured from. Minted by passing the observation instant to
273
+ * {@link recordFailure}; there is no other way to obtain one, so a streak with
274
+ * an outage budget but no origin is not a value that can be written down.
275
+ *
276
+ * The origin is the FIRST failure of the streak and is preserved across
277
+ * subsequent failures. A success resets the streak the way it always did — by
278
+ * returning to `initRetry()` — which drops the origin with it, so an operation
279
+ * that fails occasionally never accumulates outage and pays nothing for a wide
280
+ * `maxElapsedMs`; only an unbroken run of failures grows toward the bound.
281
+ */
282
+ interface TimedRetryState extends RetryState {
283
+ /** When the streak's first failure was observed (caller's clock, epoch ms). */
284
+ readonly firstFailureAtMs: number;
285
+ }
286
+ /** The starting state: zero attempts, no error yet, no streak. */
145
287
  declare function initRetry(): RetryState;
146
288
  /**
147
289
  * Record a failed attempt. PURE — returns a *new* state with `attempt`
148
290
  * incremented and `lastError` set; the input `state` is never mutated.
291
+ *
292
+ * `atMs` is the instant the failure was OBSERVED, supplied by the caller (the
293
+ * `interpret` handler owns the clock; this module never reads one — invariant
294
+ * 2). Passing it starts the streak clock on the first failure and preserves the
295
+ * original origin on every failure after it, yielding a {@link TimedRetryState}
296
+ * that a duration-bounded policy can be evaluated against. Omit it and the
297
+ * count-bounded behaviour is bit-for-bit what it always was.
149
298
  */
299
+ declare function recordFailure(state: TimedRetryState, error: unknown, atMs?: number): TimedRetryState;
300
+ declare function recordFailure(state: RetryState, error: unknown, atMs: number): TimedRetryState;
150
301
  declare function recordFailure(state: RetryState, error: unknown): RetryState;
302
+ /**
303
+ * How long the current failure streak has lasted, in milliseconds. PURE.
304
+ *
305
+ * The honest number for a give-up log — a count of attempts says nothing about
306
+ * how much patience it actually spent. Clamped at 0 so a caller's clock going
307
+ * backwards (NTP step, a rehydrated state whose origin is from a different
308
+ * host) reads as "no outage yet" rather than a negative duration.
309
+ */
310
+ declare function retryElapsedMs(state: TimedRetryState, nowMs: number): number;
151
311
  /**
152
312
  * Whether another attempt is permitted under `policy`. PURE.
153
313
  *
154
- * `attempt` is the number of failures recorded so far (= the index of the next
155
- * attempt). We may retry while that index is below `maxAttempts`, i.e. attempts
156
- * `0 .. maxAttempts-1` are allowed and the `maxAttempts`-th is refused.
314
+ * Every bound the policy DECLARES must still permit the attempt; a bound it
315
+ * does not declare is not consulted. Concretely:
316
+ *
317
+ * - `maxAttempts` — `attempt` is the number of failures recorded so far (= the
318
+ * index of the next attempt); we may retry while that index is below
319
+ * `maxAttempts`, i.e. attempts `0 .. maxAttempts-1` are allowed and the
320
+ * `maxAttempts`-th is refused. Unchanged.
321
+ * - `maxElapsedMs` — we may retry while `nowMs - firstFailureAtMs` is below
322
+ * the budget, *however many attempts that takes*. A streak's first failure
323
+ * measures a zero-length outage and is therefore always retried, unless the
324
+ * budget is itself `0` (the "no patience" setting).
325
+ * - `unbounded: true` — always permitted.
326
+ *
327
+ * The overloads are the wiring contract. A count-bounded or explicitly
328
+ * unbounded policy needs no clock, so the two-argument call every existing
329
+ * caller already writes keeps type-checking. A policy carrying `maxElapsedMs`
330
+ * matches only the second overload, which demands a {@link TimedRetryState} and
331
+ * a `nowMs` — so declaring an outage budget and then never feeding it a clock
332
+ * fails to compile instead of silently producing a bound that never fires.
157
333
  */
158
- declare function shouldRetry(state: RetryState, policy: RetryPolicy): boolean;
334
+ declare function shouldRetry(state: RetryState, policy: BackoffCurve & (CountBound | Unbounded), nowMs?: number): boolean;
335
+ declare function shouldRetry(state: TimedRetryState, policy: AnyRetryPolicy, nowMs: number): boolean;
159
336
  /**
160
337
  * The delay to wait before the next attempt, given the current state. PURE.
161
338
  *
@@ -163,6 +340,6 @@ declare function shouldRetry(state: RetryState, policy: RetryPolicy): boolean;
163
340
  * attempt index, so the delay grows with each recorded failure. `rng` is
164
341
  * injected for determinism, same contract as `backoffDelay`.
165
342
  */
166
- declare function nextDelayMs(state: RetryState, policy: RetryPolicy, rng?: Rng): number;
343
+ declare function nextDelayMs(state: RetryState, policy: BackoffCurve, rng?: Rng): number;
167
344
 
168
- export { type Jitter, type RetryPolicy, type RetryState, type Rng, asRng, backoffDelay, defaultRetryPolicy, defaultRng, initRetry, nextDelayMs, recordFailure, shouldRetry };
345
+ export { type AnyRetryPolicy, type BackoffCurve, type CountBound, type DurationBound, type DurationRetryPolicy, type Jitter, type RetryBudget, type RetryPolicy, type RetryState, type Rng, type TimedRetryState, type Unbounded, type UnboundedRetryPolicy, asRng, backoffDelay, defaultRetryPolicy, defaultRng, initRetry, nextDelayMs, recordFailure, retryElapsedMs, shouldRetry };
@@ -1,3 +1,3 @@
1
- export { asRng, backoffDelay, defaultRetryPolicy, defaultRng, initRetry, nextDelayMs, recordFailure, shouldRetry } from '../chunk-5VNWZADP.js';
1
+ export { asRng, backoffDelay, defaultRetryPolicy, defaultRng, initRetry, nextDelayMs, recordFailure, retryElapsedMs, shouldRetry } from '../chunk-Q2UM7BXU.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,4 @@
1
- import { defaultRng, initRetry, recordFailure, shouldRetry, nextDelayMs } from '../chunk-5VNWZADP.js';
1
+ import { defaultRng, initRetry, recordFailure, shouldRetry, nextDelayMs } from '../chunk-Q2UM7BXU.js';
2
2
 
3
3
  // src/retry-to-success/index.ts
4
4
  var RetryExhaustedError = class extends Error {
@@ -0,0 +1,337 @@
1
+ import { P as Port, C as Cmd, S as Sub, M as Machine, T as Transitions, R as Reducer, I as InterpretDetached, b as PortEmitter } from './core-42kapW0n.js';
2
+
3
+ /**
4
+ * @demlik/tea runtime interface surface + pure helpers — the public types,
5
+ * interfaces, and construction/composition helpers the runtime (`./run`)
6
+ * implements against: `Store`, the error-sink and supervision contracts, the
7
+ * `RuntimeRef` / `BootingRuntime` / `Runtime` handle hierarchy, `definePort`, the
8
+ * identity-typed constructors `defineMachine` / `asReducer`, and the pure tools
9
+ * `replay` (compose without running) and `tryInterpret` (Railway).
10
+ */
11
+
12
+ declare const ReducerBrand: unique symbol;
13
+ type Branded<T> = T & {
14
+ readonly [ReducerBrand]: true;
15
+ };
16
+ /**
17
+ * Compile-time exhaustiveness assertion for default branches of switches over
18
+ * discriminated unions narrowed by hand (Sub handlers, message-bridge
19
+ * dispatchers, port fanouts) — TS narrows the operand to `never` only if every
20
+ * variant is covered, so adding one produces a compile error at the `absurd(x)`
21
+ * site. Strengthens invariant 7 (identity is explicit).
22
+ *
23
+ * @example
24
+ * switch (msg.type) {
25
+ * case "a": return ...;
26
+ * case "b": return ...;
27
+ * default: return absurd(msg); // compile error if a "c" is added
28
+ * }
29
+ */
30
+ declare function absurd(x: never): never;
31
+ declare const noop: <S, M, C extends Cmd>(state: S, _msg: M) => readonly [S, readonly C[]];
32
+ /**
33
+ * Thrown by `definePort` when a name has already been registered in the current
34
+ * process. Symmetric with `SubIdCollisionError` (thrown by `reconcileSubs`) —
35
+ * both mechanize invariant 7 (identity is explicit) at the runtime layer the
36
+ * type system cannot reach (string names compared at runtime).
37
+ */
38
+ declare class PortNameCollisionError extends Error {
39
+ readonly name = "PortNameCollisionError";
40
+ readonly _tag: "PortNameCollisionError";
41
+ constructor(portName: string);
42
+ }
43
+ /**
44
+ * Thrown by `reconcileSubs` when, within ONE desired subscription set, two subs
45
+ * share an `id` but declare different `type`s — a silent bug class the type
46
+ * system cannot reach (ids are strings compared at runtime). The symmetric twin
47
+ * of `PortNameCollisionError`: both mechanize invariant 7 (identity is explicit)
48
+ * at the runtime layer. Same id across transitions is the no-churn case and does
49
+ * NOT throw; only a within-set type conflict does.
50
+ */
51
+ declare class SubIdCollisionError extends Error {
52
+ readonly name = "SubIdCollisionError";
53
+ readonly _tag: "SubIdCollisionError";
54
+ constructor(id: string, declaredType: string, conflictingType: string);
55
+ }
56
+ /**
57
+ * Test-only escape hatch — clears the module-level port-name registry so the
58
+ * collision assert in `definePort` doesn't false-positive across vitest's
59
+ * non-isolated runs. Production code MUST NOT call this.
60
+ *
61
+ * @internal test-only
62
+ */
63
+ declare function __resetPortRegistry(): void;
64
+ /**
65
+ * Which otherwise-unattributable runtime path produced an error. Every fanout is
66
+ * throw-isolated so one bad consumer never strands its siblings, and the
67
+ * isolated throw routes to the `OnError` sink rather than a bare `console.error`.
68
+ *
69
+ * - `"follow-up"` — a follow-up Msg an interpret handler returned rejected when
70
+ * re-dispatched (the original dispatcher already resolved, so no caller).
71
+ * - `"stop-save"` — the final `store.save(state)` inside `stop()` threw
72
+ * (`stop()` resolves regardless, so without the sink this was silent loss).
73
+ * - `"reduce"` — the pure `update` (reducer) threw synchronously; the configured
74
+ * `Supervision` strategy decides what happens next, but the throw is surfaced
75
+ * here as data first.
76
+ * - `"listener"` — a `runtime.subscribe(...)` listener threw during fanout.
77
+ * - `"observer"` — a `runtime.observe(...)` observer threw during fanout.
78
+ * - `"event"` — the `events` projector or an `on(type, ...)` handler threw.
79
+ * - `"boot"` — an `onBoot` handler threw (boot fanout, or a late registration).
80
+ * - `"port-emit"` — a `subscribePort` listener threw during a port emission.
81
+ * - `"sub-cleanup"` — a subscription's cleanup threw (reconcile-removal or
82
+ * `stop()` teardown).
83
+ */
84
+ type RuntimeErrorPhase = "follow-up" | "stop-save" | "reduce" | "listener" | "observer" | "event" | "boot" | "port-emit" | "sub-cleanup";
85
+ /** Context handed to an `OnError` sink alongside the error itself. */
86
+ interface RuntimeErrorContext {
87
+ readonly phase: RuntimeErrorPhase;
88
+ }
89
+ /**
90
+ * Sink for runtime failures that have no caller to reject at. Configured via
91
+ * `run(machine, { onError })`. Should be total — a throw inside the sink is
92
+ * itself surfaced via the default sink, so it can never re-introduce a silent
93
+ * failure.
94
+ */
95
+ type OnError = (error: unknown, context: RuntimeErrorContext) => void;
96
+ /** The three declared reducer-throw supervision strategies. */
97
+ type SupervisionStrategy = "stop" | "escalate" | "restart";
98
+ /**
99
+ * Declared supervision policy for a reducer (`update`) throw, at
100
+ * `run(machine, { supervision })`. The bare-string shorthands (`"stop"` /
101
+ * `"escalate"`) are accepted for the two strategies that need no host data;
102
+ * `restart` MUST be the object form because it carries the `rehydrate` callback,
103
+ * which MUST return a valid `S` synchronously (the reducer is synchronous, so
104
+ * recovery is too — no suspension across the single-writer slot).
105
+ */
106
+ type Supervision<S, M extends {
107
+ type: string;
108
+ }> = "stop" | "escalate" | {
109
+ readonly strategy: "stop";
110
+ } | {
111
+ readonly strategy: "escalate";
112
+ } | {
113
+ readonly strategy: "restart";
114
+ /**
115
+ * Host-provided rehydration to last-known-good state. Invoked when the
116
+ * reducer throws; its return value becomes the new state and the transition
117
+ * continues from there. Receives the pre-throw `state`, the `msg` that
118
+ * triggered the throw, and the thrown `error` so the host can route by
119
+ * cause.
120
+ */
121
+ readonly rehydrate: (state: S, msg: M, error: unknown) => S;
122
+ };
123
+ /**
124
+ * Raised by `idle()` when the quiescence wait hits its iteration cap without the
125
+ * dispatch tail stabilizing — `idle()` REJECTS rather than silently resolving,
126
+ * so a livelocking machine surfaces instead of masquerading as quiescent.
127
+ * Mechanizes invariant 6 at the runtime layer, the role `PortNameCollisionError`
128
+ * plays for invariant 7.
129
+ */
130
+ declare class QuiescenceTimeoutError extends Error {
131
+ readonly iterations: number;
132
+ readonly name = "QuiescenceTimeoutError";
133
+ readonly _tag: "QuiescenceTimeoutError";
134
+ constructor(iterations: number);
135
+ }
136
+ /**
137
+ * Define a typed port. `name` is metadata (devtools / debug logs); port identity
138
+ * is by reference. Each definePort call must use a unique `name` — a second call
139
+ * with the same name throws `PortNameCollisionError`. Use ports for "data leaving
140
+ * the runtime selectively" (cursor announcements, DOM-mutation broadcasts,
141
+ * telemetry) that should NOT be folded into State. Strengthens invariant 7.
142
+ */
143
+ declare function definePort<T>(name: string): Port<T>;
144
+ interface Store<S> {
145
+ load(): Promise<unknown>;
146
+ save(state: S): Promise<void>;
147
+ migrate(raw: unknown): S | null;
148
+ }
149
+ interface Schema<S> {
150
+ safeParse(raw: unknown): {
151
+ success: true;
152
+ data: S;
153
+ } | {
154
+ success: false;
155
+ };
156
+ }
157
+ /**
158
+ * Build a `Store.migrate` from a schema (job 1) and an optional thin `upcast`
159
+ * (job 2). `upcast` maps a recognized-but-OLD raw shape forward into the shape
160
+ * the schema validates; it defaults to identity (no version migration yet).
161
+ *
162
+ * Never throws — a shape the schema rejects returns `null`, the substrate's
163
+ * fresh-boot path, per the `Store.migrate` contract. An `upcast` that itself
164
+ * throws on a corrupt blob is caught and collapses to `null` (same posture).
165
+ */
166
+ declare function schemaMigrate<S>(schema: Schema<S>, upcast?: (raw: unknown) => unknown): (raw: unknown) => S | null;
167
+ type DispatchSettle = "quiescent" | "once";
168
+ interface RuntimeRef<M extends {
169
+ type: string;
170
+ }> {
171
+ /**
172
+ * Put a Msg in the runtime's inbox and resolve once processed. Runs to
173
+ * QUIESCENCE by default: settles only after the dispatched Msg AND every
174
+ * transitive interpret follow-up has drained. Pass `{ settle: "once" }` (or
175
+ * call `dispatchOnce`) for the single-step case.
176
+ *
177
+ * Rejection ordering: the ONE dispatched transition's own failure surfaces
178
+ * first, here. If it succeeds but the follow-up chain never stabilizes, the
179
+ * quiescent drain rejects with `QuiescenceTimeoutError`. Follow-up Msg
180
+ * rejections themselves route to the `onError` sink, never here.
181
+ */
182
+ dispatch(msg: M, opts?: {
183
+ readonly settle?: DispatchSettle;
184
+ }): Promise<void>;
185
+ /**
186
+ * Dispatch `msg` and resolve after exactly ONE transition's effects settle,
187
+ * WITHOUT draining the follow-up chain — equivalent to
188
+ * `dispatch(msg, { settle: "once" })`. Prefer plain `dispatch` unless you will
189
+ * `await runtime.idle()` yourself; the un-awaited follow-up chain is the
190
+ * footgun the quiescent default removed.
191
+ */
192
+ dispatchOnce(msg: M): Promise<void>;
193
+ }
194
+ interface BootingRuntime<S, M extends {
195
+ type: string;
196
+ }, E extends {
197
+ type: string;
198
+ } = never> extends RuntimeRef<M> {
199
+ dispatch(msg: M, opts?: {
200
+ readonly settle?: DispatchSettle;
201
+ }): Promise<void>;
202
+ dispatchOnce(msg: M): Promise<void>;
203
+ subscribe(listener: () => void): () => void;
204
+ observe(observer: (msg: M, state: S) => void): () => void;
205
+ /**
206
+ * Subscribe to the INITIAL State — the boot transition. Fires exactly once:
207
+ * immediately if boot already completed (so a late subscriber never misses
208
+ * it), otherwise on the boot fanout. Returns a cleanup (a no-op once fired).
209
+ */
210
+ onBoot(handler: (state: S) => void): () => void;
211
+ /**
212
+ * Subscribe to a SEMANTIC event of `type`. The handler receives exactly the
213
+ * `E` member whose `type` matches `K`, so a consumer never touches another
214
+ * event's shape and never references the machine's PRIVATE Msg names. Multiple
215
+ * handlers per type; fanout is synchronous and throw-isolated. Fires only when
216
+ * the machine's `events` projector (wired on `run`) emits that type.
217
+ */
218
+ on<K extends E["type"]>(type: K, handler: (event: Extract<E, {
219
+ type: K;
220
+ }>) => void): () => void;
221
+ /**
222
+ * Subscribe to a typed Port. Multiple listeners per port; fanout is synchronous
223
+ * and isolated. Ports are for "data leaving the runtime selectively" (see
224
+ * `definePort`) — distinct from `observe` (every transition) and State.
225
+ */
226
+ subscribePort<T>(port: Port<T>, listener: (value: T) => void): () => void;
227
+ /**
228
+ * Emit a value on a Port from OUTSIDE an interpret handler — same synchronous
229
+ * fanout as `ctx.emit`. The canonical use is `observe`-driven emission (one
230
+ * runtime publishing a state-derived signal another subscribes to via
231
+ * `subscribePort`) without spawning a "tell the outside world" Cmd per
232
+ * transition.
233
+ */
234
+ emitPort<T>(port: Port<T>, value: T): void;
235
+ /**
236
+ * Resolves to the booted `Runtime<S, M>` after boot completes (or rejects with
237
+ * the boot error). This is the ONLY way to obtain a `Runtime` — and a
238
+ * `Runtime` is the only handle whose `getState()` is total. Awaiting `ready` is
239
+ * the single gate between "boot in flight" and "State exists":
240
+ *
241
+ * ```ts
242
+ * const runtime = await run(machine, opts).ready;
243
+ * runtime.getState(); // total — never throws-before-boot
244
+ * ```
245
+ *
246
+ * Idempotent — the same settled promise, resolving to the same `Runtime`, comes
247
+ * back on every read. Expresses canon §2.3 (boot is a named, awaitable moment).
248
+ */
249
+ ready: Promise<Runtime<S, M, E>>;
250
+ stop(): Promise<void>;
251
+ }
252
+ interface Runtime<S, M extends {
253
+ type: string;
254
+ }, E extends {
255
+ type: string;
256
+ } = never> extends BootingRuntime<S, M, E> {
257
+ /**
258
+ * The current State. TOTAL — never throws. Obtaining a `Runtime` requires
259
+ * awaiting `ready`, which only resolves AFTER boot has run `init` and set the
260
+ * initial State.
261
+ */
262
+ getState(): S;
263
+ /** Resolves to this same `Runtime` once boot completes. Idempotent. */
264
+ ready: Promise<Runtime<S, M, E>>;
265
+ /**
266
+ * Resolves once the runtime has reached QUIESCENCE — every dispatched Msg AND
267
+ * every transitive interpret follow-up has been processed, with no further step
268
+ * pending on the tail. Since plain `dispatch` already runs to quiescence, this
269
+ * is mainly for follow-ups left by a `dispatchOnce` single step, or a chain
270
+ * kicked off by a Sub / boot `init` cmds with no `dispatch` to await.
271
+ *
272
+ * Idempotent and re-entrant-safe. Tail rejections are NOT surfaced here (a
273
+ * failing dispatch surfaces on its OWN promise; follow-up rejections route to
274
+ * `onError`). The one rejection `idle()` produces is `QuiescenceTimeoutError`
275
+ * on hitting the iteration cap — so a livelock stays distinguishable from a
276
+ * genuine quiesce (invariant 6). A poll is still correct when waiting on an
277
+ * EXTERNAL event the runtime cannot enqueue itself.
278
+ */
279
+ idle(): Promise<void>;
280
+ /**
281
+ * The terminal State of the run, or `undefined` while in flight — "terminal"
282
+ * per the `terminal` predicate passed to `run()` (no predicate → never
283
+ * terminal → always `undefined`). The first-class result read: the run's
284
+ * product off the State the machine already owns, NOT scraped off the `observe`
285
+ * firehose by matching an internal Msg name. Total — never throws.
286
+ */
287
+ result(): S | undefined;
288
+ /**
289
+ * Resolves with the terminal State the first time the run reaches one (per the
290
+ * `terminal` predicate). If ALREADY terminal, resolves immediately; otherwise
291
+ * on the transition that first makes `terminal` hold. The awaitable companion
292
+ * to `result()`. With no predicate this never resolves. Idempotent and
293
+ * multi-caller safe.
294
+ */
295
+ done(): Promise<S>;
296
+ }
297
+ declare function defineMachine<S, M extends {
298
+ type: string;
299
+ }, C extends Cmd, U extends Sub, Ctx>(m: Omit<Machine<S, M, C, U, Ctx>, "update"> & {
300
+ update: [S] extends [{
301
+ type: string;
302
+ }] ? Transitions<S, M, C> : never;
303
+ }): Machine<S, M, C, U, Ctx>;
304
+ declare function defineMachine<S, M extends {
305
+ type: string;
306
+ }, C extends Cmd, U extends Sub, Ctx>(m: Omit<Machine<S, M, C, U, Ctx>, "update"> & {
307
+ update: Reducer<S, M, C>;
308
+ }): Machine<S, M, C, U, Ctx>;
309
+ declare function asReducer<S, M extends {
310
+ type: string;
311
+ }, C extends Cmd>(reducer: Reducer<S, M, C>): Branded<Reducer<S, M, C>>;
312
+ type CtxArg<Ctx> = [Record<never, never>] extends [Ctx] ? {
313
+ ctx?: Ctx;
314
+ } : {
315
+ ctx: Ctx;
316
+ };
317
+ declare function replay<S, M extends {
318
+ type: string;
319
+ }, C extends Cmd, U extends Sub, Ctx>(machine: Machine<S, M, C, U, Ctx>, opts: {
320
+ msgs: readonly M[];
321
+ ctx: Ctx;
322
+ loaded?: S | null;
323
+ }): {
324
+ state: S;
325
+ cmds: C[];
326
+ subs: U[];
327
+ depSubs: {
328
+ index: number;
329
+ id: string;
330
+ }[];
331
+ };
332
+ declare function wrapDetached<C extends Cmd, M extends {
333
+ type: string;
334
+ }, Allowed extends M, Ctx>(handler: InterpretDetached<C, Allowed, Ctx>): (cmd: C, ctx: Ctx & PortEmitter, dispatch?: (msg: M) => void) => Promise<void>;
335
+ 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>;
336
+
337
+ export { type BootingRuntime as B, type CtxArg as C, type DispatchSettle as D, type OnError as O, PortNameCollisionError as P, QuiescenceTimeoutError as Q, type Runtime as R, type Store as S, __resetPortRegistry as _, type Supervision as a, type Branded as b, absurd as c, SubIdCollisionError as d, type RuntimeErrorPhase as e, type RuntimeErrorContext as f, type SupervisionStrategy as g, definePort as h, type Schema as i, type RuntimeRef as j, defineMachine as k, asReducer as l, noop as n, replay as r, schemaMigrate as s, tryInterpret as t, wrapDetached as w };
@@ -1,8 +1,9 @@
1
- import { C as Cmd } from '../core-BmlJOvxM.js';
1
+ import { C as Cmd } from '../core-42kapW0n.js';
2
2
  import { QueueItem } from '../work-queue/index.js';
3
- import '../index.js';
3
+ import '../runtime-types-gsog27b6.js';
4
4
 
5
5
  /**
6
+ * @packageDocumentation
6
7
  * @demlik/tea/saga — a forward-then-compensate transaction over an ordered list
7
8
  * of reversible steps.
8
9
  *
@@ -1,5 +1,5 @@
1
- import { queueAdapter } from '../chunk-HJO6VNQI.js';
2
- import '../chunk-TGG26XMK.js';
1
+ import { queueAdapter } from '../chunk-MU6MCIAC.js';
2
+ import '../chunk-FV3Z5MBE.js';
3
3
 
4
4
  // src/saga/index.ts
5
5
  function isCommitted(state) {