@evolu/common 8.0.0-next.4 → 8.0.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 (237) hide show
  1. package/dist/src/Array.d.ts +279 -173
  2. package/dist/src/Array.d.ts.map +1 -1
  3. package/dist/src/Array.js +105 -101
  4. package/dist/src/Assert.d.ts +44 -44
  5. package/dist/src/Assert.d.ts.map +1 -1
  6. package/dist/src/Assert.js +44 -32
  7. package/dist/src/BigInt.d.ts +6 -3
  8. package/dist/src/BigInt.d.ts.map +1 -1
  9. package/dist/src/BigInt.js +5 -2
  10. package/dist/src/Brand.d.ts +27 -40
  11. package/dist/src/Brand.d.ts.map +1 -1
  12. package/dist/src/Buffer.d.ts +27 -11
  13. package/dist/src/Buffer.d.ts.map +1 -1
  14. package/dist/src/Cache.d.ts +9 -4
  15. package/dist/src/Cache.d.ts.map +1 -1
  16. package/dist/src/Cache.js +8 -3
  17. package/dist/src/Callbacks.d.ts +30 -19
  18. package/dist/src/Callbacks.d.ts.map +1 -1
  19. package/dist/src/Console.d.ts +145 -66
  20. package/dist/src/Console.d.ts.map +1 -1
  21. package/dist/src/Console.js +91 -43
  22. package/dist/src/Crypto.d.ts +85 -42
  23. package/dist/src/Crypto.d.ts.map +1 -1
  24. package/dist/src/Crypto.js +44 -20
  25. package/dist/src/Eq.d.ts +87 -21
  26. package/dist/src/Eq.d.ts.map +1 -1
  27. package/dist/src/Eq.js +67 -19
  28. package/dist/src/Error.d.ts +5 -3
  29. package/dist/src/Error.d.ts.map +1 -1
  30. package/dist/src/Error.js +16 -0
  31. package/dist/src/Function.d.ts +119 -58
  32. package/dist/src/Function.d.ts.map +1 -1
  33. package/dist/src/Function.js +86 -43
  34. package/dist/src/Http.d.ts +394 -0
  35. package/dist/src/Http.d.ts.map +1 -0
  36. package/dist/src/Http.js +102 -0
  37. package/dist/src/Identicon.d.ts +16 -7
  38. package/dist/src/Identicon.d.ts.map +1 -1
  39. package/dist/src/Identicon.js +14 -5
  40. package/dist/src/LeakDetector.d.ts +88 -0
  41. package/dist/src/LeakDetector.d.ts.map +1 -0
  42. package/dist/src/LeakDetector.js +86 -0
  43. package/dist/src/LockManager.d.ts +18 -9
  44. package/dist/src/LockManager.d.ts.map +1 -1
  45. package/dist/src/LockManager.js +7 -8
  46. package/dist/src/Lookup.d.ts +43 -13
  47. package/dist/src/Lookup.d.ts.map +1 -1
  48. package/dist/src/Lookup.js +28 -8
  49. package/dist/src/Microtask.d.ts +1 -1
  50. package/dist/src/Microtask.d.ts.map +1 -1
  51. package/dist/src/Microtask.js +3 -7
  52. package/dist/src/Number.d.ts +49 -13
  53. package/dist/src/Number.d.ts.map +1 -1
  54. package/dist/src/Number.js +34 -5
  55. package/dist/src/Object.d.ts +160 -40
  56. package/dist/src/Object.d.ts.map +1 -1
  57. package/dist/src/Object.js +92 -46
  58. package/dist/src/Option.d.ts +10 -7
  59. package/dist/src/Option.d.ts.map +1 -1
  60. package/dist/src/Option.js +9 -6
  61. package/dist/src/Order.d.ts +21 -21
  62. package/dist/src/Order.d.ts.map +1 -1
  63. package/dist/src/Order.js +18 -21
  64. package/dist/src/Platform.d.ts +31 -0
  65. package/dist/src/Platform.d.ts.map +1 -1
  66. package/dist/src/Platform.js +98 -0
  67. package/dist/src/Random.d.ts +32 -17
  68. package/dist/src/Random.d.ts.map +1 -1
  69. package/dist/src/Random.js +12 -8
  70. package/dist/src/Redacted.d.ts +43 -29
  71. package/dist/src/Redacted.d.ts.map +1 -1
  72. package/dist/src/Redacted.js +14 -3
  73. package/dist/src/Ref.d.ts +11 -3
  74. package/dist/src/Ref.d.ts.map +1 -1
  75. package/dist/src/Ref.js +2 -2
  76. package/dist/src/RefCount.d.ts +2 -6
  77. package/dist/src/RefCount.d.ts.map +1 -1
  78. package/dist/src/RefCount.js +33 -90
  79. package/dist/src/Relation.d.ts +70 -5
  80. package/dist/src/Relation.d.ts.map +1 -1
  81. package/dist/src/Relation.js +77 -2
  82. package/dist/src/Resource.d.ts +590 -155
  83. package/dist/src/Resource.d.ts.map +1 -1
  84. package/dist/src/Resource.js +592 -343
  85. package/dist/src/Result.d.ts +838 -191
  86. package/dist/src/Result.d.ts.map +1 -1
  87. package/dist/src/Result.js +163 -102
  88. package/dist/src/Schedule.d.ts +684 -231
  89. package/dist/src/Schedule.d.ts.map +1 -1
  90. package/dist/src/Schedule.js +766 -295
  91. package/dist/src/Set.d.ts +89 -40
  92. package/dist/src/Set.d.ts.map +1 -1
  93. package/dist/src/Set.js +50 -29
  94. package/dist/src/Sqlite.d.ts +29 -72
  95. package/dist/src/Sqlite.d.ts.map +1 -1
  96. package/dist/src/Sqlite.js +41 -46
  97. package/dist/src/StackTrace.d.ts +40 -0
  98. package/dist/src/StackTrace.d.ts.map +1 -0
  99. package/dist/src/StackTrace.js +140 -0
  100. package/dist/src/Store.d.ts +5 -4
  101. package/dist/src/Store.d.ts.map +1 -1
  102. package/dist/src/Store.js +5 -4
  103. package/dist/src/String.d.ts +7 -1
  104. package/dist/src/String.d.ts.map +1 -1
  105. package/dist/src/String.js +20 -7
  106. package/dist/src/Task.d.ts +3572 -1770
  107. package/dist/src/Task.d.ts.map +1 -1
  108. package/dist/src/Task.js +1879 -1274
  109. package/dist/src/Test.d.ts +17 -125
  110. package/dist/src/Test.d.ts.map +1 -1
  111. package/dist/src/Test.js +17 -68
  112. package/dist/src/Time.d.ts +108 -49
  113. package/dist/src/Time.d.ts.map +1 -1
  114. package/dist/src/Time.js +172 -73
  115. package/dist/src/Type.d.ts +3542 -1926
  116. package/dist/src/Type.d.ts.map +1 -1
  117. package/dist/src/Type.js +3070 -1784
  118. package/dist/src/Types.d.ts +200 -108
  119. package/dist/src/Types.d.ts.map +1 -1
  120. package/dist/src/Types.js +39 -8
  121. package/dist/src/WebSocket.d.ts +48 -23
  122. package/dist/src/WebSocket.d.ts.map +1 -1
  123. package/dist/src/WebSocket.js +15 -6
  124. package/dist/src/Worker.d.ts +94 -36
  125. package/dist/src/Worker.d.ts.map +1 -1
  126. package/dist/src/Worker.js +58 -24
  127. package/dist/src/index.d.ts +55 -52
  128. package/dist/src/index.d.ts.map +1 -1
  129. package/dist/src/index.js +3 -1
  130. package/dist/src/intl/cs.d.ts +94 -0
  131. package/dist/src/intl/cs.d.ts.map +1 -0
  132. package/dist/src/intl/cs.js +220 -0
  133. package/dist/src/intl/index.d.ts +7 -0
  134. package/dist/src/intl/index.d.ts.map +1 -0
  135. package/dist/src/intl/index.js +6 -0
  136. package/dist/src/local-first/Db.d.ts +12 -8
  137. package/dist/src/local-first/Db.d.ts.map +1 -1
  138. package/dist/src/local-first/Db.js +109 -132
  139. package/dist/src/local-first/Error.d.ts +4 -4
  140. package/dist/src/local-first/Evolu.d.ts +338 -92
  141. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  142. package/dist/src/local-first/Evolu.js +47 -55
  143. package/dist/src/local-first/LocalAuth.d.ts +4 -4
  144. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  145. package/dist/src/local-first/Owner.d.ts +61 -24
  146. package/dist/src/local-first/Owner.d.ts.map +1 -1
  147. package/dist/src/local-first/Owner.js +45 -8
  148. package/dist/src/local-first/Protocol.d.ts +44 -31
  149. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  150. package/dist/src/local-first/Protocol.js +36 -13
  151. package/dist/src/local-first/Query.d.ts +147 -62
  152. package/dist/src/local-first/Query.d.ts.map +1 -1
  153. package/dist/src/local-first/Query.js +112 -58
  154. package/dist/src/local-first/Relay.d.ts +44 -14
  155. package/dist/src/local-first/Relay.d.ts.map +1 -1
  156. package/dist/src/local-first/Schema.d.ts +92 -41
  157. package/dist/src/local-first/Schema.d.ts.map +1 -1
  158. package/dist/src/local-first/Schema.js +25 -8
  159. package/dist/src/local-first/Shared.d.ts +23 -21
  160. package/dist/src/local-first/Shared.d.ts.map +1 -1
  161. package/dist/src/local-first/Shared.js +100 -57
  162. package/dist/src/local-first/Storage.d.ts +53 -32
  163. package/dist/src/local-first/Storage.d.ts.map +1 -1
  164. package/dist/src/local-first/Storage.js +194 -234
  165. package/dist/src/local-first/Timestamp.d.ts +21 -21
  166. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  167. package/dist/src/local-first/Timestamp.js +2 -2
  168. package/dist/src/local-first/index.d.ts +11 -11
  169. package/package.json +14 -13
  170. package/src/Array.ts +306 -205
  171. package/src/Assert.ts +54 -69
  172. package/src/BigInt.ts +6 -3
  173. package/src/Brand.ts +27 -40
  174. package/src/Buffer.ts +28 -12
  175. package/src/Cache.ts +9 -4
  176. package/src/Callbacks.ts +30 -19
  177. package/src/Console.ts +156 -84
  178. package/src/Crypto.ts +83 -41
  179. package/src/Eq.ts +95 -22
  180. package/src/Error.ts +8 -4
  181. package/src/Function.ts +167 -59
  182. package/src/Http.ts +549 -0
  183. package/src/Identicon.ts +16 -7
  184. package/src/LeakDetector.ts +182 -0
  185. package/src/LockManager.ts +43 -39
  186. package/src/Lookup.ts +62 -28
  187. package/src/Microtask.ts +19 -23
  188. package/src/Number.ts +64 -12
  189. package/src/Object.ts +173 -49
  190. package/src/Option.ts +10 -7
  191. package/src/Order.ts +23 -21
  192. package/src/Platform.ts +136 -0
  193. package/src/Polyfills.ts +6 -12
  194. package/src/Random.ts +32 -19
  195. package/src/Redacted.ts +44 -30
  196. package/src/Ref.ts +14 -6
  197. package/src/RefCount.ts +38 -80
  198. package/src/Relation.ts +202 -10
  199. package/src/Resource.ts +1199 -591
  200. package/src/Result.ts +930 -271
  201. package/src/Schedule.ts +859 -343
  202. package/src/Set.ts +89 -40
  203. package/src/Sqlite.ts +144 -138
  204. package/src/StackTrace.ts +230 -0
  205. package/src/Store.ts +8 -7
  206. package/src/String.ts +22 -5
  207. package/src/Task.ts +5101 -2832
  208. package/src/Test.ts +20 -168
  209. package/src/Time.ts +293 -90
  210. package/src/Type.ts +10034 -4117
  211. package/src/Types.ts +227 -110
  212. package/src/WebSocket.ts +64 -36
  213. package/src/Worker.ts +180 -72
  214. package/src/index.ts +55 -52
  215. package/src/intl/cs.ts +388 -0
  216. package/src/intl/index.ts +7 -0
  217. package/src/local-first/Db.ts +152 -179
  218. package/src/local-first/Error.ts +4 -4
  219. package/src/local-first/Evolu.ts +495 -250
  220. package/src/local-first/LocalAuth.ts +6 -12
  221. package/src/local-first/Owner.ts +58 -21
  222. package/src/local-first/Protocol.ts +49 -31
  223. package/src/local-first/Query.ts +146 -61
  224. package/src/local-first/Relay.ts +51 -21
  225. package/src/local-first/Schema.ts +109 -51
  226. package/src/local-first/Shared.ts +266 -226
  227. package/src/local-first/Storage.ts +266 -264
  228. package/src/local-first/Timestamp.ts +25 -18
  229. package/src/local-first/index.ts +11 -11
  230. package/dist/src/Skiplist.d.ts +0 -28
  231. package/dist/src/Skiplist.d.ts.map +0 -1
  232. package/dist/src/Skiplist.js +0 -64
  233. package/dist/src/Tracer.d.ts +0 -48
  234. package/dist/src/Tracer.d.ts.map +0 -1
  235. package/dist/src/Tracer.js +0 -6
  236. package/src/Skiplist.ts +0 -108
  237. package/src/Tracer.ts +0 -54
