{"version":3,"file":"dedupe.min.cjs","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":["defaultKey","url","opts","method","defaultResolver","response","clone","dedupe","skip","defaultSkip","key","inflight","Map","next","has","_key","Promise","resolve","reject","get","push","set","then","forEach","resolver","delete","catch","error"],"mappings":"8CAkCAA,EAAA,CAAAC,EAAAC,IAAAA,EAAAC,OAAA,IAAAF,EAEMG,EAAsCC,GAC1CA,EAAKC,QAEDC,EAAU,EAAAC,OAAoCC,EAAaC,MAAOV,aAAYI,GAAA,CAAA,KAC9E,MAAAO,EAAe,IAAmCC,cAET,CAAAX,EAAGC,QAElCM,EAAAP,EAAOC,GAEV,OAAIW,EAAIZ,EAAMC,GAGrB,QAAWQ,IAAMR,GAClB,GAAAS,EAAAG,IAAAC,GAMA,OAAA,IAAAC,SAAA,CAAAC,EAAAC,KAAMP,EAAAQ,IAAAJ,GAAAK,KAAA,CAAAH,EAAAC,GAAA,IAJPP,EAAaU,IAAIN,EAAS,IAOxB,IACD,OAAAF,EAAAZ,EAAAC,GAEGoB,MAAAjB,IAECM,EAAKQ,IAAAJ,GAAWQ,SAAA,EAAAN,KAAAA,EAAAO,EAAAnB,MAEfM,EAASc,eAITC,OAAOC,IAIP,MAFDhB,EAAMQ,IAAQJ,GAAAQ,SAAA,EAAAN,EAAAC,KAAAA,EAAAS,iBAEbA,CAAY,GAKhB,CAFE,MAAEA,GACJ,OAADhB,EAAAc,OAAAV,WAAeG,OAAAS,EACd,EACA,SAINpB"}