wretch
Version:
A tiny wrapper built around fetch with an intuitive syntax.
1 lines • 3.71 kB
Source Map (JSON)
{"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"}