{"version":3,"file":"dedupe.min.mjs","sources":["../../../src/middlewares/dedupe.ts"],"sourcesContent":["import type { ConfiguredMiddleware, WretchOptions } from \"../types.js\"\n\n/* Types */\n\nexport type DedupeSkipFunction = (url: string, opts: WretchOptions) => boolean\nexport type DedupeKeyFunction = (url: string, opts: WretchOptions) => string\nexport type DedupeResolverFunction = (response: Response) => Response\nexport type DedupeOptions = {\n  skip?: DedupeSkipFunction,\n  key?: DedupeKeyFunction,\n  resolver?: DedupeResolverFunction\n}\n/**\n * ## Dedupe middleware\n *\n * #### Prevents having multiple identical requests on the fly at the same time.\n *\n * **Options**\n *\n * - *skip* `(url, opts) => boolean`\n *\n * > If skip returns true, then the dedupe check is skipped.\n *\n * - *key* `(url, opts) => string`\n *\n * > Returns a key that is used to identify the request.\n *\n * - *resolver* `(response: Response) => Response`\n *\n * > This function is called when resolving the fetch response from duplicate calls.\n * By default it clones the response to allow reading the body from multiple sources.\n */\nexport type DedupeMiddleware = (options?: DedupeOptions) => ConfiguredMiddleware\n\n/* Defaults */\n\nconst defaultSkip: DedupeSkipFunction = (_, opts) => (\n  opts.skipDedupe || opts.method !== \"GET\"\n)\nconst defaultKey: DedupeKeyFunction = (url: string, opts) => opts.method + \"@\" + url\nconst defaultResolver: DedupeResolverFunction = response => response.clone()\n\nexport const dedupe: DedupeMiddleware = ({ skip = defaultSkip, key = defaultKey, resolver = defaultResolver } = {}) => {\n\n  const inflight = new Map()\n\n  return next => (url, opts) => {\n\n    if (skip(url, opts)) {\n      return next(url, opts)\n    }\n\n    const _key = key(url, opts)\n\n    if (!inflight.has(_key)) {\n      inflight.set(_key, [])\n    } else {\n      return new Promise((resolve, reject) => {\n        inflight.get(_key).push([resolve, reject])\n      })\n    }\n\n    try {\n      return next(url, opts)\n        .then(response => {\n          // Resolve pending promises\n          inflight.get(_key).forEach(([resolve]) => resolve(resolver(response)))\n          // Remove the inflight pending promises\n          inflight.delete(_key)\n          // Return the original response\n          return response\n        })\n        .catch(error => {\n          // Reject pending promises on error\n          inflight.get(_key).forEach(([resolve, reject]) => reject(error))\n          inflight.delete(_key)\n          throw error\n        })\n    } catch (error) {\n      inflight.delete(_key)\n      return Promise.reject(error)\n    }\n\n  }\n}\n"],"names":["defaultSkip","_","opts","skipDedupe","method","defaultKey","url","defaultResolver","response","clone","dedupe","skip","key","resolver","inflight","Map","next","_key","has","Promise","resolve","reject","get","push","set","then","forEach","delete","catch","error"],"mappings":"AAoCA,MAAMA,EAAkC,CAACC,EAAGC,IAC1CA,EAAKC,YAA8B,QAAhBD,EAAKE,OAEpBC,EAAgC,CAACC,EAAaJ,IAASA,EAAKE,OAAS,IAAME,EAC3EC,EAA0CC,GAAYA,EAASC,QAExDC,EAA2B,EAAGC,OAAOX,EAAaY,MAAMP,EAAYQ,WAAWN,GAAoB,MAE9G,MAAMO,EAAW,IAAIC,IAErB,OAAOC,GAAQ,CAACV,EAAKJ,KAEnB,GAAIS,EAAKL,EAAKJ,GACZ,OAAOc,EAAKV,EAAKJ,GAGnB,MAAMe,EAAOL,EAAIN,EAAKJ,GAEtB,GAAKY,EAASI,IAAID,GAGhB,OAAO,IAAIE,SAAQ,CAACC,EAASC,KAC3BP,EAASQ,IAAIL,GAAMM,KAAK,CAACH,EAASC,GAAQ,IAH5CP,EAASU,IAAIP,EAAM,IAOrB,IACE,OAAOD,EAAKV,EAAKJ,GACduB,MAAKjB,IAEJM,EAASQ,IAAIL,GAAMS,SAAQ,EAAEN,KAAaA,EAAQP,EAASL,MAE3DM,EAASa,OAAOV,GAETT,KAERoB,OAAMC,IAIL,MAFAf,EAASQ,IAAIL,GAAMS,SAAQ,EAAEN,EAASC,KAAYA,EAAOQ,KACzDf,EAASa,OAAOV,GACVY,CAAK,GAKhB,CAHC,MAAOA,GAEP,OADAf,EAASa,OAAOV,GACTE,QAAQE,OAAOQ,EACvB,EAEF"}