wretch
Version:
A tiny wrapper built around fetch with an intuitive syntax.
1 lines • 6.5 kB
Source Map (JSON)
{"version":3,"file":"retry.min.cjs","sources":["../../../src/middlewares/retry.ts"],"sourcesContent":["import type { ConfiguredMiddleware, WretchOptions } from \"../types.js\"\n\n/* Types */\n\nexport type DelayRampFunction = (delay: number, nbOfAttempts: number) => number\nexport type UntilFunction = (response?: Response, error?: Error) => boolean | Promise<boolean>\nexport type OnRetryFunctionResponse = { url?: string; options?: WretchOptions } | undefined\nexport type OnRetryFunction = (args: {\n response?: Response,\n error?: Error,\n url: string,\n options: WretchOptions\n}) => void | OnRetryFunctionResponse | Promise<OnRetryFunctionResponse>\nexport type RetryOptions = {\n /**\n * The timer between each attempt in milliseconds.\n *\n * _Default: `500`_\n */\n delayTimer?: number,\n /**\n * The custom function that is used to calculate the actual delay based on the the timer & the number of attemps.\n *\n * _Default: `delay * nbOfAttemps`_\n */\n delayRamp?: DelayRampFunction,\n /**\n * The maximum number of retries before resolving the promise with the last error. Specifying 0 means infinite retries.\n *\n * _Default: `10`_\n */\n maxAttempts?: number,\n /**\n * The request will be retried until that condition is satisfied.\n *\n * _Default: `response && response.ok`_\n */\n until?: UntilFunction,\n /**\n * Callback that will get executed before retrying the request. If this function returns an object having url and/or options properties, they will override existing values in the retried request.\n *\n * _Default: `undefined`_\n */\n onRetry?: OnRetryFunction,\n /**\n * If true, will retry the request if a network error was thrown. Will also provide an 'error' argument to the `onRetry` and `until` methods.\n *\n * _Default: `false`_\n */\n retryOnNetworkError?: boolean,\n /**\n * If true, the request will be resolved with the latest response instead of rejected with an error.\n *\n * _Default: `false`_\n */\n resolveWithLatestResponse?: boolean\n}\n\n/**\n * ## Retry middleware\n *\n * #### Retries a request multiple times in case of an error (or until a custom condition is true).\n *\n * ```ts\n * import wretch from 'wretch'\n * import { retry } from 'wretch/middlewares'\n *\n * wretch().middlewares([\n * retry({\n * // Options - defaults below\n * delayTimer: 500,\n * delayRamp: (delay, nbOfAttempts) => delay * nbOfAttempts,\n * maxAttempts: 10,\n * until: (response, error) => response && response.ok,\n * onRetry: null,\n * retryOnNetworkError: false,\n * resolveWithLatestResponse: false\n * })\n * ])\n *\n * // You can also return a Promise, which is useful if you want to inspect the body:\n * wretch().middlewares([\n * retry({\n * until: response =>\n * response.clone().json().then(body =>\n * body.field === 'something'\n * )\n * })\n * ])\n * ```\n */\nexport type RetryMiddleware = (options?: RetryOptions) => ConfiguredMiddleware\n\n/* Defaults */\n\nconst defaultDelayRamp: DelayRampFunction = (delay, nbOfAttempts) => (\n delay * nbOfAttempts\n)\nconst defaultUntil: UntilFunction = response => response && response.ok\n\nexport const retry: RetryMiddleware = ({\n delayTimer = 500,\n delayRamp = defaultDelayRamp,\n maxAttempts = 10,\n until = defaultUntil,\n onRetry = null,\n retryOnNetworkError = false,\n resolveWithLatestResponse = false\n} = {}) => {\n\n return next => (url, opts) => {\n let numberOfAttemptsMade = 0\n\n const checkStatus = (response?: Response, error?: Error) => {\n return Promise.resolve(until(response, error)).then(done => {\n // If the response is not suitable\n if (!done) {\n numberOfAttemptsMade++\n\n if (!maxAttempts || numberOfAttemptsMade <= maxAttempts) {\n // We need to recurse until we have a correct response and chain the checks\n return new Promise(resolve => {\n const delay = delayRamp(delayTimer, numberOfAttemptsMade)\n setTimeout(() => {\n if (typeof onRetry === \"function\") {\n Promise.resolve(onRetry({\n response,\n error,\n url,\n options: opts\n })).then((values = {}) => {\n resolve(next((values && values.url) ?? url, (values && values.options) ?? opts))\n })\n } else {\n resolve(next(url, opts))\n }\n }, delay)\n }).then(checkStatus).catch(error => {\n if (!retryOnNetworkError)\n throw error\n return checkStatus(null, error)\n })\n } else {\n return !!response && resolveWithLatestResponse ? response : Promise.reject(error || new Error(\"Number of attempts exceeded.\"))\n }\n }\n\n return !!response && resolveWithLatestResponse ? response : error ? Promise.reject(error) : response\n })\n }\n\n return next(url, opts)\n .then(checkStatus)\n .catch(error => {\n if (!retryOnNetworkError)\n throw error\n return checkStatus(null, error)\n })\n }\n}\n"],"names":["defaultUntil","response","ok","retry","delayTimer","delayRamp","defaultDelayRamp","maxAttempts","until","onRetry","retryOnNetworkError","resolveWithLatestResponse","next","opts","numberOfAttemptsMade","checkStatus","error","Promise","resolve","then","done","reject","setTimeout","url","options","values","_a","_b","delay","catch","Error"],"mappings":"mBA6FAA,EAAAC,GAAAA,GAAAA,EAAAC,GAEMC,EAAA,EAAAC,aAA4C,IAAcC,YAAAC,EAC1CC,cACrB,GAAAC,QAAAR,EAAAS,UAAA,KAAAC,sBAAA,EAAAC,4BAAA,GAAA,CAAA,IACKC,MAAsCC,KAE1B,IAAAC,EAEhB,QAQWC,GAAcd,EAAIe,IACvBC,QAAAC,QAAuBV,EAACP,EAAAe,IAAAG,MAAAC,GAGZA,EA8BTnB,GAAAU,EAAAV,EAAAe,EAAAC,QAAAI,OAAAL,GAAAf,QA5BMM,GAAEO,GAAAP,EAGJ,IAAAU,SAAmCC,mBAE/BI,YAAW,KACkB,mBAApBb,EACdQ,QAAgBC,QAAAT,EAAA,CACVR,WACKe,QACLO,MACAC,QAAKX,KACLM,MAAG,CAAAM,EAAA,CAAA,KACH,IAAAC,IACER,EAAMN,EAAe,QAANc,EAAMD,GAAAA,EAAAF,WAAA,IAAAG,EAAAA,EAAAH,EAAA,QAAAI,EAAAF,GAAAA,EAAAD,eAAA,IAAAG,EAAAA,EAAAd,GAAA,IAG1BK,EAAAN,EAAAW,EAAAV,GAAM,GACLe,EAAA,IACDT,KAAAJ,GAAAc,OAAAb,IACF,IAAON,EACF,MAAAM,EACN,OAAwBD,EAAA,KAAAC,EAAA,IAI3Bf,GAAAU,EAAAV,EAAAgB,QAAAI,OAAAL,GAAA,IAAAc,MAAA,oCAML,OAAElB,EAAAW,EAAAV,GACHM,KAAAJ,UAEUC,IACR,IAAIN,EACJ,MAAMM,EACL,OAAwBD,EAAA,KAAAC,EAAA,GACtB,SAIVb"}