@@ -3,11 +3,12 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import type { RandomDep } from "./Random.js";
7
- import { type NextResult } from "./Result.js";
8
- import { type Duration, Millis, type TimeDep } from "./Time.js";
9
- import { PositiveInt } from "./Type.js";
10
- import type { Predicate } from "./Types.js";
6
+ import { type Percentage } from "./Number.ts";
7
+ import type { RandomDep } from "./Random.ts";
8
+ import { type NextResult } from "./Result.ts";
9
+ import { type Duration, type DurationLiteral, Millis, PositiveMillis, type TimeDep } from "./Time.ts";
10
+ import { type Int0To100OrNonNegativeInt, PositiveInt } from "./Type.ts";
11
+ import type { Predicate } from "./Types.ts";
11
12
  /**
12
13
  * Composable scheduling strategies for {@link retry}, {@link repeat}, rate
13
14
  * limiting, and more.
@@ -18,30 +19,58 @@ import type { Predicate } from "./Types.js";
18
19
  * to stop. Multiple calls to `schedule(deps)` create independent state
19
20
  * instances.
20
21
  *
21
- * ### Example
22
+ * `Err(Done<void>)` is terminal. After a step returns it, every subsequent call
23
+ * to that step must also return `Err(Done<void>)`.
24
+ *
25
+ * With {@link retry} and {@link repeat}, the initial Task execution happens
26
+ * before the first schedule step. Schedule outputs therefore describe
27
+ * recurrences, not the initial execution. Time-based schedules establish their
28
+ * time origin on the first step call, not when `schedule(deps)` creates the
29
+ * step.
30
+ *
31
+ * ### Composing a retry policy
22
32
  *
23
33
  * ```ts
24
34
  * import {
35
+ * err,
25
36
  * exponential,
26
37
  * jitter,
27
38
  * maxDelay,
39
+ * ok,
28
40
  * retry,
29
41
  * take,
42
+ * testCreateRun,
43
+ * type RandomNumber,
44
+ * type Task,
30
45
  * } from "@evolu/common";
31
46
  *
47
+ * let attempts = 0;
48
+ * const fetchData: Task<string, { readonly type: "FetchError" }> = () => {
49
+ * attempts++;
50
+ * return attempts < 2 ? err({ type: "FetchError" }) : ok("data");
51
+ * };
52
+ *
32
53
  * const fetchWithRetry = retry(
33
54
  * fetchData,
34
55
  * // A jittered, capped, limited exponential backoff.
35
- * jitter(1)(maxDelay("20s")(take(2)(exponential("100ms")))),
56
+ * jitter("100%")(maxDelay("20s")(take(2)(exponential("100ms")))),
36
57
  * );
58
+ *
59
+ * await using run = testCreateRun({
60
+ * random: { next: () => 0 as RandomNumber },
61
+ * });
62
+ * expectOk(await run(fetchWithRetry), "data");
37
63
  * ```
38
64
  *
39
65
  * Or use a preset:
40
66
  *
41
67
  * ```ts
42
- * import { retryStrategyAws, retry } from "@evolu/common";
68
+ * import { ok, retry, retryStrategyAws, type Task } from "@evolu/common";
43
69
  *
70
+ * const fetchData: Task<string> = () => ok("data");
44
71
  * const fetchWithRetry = retry(fetchData, retryStrategyAws);
72
+ *
73
+ * expect(fetchWithRetry).toBeTypeOf("function");
45
74
  * ```
46
75
  */
47
76
  export type Schedule<out Output, in Input = unknown> = (deps: ScheduleDeps) => (input: Input) => NextResult<readonly [Output, Millis]>;
@@ -64,7 +93,7 @@ export interface ScheduleStep<Output> {
64
93
  readonly attempt: PositiveInt;
65
94
  /** Output from the {@link Schedule} step. */
66
95
  readonly output: Output;
67
- /** Delay before this step executes. */
96
+ /** Delay before the scheduled recurrence executes. */
68
97
  readonly delay: Millis;
69
98
  }
70
99
  /**
@@ -73,11 +102,15 @@ export interface ScheduleStep<Output> {
73
102
  * Outputs the attempt count (0, 1, 2, ...). Useful as a base for composition or
74
103
  * for immediate retry without backoff.
75
104
  *
76
- * ### Example
105
+ * ### Recurring immediately
77
106
  *
78
107
  * ```ts
79
- * // Retry immediately, up to 5 times
108
+ * import { forever, take, testCreateDeps } from "@evolu/common";
109
+ *
110
+ * // Retry immediately, up to 5 times.
80
111
  * const immediate = take(5)(forever);
112
+ * const step = immediate(testCreateDeps());
113
+ * expectOk(step(undefined), [0, 0]);
81
114
  * ```
82
115
  *
83
116
  * @group Constructors
@@ -86,13 +119,18 @@ export declare const forever: Schedule<number>;
86
119
  /**
87
120
  * A schedule that runs exactly once with no delay.
88
121
  *
89
- * Convenience for `take(1)(forever)`. Useful for one-shot operations.
122
+ * Equivalent to `take(1)(forever)`. Useful for one-shot operations.
90
123
  *
91
- * ### Example
124
+ * ### Scheduling one recurrence
92
125
  *
93
126
  * ```ts
94
- * // Execute once, no retry
127
+ * import { done, once, testCreateDeps } from "@evolu/common";
128
+ *
129
+ * // Produce one scheduled recurrence, then stop.
95
130
  * const oneShot = once;
131
+ * const step = oneShot(testCreateDeps());
132
+ * expectOk(step(undefined), [0, 0]);
133
+ * expectErr(step(undefined), done());
96
134
  * ```
97
135
  *
98
136
  * @group Constructors
@@ -104,33 +142,46 @@ export declare const once: Schedule<number>;
104
142
  * Convenience for `take(n)(forever)`. Outputs the current repetition count (0,
105
143
  * 1, 2, ..., n-1).
106
144
  *
107
- * ### Example
145
+ * `n` uses {@link Int0To100OrNonNegativeInt}: pass `0` to `100` as a literal, or
146
+ * a validated {@link NonNegativeInt} for larger or dynamic values.
147
+ *
148
+ * ### Limiting recurrence count
108
149
  *
109
150
  * ```ts
110
- * // Retry up to 3 times (4 total attempts including initial)
151
+ * import { done, recurs, testCreateDeps } from "@evolu/common";
152
+ *
153
+ * // Retry up to 3 times (4 total attempts including the initial operation).
111
154
  * const retry = recurs(3);
155
+ * const step = retry(testCreateDeps());
156
+ * expectOk(step(undefined), [0, 0]);
157
+ * step(undefined);
158
+ * step(undefined);
159
+ * expectErr(step(undefined), done());
112
160
  * ```
113
161
  *
114
162
  * @group Constructors
115
163
  */
