{"version":3,"sources":["../../../../../src/middleware/retry/v2/index.ts"],"sourcesContent":["import type { Middleware, ResponseGetter } from '../../index'\nimport { configs } from '../../../index'\nimport { assign } from '../../../utils/index'\nimport { Response } from '../../../response'\nimport type { Request } from '../../../request'\n\nexport interface RetryMiddlewareOptions {\n  readonly headerRetryCount: string\n  readonly headerRetryTime: string\n  readonly maxRetryTimeInSecs: number\n  readonly initialRetryTimeInSecs: number\n  readonly factor: number\n  readonly multiplier: number\n  readonly retries: number\n  validateRetry(response: Response): boolean\n}\n\nexport const defaultRetryConfigs: RetryMiddlewareOptions = {\n  headerRetryCount: 'X-Mappersmith-Retry-Count',\n  headerRetryTime: 'X-Mappersmith-Retry-Time',\n  maxRetryTimeInSecs: 5,\n  initialRetryTimeInSecs: 0.1,\n  factor: 0.2, // randomization factor\n  multiplier: 2, // exponential factor\n  retries: 5, // max retries\n  validateRetry: (response: Response) => response.responseStatus >= 500, // a function that returns true if the request should be retried\n}\n\ntype RetryMiddlewareType = Middleware<{\n  enableRetry: boolean\n  inboundRequest: Request\n}>\n\n/**\n * This middleware will automatically retry GET requests up to the configured amount of\n * retries using a randomization function that grows exponentially. The retry count and\n * the time used will be included as a header in the response.\n *\n * The retry time is calculated using the following formula:\n *   retryTime = min(\n *     random(previousRetryTime - randomizedFactor, previousRetryTime + randomizedFactor) * multipler,\n *     maxRetryTime\n *   )\n *\n * Take a look at `calculateExponentialRetryTime` for more information.\n *\n *  @param {Object} retryConfigs\n *   @param {String} retryConfigs.headerRetryCount (default: 'X-Mappersmith-Retry-Count')\n *   @param {String} retryConfigs.headerRetryTime (default: 'X-Mappersmith-Retry-Time')\n *   @param {Number} retryConfigs.maxRetryTimeInSecs (default: 5)\n *   @param {Number} retryConfigs.initialRetryTimeInSecs (default: 1)\n *   @param {Number} retryConfigs.factor (default: 0.2) - randomization factor\n *   @param {Number} retryConfigs.multiplier (default: 2) - exponential factor\n *   @param {Number} retryConfigs.retries (default: 5) - max retries\n */\nexport const RetryMiddleware = (\n  customConfigs: Partial<RetryMiddlewareOptions> = {}\n): RetryMiddlewareType =>\n  function RetryMiddleware() {\n    return {\n      request(request) {\n        this.enableRetry = request.method() === 'get'\n        this.inboundRequest = request\n        return request\n      },\n\n      response(next) {\n        const retryConfigs = assign({}, defaultRetryConfigs, customConfigs)\n        const inboundRequest = this.inboundRequest\n\n        if (!this.enableRetry) {\n          return next()\n        }\n\n        if (!configs.Promise) {\n          return next()\n        }\n\n        if (!inboundRequest) {\n          return next()\n        }\n\n        return new configs.Promise((resolve, reject) => {\n          const retryTime = retryConfigs.initialRetryTimeInSecs * 1000\n          retriableRequest(\n            resolve,\n            reject,\n            next,\n            inboundRequest\n          )(randomFromRetryTime(retryTime, retryConfigs.factor), 0, retryConfigs)\n        })\n      },\n    }\n  }\n\nexport default RetryMiddleware\n\ntype RetryFn = (retryTime: number, retryCount: number, retryConfigs: RetryMiddlewareOptions) => void\ntype RetriableRequestFn = (\n  resolve: (value: Response | PromiseLike<Response>) => void,\n  reject: (reason?: unknown) => void,\n  next: ResponseGetter,\n  response: Request\n) => RetryFn\nconst retriableRequest: RetriableRequestFn = (resolve, reject, next, request) => {\n  const retry: RetryFn = (retryTime, retryCount, retryConfigs) => {\n    const nextRetryTime = calculateExponentialRetryTime(retryTime, retryConfigs)\n    const shouldRetry = retryCount < retryConfigs.retries\n    const scheduleRequest = () => {\n      setTimeout(() => retry(nextRetryTime, retryCount + 1, retryConfigs), retryTime)\n    }\n\n    next()\n      .then((response) => {\n        if (shouldRetry && retryConfigs.validateRetry(response)) {\n          scheduleRequest()\n        } else {\n          try {\n            resolve(\n              enhancedResponse(\n                response,\n                retryConfigs.headerRetryCount,\n                retryCount,\n                retryConfigs.headerRetryTime,\n                retryTime\n              )\n            )\n          } catch (e) {\n            let errorMessage = ''\n            if (response instanceof Error) {\n              errorMessage = response.message\n            }\n            if (typeof e === 'object' && e !== null && 'message' in e) {\n              errorMessage = (e as Record<'message', string>).message\n            }\n            reject(new Response(request, 400, errorMessage, {}, [new Error(errorMessage)]))\n          }\n        }\n      })\n      .catch((response) => {\n        if (shouldRetry && retryConfigs.validateRetry(response)) {\n          scheduleRequest()\n        } else {\n          try {\n            reject(\n              enhancedResponse(\n                response,\n                retryConfigs.headerRetryCount,\n                retryCount,\n                retryConfigs.headerRetryTime,\n                retryTime\n              )\n            )\n          } catch (e) {\n            let errorMessage = ''\n            if (response instanceof Error) {\n              errorMessage = response.message\n            }\n            if (typeof e === 'object' && e !== null && 'message' in e) {\n              errorMessage = (e as Record<'message', string>).message\n            }\n            reject(new Response(request, 400, errorMessage, {}, [response]))\n          }\n        }\n      })\n  }\n\n  return retry\n}\n\n/**\n * Increases the retry time for each attempt using a randomization function that grows exponentially.\n * The value is limited by `retryConfigs.maxRetryTimeInSecs`.\n * @param {Number} retryTime\n *\n * @return {Number}\n */\nexport const calculateExponentialRetryTime = (\n  retryTime: number,\n  retryConfigs: RetryMiddlewareOptions\n) =>\n  Math.min(\n    randomFromRetryTime(retryTime, retryConfigs.factor) * retryConfigs.multiplier,\n    retryConfigs.maxRetryTimeInSecs * 1000\n  )\n\nconst randomFromRetryTime = (retryTime: number, factor: number) => {\n  const delta = factor * retryTime\n  return random(retryTime - delta, retryTime + delta)\n}\n\nconst random = (min: number, max: number) => {\n  return Math.random() * (max - min) + min\n}\n\nconst enhancedResponse = (\n  response: Response,\n  headerRetryCount: string,\n  retryCount: number,\n  headerRetryTime: string,\n  retryTime: number\n) =>\n  response.enhance({\n    headers: {\n      [headerRetryCount]: retryCount,\n      [headerRetryTime]: retryTime,\n    },\n  })\n"],"mappings":";AACA,SAAS,eAAe;AACxB,SAAS,cAAc;AACvB,SAAS,gBAAgB;AAclB,IAAM,sBAA8C;AAAA,EACzD,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,QAAQ;AAAA;AAAA,EACR,YAAY;AAAA;AAAA,EACZ,SAAS;AAAA;AAAA,EACT,eAAe,CAAC,aAAuB,SAAS,kBAAkB;AAAA;AACpE;AA6BO,IAAM,kBAAkB,CAC7B,gBAAiD,CAAC,MAElD,SAASA,mBAAkB;AACzB,SAAO;AAAA,IACL,QAAQ,SAAS;AACf,WAAK,cAAc,QAAQ,OAAO,MAAM;AACxC,WAAK,iBAAiB;AACtB,aAAO;AAAA,IACT;AAAA,IAEA,SAAS,MAAM;AACb,YAAM,eAAe,OAAO,CAAC,GAAG,qBAAqB,aAAa;AAClE,YAAM,iBAAiB,KAAK;AAE5B,UAAI,CAAC,KAAK,aAAa;AACrB,eAAO,KAAK;AAAA,MACd;AAEA,UAAI,CAAC,QAAQ,SAAS;AACpB,eAAO,KAAK;AAAA,MACd;AAEA,UAAI,CAAC,gBAAgB;AACnB,eAAO,KAAK;AAAA,MACd;AAEA,aAAO,IAAI,QAAQ,QAAQ,CAAC,SAAS,WAAW;AAC9C,cAAM,YAAY,aAAa,yBAAyB;AACxD;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,EAAE,oBAAoB,WAAW,aAAa,MAAM,GAAG,GAAG,YAAY;AAAA,MACxE,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEF,IAAO,aAAQ;AASf,IAAM,mBAAuC,CAAC,SAAS,QAAQ,MAAM,YAAY;AAC/E,QAAM,QAAiB,CAAC,WAAW,YAAY,iBAAiB;AAC9D,UAAM,gBAAgB,8BAA8B,WAAW,YAAY;AAC3E,UAAM,cAAc,aAAa,aAAa;AAC9C,UAAM,kBAAkB,MAAM;AAC5B,iBAAW,MAAM,MAAM,eAAe,aAAa,GAAG,YAAY,GAAG,SAAS;AAAA,IAChF;AAEA,SAAK,EACF,KAAK,CAAC,aAAa;AAClB,UAAI,eAAe,aAAa,cAAc,QAAQ,GAAG;AACvD,wBAAgB;AAAA,MAClB,OAAO;AACL,YAAI;AACF;AAAA,YACE;AAAA,cACE;AAAA,cACA,aAAa;AAAA,cACb;AAAA,cACA,aAAa;AAAA,cACb;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,GAAG;AACV,cAAI,eAAe;AACnB,cAAI,oBAAoB,OAAO;AAC7B,2BAAe,SAAS;AAAA,UAC1B;AACA,cAAI,OAAO,MAAM,YAAY,MAAM,QAAQ,aAAa,GAAG;AACzD,2BAAgB,EAAgC;AAAA,UAClD;AACA,iBAAO,IAAI,SAAS,SAAS,KAAK,cAAc,CAAC,GAAG,CAAC,IAAI,MAAM,YAAY,CAAC,CAAC,CAAC;AAAA,QAChF;AAAA,MACF;AAAA,IACF,CAAC,EACA,MAAM,CAAC,aAAa;AACnB,UAAI,eAAe,aAAa,cAAc,QAAQ,GAAG;AACvD,wBAAgB;AAAA,MAClB,OAAO;AACL,YAAI;AACF;AAAA,YACE;AAAA,cACE;AAAA,cACA,aAAa;AAAA,cACb;AAAA,cACA,aAAa;AAAA,cACb;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,GAAG;AACV,cAAI,eAAe;AACnB,cAAI,oBAAoB,OAAO;AAC7B,2BAAe,SAAS;AAAA,UAC1B;AACA,cAAI,OAAO,MAAM,YAAY,MAAM,QAAQ,aAAa,GAAG;AACzD,2BAAgB,EAAgC;AAAA,UAClD;AACA,iBAAO,IAAI,SAAS,SAAS,KAAK,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAAA,QACjE;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAEA,SAAO;AACT;AASO,IAAM,gCAAgC,CAC3C,WACA,iBAEA,KAAK;AAAA,EACH,oBAAoB,WAAW,aAAa,MAAM,IAAI,aAAa;AAAA,EACnE,aAAa,qBAAqB;AACpC;AAEF,IAAM,sBAAsB,CAAC,WAAmB,WAAmB;AACjE,QAAM,QAAQ,SAAS;AACvB,SAAO,OAAO,YAAY,OAAO,YAAY,KAAK;AACpD;AAEA,IAAM,SAAS,CAAC,KAAa,QAAgB;AAC3C,SAAO,KAAK,OAAO,KAAK,MAAM,OAAO;AACvC;AAEA,IAAM,mBAAmB,CACvB,UACA,kBACA,YACA,iBACA,cAEA,SAAS,QAAQ;AAAA,EACf,SAAS;AAAA,IACP,CAAC,gBAAgB,GAAG;AAAA,IACpB,CAAC,eAAe,GAAG;AAAA,EACrB;AACF,CAAC;","names":["RetryMiddleware"]}