@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.
- package/dist/src/Array.d.ts +279 -173
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +105 -101
- package/dist/src/Assert.d.ts +44 -44
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +44 -32
- package/dist/src/BigInt.d.ts +6 -3
- package/dist/src/BigInt.d.ts.map +1 -1
- package/dist/src/BigInt.js +5 -2
- package/dist/src/Brand.d.ts +27 -40
- package/dist/src/Brand.d.ts.map +1 -1
- package/dist/src/Buffer.d.ts +27 -11
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Cache.d.ts +9 -4
- package/dist/src/Cache.d.ts.map +1 -1
- package/dist/src/Cache.js +8 -3
- package/dist/src/Callbacks.d.ts +30 -19
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Console.d.ts +145 -66
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +91 -43
- package/dist/src/Crypto.d.ts +85 -42
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +44 -20
- package/dist/src/Eq.d.ts +87 -21
- package/dist/src/Eq.d.ts.map +1 -1
- package/dist/src/Eq.js +67 -19
- package/dist/src/Error.d.ts +5 -3
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +16 -0
- package/dist/src/Function.d.ts +119 -58
- package/dist/src/Function.d.ts.map +1 -1
- package/dist/src/Function.js +86 -43
- package/dist/src/Http.d.ts +394 -0
- package/dist/src/Http.d.ts.map +1 -0
- package/dist/src/Http.js +102 -0
- package/dist/src/Identicon.d.ts +16 -7
- package/dist/src/Identicon.d.ts.map +1 -1
- package/dist/src/Identicon.js +14 -5
- package/dist/src/LeakDetector.d.ts +88 -0
- package/dist/src/LeakDetector.d.ts.map +1 -0
- package/dist/src/LeakDetector.js +86 -0
- package/dist/src/LockManager.d.ts +18 -9
- package/dist/src/LockManager.d.ts.map +1 -1
- package/dist/src/LockManager.js +7 -8
- package/dist/src/Lookup.d.ts +43 -13
- package/dist/src/Lookup.d.ts.map +1 -1
- package/dist/src/Lookup.js +28 -8
- package/dist/src/Microtask.d.ts +1 -1
- package/dist/src/Microtask.d.ts.map +1 -1
- package/dist/src/Microtask.js +3 -7
- package/dist/src/Number.d.ts +49 -13
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +34 -5
- package/dist/src/Object.d.ts +160 -40
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +92 -46
- package/dist/src/Option.d.ts +10 -7
- package/dist/src/Option.d.ts.map +1 -1
- package/dist/src/Option.js +9 -6
- package/dist/src/Order.d.ts +21 -21
- package/dist/src/Order.d.ts.map +1 -1
- package/dist/src/Order.js +18 -21
- package/dist/src/Platform.d.ts +31 -0
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +98 -0
- package/dist/src/Random.d.ts +32 -17
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +12 -8
- package/dist/src/Redacted.d.ts +43 -29
- package/dist/src/Redacted.d.ts.map +1 -1
- package/dist/src/Redacted.js +14 -3
- package/dist/src/Ref.d.ts +11 -3
- package/dist/src/Ref.d.ts.map +1 -1
- package/dist/src/Ref.js +2 -2
- package/dist/src/RefCount.d.ts +2 -6
- package/dist/src/RefCount.d.ts.map +1 -1
- package/dist/src/RefCount.js +33 -90
- package/dist/src/Relation.d.ts +70 -5
- package/dist/src/Relation.d.ts.map +1 -1
- package/dist/src/Relation.js +77 -2
- package/dist/src/Resource.d.ts +590 -155
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +592 -343
- package/dist/src/Result.d.ts +838 -191
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +163 -102
- package/dist/src/Schedule.d.ts +684 -231
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Schedule.js +766 -295
- package/dist/src/Set.d.ts +89 -40
- package/dist/src/Set.d.ts.map +1 -1
- package/dist/src/Set.js +50 -29
- package/dist/src/Sqlite.d.ts +29 -72
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +41 -46
- package/dist/src/StackTrace.d.ts +40 -0
- package/dist/src/StackTrace.d.ts.map +1 -0
- package/dist/src/StackTrace.js +140 -0
- package/dist/src/Store.d.ts +5 -4
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +5 -4
- package/dist/src/String.d.ts +7 -1
- package/dist/src/String.d.ts.map +1 -1
- package/dist/src/String.js +20 -7
- package/dist/src/Task.d.ts +3572 -1770
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +1879 -1274
- package/dist/src/Test.d.ts +17 -125
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +17 -68
- package/dist/src/Time.d.ts +108 -49
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +172 -73
- package/dist/src/Type.d.ts +3542 -1926
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +3070 -1784
- package/dist/src/Types.d.ts +200 -108
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/Types.js +39 -8
- package/dist/src/WebSocket.d.ts +48 -23
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +15 -6
- package/dist/src/Worker.d.ts +94 -36
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +58 -24
- package/dist/src/index.d.ts +55 -52
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -1
- package/dist/src/intl/cs.d.ts +94 -0
- package/dist/src/intl/cs.d.ts.map +1 -0
- package/dist/src/intl/cs.js +220 -0
- package/dist/src/intl/index.d.ts +7 -0
- package/dist/src/intl/index.d.ts.map +1 -0
- package/dist/src/intl/index.js +6 -0
- package/dist/src/local-first/Db.d.ts +12 -8
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +109 -132
- package/dist/src/local-first/Error.d.ts +4 -4
- package/dist/src/local-first/Evolu.d.ts +338 -92
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +47 -55
- package/dist/src/local-first/LocalAuth.d.ts +4 -4
- package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
- package/dist/src/local-first/Owner.d.ts +61 -24
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +45 -8
- package/dist/src/local-first/Protocol.d.ts +44 -31
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +36 -13
- package/dist/src/local-first/Query.d.ts +147 -62
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Query.js +112 -58
- package/dist/src/local-first/Relay.d.ts +44 -14
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Schema.d.ts +92 -41
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +25 -8
- package/dist/src/local-first/Shared.d.ts +23 -21
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +100 -57
- package/dist/src/local-first/Storage.d.ts +53 -32
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +194 -234
- package/dist/src/local-first/Timestamp.d.ts +21 -21
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +2 -2
- package/dist/src/local-first/index.d.ts +11 -11
- package/package.json +14 -13
- package/src/Array.ts +306 -205
- package/src/Assert.ts +54 -69
- package/src/BigInt.ts +6 -3
- package/src/Brand.ts +27 -40
- package/src/Buffer.ts +28 -12
- package/src/Cache.ts +9 -4
- package/src/Callbacks.ts +30 -19
- package/src/Console.ts +156 -84
- package/src/Crypto.ts +83 -41
- package/src/Eq.ts +95 -22
- package/src/Error.ts +8 -4
- package/src/Function.ts +167 -59
- package/src/Http.ts +549 -0
- package/src/Identicon.ts +16 -7
- package/src/LeakDetector.ts +182 -0
- package/src/LockManager.ts +43 -39
- package/src/Lookup.ts +62 -28
- package/src/Microtask.ts +19 -23
- package/src/Number.ts +64 -12
- package/src/Object.ts +173 -49
- package/src/Option.ts +10 -7
- package/src/Order.ts +23 -21
- package/src/Platform.ts +136 -0
- package/src/Polyfills.ts +6 -12
- package/src/Random.ts +32 -19
- package/src/Redacted.ts +44 -30
- package/src/Ref.ts +14 -6
- package/src/RefCount.ts +38 -80
- package/src/Relation.ts +202 -10
- package/src/Resource.ts +1199 -591
- package/src/Result.ts +930 -271
- package/src/Schedule.ts +859 -343
- package/src/Set.ts +89 -40
- package/src/Sqlite.ts +144 -138
- package/src/StackTrace.ts +230 -0
- package/src/Store.ts +8 -7
- package/src/String.ts +22 -5
- package/src/Task.ts +5101 -2832
- package/src/Test.ts +20 -168
- package/src/Time.ts +293 -90
- package/src/Type.ts +10034 -4117
- package/src/Types.ts +227 -110
- package/src/WebSocket.ts +64 -36
- package/src/Worker.ts +180 -72
- package/src/index.ts +55 -52
- package/src/intl/cs.ts +388 -0
- package/src/intl/index.ts +7 -0
- package/src/local-first/Db.ts +152 -179
- package/src/local-first/Error.ts +4 -4
- package/src/local-first/Evolu.ts +495 -250
- package/src/local-first/LocalAuth.ts +6 -12
- package/src/local-first/Owner.ts +58 -21
- package/src/local-first/Protocol.ts +49 -31
- package/src/local-first/Query.ts +146 -61
- package/src/local-first/Relay.ts +51 -21
- package/src/local-first/Schema.ts +109 -51
- package/src/local-first/Shared.ts +266 -226
- package/src/local-first/Storage.ts +266 -264
- package/src/local-first/Timestamp.ts +25 -18
- package/src/local-first/index.ts +11 -11
- package/dist/src/Skiplist.d.ts +0 -28
- package/dist/src/Skiplist.d.ts.map +0 -1
- package/dist/src/Skiplist.js +0 -64
- package/dist/src/Tracer.d.ts +0 -48
- package/dist/src/Tracer.d.ts.map +0 -1
- package/dist/src/Tracer.js +0 -6
- package/src/Skiplist.ts +0 -108
- package/src/Tracer.ts +0 -54
package/dist/src/Schedule.d.ts
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import type
|
|
7
|
-
import {
|
|
8
|
-
import { type
|
|
9
|
-
import {
|
|
10
|
-
import type
|
|
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
|
-
*
|
|
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(
|
|
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 {
|
|
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
|
|
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
|
-
* ###
|
|
105
|
+
* ### Recurring immediately
|
|
77
106
|
*
|
|
78
107
|
* ```ts
|
|
79
|
-
*
|
|
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
|
-
*
|
|
122
|
+
* Equivalent to `take(1)(forever)`. Useful for one-shot operations.
|
|
90
123
|
*
|
|
91
|
-
* ###
|
|
124
|
+
* ### Scheduling one recurrence
|
|
92
125
|
*
|
|
93
126
|
* ```ts
|
|
94
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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:
|
|
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
|
-
* ###
|
|
171
|
+
* ### Constant spacing
|
|
124
172
|
*
|
|
125
173
|
* ```ts
|
|
126
|
-
*
|
|
127
|
-
* const poll = spaced("1s");
|
|
174
|
+
* import { spaced, take, testCreateDeps } from "@evolu/common";
|
|
128
175
|
*
|
|
129
|
-
* //
|
|
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^(
|
|
193
|
+
* Computes delay as `base * factor^(step - 1)`:
|
|
143
194
|
*
|
|
144
|
-
* -
|
|
145
|
-
* -
|
|
146
|
-
* -
|
|
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
|
-
* ###
|
|
202
|
+
* ### Exponential growth factors
|
|
152
203
|
*
|
|
153
204
|
* ```ts
|
|
154
|
-
*
|
|
155
|
-
* const exp = exponential("100ms");
|
|
205
|
+
* import { exponential, testCreateDeps } from "@evolu/common";
|
|
156
206
|
*
|
|
157
|
-
* //
|
|
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 *
|
|
224
|
+
* Delay increases linearly: `base * step`:
|
|
168
225
|
*
|
|
169
|
-
* -
|
|
170
|
-
* -
|
|
171
|
-
* -
|
|
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
|
-
* ###
|
|
233
|
+
* ### Linear growth
|
|
177
234
|
*
|
|
178
235
|
* ```ts
|
|
236
|
+
* import { linear, testCreateDeps } from "@evolu/common";
|
|
237
|
+
*
|
|
179
238
|
* // 100ms, 200ms, 300ms, 400ms, ...
|
|
180
|
-
* const
|
|
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
|
-
* -
|
|
192
|
-
* -
|
|
193
|
-
* -
|
|
194
|
-
* -
|
|
195
|
-
* -
|
|
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
|
-
* ###
|
|
261
|
+
* ### Fibonacci growth
|
|
201
262
|
*
|
|
202
263
|
* ```ts
|
|
203
|
-
*
|
|
204
|
-
*
|
|
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
|
|
281
|
+
* maintains a consistent cadence from the first schedule step.
|
|
216
282
|
*
|
|
217
|
-
* If execution
|
|
218
|
-
* immediately
|
|
219
|
-
*
|
|
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
|
-
* ###
|
|
287
|
+
* ### Maintaining a fixed cadence
|
|
222
288
|
*
|
|
223
289
|
* ```ts
|
|
224
|
-
*
|
|
225
|
-
* const healthCheck = take(10)(fixed("5s"));
|
|
290
|
+
* import { fixed, take, testCreateDeps } from "@evolu/common";
|
|
226
291
|
*
|
|
227
|
-
* //
|
|
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
|
|
238
|
-
*
|
|
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
|
|
313
|
+
* Useful for aligning executions to regular intervals from the first step.
|
|
241
314
|
*
|
|
242
|
-
* ###
|
|
315
|
+
* ### Aligning to time windows
|
|
243
316
|
*
|
|
244
317
|
* ```ts
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
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
|
-
* ###
|
|
341
|
+
* ### Scheduling one delayed recurrence
|
|
259
342
|
*
|
|
260
343
|
* ```ts
|
|
261
|
-
*
|
|
262
|
-
*
|
|
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
|
-
* ###
|
|
361
|
+
* ### Sequencing custom delays
|
|
275
362
|
*
|
|
276
363
|
* ```ts
|
|
277
|
-
*
|
|
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
|
|
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
|
-
* ###
|
|
384
|
+
* ### Tracking elapsed time
|
|
291
385
|
*
|
|
292
386
|
* ```ts
|
|
293
|
-
*
|
|
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
|
-
*
|
|
399
|
+
* expectOk(withTiming(testCreateDeps())(undefined), [[100, 0], 100]);
|
|
296
400
|
*
|
|
297
|
-
* //
|
|
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
|
-
* ###
|
|
421
|
+
* ### Time-boxing a schedule
|
|
313
422
|
*
|
|
314
423
|
* ```ts
|
|
315
|
-
*
|
|
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
|
|
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
|
-
* ###
|
|
456
|
+
* ### Adding constant output
|
|
334
457
|
*
|
|
335
458
|
* ```ts
|
|
336
|
-
*
|
|
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
|
-
* //
|
|
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
|
-
* ###
|
|
491
|
+
* ### Unfolding state
|
|
357
492
|
*
|
|
358
493
|
* ```ts
|
|
359
|
-
*
|
|
360
|
-
* const counter = unfoldSchedule(0, (n) => n + 1);
|
|
494
|
+
* import { testCreateDeps, unfoldSchedule } from "@evolu/common";
|
|
361
495
|
*
|
|
362
|
-
* //
|
|
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
|
|
529
|
+
* Limits a schedule to a maximum number of steps.
|
|
530
|
+
*
|
|
531
|
+
* After `n` steps, returns `Err(Done<void>)` (stop).
|
|
386
532
|
*
|
|
387
|
-
*
|
|
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
|
-
* ###
|
|
536
|
+
* ### Limiting a schedule
|
|
390
537
|
*
|
|
391
538
|
* ```ts
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
* //
|
|
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:
|
|
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
|
|
404
|
-
* `Err(Done<void>)`.
|
|
555
|
+
* After `duration` has elapsed since the first step, returns `Err(Done<void>)`.
|
|
405
556
|
*
|
|
406
|
-
* ###
|
|
557
|
+
* ### Limiting elapsed time
|
|
407
558
|
*
|
|
408
559
|
* ```ts
|
|
409
|
-
*
|
|
410
|
-
*
|
|
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
|
-
* ###
|
|
583
|
+
* ### Capping delays
|
|
422
584
|
*
|
|
423
585
|
* ```ts
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
* // 1s, 2s, 4s, 8s,
|
|
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
|
-
*
|
|
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.
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
446
|
-
*
|
|
619
|
+
* import {
|
|
620
|
+
* exponential,
|
|
621
|
+
* jitter,
|
|
622
|
+
* spaced,
|
|
623
|
+
* testCreateDeps,
|
|
624
|
+
* type RandomNumber,
|
|
625
|
+
* } from "@evolu/common";
|
|
447
626
|
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
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: (
|
|
643
|
+
export declare const jitter: (percentage?: Percentage, mode?: "below" | "around") => (<Output, Input>(schedule: Schedule<Output, Input>) => Schedule<Output, Input>);
|
|
455
644
|
/**
|
|
456
|
-
*
|
|
645
|
+
* Replaces the schedule's first delay.
|
|
457
646
|
*
|
|
458
|
-
*
|
|
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
|
-
* ###
|
|
650
|
+
* ### Replacing the first delay
|
|
461
651
|
*
|
|
462
652
|
* ```ts
|
|
463
|
-
*
|
|
464
|
-
*
|
|
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
|
-
* ###
|
|
667
|
+
* ### Adding to every delay
|
|
474
668
|
*
|
|
475
669
|
* ```ts
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
* //
|
|
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
|
-
* ###
|
|
685
|
+
* ### Transforming delays
|
|
490
686
|
*
|
|
491
687
|
* ```ts
|
|
492
|
-
*
|
|
493
|
-
* const slower = modifyDelay((d) => d * 2)(exponential("100ms"));
|
|
688
|
+
* import { exponential, modifyDelay, testCreateDeps } from "@evolu/common";
|
|
494
689
|
*
|
|
495
|
-
* //
|
|
496
|
-
* const
|
|
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
|
|
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
|
-
*
|
|
506
|
-
*
|
|
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
|
-
* ###
|
|
719
|
+
* ### Compensating for execution time
|
|
511
720
|
*
|
|
512
721
|
* ```ts
|
|
513
|
-
*
|
|
514
|
-
*
|
|
515
|
-
*
|
|
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
|
-
* ###
|
|
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
|
-
* //
|
|
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
|
-
* ###
|
|
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
|
|
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
|
-
* ###
|
|
810
|
+
* ### Continuing by output
|
|
568
811
|
*
|
|
569
812
|
* ```ts
|
|
570
|
-
*
|
|
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
|
-
* ###
|
|
840
|
+
* ### Stopping by output
|
|
585
841
|
*
|
|
586
842
|
* ```ts
|
|
587
|
-
*
|
|
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
|
|
867
|
+
* Resets a running schedule after a period of inactivity.
|
|
598
868
|
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
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
|
-
* ###
|
|
873
|
+
* ### Resetting after inactivity
|
|
604
874
|
*
|
|
605
875
|
* ```ts
|
|
606
|
-
*
|
|
607
|
-
*
|
|
608
|
-
*
|
|
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:
|
|
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
|
-
* ###
|
|
898
|
+
* ### Mapping schedule output
|
|
621
899
|
*
|
|
622
900
|
* ```ts
|
|
623
|
-
* import {
|
|
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
|
-
* ###
|
|
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
|
|
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
|
-
* ###
|
|
957
|
+
* ### Folding schedule output
|
|
670
958
|
*
|
|
671
959
|
* ```ts
|
|
672
|
-
*
|
|
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
|
-
* (
|
|
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
|
-
* (
|
|
690
|
-
* attempts:
|
|
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
|
-
* ###
|
|
1008
|
+
* ### Counting repetitions
|
|
707
1009
|
*
|
|
708
1010
|
* ```ts
|
|
709
|
-
*
|
|
710
|
-
*
|
|
711
|
-
* //
|
|
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
|
-
* ###
|
|
1028
|
+
* ### Exposing and observing delays
|
|
724
1029
|
*
|
|
725
1030
|
* ```ts
|
|
726
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
731
|
-
*
|
|
732
|
-
*
|
|
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
|
|
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
|
-
* ###
|
|
1062
|
+
* ### Collecting outputs
|
|
745
1063
|
*
|
|
746
1064
|
* ```ts
|
|
747
|
-
*
|
|
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
|
-
*
|
|
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
|
-
* ###
|
|
1088
|
+
* ### Collecting inputs
|
|
762
1089
|
*
|
|
763
1090
|
* ```ts
|
|
764
|
-
*
|
|
765
|
-
*
|
|
766
|
-
*
|
|
767
|
-
*
|
|
768
|
-
*
|
|
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
|
-
* ###
|
|
1117
|
+
* ### Collecting while output matches
|
|
781
1118
|
*
|
|
782
1119
|
* ```ts
|
|
783
|
-
*
|
|
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
|
-
*
|
|
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
|
|
1147
|
+
* predicate returns true.
|
|
798
1148
|
*
|
|
799
|
-
* ###
|
|
1149
|
+
* ### Collecting until output matches
|
|
800
1150
|
*
|
|
801
1151
|
* ```ts
|
|
802
|
-
*
|
|
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
|
-
*
|
|
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
|
-
* ###
|
|
1181
|
+
* ### Sequencing strategies
|
|
819
1182
|
*
|
|
820
1183
|
* ```ts
|
|
821
|
-
*
|
|
822
|
-
*
|
|
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
|
-
*
|
|
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
|
-
* ###
|
|
1212
|
+
* ### Combining constraints with AND
|
|
839
1213
|
*
|
|
840
1214
|
* ```ts
|
|
841
|
-
*
|
|
842
|
-
*
|
|
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
|
-
* ###
|
|
1244
|
+
* ### Combining constraints with OR
|
|
857
1245
|
*
|
|
858
1246
|
* ```ts
|
|
859
|
-
*
|
|
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(
|
|
862
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
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
|
-
* ###
|
|
1317
|
+
* ### Observing schedule output
|
|
897
1318
|
*
|
|
898
1319
|
* ```ts
|
|
899
|
-
*
|
|
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
|
-
*
|
|
1331
|
+
* messages.push(`Next delay: ${delay}ms`);
|
|
902
1332
|
* })(exponential("100ms"));
|
|
903
1333
|
*
|
|
904
|
-
* // Track
|
|
905
|
-
* const
|
|
906
|
-
* const tracked = tapScheduleOutput((
|
|
907
|
-
*
|
|
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
|
-
* ###
|
|
1356
|
+
* ### Observing schedule input
|
|
921
1357
|
*
|
|
922
1358
|
* ```ts
|
|
923
|
-
*
|
|
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
|
-
*
|
|
1370
|
+
* // Log errors for debugging.
|
|
1371
|
+
* const messages: Array<string> = [];
|
|
928
1372
|
* const logged = tapScheduleInput((error: MyError) => {
|
|
929
|
-
*
|
|
1373
|
+
* messages.push(`Retrying after error: ${error.type}`);
|
|
930
1374
|
* })(retrySchedule);
|
|
931
1375
|
*
|
|
932
|
-
* //
|
|
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
|
|
1394
|
+
* AWS SDK for Java 2.1 ordinary-failure retry timing.
|
|
944
1395
|
*
|
|
945
|
-
* Exponential backoff (
|
|
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/
|
|
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
|