116
- export declare const recurs: (n: number) => Schedule<number>;
164
+ export declare const recurs: (n: Int0To100OrNonNegativeInt) => Schedule<number>;
117
165
  /**
118
166
  * Constant delay schedule.
119
167
  *
120
168
  * Always waits the same duration after each execution completes. Never stops —
121
169
  * combine with {@link take} or {@link maxElapsed} to limit.
122
170
  *
123
- * ### Example
171
+ * ### Constant spacing
124
172
  *
125
173
  * ```ts
126
- * // 1s, 1s, 1s, ... (polling)
127
- * const poll = spaced("1s");
174
+ * import { spaced, take, testCreateDeps } from "@evolu/common";
128
175
  *
129
- * // Retry 3 times with 500ms between each
176
+ * // Poll every second, retry three times, or run a long-lived heartbeat.
177
+ * const poll = spaced("1s");
130
178
  * const retry = take(3)(spaced("500ms"));
131
- *
132
- * // Heartbeat schedule
133
179
  * const heartbeat = spaced("30s");
180
+ * const deps = testCreateDeps();
181
+ *
182
+ * expectOk(poll(deps)(undefined), [1000, 1000]);
183
+ * expectOk(retry(deps)(undefined), [500, 500]);
184
+ * expectOk(heartbeat(deps)(undefined), [30000, 30000]);
134
185
  * ```
135
186
  *
136
187
  * @group Constructors
@@ -139,23 +190,29 @@ export declare const spaced: (duration: Duration) => Schedule<Millis>;
139
190
  /**
140
191
  * Exponential backoff schedule.
141
192
  *
142
- * Computes delay as `base * factor^(attempt - 1)`:
193
+ * Computes delay as `base * factor^(step - 1)`:
143
194
  *
144
- * - Attempt 1: `base`
145
- * - Attempt 2: `base * factor`
146
- * - Attempt 3: `base * factor²`
195
+ * - Step 1: `base`
196
+ * - Step 2: `base * factor`
197
+ * - Step 3: `base * factor²`
147
198
  * - ...
148
199
  *
149
200
  * Never stops — combine with {@link take} or {@link maxElapsed} to limit.
150
201
  *
151
- * ### Example
202
+ * ### Exponential growth factors
152
203
  *
153
204
  * ```ts
154
- * // 100ms, 200ms, 400ms, 800ms, ...
155
- * const exp = exponential("100ms");
205
+ * import { exponential, testCreateDeps } from "@evolu/common";
156
206
  *
157
- * // 100ms, 150ms, 225ms, 338ms, ... (gentler growth)
207
+ * // Standard doubling and gentler 1.5× growth.
208
+ * const standard = exponential("100ms");
158
209
  * const gentle = exponential("100ms", 1.5);
210
+ * const standardStep = standard(testCreateDeps());
211
+ * const gentleStep = gentle(testCreateDeps());
212
+ * expectOk(standardStep(undefined), [100, 100]);
213
+ * expectOk(standardStep(undefined), [200, 200]);
214
+ * expectOk(gentleStep(undefined), [100, 100]);
215
+ * expectOk(gentleStep(undefined), [150, 150]);
159
216
  * ```
160
217
  *
161
218
  * @group Constructors
@@ -164,20 +221,24 @@ export declare const exponential: (base: Duration, factor?: number) => Schedule<
164
221
  /**
165
222
  * Linear backoff schedule.
166
223
  *
167
- * Delay increases linearly: `base * attempt`:
224
+ * Delay increases linearly: `base * step`:
168
225
  *
169
- * - Attempt 1: `base`
170
- * - Attempt 2: `base * 2`
171
- * - Attempt 3: `base * 3`
226
+ * - Step 1: `base`
227
+ * - Step 2: `base * 2`
228
+ * - Step 3: `base * 3`
172
229
  * - ...
173
230
  *
174
231
  * Never stops — combine with {@link take} or {@link maxElapsed} to limit.
175
232
  *
176
- * ### Example
233
+ * ### Linear growth
177
234
  *
178
235
  * ```ts
236
+ * import { linear, testCreateDeps } from "@evolu/common";
237
+ *
179
238
  * // 100ms, 200ms, 300ms, 400ms, ...
180
- * const lin = linear("100ms");
239
+ * const step = linear("100ms")(testCreateDeps());
240
+ * expectOk(step(undefined), [100, 100]);
241
+ * expectOk(step(undefined), [200, 200]);
181
242
  * ```
182
243
  *
183
244
  * @group Constructors
@@ -188,20 +249,25 @@ export declare const linear: (base: Duration) => Schedule<Millis>;
188
249
  *
189
250
  * Delays follow the Fibonacci sequence, growing more slowly than exponential:
190
251
  *
191
- * - Attempt 1: `initial`
192
- * - Attempt 2: `initial`
193
- * - Attempt 3: `initial * 2`
194
- * - Attempt 4: `initial * 3`
195
- * - Attempt 5: `initial * 5`
252
+ * - Step 1: `initial`
253
+ * - Step 2: `initial`
254
+ * - Step 3: `initial * 2`
255
+ * - Step 4: `initial * 3`
256
+ * - Step 5: `initial * 5`
196
257
  * - ...
197
258
  *
198
259
  * Never stops — combine with {@link take} or {@link maxElapsed} to limit.
199
260
  *
200
- * ### Example
261
+ * ### Fibonacci growth
201
262
  *
202
263
  * ```ts
203
- * // 100ms, 100ms, 200ms, 300ms, 500ms, 800ms, ...
204
- * const fib = fibonacci("100ms");
264
+ * import { fibonacci, testCreateDeps } from "@evolu/common";
265
+ *
266
+ * // 100ms, 100ms, 200ms, 300ms, 500ms, ...
267
+ * const step = fibonacci("100ms")(testCreateDeps());
268
+ * expectOk(step(undefined), [100, 100]);
269
+ * expectOk(step(undefined), [100, 100]);
270
+ * expectOk(step(undefined), [200, 200]);
205
271
  * ```
206
272
  *
207
273
  * @group Constructors
@@ -212,20 +278,27 @@ export declare const fibonacci: (initial: Duration) => Schedule<Millis>;
212
278
  *
213
279
  * Recurs on a fixed interval, outputting the repetition count (0, 1, 2, ...).
214
280
  * Unlike {@link spaced}, which waits a duration _after_ each execution, `fixed`
215
- * maintains a consistent cadence from when the schedule started.
281
+ * maintains a consistent cadence from the first schedule step.
216
282
  *
217
- * If execution takes longer than the interval, the next execution happens
218
- * immediately but subsequent runs still align to the original window
219
- * boundaries. This prevents "pile-up" while maintaining predictable timing.
283
+ * If execution falls behind by one or more intervals, missed recurrences happen
284
+ * immediately until the schedule catches up to the original cadence. Use
285
+ * {@link windowed} to skip missed recurrences instead.
220
286
  *
221
- * ### Example
287
+ * ### Maintaining a fixed cadence
222
288
  *
223
289
  * ```ts
224
- * // Health check every 5 seconds, aligned to windows
225
- * const healthCheck = take(10)(fixed("5s"));
290
+ * import { fixed, take, testCreateDeps } from "@evolu/common";
226
291
  *
227
- * // Cron-like behavior: run at consistent intervals
292
+ * // A bounded health check and an unbounded cron-like cadence.
293
+ * const healthCheck = take(10)(fixed("5s"));
228
294
  * const cronLike = fixed("1m");
295
+ *
296
+ * const healthDeps = testCreateDeps();
297
+ * const healthStep = healthCheck(healthDeps);
298
+ * expectOk(healthStep(undefined), [0, 5000]);
299
+ * healthDeps.time.advance("3s");
300
+ * expectOk(healthStep(undefined), [1, 2000]);
301
+ * expectOk(cronLike(testCreateDeps())(undefined), [0, 60000]);
229
302
  * ```
230
303
  *
231
304
  * @group Constructors
@@ -234,17 +307,27 @@ export declare const fixed: (interval: Duration) => Schedule<number>;
234
307
  /**
235
308
  * Divides the timeline into fixed windows and sleeps until the next boundary.
236
309
  *
237
- * Similar to {@link fixed}, but always sleeps until the next window boundary
238
- * regardless of when the last execution started. Outputs the repetition count.
310
+ * Similar to {@link fixed}, but skips missed recurrences and always sleeps until
311
+ * the next window boundary. Outputs the repetition count.
239
312
  *
240
- * Useful for aligning executions to regular intervals from the start time.
313
+ * Useful for aligning executions to regular intervals from the first step.
241
314
  *
242
- * ### Example
315
+ * ### Aligning to time windows
243
316
  *
244
317
  * ```ts
245
- * // Execute at regular 5-second boundaries from start
246
- * const aligned = windowed("5s");
247
- * // If elapsed is 3s, waits 2s. If elapsed is 7s, waits 3s.
318
+ * import { testCreateDeps, windowed } from "@evolu/common";
319
+ *
320
+ * const stepAfter = (elapsed: "3s" | "7s") => {
321
+ * const deps = testCreateDeps();
322
+ * const step = windowed("5s")(deps);
323
+ * step(undefined);
324
+ * deps.time.advance(elapsed);
325
+ * return step(undefined);
326
+ * };
327
+ *
328
+ * // At 3s the next boundary is 2s away; at 7s it is 3s away.
329
+ * expectOk(stepAfter("3s"), [1, 2000]);
330
+ * expectOk(stepAfter("7s"), [1, 3000]);
248
331
  * ```
249
332
  *
250
333
  * @group Constructors
@@ -255,11 +338,15 @@ export declare const windowed: (interval: Duration) => Schedule<number>;
255
338
  *
256
339
  * Convenience for `take(1)(spaced(delay))`. Useful for simple one-shot delays.
257
340
  *
258
- * ### Example
341
+ * ### Scheduling one delayed recurrence
259
342
  *
260
343
  * ```ts
261
- * // Wait 1 second then stop
262
- * const oneShot = fromDelay("1s");
344
+ * import { done, fromDelay, testCreateDeps } from "@evolu/common";
345
+ *
346
+ * // Wait one second, then stop.
347
+ * const step = fromDelay("1s")(testCreateDeps());
348
+ * expectOk(step(undefined), [1000, 1000]);
349
+ * expectErr(step(undefined), done());
263
350
  * ```
264
351
  *
265
352
  * @group Constructors
@@ -269,35 +356,57 @@ export declare const fromDelay: (delay: Duration) => Schedule<Millis>;
269
356
  * A schedule that runs through a sequence of delays.
270
357
  *
271
358
  * Convenience for sequencing single-delay schedules. Useful for predefined
272
- * retry patterns.
359
+ * retry patterns. With no delays, returns a schedule that stops immediately.
273
360
  *
274
- * ### Example
361
+ * ### Sequencing custom delays
275
362
  *
276
363
  * ```ts
277
- * // Custom retry sequence: 100ms, 500ms, 2s
364
+ * import { done, fromDelays, testCreateDeps } from "@evolu/common";
365
+ *
366
+ * // A custom retry sequence: 100ms, 500ms, then 2s.
278
367
  * const custom = fromDelays("100ms", "500ms", "2s");
368
+ * const step = custom(testCreateDeps());
369
+ * expectOk(step(undefined), [100, 100]);
370
+ * expectOk(step(undefined), [500, 500]);
371
+ * expectOk(step(undefined), [2000, 2000]);
372
+ * expectErr(step(undefined), done());
279
373
  * ```
280
374
  *
281
375
  * @group Constructors
282
376
  */
