{"version":3,"file":"retry.min.cjs","names":[],"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 = (\n  response?: Response,\n  error?: Error\n) => boolean | Promise<boolean>\nexport type OnRetryFunctionResponse =\n  | { url?: string; options?: WretchOptions }\n  | undefined\nexport type OnRetryFunction = (args: {\n  response?: Response\n  error?: Error\n  url: string\n  attempt: number\n  options: WretchOptions\n}) => void | OnRetryFunctionResponse | Promise<OnRetryFunctionResponse>\nexport type SkipFunction = (url: string, opts: WretchOptions) => boolean\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 || (response.status >= 400 && response.status < 500))`_\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   * The callback receives an object with: `response`, `error`, `url`, `attempt` (current attempt number, starting at 1), and `options`.\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   * If skip returns true, the request will not be retried.\n   *\n   * Example:\n   * ```js\n   * (url, options) => (\n   *    options.method !== \"GET\"\n   * )\n   * ```\n   *\n   * _Default: `undefined`_\n   */\n  skip?: SkipFunction\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 * > **💡 By default, the request will be retried only for server errors (5xx) and other non-successful responses, but not for client errors (4xx).**\n * >\n * > ```js\n * > // To retry on all non-2xx responses (including 4xx):\n * > until: (response, error) => response && response.ok\n * > ```\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 || (response.status >= 400 && response.status < 500)),\n *     onRetry: null,\n *     retryOnNetworkError: false,\n *     resolveWithLatestResponse: false,\n *     skip: undefined\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\nconst defaultUntil: UntilFunction = response =>\n  response && (response.ok || (response.status >= 400 && response.status < 500))\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  skip,\n} = {}) => {\n  return next => (url, opts) => {\n    let attempts = 0\n\n    if (skip && skip(url, opts)) {\n      return next(url, opts)\n    }\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          attempts++\n\n          if (!maxAttempts || attempts <= 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, attempts)\n              setTimeout(() => {\n                if (typeof onRetry === \"function\") {\n                  Promise.resolve(\n                    onRetry({\n                      response,\n                      error,\n                      url,\n                      attempt: attempts,\n                      options: opts,\n                    })\n                  ).then((values = {}) => {\n                    resolve(\n                      next(\n                        (values && values.url) ?? url,\n                        (values && values.options) ?? opts\n                      )\n                    )\n                  })\n                } else {\n                  resolve(next(url, opts))\n                }\n              }, delay)\n            })\n              .then(checkStatus)\n              .catch(error => {\n                if (!retryOnNetworkError) throw error\n                return checkStatus(null, error)\n              })\n          } else {\n            return !!response && resolveWithLatestResponse\n              ? response\n              : Promise.reject(\n                error || new Error(\"Number of attempts exceeded.\")\n              )\n          }\n        }\n\n        return !!response && resolveWithLatestResponse\n          ? response\n          : error\n            ? Promise.reject(error)\n            : response\n      })\n    }\n\n    return next(url, opts)\n      .then(checkStatus)\n      .catch(error => {\n        if (!retryOnNetworkError) throw error\n        return checkStatus(null, error)\n      })\n  }\n}\n"],"mappings":"mEA6HA,MAAM,GAAuC,EAAO,IAClD,EAAQ,EACJ,EAA8B,GAClC,IAAa,EAAS,IAAO,EAAS,QAAU,KAAO,EAAS,OAAS,KAE9D,GAA0B,CACrC,aAAa,IACb,YAAY,EACZ,cAAc,GACd,QAAQ,EACR,UAAU,KACV,sBAAsB,GACtB,4BAA4B,GAC5B,QACE,EAAE,GACG,IAAS,EAAK,IAAS,CAC5B,IAAI,EAAW,EAEf,GAAI,GAAQ,EAAK,EAAK,EAAK,CACzB,OAAO,EAAK,EAAK,EAAK,CAGxB,IAAM,GAAe,EAAqB,IACjC,QAAQ,QAAQ,EAAM,EAAU,EAAM,CAAC,CAAC,KAAK,GAE7C,EA4CI,GAAY,EACjB,EACA,EACE,QAAQ,OAAO,EAAM,CACrB,GA/CJ,IAEI,CAAC,GAAe,GAAY,EAEvB,IAAI,QAAQ,GAAW,CAC5B,IAAM,EAAQ,EAAU,EAAY,EAAS,CAC7C,eAAiB,CACX,OAAO,GAAY,WACrB,QAAQ,QACN,EAAQ,CACN,WACA,QACA,MACA,QAAS,EACT,QAAS,EACV,CAAC,CACH,CAAC,MAAM,EAAS,EAAE,GAAK,CACtB,EACE,GACG,GAAU,EAAO,MAAQ,GACzB,GAAU,EAAO,UAAY,EAC/B,CACF,EACD,CAEF,EAAQ,EAAK,EAAK,EAAK,CAAC,EAEzB,EAAM,EACT,CACC,KAAK,EAAY,CACjB,MAAM,GAAS,CACd,GAAI,CAAC,EAAqB,MAAM,EAChC,OAAO,EAAY,KAAM,EAAM,EAC/B,CAEK,GAAY,EACjB,EACA,QAAQ,OACR,GAAa,MAAM,+BAA+B,CACnD,EASP,CAGJ,OAAO,EAAK,EAAK,EAAK,CACnB,KAAK,EAAY,CACjB,MAAM,GAAS,CACd,GAAI,CAAC,EAAqB,MAAM,EAChC,OAAO,EAAY,KAAM,EAAM,EAC/B"}