@xo-cash/utils 0.0.3 → 0.0.4-development.16005132483
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/index.d.mts +330 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +523 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,528 @@ import { BchVmVersions, XOTemplateBaseTypes, XOTemplateLockingTypes, XOTemplateN
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { FungibleTokenAmount, NFTCommitment, PublicKey, Satoshis, SchnorrSignature, TemplateIdentifier, Timestamp, TokenCategory, TransactionHash } from "@xo-cash/primitives";
|
|
5
5
|
|
|
6
|
+
//#region source/errors.ts
|
|
7
|
+
/**
|
|
8
|
+
* Error thrown when a waitFor timeout is reached
|
|
9
|
+
*/
|
|
10
|
+
var WaitForTimeoutError = class extends Error {
|
|
11
|
+
constructor(type) {
|
|
12
|
+
super(`Timeout waiting for event "${type}"`);
|
|
13
|
+
this.name = "WaitForTimeoutError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region source/event-emitter.ts
|
|
19
|
+
/**
|
|
20
|
+
* A simple event emitter implementation.
|
|
21
|
+
* @template T - The event map type.
|
|
22
|
+
*/
|
|
23
|
+
var EventEmitter = class {
|
|
24
|
+
/**
|
|
25
|
+
* The listeners map.
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
#listeners = /* @__PURE__ */ new Map();
|
|
29
|
+
/**
|
|
30
|
+
* Add a listener for an event.
|
|
31
|
+
* @param type - The event type.
|
|
32
|
+
* @param listener - The listener function.
|
|
33
|
+
* @param debounceMilliseconds - The debounce time in milliseconds.
|
|
34
|
+
* @returns An off callback that can be called to stop listening for events.
|
|
35
|
+
*/
|
|
36
|
+
on(type, listener, debounceMilliseconds = 0) {
|
|
37
|
+
const { cancel, listener: cancellableListener } = this.cancellable(listener);
|
|
38
|
+
const wrappedListener = debounceMilliseconds > 0 ? this.debounce(cancellableListener, debounceMilliseconds) : cancellableListener;
|
|
39
|
+
if (!this.#listeners.has(type)) this.#listeners.set(type, /* @__PURE__ */ new Set());
|
|
40
|
+
const listenerEntry = {
|
|
41
|
+
listener,
|
|
42
|
+
wrappedListener,
|
|
43
|
+
cancel
|
|
44
|
+
};
|
|
45
|
+
this.#listeners.get(type)?.add(listenerEntry);
|
|
46
|
+
return () => this.off(type, listener);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Add a one-time listener for an event.
|
|
50
|
+
* @param type - The event type.
|
|
51
|
+
* @param listener - The listener function.
|
|
52
|
+
* @param debounceMilliseconds - The debounce time in milliseconds.
|
|
53
|
+
* @returns An off callback that can be called to stop listening for events.
|
|
54
|
+
*/
|
|
55
|
+
once(type, listener, debounceMilliseconds = 0) {
|
|
56
|
+
const wrappedListener = (detail) => {
|
|
57
|
+
this.off(type, listener);
|
|
58
|
+
listener(detail);
|
|
59
|
+
};
|
|
60
|
+
const { cancel, listener: cancellableListener } = this.cancellable(wrappedListener);
|
|
61
|
+
const debouncedListener = debounceMilliseconds > 0 ? this.debounce(cancellableListener, debounceMilliseconds) : cancellableListener;
|
|
62
|
+
if (!this.#listeners.has(type)) this.#listeners.set(type, /* @__PURE__ */ new Set());
|
|
63
|
+
const listenerEntry = {
|
|
64
|
+
listener,
|
|
65
|
+
wrappedListener: debouncedListener,
|
|
66
|
+
cancel
|
|
67
|
+
};
|
|
68
|
+
this.#listeners.get(type)?.add(listenerEntry);
|
|
69
|
+
return () => this.off(type, listener);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Remove a listener for an event.
|
|
73
|
+
* @param type - The event type.
|
|
74
|
+
* @param listener - The listener function.
|
|
75
|
+
*/
|
|
76
|
+
off(type, listener) {
|
|
77
|
+
const listeners = this.#listeners.get(type);
|
|
78
|
+
if (!listeners) return;
|
|
79
|
+
Array.from(listeners).filter((entry) => !listener || entry.listener === listener || entry.wrappedListener === listener).forEach((entry) => {
|
|
80
|
+
entry.cancel();
|
|
81
|
+
listeners.delete(entry);
|
|
82
|
+
});
|
|
83
|
+
if (!listener || this.#listeners.get(type)?.size === 0) this.#listeners.delete(type);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Emit an event.
|
|
87
|
+
*
|
|
88
|
+
* @remarks The caller is responsible for ensuring the payload suits the intended mutability requirements.
|
|
89
|
+
* By default, the payload will be mutable, so listeners may mutate the payload, effecting both
|
|
90
|
+
* the original object and the other listeners.
|
|
91
|
+
* To prevent this, the caller can use the {@link Object.freeze} or {@link deepFreeze} function to freeze the payload.
|
|
92
|
+
* This will need to be defined in the EventMap using the built-in {@link Readonly} type or the provided {@link DeeplyReadonly} type.
|
|
93
|
+
*
|
|
94
|
+
* @param type - The event type.
|
|
95
|
+
* @param payload - The event payload.
|
|
96
|
+
* @returns True if there are listeners for the event, false otherwise.
|
|
97
|
+
*/
|
|
98
|
+
emit(type, payload) {
|
|
99
|
+
const listeners = this.#listeners.get(type);
|
|
100
|
+
if (!listeners) return false;
|
|
101
|
+
listeners.forEach((entry) => {
|
|
102
|
+
try {
|
|
103
|
+
entry.wrappedListener(payload);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error(error);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return listeners.size > 0;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Remove all listeners.
|
|
112
|
+
*/
|
|
113
|
+
removeAllListeners() {
|
|
114
|
+
for (const [type, listeners] of this.#listeners.entries()) listeners.forEach((entry) => {
|
|
115
|
+
this.off(type, entry.listener);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Wait for an event to be emitted that matches the provided predicate function's criteria.
|
|
120
|
+
* @param type - The event type.
|
|
121
|
+
* @param predicate - Predicate function to filter for whether the event payload matches the criteria.
|
|
122
|
+
* @param timeoutMs - The timeout in milliseconds.
|
|
123
|
+
* @returns The event payload.
|
|
124
|
+
*/
|
|
125
|
+
async waitFor(type, predicate, timeoutMs) {
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
let timeoutId;
|
|
128
|
+
const cleanup = (listener) => {
|
|
129
|
+
this.off(type, listener);
|
|
130
|
+
if (timeoutId !== void 0) clearTimeout(timeoutId);
|
|
131
|
+
};
|
|
132
|
+
const listener = (payload) => {
|
|
133
|
+
try {
|
|
134
|
+
if (!predicate(payload)) return;
|
|
135
|
+
cleanup(listener);
|
|
136
|
+
resolve(payload);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
cleanup(listener);
|
|
139
|
+
reject(error);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
if (timeoutMs !== void 0) timeoutId = setTimeout(() => {
|
|
143
|
+
this.off(type, listener);
|
|
144
|
+
reject(new WaitForTimeoutError(String(type)));
|
|
145
|
+
}, timeoutMs);
|
|
146
|
+
this.on(type, listener);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Debounce a function.
|
|
151
|
+
*
|
|
152
|
+
* @remarks If {@link off} is called on a listener while it is debounced, the timeout is not cleared with clearTimeout.
|
|
153
|
+
* Instead, the function is no-oped.
|
|
154
|
+
*
|
|
155
|
+
* @param func - The function to debounce.
|
|
156
|
+
* @param wait - The wait time in milliseconds.
|
|
157
|
+
* @returns The debounced function.
|
|
158
|
+
*/
|
|
159
|
+
debounce(func, wait) {
|
|
160
|
+
let timeout;
|
|
161
|
+
return (detail) => {
|
|
162
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
163
|
+
timeout = setTimeout(() => {
|
|
164
|
+
func(detail);
|
|
165
|
+
}, wait);
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Make a function cancellable.
|
|
170
|
+
* @param func - The function to make cancelable.
|
|
171
|
+
* @returns The cancellable function with a cancel method.
|
|
172
|
+
*/
|
|
173
|
+
cancellable(func) {
|
|
174
|
+
let cancelled = false;
|
|
175
|
+
return {
|
|
176
|
+
cancel: () => cancelled = true,
|
|
177
|
+
listener: (detail) => {
|
|
178
|
+
if (cancelled) return;
|
|
179
|
+
func(detail);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region source/exponential-backoff/errors.ts
|
|
187
|
+
/**
|
|
188
|
+
* Error thrown when the maximum number of retries is hit in an exponential backoff
|
|
189
|
+
*/
|
|
190
|
+
var ExponentialBackoffMaxRetriesHitError = class extends Error {
|
|
191
|
+
constructor(errors) {
|
|
192
|
+
super("Exponential backoff: Max retries hit", { cause: errors });
|
|
193
|
+
this.name = "ExponentialBackoffMaxRetriesHitError";
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Error thrown when the exponential backoff retries are stopped
|
|
198
|
+
*/
|
|
199
|
+
var ExponentialBackoffStoppedRetriesError = class extends Error {
|
|
200
|
+
constructor(reason) {
|
|
201
|
+
const reasonError = reason instanceof Error ? reason : /* @__PURE__ */ new Error(`${reason}`);
|
|
202
|
+
super(`Exponential backoff was aborted: "${reasonError.message}"`, { cause: reasonError });
|
|
203
|
+
this.name = "ExponentialBackoffStoppedRetriesError";
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Error thrown when an exponential backoff option is too small
|
|
208
|
+
*/
|
|
209
|
+
var ExponentialBackoffNumberTooSmallError = class extends Error {
|
|
210
|
+
constructor(option, value, min) {
|
|
211
|
+
super(`Exponential backoff option "${option}" is too small. Must be at least ${min}. Received value: ${value}`);
|
|
212
|
+
this.name = "ExponentialBackoffNumberTooSmallError";
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Error thrown when an exponential backoff option is out of bounds
|
|
217
|
+
*/
|
|
218
|
+
var ExponentialBackoffNumberOutOfBoundsError = class extends Error {
|
|
219
|
+
constructor(option, value, min, max) {
|
|
220
|
+
super(`Exponential backoff option "${option}" is out of bounds. Must be between ${min} and ${max}. Received value: ${value}`);
|
|
221
|
+
this.name = "ExponentialBackoffNumberOutOfBoundsError";
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* Error thrown when an exponential backoff option is an invalid infinite integer
|
|
226
|
+
*/
|
|
227
|
+
var ExponentialBackoffNumberNotFiniteError = class extends Error {
|
|
228
|
+
constructor(option, value) {
|
|
229
|
+
super(`Exponential backoff option "${option}" is invalid. Must be a finite number. Received value: ${value}`);
|
|
230
|
+
this.name = "ExponentialBackoffNumberNotFiniteError";
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Error thrown when an exponential backoff option is not an integer
|
|
235
|
+
*/
|
|
236
|
+
var ExponentialBackoffNonIntegerError = class extends Error {
|
|
237
|
+
constructor(option, value) {
|
|
238
|
+
super(`Exponential backoff option "${option}" is invalid. Must be an integer. Received value: ${value}`);
|
|
239
|
+
this.name = "ExponentialBackoffNonIntegerError";
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Error thrown when an externally aborted exponential backoff is aborted
|
|
244
|
+
* due to the external abort signal that was passed in to the constructor being aborted by an upstream consumer
|
|
245
|
+
*/
|
|
246
|
+
var ExternallyAbortedExponentialBackoffExternalSignalAbortedError = class extends Error {
|
|
247
|
+
constructor(reason) {
|
|
248
|
+
const reasonError = reason instanceof Error ? reason : /* @__PURE__ */ new Error(`${reason}`);
|
|
249
|
+
super(`Externally aborted exponential backoff: "${reasonError.message}"`, { cause: reasonError });
|
|
250
|
+
this.name = "ExternallyAbortedExponentialBackoffExternalSignalAbortedError";
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* Error thrown when an externally aborted exponential backoff is aborted
|
|
255
|
+
* due to the internal abort signal being aborted using the .abort() method
|
|
256
|
+
*/
|
|
257
|
+
var ExternallyAbortedExponentialBackoffInternalSignalAbortedError = class extends Error {
|
|
258
|
+
constructor(reason) {
|
|
259
|
+
const reasonError = reason instanceof Error ? reason : /* @__PURE__ */ new Error(`${reason}`);
|
|
260
|
+
super(`Externally aborted exponential backoff: "${reasonError.message}"`, { cause: reasonError });
|
|
261
|
+
this.name = "ExternallyAbortedExponentialBackoffInternalSignalAbortedError";
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region source/misc.ts
|
|
267
|
+
/**
|
|
268
|
+
* Validate the value is within the bounds, returning true if it is within the bounds, false otherwise
|
|
269
|
+
*
|
|
270
|
+
* @param value - The value to validate
|
|
271
|
+
* @param min - The minimum value
|
|
272
|
+
* @param max - The maximum value
|
|
273
|
+
*
|
|
274
|
+
* @returns True if the value is within the bounds, false otherwise
|
|
275
|
+
*/
|
|
276
|
+
const isWithinBounds = (value, min, max) => {
|
|
277
|
+
if (value < min || value > max) return false;
|
|
278
|
+
return true;
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region source/exponential-backoff/exponential-backoff.ts
|
|
283
|
+
/**
|
|
284
|
+
* Exponential backoff is a technique used to retry a function after a delay.
|
|
285
|
+
*
|
|
286
|
+
* The delay increases exponentially with each attempt, up to a maximum delay.
|
|
287
|
+
*
|
|
288
|
+
* The jitter is a random amount of time subtracted from the delay to prevent thundering herd problems.
|
|
289
|
+
*
|
|
290
|
+
* The growth rate is the factor by which the delay increases with each attempt.
|
|
291
|
+
*/
|
|
292
|
+
var ExponentialBackoff = class ExponentialBackoff {
|
|
293
|
+
#options;
|
|
294
|
+
/**
|
|
295
|
+
* Creates a new exponential-backoff instance.
|
|
296
|
+
*
|
|
297
|
+
* Unspecified options use the defaults listed below.
|
|
298
|
+
*
|
|
299
|
+
* @param options - Exponential-backoff configuration overrides.
|
|
300
|
+
* @param options.maxDelay - Maximum delay between retries. Default: `10_000` ms.
|
|
301
|
+
* @param options.maxAttempts - Maximum number of attempts; `0` retries indefinitely. Default: `10`.
|
|
302
|
+
* @param options.baseDelay - Delay used as the basis for the first retry. Default: `1_000` ms.
|
|
303
|
+
* @param options.growthRate - Multiplier applied to the delay after each attempt. Default: `2`.
|
|
304
|
+
* @param options.jitter - Maximum proportional reduction subtracted from each delay (0–1). Default: `0.1`.
|
|
305
|
+
*
|
|
306
|
+
* @throws An {@link ExponentialBackoffNumberNotFiniteError} if a provided option is not a finite number
|
|
307
|
+
* @throws An {@link ExponentialBackoffNumberOutOfBoundsError} if a provided option is out of bounds
|
|
308
|
+
* @throws An {@link ExponentialBackoffNumberTooSmallError} if a provided option is too small
|
|
309
|
+
* @throws An {@link ExponentialBackoffNonIntegerError} if a provided option is not an integer
|
|
310
|
+
*/
|
|
311
|
+
constructor(options = {}) {
|
|
312
|
+
this.#options = {
|
|
313
|
+
maxDelay: 1e4,
|
|
314
|
+
maxAttempts: 10,
|
|
315
|
+
baseDelay: 1e3,
|
|
316
|
+
growthRate: 2,
|
|
317
|
+
jitter: .1,
|
|
318
|
+
...options
|
|
319
|
+
};
|
|
320
|
+
ExponentialBackoff.validateOptions(this.#options);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Create a new ExponentialBackoff instance
|
|
324
|
+
*
|
|
325
|
+
* @param config - The configuration for the exponential backoff
|
|
326
|
+
*
|
|
327
|
+
* @throws An {@link ExponentialBackoffNumberNotFiniteError} if a provided option is not a finite number
|
|
328
|
+
* @throws An {@link ExponentialBackoffNumberOutOfBoundsError} if a provided option is out of bounds
|
|
329
|
+
* @throws An {@link ExponentialBackoffNumberTooSmallError} if a provided option is too small
|
|
330
|
+
* @throws An {@link ExponentialBackoffNonIntegerError} if a provided option is not an integer
|
|
331
|
+
*
|
|
332
|
+
* @returns The ExponentialBackoff instance
|
|
333
|
+
*/
|
|
334
|
+
static from(config) {
|
|
335
|
+
return new ExponentialBackoff(config);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Run the function with exponential backoff
|
|
339
|
+
*
|
|
340
|
+
* @param taskFn - The function to run
|
|
341
|
+
* @param options - Backoff configuration and options for this run
|
|
342
|
+
*
|
|
343
|
+
* @throws An {@link ExponentialBackoffMaxRetriesHitError} with all the errors that were thrown by the task function
|
|
344
|
+
* @throws An {@link ExponentialBackoffStoppedRetriesError} if the abort signal is activated
|
|
345
|
+
*
|
|
346
|
+
* @returns The result of the function
|
|
347
|
+
*/
|
|
348
|
+
static run(taskFn, options = {}) {
|
|
349
|
+
const { onError, signal, ...backoffOptions } = options;
|
|
350
|
+
return ExponentialBackoff.from(backoffOptions).run(taskFn, {
|
|
351
|
+
onError,
|
|
352
|
+
signal
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Validate the options for the exponential backoff
|
|
357
|
+
*
|
|
358
|
+
* @param options - The options to validate
|
|
359
|
+
*
|
|
360
|
+
* @throws {@link ExponentialBackoffNumberNotFiniteError} if a provided option is not a finite number
|
|
361
|
+
* @throws {@link ExponentialBackoffNonIntegerError} if a provided option is not an integer
|
|
362
|
+
* @throws {@link ExponentialBackoffNumberOutOfBoundsError} if a provided option is out of bounds
|
|
363
|
+
* @throws {@link ExponentialBackoffNumberTooSmallError} if a provided option is too small
|
|
364
|
+
*/
|
|
365
|
+
static validateOptions(options) {
|
|
366
|
+
/** Validate the value is finite, throwing an {@link ExponentialBackoffInvalidInfiniteIntegerError} if the value is infinite */
|
|
367
|
+
const assertIsFinite = (key, value) => {
|
|
368
|
+
if (!Number.isFinite(value)) throw new ExponentialBackoffNumberNotFiniteError(key, value);
|
|
369
|
+
};
|
|
370
|
+
/** Validate the value is an integer, throwing a {@link ExponentialBackoffNonIntegerError} if it is not an integer */
|
|
371
|
+
const assertIsInteger = (key, value) => {
|
|
372
|
+
if (!Number.isInteger(value)) throw new ExponentialBackoffNonIntegerError(key, value);
|
|
373
|
+
};
|
|
374
|
+
/** Validate the value is greater than the minimum, throwing a {@link ExponentialBackoffNumberTooSmallError} if it is not */
|
|
375
|
+
const assertIsHigherThan = (key, value, min) => {
|
|
376
|
+
if (value < min) throw new ExponentialBackoffNumberTooSmallError(key, value, min);
|
|
377
|
+
};
|
|
378
|
+
/** Validate the value is within the bounds, throwing a {@link ExponentialBackoffNumberOutOfBoundsError} if it is not within the bounds */
|
|
379
|
+
const assertIsWithinBounds = (key, value, min, max) => {
|
|
380
|
+
if (!isWithinBounds(value, min, max)) throw new ExponentialBackoffNumberOutOfBoundsError(key, value, min, max);
|
|
381
|
+
};
|
|
382
|
+
assertIsFinite("maxDelay", options.maxDelay);
|
|
383
|
+
assertIsHigherThan("maxDelay", options.maxDelay, 0);
|
|
384
|
+
assertIsFinite("maxAttempts", options.maxAttempts);
|
|
385
|
+
assertIsInteger("maxAttempts", options.maxAttempts);
|
|
386
|
+
assertIsHigherThan("maxAttempts", options.maxAttempts, 0);
|
|
387
|
+
assertIsFinite("baseDelay", options.baseDelay);
|
|
388
|
+
assertIsHigherThan("baseDelay", options.baseDelay, 0);
|
|
389
|
+
assertIsFinite("growthRate", options.growthRate);
|
|
390
|
+
assertIsHigherThan("growthRate", options.growthRate, 0);
|
|
391
|
+
assertIsFinite("jitter", options.jitter);
|
|
392
|
+
assertIsWithinBounds("jitter", options.jitter, 0, 1);
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Run the function with exponential backoff
|
|
396
|
+
*
|
|
397
|
+
* If the function fails but we have not hit the max attempts, the error will be passed to the onError callback
|
|
398
|
+
* and the function will be retried with an exponential delay
|
|
399
|
+
*
|
|
400
|
+
* If every attempt fails, an ExponentialBackoffMaxRetriesHitError will be thrown
|
|
401
|
+
* with all errors from the task function.
|
|
402
|
+
*
|
|
403
|
+
* @param taskFn - The function to run
|
|
404
|
+
* @param options - Options that control this run
|
|
405
|
+
*
|
|
406
|
+
* @throws An {@link ExponentialBackoffMaxRetriesHitError} with all the errors that were thrown by the task function
|
|
407
|
+
* @throws An {@link ExponentialBackoffStoppedRetriesError} if the abort signal is activated
|
|
408
|
+
*
|
|
409
|
+
* @returns The result of the function
|
|
410
|
+
*/
|
|
411
|
+
async run(taskFn, options = {}) {
|
|
412
|
+
const { onError, signal: suppliedSignal } = options;
|
|
413
|
+
const abortController = new AbortController();
|
|
414
|
+
const stopRetries = abortController.abort.bind(abortController);
|
|
415
|
+
const signals = [abortController.signal];
|
|
416
|
+
if (suppliedSignal !== void 0) signals.push(suppliedSignal);
|
|
417
|
+
const signal = AbortSignal.any(signals);
|
|
418
|
+
if (signal.aborted) throw new ExponentialBackoffStoppedRetriesError(signal.reason);
|
|
419
|
+
const errors = [];
|
|
420
|
+
let attempt = 0;
|
|
421
|
+
const unlimitedAttempts = this.#options.maxAttempts === 0;
|
|
422
|
+
while (true) {
|
|
423
|
+
try {
|
|
424
|
+
return await taskFn({ stopRetries });
|
|
425
|
+
} catch (error) {
|
|
426
|
+
const errorInstance = error instanceof Error ? error : /* @__PURE__ */ new Error(`${error}`);
|
|
427
|
+
onError?.(errorInstance);
|
|
428
|
+
if (!unlimitedAttempts) errors.push(errorInstance);
|
|
429
|
+
}
|
|
430
|
+
if (signal.aborted) throw new ExponentialBackoffStoppedRetriesError(signal.reason);
|
|
431
|
+
const nextAttemptExceedsMaxAttempts = attempt + 1 >= this.#options.maxAttempts;
|
|
432
|
+
if (!unlimitedAttempts && nextAttemptExceedsMaxAttempts) break;
|
|
433
|
+
const delay = this.#calculateDelay(this.#options, attempt);
|
|
434
|
+
await new Promise((resolve, reject) => {
|
|
435
|
+
let timeout;
|
|
436
|
+
const abortHandler = () => {
|
|
437
|
+
clearTimeout(timeout);
|
|
438
|
+
reject(new ExponentialBackoffStoppedRetriesError(signal.reason));
|
|
439
|
+
};
|
|
440
|
+
const timeoutHandler = () => {
|
|
441
|
+
signal.removeEventListener("abort", abortHandler);
|
|
442
|
+
resolve(void 0);
|
|
443
|
+
};
|
|
444
|
+
timeout = setTimeout(timeoutHandler, delay);
|
|
445
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
446
|
+
});
|
|
447
|
+
attempt++;
|
|
448
|
+
}
|
|
449
|
+
throw new ExponentialBackoffMaxRetriesHitError(errors);
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Calculate the delay before we should attempt to retry
|
|
453
|
+
*
|
|
454
|
+
* @param options - The configuration for the exponential backoff
|
|
455
|
+
* @param attempt - The current attempt number
|
|
456
|
+
* @returns The time in milliseconds before another attempt should be made
|
|
457
|
+
*/
|
|
458
|
+
#calculateDelay(options, attempt) {
|
|
459
|
+
const power = options.growthRate ** attempt;
|
|
460
|
+
const rawDelay = options.baseDelay * power;
|
|
461
|
+
const cappedDelay = Math.min(rawDelay, options.maxDelay);
|
|
462
|
+
return cappedDelay - Math.random() * options.jitter * cappedDelay;
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
//#endregion
|
|
467
|
+
//#region source/exponential-backoff/exponential-backoff-externally-aborted.ts
|
|
468
|
+
/**
|
|
469
|
+
* An exponential backoff that can be stopped by calling `.abort()` or by passing an
|
|
470
|
+
* `abortSignal` to the constructor.
|
|
471
|
+
*
|
|
472
|
+
* @remarks One instance can run many tasks. Aborting it stops retries for every run.
|
|
473
|
+
*/
|
|
474
|
+
var ExponentialBackoffExternallyAbortable = class {
|
|
475
|
+
#abortController = new AbortController();
|
|
476
|
+
#exponentialBackoff;
|
|
477
|
+
constructor(options = {}) {
|
|
478
|
+
const { abortSignal, ...backoffOptions } = options;
|
|
479
|
+
this.#exponentialBackoff = new ExponentialBackoff(backoffOptions);
|
|
480
|
+
abortSignal?.addEventListener("abort", () => {
|
|
481
|
+
this.#abortController.abort(new ExternallyAbortedExponentialBackoffExternalSignalAbortedError(abortSignal.reason));
|
|
482
|
+
}, {
|
|
483
|
+
once: true,
|
|
484
|
+
signal: this.#abortController.signal
|
|
485
|
+
});
|
|
486
|
+
if (abortSignal?.aborted) this.#abortController.abort(new ExternallyAbortedExponentialBackoffExternalSignalAbortedError(abortSignal.reason));
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Run the function with exponential backoff
|
|
490
|
+
*
|
|
491
|
+
* If the function fails but we have not hit the max attempts, the error will be passed to the onError callback
|
|
492
|
+
* and the function will be retried with an exponential delay
|
|
493
|
+
*
|
|
494
|
+
* If every attempt fails, an ExponentialBackoffMaxRetriesHitError will be thrown
|
|
495
|
+
* with all errors from the task function.
|
|
496
|
+
*
|
|
497
|
+
* @param taskFn - The function to run
|
|
498
|
+
* @param options - Options that control this run
|
|
499
|
+
*
|
|
500
|
+
* @throws An {@link ExponentialBackoffMaxRetriesHitError} with all the errors that were thrown by the task function
|
|
501
|
+
* @throws An {@link ExponentialBackoffStoppedRetriesError} if the abort signal is activated
|
|
502
|
+
* @throws An {@link ExternallyAbortedExponentialBackoffExternalSignalAbortedError}
|
|
503
|
+
* if the abort signal that was provided during construction is activated
|
|
504
|
+
* @throws An {@link ExternallyAbortedExponentialBackoffInternalSignalAbortedError}
|
|
505
|
+
* if {@link ExponentialBackoffExternallyAbortable.abort} is called on this class
|
|
506
|
+
*
|
|
507
|
+
* @returns The result of the function
|
|
508
|
+
*/
|
|
509
|
+
run(taskFn, options = {}) {
|
|
510
|
+
let signal = this.#abortController.signal;
|
|
511
|
+
if (options.signal !== void 0) signal = AbortSignal.any([signal, options.signal]);
|
|
512
|
+
return this.#exponentialBackoff.run(taskFn, {
|
|
513
|
+
...options,
|
|
514
|
+
signal
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Stops retries for all current and future runs.
|
|
519
|
+
*
|
|
520
|
+
* @param reason - The reason for stopping retries
|
|
521
|
+
*/
|
|
522
|
+
abort(reason) {
|
|
523
|
+
this.#abortController.abort(new ExternallyAbortedExponentialBackoffInternalSignalAbortedError(reason));
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
//#endregion
|
|
6
528
|
//#region source/extended-json.ts
|
|
7
529
|
/**
|
|
8
530
|
* Matches a bigint encoded in Extended JSON format: `<bigint: 123n>`.
|
|
@@ -1535,5 +2057,5 @@ const compileCashAssemblyString = (parameters) => {
|
|
|
1535
2057
|
};
|
|
1536
2058
|
|
|
1537
2059
|
//#endregion
|
|
1538
|
-
export { AsyncPushIterator, CASHASSEMBLY_EVALUATION_PATTERN, CASHASSEMBLY_EXPRESSION_PATTERN, CASHASSEMBLY_LITERAL_TOKEN_PATTERN, CASHASSEMBLY_VARIABLE_METHOD_REFERENCE_PATTERN, CASHASSEMBLY_VARIABLE_PATTERN, CashAssemblyCompilationFailedError, CashAssemblyNumberNotSafeIntegerError, CashAssemblyPrimitiveMethodMissingError, CashAssemblyPrimitiveVariableMissingError, CashAssemblyRequiredVariableMissingError, CashAssemblyUnsupportedValueTypeError, CashAssemblyVariableTypeMismatchError, CashAssemblyVmNumberDecodeError, SSEEventParser, TemplateInvalidError, TemplateJsonMalformedError, TemplateSerializationFailedError, VIEW_PROPERTIES_DESCRIPTION_MAX_LENGTH, VIEW_PROPERTIES_ICON_MAX_LENGTH, VIEW_PROPERTIES_NAME_MAX_LENGTH, bchVmVersionSchema, buildErrorDescription, compileCashAssemblyEvaluations, compileCashAssemblyString, convertValueToBytes, decodeCompiledCashAssemblyEvaluation, extendedJsonReplacer, extendedJsonReviver, extractCashAssemblyEvaluations, extractVariablesFromEvaluations, fromExtendedJson, generateCashAssemblyBytecode, generateTemplateIdentifier, isCashAssemblyExpression, parseTemplate, resolvePrimitiveMethodBytes, satoshisSchema, scriptToScriptHash, serializeTemplate, toExtendedJson, uint8ArraySchema, xoTemplateActionIntentSchema, xoTemplateActionRequirementsSchema, xoTemplateActionRoleRequirementsSchema, xoTemplateActionRoleSchema, xoTemplateActionSchema, xoTemplateAssetAmountsSchema, xoTemplateBaseTypeSchema, xoTemplateConstantSchema, xoTemplateDataSchema, xoTemplateDefaultsSchema, xoTemplateIconSchema, xoTemplateImportDefaultValueSchema, xoTemplateInputSchema, xoTemplateIntentSchema, xoTemplateLockingScriptIntentSchema, xoTemplateLockingScriptRoleSchema, xoTemplateLockingScriptSchema, xoTemplateLockingTypeSchema, xoTemplateNftCapabilitySchema, xoTemplateNonFungibleTokenDetailsSchema, xoTemplateOutputIntentSchema, xoTemplateOutputSchema, xoTemplatePrimitiveTypeSchema, xoTemplateResourceSchema, xoTemplateRoleSlotSchema, xoTemplateRoleSlotsRequirementsSchema, xoTemplateSchema, xoTemplateStateSchema, xoTemplateTokenSchema, xoTemplateTransactionInputSchema, xoTemplateTransactionOutputSchema, xoTemplateTransactionRoleDataSchema, xoTemplateTransactionSchema, xoTemplateVariableSchema, xoTemplateViewPropertiesSchema };
|
|
2060
|
+
export { AsyncPushIterator, CASHASSEMBLY_EVALUATION_PATTERN, CASHASSEMBLY_EXPRESSION_PATTERN, CASHASSEMBLY_LITERAL_TOKEN_PATTERN, CASHASSEMBLY_VARIABLE_METHOD_REFERENCE_PATTERN, CASHASSEMBLY_VARIABLE_PATTERN, CashAssemblyCompilationFailedError, CashAssemblyNumberNotSafeIntegerError, CashAssemblyPrimitiveMethodMissingError, CashAssemblyPrimitiveVariableMissingError, CashAssemblyRequiredVariableMissingError, CashAssemblyUnsupportedValueTypeError, CashAssemblyVariableTypeMismatchError, CashAssemblyVmNumberDecodeError, EventEmitter, ExponentialBackoff, ExponentialBackoffExternallyAbortable, ExponentialBackoffMaxRetriesHitError, ExponentialBackoffNonIntegerError, ExponentialBackoffNumberNotFiniteError, ExponentialBackoffNumberOutOfBoundsError, ExponentialBackoffNumberTooSmallError, ExponentialBackoffStoppedRetriesError, ExternallyAbortedExponentialBackoffExternalSignalAbortedError, ExternallyAbortedExponentialBackoffInternalSignalAbortedError, SSEEventParser, TemplateInvalidError, TemplateJsonMalformedError, TemplateSerializationFailedError, VIEW_PROPERTIES_DESCRIPTION_MAX_LENGTH, VIEW_PROPERTIES_ICON_MAX_LENGTH, VIEW_PROPERTIES_NAME_MAX_LENGTH, WaitForTimeoutError, bchVmVersionSchema, buildErrorDescription, compileCashAssemblyEvaluations, compileCashAssemblyString, convertValueToBytes, decodeCompiledCashAssemblyEvaluation, extendedJsonReplacer, extendedJsonReviver, extractCashAssemblyEvaluations, extractVariablesFromEvaluations, fromExtendedJson, generateCashAssemblyBytecode, generateTemplateIdentifier, isCashAssemblyExpression, parseTemplate, resolvePrimitiveMethodBytes, satoshisSchema, scriptToScriptHash, serializeTemplate, toExtendedJson, uint8ArraySchema, xoTemplateActionIntentSchema, xoTemplateActionRequirementsSchema, xoTemplateActionRoleRequirementsSchema, xoTemplateActionRoleSchema, xoTemplateActionSchema, xoTemplateAssetAmountsSchema, xoTemplateBaseTypeSchema, xoTemplateConstantSchema, xoTemplateDataSchema, xoTemplateDefaultsSchema, xoTemplateIconSchema, xoTemplateImportDefaultValueSchema, xoTemplateInputSchema, xoTemplateIntentSchema, xoTemplateLockingScriptIntentSchema, xoTemplateLockingScriptRoleSchema, xoTemplateLockingScriptSchema, xoTemplateLockingTypeSchema, xoTemplateNftCapabilitySchema, xoTemplateNonFungibleTokenDetailsSchema, xoTemplateOutputIntentSchema, xoTemplateOutputSchema, xoTemplatePrimitiveTypeSchema, xoTemplateResourceSchema, xoTemplateRoleSlotSchema, xoTemplateRoleSlotsRequirementsSchema, xoTemplateSchema, xoTemplateStateSchema, xoTemplateTokenSchema, xoTemplateTransactionInputSchema, xoTemplateTransactionOutputSchema, xoTemplateTransactionRoleDataSchema, xoTemplateTransactionSchema, xoTemplateVariableSchema, xoTemplateViewPropertiesSchema };
|
|
1539
2061
|
//# sourceMappingURL=index.mjs.map
|