283
377
  export declare const fromDelays: (...delays: ReadonlyArray<Duration>) => Schedule<Millis>;
284
378
  /**
285
- * A schedule that outputs the total elapsed time since the schedule started.
379
+ * A schedule that outputs the total elapsed time since its first step.
286
380
  *
287
381
  * Never stops — combine with {@link take} or {@link maxElapsed} to limit. Useful
288
382
  * for tracking how long a retry sequence has been running.
289
383
  *
290
- * ### Example
384
+ * ### Tracking elapsed time
291
385
  *
292
386
  * ```ts
293
- * // Track elapsed time alongside retries
387
+ * import {
388
+ * done,
389
+ * elapsed,
390
+ * exponential,
391
+ * intersectSchedules,
392
+ * testCreateDeps,
393
+ * whileScheduleOutput,
394
+ * type Millis,
395
+ * } from "@evolu/common";
396
+ *
397
+ * // Track elapsed time alongside each backoff step.
294
398
  * const withTiming = intersectSchedules(exponential("100ms"), elapsed);
295
- * // Outputs: [[100, 0], [200, ~100], [400, ~300], ...]
399
+ * expectOk(withTiming(testCreateDeps())(undefined), [[100, 0], 100]);
296
400
  *
297
- * // Stop after 30 seconds of elapsed time
401
+ * // Or stop a schedule after 30 seconds of elapsed time.
298
402
  * const timeLimited = whileScheduleOutput((ms: Millis) => ms < 30000)(
299
403
  * elapsed,
300
404
  * );
405
+ * const deps = testCreateDeps();
406
+ * const step = timeLimited(deps);
407
+ * step(undefined);
408
+ * deps.time.advance("30s");
409
+ * expectErr(step(undefined), done());
301
410
  * ```
302
411
  *
303
412
  * @group Constructors
@@ -309,17 +418,31 @@ export declare const elapsed: Schedule<Millis>;
309
418
  * Outputs the elapsed time. Useful for time-boxed operations or combining with
310
419
  * other schedules to create time-limited variants.
311
420
  *
312
- * ### Example
421
+ * ### Time-boxing a schedule
313
422
  *
314
423
  * ```ts
315
- * // Run for at most 30 seconds
424
+ * import {
425
+ * done,
426
+ * during,
427
+ * exponential,
428
+ * intersectSchedules,
429
+ * testCreateDeps,
430
+ * } from "@evolu/common";
431
+ *
432
+ * // Run for at most 30 seconds.
316
433
  * const timeLimited = during("30s");
434
+ * const deps = testCreateDeps();
435
+ * const step = timeLimited(deps);
436
+ * expectOk(step(undefined), [0, 0]);
437
+ * deps.time.advance("30.1s");
438
+ * expectErr(step(undefined), done());
317
439
  *
318
- * // Combine with exponential for time-boxed retry
440
+ * // Combine elapsed time with backoff for a time-boxed retry.
319
441
  * const timedRetry = intersectSchedules(
320
442
  * exponential("100ms"),
321
443
  * during("10s"),
322
444
  * );
445
+ * expectOk(timedRetry(testCreateDeps())(undefined), [[100, 0], 100]);
323
446
  * ```
324
447
  *
325
448
  * @group Constructors
@@ -330,17 +453,29 @@ export declare const during: (duration: Duration) => Schedule<Millis>;
330
453
  *
331
454
  * Never stops — combine with {@link take} or {@link maxElapsed} to limit.
332
455
  *
333
- * ### Example
456
+ * ### Adding constant output
334
457
  *
335
458
  * ```ts
336
- * // Always output "retry"
459
+ * import {
460
+ * always,
461
+ * exponential,
462
+ * intersectSchedules,
463
+ * testCreateDeps,
464
+ * } from "@evolu/common";
465
+ *
466
+ * // Always emit the same label.
337
467
  * const labeled = always("retry");
468
+ * expectOk(labeled(testCreateDeps())(undefined), ["retry", 0]);
338
469
  *
339
- * // Combine with timing
470
+ * // Add a label while preserving exponential timing.
340
471
  * const withLabel = intersectSchedules(
341
472
  * exponential("100ms"),
342
473
  * always("backoff"),
343
474
  * );
475
+ * expectOk(withLabel(testCreateDeps())(undefined), [
476
+ * [100, "backoff"],
477
+ * 100,
478
+ * ]);
344
479
  * ```
345
480
  *
346
481
  * @group Constructors
@@ -353,18 +488,17 @@ export declare const always: <A>(value: A) => Schedule<A>;
353
488
  * provided function. Never stops — combine with {@link take} or
354
489
  * {@link maxElapsed} to limit.
355
490
  *
356
- * ### Example
491
+ * ### Unfolding state
357
492
  *
358
493
  * ```ts
359
- * // Counter: 0, 1, 2, 3, ...
360
- * const counter = unfoldSchedule(0, (n) => n + 1);
494
+ * import { testCreateDeps, unfoldSchedule } from "@evolu/common";
361
495
  *
362
- * // Custom backoff: 100, 150, 225, 338, ... (×1.5 each time)
496
+ * // Unfold counters, custom backoff values, or state machines.
497
+ * const counter = unfoldSchedule(0, (n) => n + 1);
363
498
  * const customBackoff = unfoldSchedule(100, (delay) =>
364
499
  * Math.round(delay * 1.5),
365
500
  * );
366
501
  *
367
- * // State machine
368
502
  * type Phase = "init" | "warmup" | "active";
369
503
  * const phases = unfoldSchedule<Phase>("init", (phase) => {
370
504
  * switch (phase) {
@@ -376,38 +510,66 @@ export declare const always: <A>(value: A) => Schedule<A>;
376
510
  * return "active";
377
511
  * }
378
512
  * });
513
+ *
514
+ * const counterStep = counter(testCreateDeps());
515
+ * const backoffStep = customBackoff(testCreateDeps());
516
+ * const phaseStep = phases(testCreateDeps());
517
+ * expectOk(counterStep(undefined), [0, 0]);
518
+ * expectOk(counterStep(undefined), [1, 0]);
519
+ * backoffStep(undefined);
520
+ * phaseStep(undefined);
521
+ * expectOk(backoffStep(undefined), [150, 0]);
522
+ * expectOk(phaseStep(undefined), ["warmup", 0]);
379
523
  * ```
380
524
  *
381
525
  * @group Constructors
382
526
  */
383
527
  export declare const unfoldSchedule: <State>(initial: State, next: (state: State) => State) => Schedule<State>;
384
528
  /**
385
- * Limits a schedule to a maximum number of attempts.
529
+ * Limits a schedule to a maximum number of steps.
530
+ *
531
+ * After `n` steps, returns `Err(Done<void>)` (stop).
386
532
  *
387
- * After `n` attempts, returns `Err(Done<void>)` (stop).
533
+ * `n` uses {@link Int0To100OrNonNegativeInt}: pass `0` to `100` as a literal, or
534
+ * a validated {@link NonNegativeInt} for larger or dynamic values.
388
535
  *
389
- * ### Example
536
+ * ### Limiting a schedule
390
537
  *
391
538
  * ```ts
392
- * // Exponential backoff, max 3 retries
393
- * const limited = take(3)(exponential("100ms"));
394
- * // Attempt 1: 100ms, Attempt 2: 200ms, Attempt 3: 400ms, Attempt 4: Err(Done<void>)
539
+ * import { done, exponential, take, testCreateDeps } from "@evolu/common";
540
+ *
541
+ * // Three exponential retries, then Done.
542
+ * const step = take(3)(exponential("100ms"))(testCreateDeps());
543
+ * step(undefined);
544
+ * step(undefined);
545
+ * step(undefined);
546
+ * expectErr(step(undefined), done());
395
547
  * ```
396
548
  *
397
549
  * @group Limiting
398
550
  */
