@@ -40,32 +40,43 @@ export async function post(url: URL | string, options: RequestInit) {
40
40
return response ;
41
41
}
42
42
43
- export async function retryWithBackoff < T > (
43
+ type RetryOptions = {
44
+ maxAttempts ?: number ;
45
+ baseDelay ?: number ;
46
+ retryOnStatus ?: number [ ] ; // e.g. [500, 502, 503]
47
+ shouldRetry ?: ( err : any ) => boolean ;
48
+ } ;
49
+
50
+ export async function retryWithBackoff < T > (
44
51
fn : ( ) => Promise < T > ,
45
- maxAttempts = 5 ,
46
- baseDelay = 100
52
+ {
53
+ maxAttempts = 5 ,
54
+ baseDelay = 100 ,
55
+ retryOnStatus = [ ] ,
56
+ shouldRetry,
57
+ } : RetryOptions = { }
47
58
) : Promise < T > {
48
59
for ( let attempt = 1 ; attempt <= maxAttempts ; attempt ++ ) {
49
60
try {
50
61
return await fn ( ) ;
51
62
} catch ( err : any ) {
52
- const error = err as Error & { code ?: string } ;
53
63
const isLast = attempt === maxAttempts ;
54
64
55
- // retry only for certain errors
65
+ const error = err as Error & { code ?: string ; status ?: number } ;
66
+
56
67
const retryable =
57
- // err?. 404s but that will probably not be 404 soon ?
58
- error ? .message ?. includes ( '404' ) ||
59
- // retry for timeouts as well
60
- error ?. message ?. includes ( '5' ) ;
68
+ ( typeof error . status === "number" && retryOnStatus . includes ( error . status ) ) ||
69
+ error . message ?. includes ( "5" ) ||
70
+ error . message ?. includes ( "404" ) ||
71
+ shouldRetry ?. ( error ) ;
61
72
62
73
if ( ! retryable || isLast ) throw error ;
63
74
64
75
const jitter = Math . floor ( Math . random ( ) * baseDelay ) ;
65
76
const delay = baseDelay * 2 ** ( attempt - 1 ) + jitter ;
66
77
console . warn ( `Retry ${ attempt } /${ maxAttempts } failed. Retrying in ${ delay } ms...` ) ;
67
78
68
- await new Promise ( res => setTimeout ( res , delay ) ) ;
79
+ await new Promise ( ( res ) => setTimeout ( res , delay ) ) ;
69
80
}
70
81
}
71
82
0 commit comments