Suite of AT Protocol TypeScript libraries built on web standards
1import { wait } from "./util.ts";
2
3export type RetryOptions = {
4 maxRetries?: number;
5 getWaitMs?: (n: number) => number | null;
6};
7
8export async function retry<T>(
9 fn: () => Promise<T> | T,
10 opts: RetryOptions & {
11 retryable?: (err: unknown) => boolean;
12 } = {},
13): Promise<T> {
14 const { maxRetries = 3, retryable = () => true, getWaitMs = backoffMs } =
15 opts;
16 let retries = 0;
17 let doneError: unknown;
18 while (!doneError) {
19 try {
20 return await fn();
21 } catch (err) {
22 const waitMs = getWaitMs(retries);
23 const willRetry = retries < maxRetries && waitMs !== null &&
24 retryable(err);
25 if (willRetry) {
26 retries += 1;
27 if (waitMs !== 0) {
28 await wait(waitMs);
29 }
30 } else {
31 doneError = err;
32 }
33 }
34 }
35 throw doneError;
36}
37
38export function createRetryable(retryable: (err: unknown) => boolean): {
39 <T>(fn: () => Promise<T>, opts?: RetryOptions): Promise<T>;
40} {
41 return <T>(fn: () => Promise<T>, opts?: RetryOptions) =>
42 retry(fn, { ...opts, retryable });
43}
44
45export function backoffMs(n: number, multiplier = 100, max = 1000): number {
46 const exponentialMs = Math.pow(2, n) * multiplier;
47 const ms = Math.min(exponentialMs, max);
48 return jitter(ms);
49}
50
51function jitter(value: number) {
52 const delta = value * 0.15;
53 return value + randomRange(-delta, delta);
54}
55
56function randomRange(from: number, to: number) {
57 const rand = Math.random() * (to - from);
58 return rand + from;
59}