399
- export declare const take: (n: number) => <Output, Input>(schedule: Schedule<Output, Input>) => Schedule<Output, Input>;
551
+ export declare const take: (n: Int0To100OrNonNegativeInt) => <Output, Input>(schedule: Schedule<Output, Input>) => Schedule<Output, Input>;
400
552
  /**
401
- * Limits schedule execution to a maximum elapsed time.
553
+ * Limits schedule execution to a maximum elapsed time since its first step.
402
554
  *
403
- * After `duration` has elapsed since the schedule started, returns
404
- * `Err(Done<void>)`.
555
+ * After `duration` has elapsed since the first step, returns `Err(Done<void>)`.
405
556
  *
406
- * ### Example
557
+ * ### Limiting elapsed time
407
558
  *
408
559
  * ```ts
409
- * // Retry for at most 30 seconds
410
- * const timeLimited = maxElapsed("30s")(exponential("1s"));
560
+ * import {
561
+ * done,
562
+ * exponential,
563
+ * maxElapsed,
564
+ * testCreateDeps,
565
+ * } from "@evolu/common";
566
+ *
567
+ * // Retry for at most 30 seconds.
568
+ * const deps = testCreateDeps();
569
+ * const step = maxElapsed("30s")(exponential("1s"))(deps);
570
+ * expectOk(step(undefined), [1000, 1000]);
571
+ * deps.time.advance("30s");
572
+ * expectErr(step(undefined), done());
411
573
  * ```
412
574
  *
413
575
  * @group Limiting
@@ -418,50 +580,82 @@ export declare const maxElapsed: (duration: Duration) => <Output, Input>(schedul
418
580
  *
419
581
  * If the schedule returns a delay greater than `max`, returns `max` instead.
420
582
  *
421
- * ### Example
583
+ * ### Capping delays
422
584
  *
423
585
  * ```ts
424
- * // Exponential capped at 10 seconds
425
- * const capped = maxDelay("10s")(exponential("1s"));
426
- * // 1s, 2s, 4s, 8s, 10s, 10s, 10s, ...
586
+ * import { exponential, maxDelay, testCreateDeps } from "@evolu/common";
587
+ *
588
+ * // Exponential delays grow 1s, 2s, 4s, 8s, then stay capped at 10s.
589
+ * const step = maxDelay("10s")(exponential("1s"))(testCreateDeps());
590
+ * step(undefined);
591
+ * step(undefined);
592
+ * step(undefined);
593
+ * step(undefined);
594
+ * expectOk(step(undefined), [16000, 10000]);
427
595
  * ```
428
596
  *
429
597
  * @group Limiting
430
598
  */
431
599
  export declare const maxDelay: (max: Duration) => <Output, Input>(schedule: Schedule<Output, Input>) => Schedule<Output, Input>;
432
600
  /**
433
- * Adds randomized jitter to delays.
601
+ * Randomizes delays by up to a percentage.
434
602
  *
435
603
  * Jitter helps prevent "thundering herd" when many clients retry simultaneously
436
- * after a service recovers. The delay is randomized within a range:
604
+ * after a service recovers. By default, the original delay is the upper bound:
605
+ *
606
+ * - `"0%"` — no jitter (original delay)
607
+ * - `"50%"` — equal jitter, shortens the delay by up to 50%
608
+ * - `"100%"` — full jitter, shortens the delay by up to 100%
437
609
  *
438
- * - `factor = 0` no jitter (original delay)
439
- * - `factor = 0.5` — delay varies ±50% (e.g., 1s becomes 500ms-1500ms)
440
- * - `factor = 1` — full jitter, delay varies 0-200% (e.g., 1s becomes 0-2s)
610
+ * Pass `"around"` to preserve the average delay for periodic work:
441
611
  *
442
- * ### Example
612
+ * - `"0%"` — no jitter (original delay)
613
+ * - `"50%"` — varies by up to 50% below or above the original delay
614
+ * - `"100%"` — varies by up to 100% below or above the original delay
615
+ *
616
+ * ### Jittering below or around a delay
443
617
  *
444
618
  * ```ts
445
- * // AWS-style full jitter
446
- * const awsStyle = jitter(1)(exponential("1s"));
619
+ * import {
620
+ * exponential,
621
+ * jitter,
622
+ * spaced,
623
+ * testCreateDeps,
624
+ * type RandomNumber,
625
+ * } from "@evolu/common";
447
626
  *
448
- * // Conservative jitter (±25%)
449
- * const conservative = jitter(0.25)(exponential("1s"));
627
+ * const deps = {
628
+ * ...testCreateDeps(),
629
+ * random: { next: () => 0.5 as RandomNumber },
630
+ * };
631
+ *
632
+ * // Shorten retry delays by at most 25%.
633
+ * const conservative = jitter("25%")(exponential("1s"));
634
+ * // Poll around a 30s target cadence, from 27s to 33s.
635
+ * const polling = jitter("10%", "around")(spaced("30s"));
636
+ *
637
+ * expectOk(conservative(deps)(undefined), [1000, 875]);
638
+ * expectOk(polling(deps)(undefined), [30000, 30000]);
450
639
  * ```
451
640
  *
452
641
  * @group Delay
453
642
  */
454
- export declare const jitter: (factor?: number) => <Output, Input>(schedule: Schedule<Output, Input>) => Schedule<Output, Input>;
643
+ export declare const jitter: (percentage?: Percentage, mode?: "below" | "around") => (<Output, Input>(schedule: Schedule<Output, Input>) => Schedule<Output, Input>);
455
644
  /**
456
- * Adds an initial delay before the first attempt.
645
+ * Replaces the schedule's first delay.
457
646
  *
458
- * Subsequent attempts use the schedule's normal delays.
647
+ * The first successful step uses `initialDelay` instead of the delay produced
648
+ * by the schedule. Subsequent steps use the schedule's delays unchanged.
459
649
  *
460
- * ### Example
650
+ * ### Replacing the first delay
461
651
  *
462
652
  * ```ts
463
- * // Wait 1s before first attempt, then exponential backoff
464
- * const withWarmup = delayed("1s")(exponential("100ms"));
653
+ * import { delayed, exponential, testCreateDeps } from "@evolu/common";
654
+ *
655
+ * const step = delayed("1s")(exponential("100ms"))(testCreateDeps());
656
+ * // Only the first delay is replaced; later exponential delays are unchanged.
657
+ * expectOk(step(undefined), [100, 1000]);
658
+ * expectOk(step(undefined), [200, 200]);
465
659
  * ```
466
660
  *
467
661
  * @group Delay
@@ -470,12 +664,14 @@ export declare const delayed: (initialDelay: Duration) => <Output, Input>(schedu
470
664
  /**
471
665
  * Adds a fixed delay to the schedule's existing delay.
472
666
  *
473
- * ### Example
667
+ * ### Adding to every delay
474
668
  *
475
669
  * ```ts
476
- * // Add 500ms to each delay
477
- * const slower = addDelay("500ms")(exponential("100ms"));
478
- * // Delays: 600ms, 700ms, 900ms, 1300ms, ...
670
+ * import { addDelay, exponential, testCreateDeps } from "@evolu/common";
671
+ *
672
+ * // Add 500ms to every exponential delay.
673
+ * const step = addDelay("500ms")(exponential("100ms"))(testCreateDeps());
674
+ * expectOk(step(undefined), [100, 600]);
479
675
  * ```
480
676
  *
481
677
  * @group Delay
@@ -486,33 +682,58 @@ export declare const addDelay: (extra: Duration) => (<Output, Input>(schedule: S
486
682
  *
487
683
  * More flexible than {@link maxDelay} — can implement any delay transformation.
488
684
  *
489
- * ### Example
685
+ * ### Transforming delays
490
686
  *
491
687
  * ```ts
492
- * // Double all delays
493
- * const slower = modifyDelay((d) => d * 2)(exponential("100ms"));
688
+ * import { exponential, modifyDelay, testCreateDeps } from "@evolu/common";
494
689
  *
495
- * // Cap at 10s (equivalent to maxDelay)
496
- * const capped = modifyDelay((d) => Math.min(d, 10000))(exponential("1s"));
690
+ * // Arbitrary transformations can double or cap delays.
691
+ * const slower = modifyDelay((delay) => delay * 2)(exponential("100ms"));
692
+ * // Equivalent to maxDelay("10s") for this schedule.
693
+ * const capped = modifyDelay((delay) => Math.min(delay, 10000))(
694
+ * exponential("20s"),
695
+ * );
696
+ * const deps = testCreateDeps();
697
+ *
698
+ * expectOk(slower(deps)(undefined), [100, 200]);
699
+ * expectOk(capped(deps)(undefined), [20000, 10000]);
497
700
  * ```
498
701
  *
499
702
  * @group Delay
500
703
  */
501
704
  export declare const modifyDelay: (f: (delay: Millis) => number) => <Output, Input>(schedule: Schedule<Output, Input>) => Schedule<Output, Input>;
