import { randomBytes } from "crypto" ; import { onExit } from "./exit" ; let exiting = false ; let onExitRegistered = false ; export type RetryDelay = ( attempt : number ) => number ; export interface RetryDelayOptions { cancel : ( ) => boolean ; factor : number ; maxTimeoutMs : number ; minTimeoutMs : number ; randomize : boolean ; retries : number ; } const defaultRetryDelayOptions = { cancel : ( ) => false , factor : 2 , maxTimeoutMs : 1000 , minTimeoutMs : 100 , randomize : true , retries : Infinity , } ; export function rand ( min , max ) { return randomBytes ( 4 ) . readUInt32BE ( 0 ) * ( max - min ) / 0xffffffff + min ; } remarks returns export function makeRetryDelay ( options = { } ) { const opts = { ... defaultRetryDelayOptions , ... options } ; if ( opts . minTimeoutMs < 1 ) { throw new Error ( `minTimeoutMs must be at least 1 (min=${ opts . minTimeoutMs })` ) ; } if ( opts . maxTimeoutMs <= opts . minTimeoutMs ) { throw new Error ( `maxTimeoutMs must be greater than minTimeoutMs(min=${ opts . minTimeoutMs } max=${ opts . maxTimeoutMs })` ) ; } if ( ! onExitRegistered ) { onExit ( ( ) => exiting = true ) ; onExitRegistered = true ; } return ( attempt ) => { if ( attempt < 0 ) { throw new Error ( `Retry attempt cannot be less than 0. (attempt=${ attempt })` ) ; } if ( exiting || attempt >= opts . retries || opts . cancel ( ) ) return - 1 ; const jitter = opts . randomize ? rand ( 1 , 2 ) : 1 ; let retry = jitter * opts . minTimeoutMs * Math . pow ( opts . factor , attempt ) ; if ( retry > opts . maxTimeoutMs ) { retry = opts . maxTimeoutMs ; if ( opts . randomize ) { retry -= rand ( 0 , opts . minTimeoutMs ) ; } } return retry ; } ; }