502
705
  /**
503
- * Adjusts delay by subtracting execution time.
706
+ * Adjusts delay by subtracting time elapsed beyond the previously returned
707
+ * delay.
708
+ *
709
+ * In a normal executor loop, this corresponds to the previous execution time.
710
+ * If the runtime wakes later than requested, the extra lag is also compensated.
711
+ * If execution and lag took longer than the delay, returns 0.
504
712
  *
505
- * A simple combinator that subtracts the previous execution time from the
506
- * schedule's delay. If execution took longer than the delay, returns 0.
713
+ * When composing with delay-shaping combinators such as {@link maxDelay}, put
714
+ * `compensate` near the outside of the stack so it observes the final returned
715
+ * delay.
507
716
  *
508
717
  * For window-aligned scheduling, use {@link fixed} instead.
509
718
  *
510
- * ### Example
719
+ * ### Compensating for execution time
511
720
  *
512
721
  * ```ts
513
- * // Poll every 5s, accounting for execution time
514
- * const polling = compensate(spaced("5s"));
515
- * // If poll takes 1s → wait 4s. If poll takes 6s → wait 0s.
722
+ * import { compensate, spaced, testCreateDeps } from "@evolu/common";
723
+ *
724
+ * const fastDeps = testCreateDeps();
725
+ * const fastStep = compensate(spaced("5s"))(fastDeps);
726
+ * expectOk(fastStep(undefined), [5000, 5000]);
727
+ * // Five seconds waiting plus one second working leaves four seconds.
728
+ * fastDeps.time.advance("6s");
729
+ * expectOk(fastStep(undefined), [5000, 4000]);
730
+ *
731
+ * const slowDeps = testCreateDeps();
732
+ * const slowStep = compensate(spaced("5s"))(slowDeps);
733
+ * expectOk(slowStep(undefined), [5000, 5000]);
734
+ * // Five seconds waiting plus six seconds working leaves no delay.
735
+ * slowDeps.time.advance("11s");
736
+ * expectOk(slowStep(undefined), [5000, 0]);
516
737
  * ```
517
738
  *
518
739
  * @group Delay
@@ -525,15 +746,26 @@ export declare const compensate: <Output, Input>(schedule: Schedule<Output, Inpu
525
746
  * Useful for input-aware retry strategies, e.g., only retry certain error
526
747
  * types.
527
748
  *
528
- * ### Example
749
+ * ### Continuing by input
529
750
  *
530
751
  * ```ts
752
+ * import {
753
+ * done,
754
+ * exponential,
755
+ * testCreateDeps,
756
+ * whileScheduleInput,
757
+ * type Typed,
758
+ * } from "@evolu/common";
759
+ *
531
760
  * interface MyError extends Typed<"Transient" | "Fatal"> {}
532
761
  *
533
- * // Only retry transient errors
762
+ * // Retry only transient errors.
534
763
  * const retryTransient = whileScheduleInput(
535
764
  * (error: MyError) => error.type === "Transient",
536
765
  * )(exponential("100ms"));
766
+ * const step = retryTransient(testCreateDeps());
767
+ * expectOk(step({ type: "Transient" }), [100, 100]);
768
+ * expectErr(step({ type: "Fatal" }), done());
537
769
  * ```
538
770
  *
539
771
  * @group Filtering
@@ -545,15 +777,26 @@ export declare const whileScheduleInput: <Input>(predicate: Predicate<Input>) =>
545
777
  * Stops (returns `Err(Done<void>)`) when {@link Predicate} returns `true`.
546
778
  * Useful for stopping retry on specific error conditions.
547
779
  *
548
- * ### Example
780
+ * ### Stopping by input
549
781
  *
550
782
  * ```ts
783
+ * import {
784
+ * done,
785
+ * exponential,
786
+ * testCreateDeps,
787
+ * untilScheduleInput,
788
+ * type Typed,
789
+ * } from "@evolu/common";
790
+ *
551
791
  * interface MyError extends Typed<"Transient" | "Fatal"> {}
552
792
  *
553
- * // Stop retrying on fatal errors
793
+ * // Stop retrying when an error is fatal.
554
794
  * const stopOnFatal = untilScheduleInput(
555
795
  * (error: MyError) => error.type === "Fatal",
556
796
  * )(exponential("100ms"));
797
+ * const step = stopOnFatal(testCreateDeps());
798
+ * expectOk(step({ type: "Transient" }), [100, 100]);
799
+ * expectErr(step({ type: "Fatal" }), done());
557
800
  * ```
558
801
  *
559
802
  * @group Filtering
@@ -564,13 +807,26 @@ export declare const untilScheduleInput: <Input>(predicate: Predicate<Input>) =>
564
807
  *
565
808
  * Stops (returns `Err(Done<void>)`) when {@link Predicate} returns `false`.
566
809
  *
567
- * ### Example
810
+ * ### Continuing by output
568
811
  *
569
812
  * ```ts
570
- * // Continue while delay is under 5 seconds
813
+ * import {
814
+ * done,
815
+ * exponential,
816
+ * testCreateDeps,
817
+ * whileScheduleOutput,
818
+ * type Millis,
819
+ * } from "@evolu/common";
820
+ *
821
+ * // Continue while the exponential delay is below five seconds.
571
822
  * const capped = whileScheduleOutput((delay: Millis) => delay < 5000)(
572
823
  * exponential("1s"),
573
824
  * );
825
+ * const step = capped(testCreateDeps());
826
+ * step(undefined);
827
+ * step(undefined);
828
+ * step(undefined);
829
+ * expectErr(step(undefined), done());
574
830
  * ```
575
831
  *
576
832
  * @group Filtering
@@ -581,51 +837,80 @@ export declare const whileScheduleOutput: <Output>(predicate: Predicate<Output>)
581
837
  *
582
838
  * Stops (returns `Err(Done<void>)`) when {@link Predicate} returns `true`.
583
839
  *
584
- * ### Example
840
+ * ### Stopping by output
585
841
  *
586
842
  * ```ts
587
- * // Stop when delay reaches 1 second
843
+ * import {
844
+ * done,
845
+ * exponential,
846
+ * testCreateDeps,
847
+ * untilScheduleOutput,
848
+ * type Millis,
849
+ * } from "@evolu/common";
850
+ *
851
+ * // Stop once the exponential delay reaches at least one second.
588
852
  * const limited = untilScheduleOutput((delay: Millis) => delay >= 1000)(
589
853
  * exponential("100ms"),
590
854
  * );
855
+ * const step = limited(testCreateDeps());
856
+ * step(undefined);
857
+ * step(undefined);
858
+ * step(undefined);
859
+ * step(undefined);
860
+ * expectErr(step(undefined), done());
591
861
  * ```
592
862
  *
593
863
  * @group Filtering
594
864
  */
595
865
  export declare const untilScheduleOutput: <Output>(predicate: Predicate<Output>) => <Input>(schedule: Schedule<Output, Input>) => Schedule<Output, Input>;
596
866
  /**
597
- * Resets the schedule after a period of inactivity.
867
+ * Resets a running schedule after a period of inactivity.
598
868
  *
599
- * If `elapsedSincePrevious` is greater than or equal to `duration`, creates a
600
- * fresh state. Useful for circuit breakers that should "forget" failures after
601
- * idle time.
869
+ * Before each step, if the time since the previous step is at least `duration`,
870
+ * replaces the wrapped schedule with fresh state. Once the wrapped schedule
871
+ * returns `Done`, termination is final.
602
872
  *
603
- * ### Example
873
+ * ### Resetting after inactivity
604
874
  *
605
875
  * ```ts
606
- * // Reset retry count after 1 minute of success
607
- * const circuitBreaker = resetScheduleAfter("1m")(
608
- * take(5)(exponential("1s")),
609
- * );
876
+ * import {
877
+ * exponential,
878
+ * resetScheduleAfter,
879
+ * testCreateDeps,
880
+ * } from "@evolu/common";
881
+ *
882
+ * const backoff = resetScheduleAfter("1m")(exponential("1s"));
883
+ * const deps = testCreateDeps();
884
+ * const step = backoff(deps);
885
+ * expectOk(step(undefined), [1000, 1000]);
886
+ * deps.time.advance("1m");
887
+ * expectOk(step(undefined), [1000, 1000]);
610
888
  * ```
611
889
  *
612
890
  * @group State
613
891
  */
614
- export declare const resetScheduleAfter: (duration: Duration) => <Output, Input>(schedule: Schedule<Output, Input>) => Schedule<Output, Input>;
892
+ export declare const resetScheduleAfter: (duration: DurationLiteral | PositiveMillis) => <Output, Input>(schedule: Schedule<Output, Input>) => Schedule<Output, Input>;
615
893
  /**
616
894
  * Transforms the output of a schedule.
617
895
  *
618
896
  * The delay (second tuple element) remains unchanged.
619
897
  *
620
- * ### Example
898
+ * ### Mapping schedule output
621
899
  *
622
900
  * ```ts
623
- * import { exponential, mapSchedule } from "@evolu/common";
901
+ * import {
902
+ * exponential,
903
+ * mapSchedule,
904
+ * testCreateDeps,
905
+ * type Millis,
906
+ * } from "@evolu/common";
624
907
  *
625
- * const schedule = mapSchedule((delay) => ({
908
+ * const schedule = mapSchedule((delay: Millis) => ({
626
909
  * delay,
627
910
  * doubled: delay * 2,
628
911
  * }))(exponential("100ms"));
912
+ * const step = schedule(testCreateDeps());
913
+ * expectOk(step(undefined), [{ delay: 100, doubled: 200 }, 100]);
629
914
  * ```
630
915
  *
631
916
  * @group Transform
@@ -639,20 +924,23 @@ export declare const mapSchedule: <A, B>(f: (a: A) => B) => <Input>(schedule: Sc
639
924
  * directly (the "identity" schedule). When called with a schedule, wraps it to
640
925
  * preserve timing behavior but replace output with input.
641
926
  *
642
- * ### Example
927
+ * ### Passing through input
643
928
  *
644
929
  * ```ts
645
- * import { exponential, passthrough } from "@evolu/common";
930
+ * import { exponential, passthrough, testCreateDeps } from "@evolu/common";
646
931
  *
647
932
  * interface MyError {
648
933
  * readonly message: string;
649
934
  * }
650
935
  *
651
- * // Constructor: output equals input
936
+ * // Constructor form emits input immediately; combinator form keeps timing.
652
937
  * const identity = passthrough<MyError>();
653
- *
654
- * // Combinator: preserve timing, replace output
655
938
  * const withInput = passthrough(exponential("100ms"));
939
+ * const error = { message: "Unavailable" };
940
+ * const deps = testCreateDeps();
941
+ *
942
+ * expectOk(identity(deps)(error), [error, 0]);
943
+ * expectOk(withInput(deps)(error), [error, 100]);
656
944
  * ```
657
945
  *
658
946
  * @group Constructors
@@ -666,31 +954,45 @@ export declare function passthrough<Output, Input>(schedule: Schedule<Output, In
666
954
  * Each step outputs the accumulated value. Useful for tracking totals,
667
955
  * collecting outputs, or building up metadata across attempts.
668
956
  *
669
- * ### Example
957
+ * ### Folding schedule output
670
958
  *
671
959
  * ```ts
672
- * // Track total delay spent
960
+ * import {
961
+ * exponential,
962
+ * foldSchedule,
963
+ * minMillis,
964
+ * spaced,
965
+ * take,
966
+ * testCreateDeps,
967
+ * type Millis,
968
+ * } from "@evolu/common";
969
+ *
970
+ * // Accumulate totals, complete output histories, or structured metadata.
673
971
  * const withTotal = foldSchedule(
674
972
  * 0,
675
973
  * (total: number, delay: Millis) => total + delay,
676
974
  * )(exponential("100ms"));
677
- * // Outputs: 100, 300, 700, 1500, ... (cumulative)
678
- *
679
- * // Collect all outputs
680
975
  * const collected = foldSchedule<ReadonlyArray<Millis>, Millis>(
681
976
  * [],
682
- * (acc, delay) => [...acc, delay],
977
+ * (outputs, delay) => [...outputs, delay],
683
978
  * )(take(3)(spaced("1s")));
684
- * // Outputs: [1000], [1000, 1000], [1000, 1000, 1000]
685
- *
686
- * // Count attempts with custom output
687
979
  * const counted = foldSchedule(
688
980
  * { attempts: 0, lastDelay: minMillis },
689
- * (acc, delay: Millis) => ({
690
- * attempts: acc.attempts + 1,
981
+ * (state, delay: Millis) => ({
982
+ * attempts: state.attempts + 1,
691
983
  * lastDelay: delay,
692
984
  * }),
693
985
  * )(exponential("100ms"));
986
+ *
987
+ * const deps = testCreateDeps();
988
+ * const totalStep = withTotal(deps);
989
+ * totalStep(undefined);
990
+ * expectOk(totalStep(undefined), [300, 200]);
991
+ * expectOk(collected(deps)(undefined), [[1000], 1000]);
992
+ * expectOk(counted(deps)(undefined), [
993
+ * { attempts: 1, lastDelay: 100 },
994
+ * 100,
995
+ * ]);
694
996
  * ```
695
997
  *
696
998
  * @group Transform
@@ -703,12 +1005,15 @@ export declare const foldSchedule: <Z, Output>(initial: Z, f: (acc: Z, output: O
703
1005
  * Outputs 0, 1, 2, ... while preserving the underlying schedule's timing and
704
1006
  * termination behavior.
705
1007
  *
706
- * ### Example
1008
+ * ### Counting repetitions
707
1009
  *
708
1010
  * ```ts
709
- * // Track how many retries occurred
710
- * const counted = repetitions(exponential("100ms"));
711
- * // Outputs: 0, 1, 2, ... with exponential delays
1011
+ * import { exponential, repetitions, testCreateDeps } from "@evolu/common";
1012
+ *
1013
+ * // Count retries while preserving exponential timing.
1014
+ * const step = repetitions(exponential("100ms"))(testCreateDeps());
1015
+ * expectOk(step(undefined), [0, 100]);
1016
+ * expectOk(step(undefined), [1, 200]);
712
1017
  * ```
713
1018
  *
714
1019
  * @group Transform
@@ -720,17 +1025,28 @@ export declare const repetitions: <Output, Input>(schedule: Schedule<Output, Inp
720
1025
  * Wraps a schedule to output its delay (in milliseconds) instead of the
721
1026
  * original output. Useful for monitoring or logging delay patterns.
722
1027
  *
723
- * ### Example
1028
+ * ### Exposing and observing delays
724
1029
  *
725
1030
  * ```ts
726
- * // Monitor exponential delays
1031
+ * import {
1032
+ * delays,
1033
+ * exponential,
1034
+ * tapScheduleOutput,
1035
+ * testCreateDeps,
1036
+ * type Millis,
1037
+ * } from "@evolu/common";
1038
+ *
1039
+ * // Expose delays for monitoring, or observe them without changing output.
727
1040
  * const monitorDelays = delays(exponential("100ms"));
728
- * // Outputs: 100, 200, 400, 800, ... (the delays themselves)
1041
+ * const observed: Array<Millis> = [];
1042
+ * const logged = tapScheduleOutput((delay: Millis) => {
1043
+ * observed.push(delay);
1044
+ * })(delays(exponential("100ms")));
1045
+ * const deps = testCreateDeps();
729
1046
  *
730
- * // Log delays for debugging
731
- * const logged = tapScheduleOutput(console.log)(
732
- * delays(exponential("100ms")),
733
- * );
1047
+ * expectOk(monitorDelays(deps)(undefined), [100, 100]);
1048
+ * expectOk(logged(deps)(undefined), [100, 100]);
1049
+ * expect(observed).toEqual([100]);
734
1050
  * ```
735
1051
  *
736
1052
  * @group Transform
@@ -739,14 +1055,25 @@ export declare const delays: <Output, Input>(schedule: Schedule<Output, Input>)
739
1055
  /**
740
1056
  * Collects all outputs into an array.
741
1057
  *
742
- * Each step outputs an array containing all outputs so far.
1058
+ * Each step outputs a new snapshot containing all outputs so far. Because all
1059
+ * outputs are retained and copied on each step, use this combinator with finite
1060
+ * schedules.
743
1061
  *
744
- * ### Example
1062
+ * ### Collecting outputs
745
1063
  *
746
1064
  * ```ts
747
- * // Collect all delays
1065
+ * import {
1066
+ * collectAllScheduleOutputs,
1067
+ * spaced,
1068
+ * take,
1069
+ * testCreateDeps,
1070
+ * } from "@evolu/common";
1071
+ *
1072
+ * // Retain every delay produced by the finite schedule.
748
1073
  * const collected = collectAllScheduleOutputs(take(3)(spaced("100ms")));
749
- * // Outputs: [100], [100, 100], [100, 100, 100]
1074
+ * const step = collected(testCreateDeps());
1075
+ * step(undefined);
1076
+ * expectOk(step(undefined), [[100, 100], 100]);
750
1077
  * ```
751
1078
  *
752
1079
  * @group Collection
@@ -758,14 +1085,24 @@ export declare const collectAllScheduleOutputs: <Output, Input>(schedule: Schedu
758
1085
  * Each step outputs an array containing all inputs received so far. Mirror of
759
1086
  * {@link collectAllScheduleOutputs} but for inputs.
760
1087
  *
761
- * ### Example
1088
+ * ### Collecting inputs
762
1089
  *
763
1090
  * ```ts
764
- * // Collect all errors during retry
765
- * const errorHistory = collectScheduleInputs(
766
- * take(3)(exponential("100ms")),
767
- * );
768
- * // After 3 retries, outputs array of all error inputs
1091
+ * import {
1092
+ * collectScheduleInputs,
1093
+ * exponential,
1094
+ * take,
1095
+ * testCreateDeps,
1096
+ * type Millis,
1097
+ * type Schedule,
1098
+ * } from "@evolu/common";
1099
+ *
1100
+ * const retries: Schedule<Millis, string> = take(3)(exponential("100ms"));
1101
+ * // Keep every error received during retry.
1102
+ * const errorHistory = collectScheduleInputs(retries);
1103
+ * const step = errorHistory(testCreateDeps());
1104
+ * step("network");
1105
+ * expectOk(step("timeout"), [["network", "timeout"], 200]);
769
1106
  * ```
770
1107
  *
771
1108
  * @group Collection
@@ -777,14 +1114,27 @@ export declare const collectScheduleInputs: <Output, Input>(schedule: Schedule<O
777
1114
  * More flexible than {@link collectAllScheduleOutputs} — stops collecting when
778
1115
  * the predicate returns false.
779
1116
  *
780
- * ### Example
1117
+ * ### Collecting while output matches
781
1118
  *
782
1119
  * ```ts
783
- * // Collect delays while under 1 second
1120
+ * import {
1121
+ * collectWhileScheduleOutput,
1122
+ * done,
1123
+ * exponential,
1124
+ * testCreateDeps,
1125
+ * type Millis,
1126
+ * } from "@evolu/common";
1127
+ *
1128
+ * // Collect delays only while they remain below one second.
784
1129
  * const smallDelays = collectWhileScheduleOutput(
785
1130
  * (delay: Millis) => delay < 1000,
786
1131
  * )(exponential("100ms"));
787
- * // Outputs: [100], [100, 200], [100, 200, 400], [100, 200, 400, 800], stops
1132
+ * const step = smallDelays(testCreateDeps());
1133
+ * step(undefined);
1134
+ * step(undefined);
1135
+ * step(undefined);
1136
+ * expectOk(step(undefined), [[100, 200, 400, 800], 800]);
1137
+ * expectErr(step(undefined), done());
788
1138
  * ```
789
1139
  *
790
1140
  * @group Collection
@@ -794,16 +1144,29 @@ export declare const collectWhileScheduleOutput: <Output>(predicate: Predicate<O
794
1144
  * Collects outputs until a predicate becomes true.
795
1145
  *
796
1146
  * Mirror of {@link collectWhileScheduleOutput} — stops collecting when the
797
- * predicate returns true (inclusive of the matching output).
1147
+ * predicate returns true.
798
1148
  *
799
- * ### Example
1149
+ * ### Collecting until output matches
800
1150
  *
801
1151
  * ```ts
802
- * // Collect delays until reaching 1 second
1152
+ * import {
1153
+ * collectUntilScheduleOutput,
1154
+ * done,
1155
+ * exponential,
1156
+ * testCreateDeps,
1157
+ * type Millis,
1158
+ * } from "@evolu/common";
1159
+ *
1160
+ * // Collect delays until the next delay reaches at least one second.
803
1161
  * const untilLarge = collectUntilScheduleOutput(
804
1162
  * (delay: Millis) => delay >= 1000,
805
1163
  * )(exponential("100ms"));
806
- * // Outputs: [100], [100, 200], [100, 200, 400], [100, 200, 400, 800], stops
1164
+ * const step = untilLarge(testCreateDeps());
1165
+ * step(undefined);
1166
+ * step(undefined);
1167
+ * step(undefined);
1168
+ * expectOk(step(undefined), [[100, 200, 400, 800], 800]);
1169
+ * expectErr(step(undefined), done());
807
1170
  * ```
808
1171
  *
809
1172
  * @group Collection
@@ -815,16 +1178,27 @@ export declare const collectUntilScheduleOutput: <Output>(predicate: Predicate<O
815
1178
  * Useful for adaptive strategies that start aggressive and become more
816
1179
  * conservative over time.
817
1180
  *
818
- * ### Example
1181
+ * ### Sequencing strategies
819
1182
  *
820
1183
  * ```ts
821
- * // Fast retries first, then slower, then final fallback
822
- * const adaptive = sequenceSchedules(
1184
+ * import {
1185
+ * exponential,
1186
+ * fixed,
1187
+ * sequenceSchedules,
1188
+ * take,
1189
+ * testCreateDeps,
1190
+ * } from "@evolu/common";
1191
+ *
1192
+ * // Fast retries first, then slower retries, then a steady fallback.
1193
+ * const step = sequenceSchedules(
823
1194
  * take(3)(exponential("100ms")),
824
1195
  * take(5)(fixed("500ms")),
825
1196
  * fixed("1s"),
826
- * );
827
- * // Runs: 100ms, 200ms, 400ms, then 500ms×5, then 1s forever
1197
+ * )(testCreateDeps());
1198
+ * step(undefined);
1199
+ * step(undefined);
1200
+ * step(undefined);
1201
+ * expectOk(step(undefined), [0, 500]);
828
1202
  * ```
829
1203
  *
830
1204
  * @group Composition
@@ -835,14 +1209,28 @@ export declare const sequenceSchedules: <Output, Input>(...schedules: ReadonlyAr
835
1209
  *
836
1210
  * Continues only while both schedules want to continue. Uses the maximum delay.
837
1211
  *
838
- * ### Example
1212
+ * ### Combining constraints with AND
839
1213
  *
840
1214
  * ```ts
841
- * // Retry up to 5 times AND within 30 seconds (both conditions must be met)
842
- * const both = intersectSchedules(
1215
+ * import {
1216
+ * done,
1217
+ * exponential,
1218
+ * forever,
1219
+ * intersectSchedules,
1220
+ * maxElapsed,
1221
+ * take,
1222
+ * testCreateDeps,
1223
+ * } from "@evolu/common";
1224
+ *
1225
+ * // Retry at most 5 times and only within 30 seconds.
1226
+ * const deps = testCreateDeps();
1227
+ * const step = intersectSchedules(
843
1228
  * take(5)(exponential("1s")),
844
1229
  * maxElapsed("30s")(forever),
845
- * );
1230
+ * )(deps);
1231
+ * expectOk(step(undefined), [[1000, 0], 1000]);
1232
+ * deps.time.advance("30s");
1233
+ * expectErr(step(undefined), done());
846
1234
  * ```
847
1235
  *
848
1236
  * @group Composition
@@ -853,14 +1241,26 @@ export declare const intersectSchedules: <OutputA, OutputB, Input>(a: Schedule<O
853
1241
  *
854
1242
  * Continues while either schedule wants to continue. Uses the minimum delay.
855
1243
  *
856
- * ### Example
1244
+ * ### Combining constraints with OR
857
1245
  *
858
1246
  * ```ts
859
- * // Retry up to 5 times OR up to 30 seconds, whichever is longer
1247
+ * import {
1248
+ * done,
1249
+ * spaced,
1250
+ * take,
1251
+ * testCreateDeps,
1252
+ * unionSchedules,
1253
+ * } from "@evolu/common";
1254
+ *
1255
+ * // The second policy keeps the union alive after the first one stops.
860
1256
  * const either = unionSchedules(
861
- * take(5)(exponential("1s")),
862
- * maxElapsed("30s")(forever),
1257
+ * take(1)(spaced("100ms")),
1258
+ * take(2)(spaced("200ms")),
863
1259
  * );
1260
+ * const step = either(testCreateDeps());
1261
+ * expectOk(step(undefined), [100, 100]);
1262
+ * expectOk(step(undefined), [200, 200]);
1263
+ * expectErr(step(undefined), done());
864
1264
  * ```
865
1265
  *
866
1266
  * @group Composition
@@ -873,15 +1273,36 @@ export declare const unionSchedules: <OutputA, OutputB, Input>(a: Schedule<Outpu
873
1273
  * base schedule. Useful for implementing error-aware backoff where certain
874
1274
  * errors (e.g., throttling) use different delays.
875
1275
  *
876
- * ### Example
1276
+ * Each branch has independent state. Place combinators such as {@link take}
1277
+ * outside `whenInput` when their state must be shared across both branches.
1278
+ *
1279
+ * ### Selecting a schedule by input
877
1280
  *
878
1281
  * ```ts
1282
+ * import {
1283
+ * done,
1284
+ * exponential,
1285
+ * take,
1286
+ * testCreateDeps,
1287
+ * whenInput,
1288
+ * type Millis,
1289
+ * type Typed,
1290
+ * } from "@evolu/common";
1291
+ *
879
1292
  * interface MyError extends Typed<"Throttled" | "NetworkError"> {}
880
1293
  *
881
- * const awsWithThrottling = whenInput<MyError, Millis>(
882
- * (error) => error.type === "Throttled",
883
- * exponential("1s"), // throttled: 1s base
884
- * )(exponential("100ms")); // normal: 100ms base
1294
+ * // The outer take shares one retry limit across both error branches.
1295
+ * const awsWithThrottling = take(3)(
1296
+ * whenInput<MyError, Millis>(
1297
+ * (error) => error.type === "Throttled",
1298
+ * exponential("1s"),
1299
+ * )(exponential("100ms")),
1300
+ * );
1301
+ * const step = awsWithThrottling(testCreateDeps());
1302
+ * expectOk(step({ type: "Throttled" }), [1000, 1000]);
1303
+ * expectOk(step({ type: "NetworkError" }), [100, 100]);
1304
+ * expectOk(step({ type: "Throttled" }), [2000, 2000]);
1305
+ * expectErr(step({ type: "NetworkError" }), done());
885
1306
  * ```
886
1307
  *
887
1308
  * @group Composition
@@ -893,19 +1314,34 @@ export declare const whenInput: <Input, Output>(predicate: Predicate<Input>, alt
893
1314
  * Useful for logging, monitoring, or debugging without changing schedule
894
1315
  * behavior.
895
1316
  *
896
- * ### Example
1317
+ * ### Observing schedule output
897
1318
  *
898
1319
  * ```ts
899
- * // Log each delay for debugging
1320
+ * import {
1321
+ * exponential,
1322
+ * retryStrategyAws,
1323
+ * tapScheduleOutput,
1324
+ * testCreateDeps,
1325
+ * type Millis,
1326
+ * } from "@evolu/common";
1327
+ *
1328
+ * // Log each delay for debugging.
1329
+ * const messages: Array<string> = [];
900
1330
  * const logged = tapScheduleOutput((delay: Millis) => {
901
- * console.log(`Next delay: ${delay}ms`);
1331
+ * messages.push(`Next delay: ${delay}ms`);
902
1332
  * })(exponential("100ms"));
903
1333
  *
904
- * // Track metrics
905
- * const recorded: Array<Millis> = [];
906
- * const tracked = tapScheduleOutput((delay: Millis) => {
907
- * recorded.push(delay);
1334
+ * // Track the preset's pre-jitter delay candidates without changing it.
1335
+ * const recordedCandidates: Array<Millis> = [];
1336
+ * const tracked = tapScheduleOutput((candidate: Millis) => {
1337
+ * recordedCandidates.push(candidate);
908
1338
  * })(retryStrategyAws);
1339
+ * const deps = testCreateDeps();
1340
+ * logged(deps)(undefined);
1341
+ * tracked(deps)(undefined);
1342
+ *
1343
+ * expect(messages).toEqual(["Next delay: 100ms"]);
1344
+ * expect(recordedCandidates).toEqual([50]);
909
1345
  * ```
910
1346
  *
911
1347
  * @group Side effects
@@ -917,37 +1353,54 @@ export declare const tapScheduleOutput: <Output>(f: (output: Output) => void) =>
917
1353
  * Useful for logging errors during retry or monitoring what values are being
918
1354
  * processed.
919
1355
  *
920
- * ### Example
1356
+ * ### Observing schedule input
921
1357
  *
922
1358
  * ```ts
923
- * interface MyError extends Typed<string> {}
1359
+ * import {
1360
+ * exponential,
1361
+ * tapScheduleInput,
1362
+ * testCreateDeps,
1363
+ * type Millis,
1364
+ * type Schedule,
1365
+ * type Typed,
1366
+ * } from "@evolu/common";
924
1367
  *
1368
+ * interface MyError extends Typed<"NetworkError" | "Timeout"> {}
925
1369
  * const retrySchedule: Schedule<Millis, MyError> = exponential("100ms");
926
- *
927
- * // Log each error during retry
1370
+ * // Log errors for debugging.
1371
+ * const messages: Array<string> = [];
928
1372
  * const logged = tapScheduleInput((error: MyError) => {
929
- * console.log(`Retrying after error: ${error.type}`);
1373
+ * messages.push(`Retrying after error: ${error.type}`);
930
1374
  * })(retrySchedule);
931
1375
  *
932
- * // Track retry reasons
1376
+ * // Or retain just the retry reasons for metrics.
933
1377
  * const reasons: Array<string> = [];
934
1378
  * const tracked = tapScheduleInput((error: MyError) => {
935
1379
  * reasons.push(error.type);
936
1380
  * })(retrySchedule);
1381
+ * const deps = testCreateDeps();
1382
+ * const error: MyError = { type: "NetworkError" };
1383
+ * logged(deps)(error);
1384
+ * tracked(deps)(error);
1385
+ *
1386
+ * expect(messages).toEqual(["Retrying after error: NetworkError"]);
1387
+ * expect(reasons).toEqual(["NetworkError"]);
937
1388
  * ```
938
1389
  *
939
1390
  * @group Side effects
940
1391
  */
941
1392
  export declare const tapScheduleInput: <Input>(f: (input: Input) => void) => <Output>(schedule: Schedule<Output, Input>) => Schedule<Output, Input>;
942
1393
  /**
943
- * AWS standard retry strategy.
1394
+ * AWS SDK for Java 2.1 ordinary-failure retry timing.
944
1395
  *
945
- * Exponential backoff (100ms base), max 2 retries (3 total attempts), 20s cap,
1396
+ * Exponential backoff (50ms base), max 2 retries (3 total attempts), 20s cap,
946
1397
  * full jitter.
947
1398
  *
1399
+ * This schedule does not model throttling-specific timing, token accounting, or
1400
+ * circuit breaking.
1401
+ *
948
1402
  * @group Retry Strategies
949
- * @see https://github.com/aws/aws-sdk-java-v2/blob/master/core/retries/src/main/java/software/amazon/awssdk/retries/StandardRetryStrategy.java
950
- * @see https://github.com/aws/aws-sdk-java-v2/blob/master/core/retries/src/main/java/software/amazon/awssdk/retries/DefaultRetryStrategy.java
1403
+ * @see https://github.com/aws/aws-sdk-java-v2/blob/b69b75f07b6ebd93fd44b032d49b76a3b71fbb90/core/retries/src/main/java/software/amazon/awssdk/retries/DefaultRetryStrategy.java
951
1404
  */
952
1405
  export declare const retryStrategyAws: Schedule<Millis>;
953
1406
  //# sourceMappingURL=Schedule.d.ts.map