diff --git a/dist/browser/index.global.js b/dist/browser/index.global.js index 9a27b8c..67d026f 100644 --- a/dist/browser/index.global.js +++ b/dist/browser/index.global.js @@ -1,2 +1,2 @@ -"use strict";var fetchff=(()=>{var _=Object.defineProperty,Fe=Object.defineProperties,qe=Object.getOwnPropertyDescriptor,He=Object.getOwnPropertyDescriptors,Ne=Object.getOwnPropertyNames,le=Object.getOwnPropertySymbols;var ue=Object.prototype.hasOwnProperty,Se=Object.prototype.propertyIsEnumerable;var ee=(e,t,n)=>t in e?_(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,C=(e,t)=>{for(var n in t||(t={}))ue.call(t,n)&&ee(e,n,t[n]);if(le)for(var n of le(t))Se.call(t,n)&&ee(e,n,t[n]);return e},B=(e,t)=>Fe(e,He(t));var Be=(e,t)=>{for(var n in t)_(e,n,{get:t[n],enumerable:!0})},Me=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of Ne(t))!ue.call(e,s)&&s!==n&&_(e,s,{get:()=>t[s],enumerable:!(o=qe(t,s))||o.enumerable});return e};var Ue=e=>Me(_({},"__esModule",{value:!0}),e);var A=(e,t,n)=>ee(e,typeof t!="symbol"?t+"":t,n);var Le={};Be(Le,{createApiFetcher:()=>_e,fetchf:()=>xe});async function O(e,t){if(t){if(typeof t=="function"){let n=await t(e);n&&Object.assign(e,n)}else if(Array.isArray(t))for(let n of t){let o=await n(e);o&&Object.assign(e,o)}}}var L=class extends Error{constructor(n,o,s){super(n);A(this,"response");A(this,"request");A(this,"config");A(this,"status");A(this,"statusText");this.name="ResponseError",this.message=n,this.status=s.status,this.statusText=s.statusText,this.request=o,this.config=o,this.response=s}};var M="application/json",j="Content-Type",x="undefined",b="object",J="string",$="AbortError",fe="TimeoutError",pe="CanceledError",U="GET",de="HEAD";function te(e){return e instanceof URLSearchParams}function Re(e){let t="";for(let n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t+=n+":"+e[n]);return t}function ne(e){let t={},n=Object.keys(e);n.sort();for(let o=0,s=n.length;o{a=typeof a=="function"?a():a,a=a===null||a===void 0?"":a,n[n.length]=o(l)+"="+o(a)},i=(l,a)=>{let d,g,y;if(l)if(Array.isArray(a))for(d=0,g=a.length;d{let o=n.substring(1);return String(t[o]?t[o]:n)}):e}function ge(e){let t=typeof e;if(t===x||e===null)return!1;if(t===J||t==="number"||t==="boolean"||Array.isArray(e))return!0;if(Buffer.isBuffer(e)||e instanceof Date)return!1;if(t===b){let n=Object.getPrototypeOf(e);if(n===Object.prototype||n===null||typeof e.toJSON=="function")return!0}return!1}async function re(e){return new Promise(t=>setTimeout(()=>t(!0),e))}function se(e){return e&&typeof e===b&&typeof e.data!==x&&Object.keys(e).length===1?se(e.data):e}function Pe(e){if(!e)return{};let t={};if(e instanceof Headers)e.forEach((n,o)=>{t[o]=n});else if(typeof e===b&&e!==null)for(let[n,o]of Object.entries(e))t[n.toLowerCase()]=o;return t}function F(e,t){e&&t in e&&delete e[t]}var k=new Map;async function Ee(e,t,n=0,o=!1,s=!0){let i=Date.now(),h=k.get(e);if(h){let a=h[3],d=h[0],g=h[1];if(!a&&i-h[2]{let a=new DOMException(`${e.url} aborted due to timeout`,fe);v(e,a)},t):null;return k.set(e,[m,l,i,o]),m}async function v(e,t=null){let n=k.get(e);if(n){let o=n[0],s=n[1];t&&!o.signal.aborted&&o.abort(t),s!==null&&clearTimeout(s),k.delete(e)}}async function Ce(e){var o;if(!(e!=null&&e.body))return null;let t=String(((o=e.headers)==null?void 0:o.get(j))||"").split(";")[0],n;try{if(t.includes(M)||t.includes("+json"))n=await e.json();else if(t.includes("multipart/form-data"))n=await e.formData();else if(t.includes("application/octet-stream"))n=await e.blob();else if(t.includes("application/x-www-form-urlencoded"))n=await e.formData();else if(t.includes("text/"))n=await e.text();else try{n=await e.clone().json()}catch(s){n=await e.text()}}catch(s){n=null}return n}function G(e){let t=0;for(let n=0,o=e.length;n{y+=u+"="+r+"&"}),y=G(y);else if(typeof Blob!==x&&s instanceof Blob||typeof File!==x&&s instanceof File)y="BF"+s.size+s.type;else if(s instanceof ArrayBuffer||ArrayBuffer.isView(s))y="AB"+s.byteLength;else{let r=typeof s===b?ne(s):String(s);y=G(JSON.stringify(r))}return n+t+i+h+m+l+a+d+g+y}function ke(e,t){return t?Date.now()-e>t*1e3:!1}function be(e,t){let n=oe.get(e);if(n){if(!ke(n.timestamp,t))return n;oe.delete(e)}return null}function we(e,t,n=!1){oe.set(e,{data:t,isLoading:n,timestamp:Date.now()})}var Qe={method:U,strategy:"reject",timeout:3e4,dedupeTime:1e3,defaultResponse:null,headers:{Accept:M+", text/plain, */*","Accept-Encoding":"gzip, deflate, br",[j]:M+";charset=utf-8"},retry:{delay:1e3,maxDelay:3e4,resetTimeout:!0,backoff:1.5,retryOn:[408,409,425,429,500,502,503,504]}};function z(e){let t=C(C({},Qe),e),n=t.fetcher,o=(n==null?void 0:n.create(t))||null,s=()=>o,i=(r,u)=>typeof r[u]!==x?r[u]:t[u],h=(...r)=>{var u;(u=t.logger)!=null&&u.warn&&t.logger.warn(...r)},m=(r,u,f)=>{let R=i(f,"method").toUpperCase(),P=R===U||R===de,c=he(r,i(f,"urlPathParams")),E=i(f,"params"),q=i(f,"body")||i(f,"data"),K=!!(u&&(P||q)),T;P||(T=q||u);let Q=i(f,"withCredentials")?"include":i(f,"credentials");F(f,"data"),F(f,"withCredentials");let D=E||K?me(c,E||u):c,w=D.includes("://")?"":i(f,"baseURL")||i(f,"apiUrl");return T&&typeof T!==J&&!te(T)&&ge(T)&&(T=JSON.stringify(T)),B(C({},f),{credentials:Q,body:T,method:R,url:w+D})},l=async(r,u)=>{d(r)||h("API ERROR",r),await O(r,u==null?void 0:u.onError),await O(r,t==null?void 0:t.onError)},a=async(r,u,f)=>{let R=d(r),P=i(f,"strategy"),c=i(f,"rejectCancelled");if(!(R&&!c)){if(P==="silent")await new Promise(()=>null);else if(P==="reject")return Promise.reject(r)}return y(u,f,r)},d=r=>r.name===$||r.name===pe,g=async(r,u=null,f=null)=>{var ie;let R=f||{},P=C(C({},t),R),c=null,E=m(r,u,P),{timeout:q,cancellable:K,dedupeTime:T,pollingInterval:Y,shouldStopPolling:Q,cacheTime:D,cacheKey:W}=P,w;if(W?w=W(E):w=Te(E),D&&w){let I=P.cacheBuster;if(!I||!I(E)){let p=be(w,D);if(p)return p.data}}let{retries:V=0,delay:Oe,backoff:De,retryOn:X,shouldRetry:ae,maxDelay:Ie,resetTimeout:Ae}=P.retry,H=0,Z=0,N=Oe;for(;H<=V;)try{let I=await Ee(E,q,T,K,!!(q&&(!V||Ae))),p=C({signal:I.signal},E);if(await O(p,R==null?void 0:R.onRequest),await O(p,t==null?void 0:t.onRequest),n!==null&&o!==null?c=await o.request(p):c=await fetch(p.url,p),c instanceof Response&&(c.config=p,c.data=await Ce(c),!c.ok))throw new L(`${p.url} failed! Status: ${c.status||null}`,p,c);if(await O(c,R==null?void 0:R.onResponse),await O(c,t==null?void 0:t.onResponse),v(E),Y&&(!Q||!Q(c,Z))){Z++,h(`Polling attempt ${Z}...`),await re(Y);continue}let S=y(c,p);if(D&&w){let ce=p.skipCache;(!ce||!ce(S,p))&&we(w,S)}return S}catch(I){let p=I,S=((ie=p==null?void 0:p.response)==null?void 0:ie.status)||(p==null?void 0:p.status)||0;if(H===V||!(!ae||await ae(p,H))||!(X!=null&&X.includes(S)))return await l(p,E),v(E),a(p,c,E);h(`Attempt ${H+1} failed. Retry in ${N}ms.`),await re(N),N*=De,N=Math.min(N,Ie),H++}return y(c,E)},y=(r,u,f=null)=>{let R=i(u,"defaultResponse"),P=i(u,"flattenResponse");if(!r)return P?R:{error:f,headers:null,data:R,config:u};F(f,"response"),F(f,"request"),F(f,"config");let c=r==null?void 0:r.data;return(c==null||typeof c===b&&Object.keys(c).length===0)&&(c=R),P?se(c):r instanceof Response?{body:r.body,bodyUsed:r.bodyUsed,formData:r.formData,ok:r.ok,redirected:r.redirected,type:r.type,url:r.url,status:r.status,statusText:r.statusText,blob:r.blob.bind(r),json:r.json.bind(r),text:r.text.bind(r),clone:r.clone.bind(r),arrayBuffer:r.arrayBuffer.bind(r),error:f,data:c,headers:Pe(r.headers),config:u}:r};return{getInstance:s,buildConfig:m,config:e,request:g}}function _e(e){let t=e.endpoints,n=z(e);function o(){return n.getInstance()}function s(l){return console.error(`Add ${l} to 'endpoints'.`),Promise.resolve(null)}async function i(l,a={},d={},g={}){let y=t[l];return await n.request(y.url,a,B(C(C({},y||{}),g),{urlPathParams:d}))}function h(l){return l in m?m[l]:t[l]?m.request.bind(null,l):s.bind(null,l)}let m={config:e,endpoints:t,requestHandler:n,getInstance:o,request:i};return new Proxy(m,{get:(l,a)=>h(a)})}async function xe(e,t={}){return z(t).request(e,null,t)}return Ue(Le);})(); +"use strict";var fetchff=(()=>{var M=Object.defineProperty;var we=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames;var Oe=Object.prototype.hasOwnProperty;var Fe=(e,t,n)=>t in e?M(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Ie=(e,t)=>{for(var n in t)M(e,n,{get:t[n],enumerable:!0})},qe=(e,t,n,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of xe(t))!Oe.call(e,r)&&r!==n&&M(e,r,{get:()=>t[r],enumerable:!(a=we(t,r))||a.enumerable});return e};var Ae=e=>qe(M({},"__esModule",{value:!0}),e);var w=(e,t,n)=>Fe(e,typeof t!="symbol"?t+"":t,n);var Be={};Ie(Be,{createApiFetcher:()=>Ue,fetchf:()=>Se});async function T(e,t){if(t){if(typeof t=="function"){let n=await t(e);n&&Object.assign(e,n)}else if(Array.isArray(t))for(let n of t){let a=await n(e);a&&Object.assign(e,a)}}}var _=class extends Error{constructor(n,a,r){super(n);w(this,"response");w(this,"request");w(this,"config");w(this,"status");w(this,"statusText");this.name="ResponseError",this.message=n,this.status=r.status,this.statusText=r.statusText,this.request=a,this.config=a,this.response=r}};var Q="application/",A=Q+"json",L="Content-Type",C="undefined",E="object",v="string",j="AbortError",ie="TimeoutError",le="CanceledError",N="GET",ue="HEAD";function W(e){return e instanceof URLSearchParams}function fe(e){let t="";for(let n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t+=n+":"+e[n]);return t}function X(e){let t={},n=Object.keys(e);n.sort();for(let a=0,r=n.length;a{o=typeof o=="function"?o():o,o=o===null||o===void 0?"":o,n[n.length]=a(f)+"="+a(o)},u=(f,o)=>{let p,g,R;if(f)if(Array.isArray(o))for(p=0,g=o.length;p{let a=n.substring(1);return t[a]?String(t[a]):n}):e}function Re(e){let t=typeof e;if(t===C||e===null)return!1;if(t===v||t==="number"||t==="boolean"||Array.isArray(e))return!0;if(Buffer.isBuffer(e)||e instanceof Date)return!1;if(t===E){let n=Object.getPrototypeOf(e);if(n===Object.prototype||n===null||typeof e.toJSON=="function")return!0}return!1}async function Z(e){return new Promise(t=>setTimeout(()=>t(!0),e))}function ee(e){return e&&typeof e===E&&typeof e.data!==C&&Object.keys(e).length===1?ee(e.data):e}function me(e){if(!e)return{};let t={};if(e instanceof Headers)e.forEach((n,a)=>{t[a]=n});else if(typeof e===E&&e!==null)for(let[n,a]of Object.entries(e))t[n.toLowerCase()]=a;return t}function J(e,t){e&&t in e&&delete e[t]}var H=new Map;async function ye(e,t,n=0,a=!1,r=!0){let u=Date.now(),m=H.get(e);if(m){let o=m[3],p=m[0],g=m[1];if(!o&&u-m[2]{let o=new DOMException(`${e.url} aborted due to timeout`,ie);$(e,o)},t):null;return H.set(e,[y,f,u,a]),y}async function $(e,t=null){let n=H.get(e);if(n){let a=n[0],r=n[1];t&&!a.signal.aborted&&a.abort(t),r!==null&&clearTimeout(r),H.delete(e)}}async function he(e){var a;if(!(e!=null&&e.body))return null;let t=String(((a=e.headers)==null?void 0:a.get(L))||"").split(";")[0],n;try{if(t.includes(A)||t.includes("+json"))n=await e.json();else if(t.includes("multipart/form-data"))n=await e.formData();else if(t.includes(Q+"octet-stream"))n=await e.blob();else if(t.includes(Q+"x-www-form-urlencoded"))n=await e.formData();else if(t.includes("text/"))n=await e.text();else try{n=await e.clone().json()}catch(r){n=await e.text()}}catch(r){n=null}return n}function G(e){let t=0;for(let n=0,a=e.length;n{R+=i+"="+s+"&"}),R=G(R);else if(typeof Blob!==C&&r instanceof Blob||typeof File!==C&&r instanceof File)R="BF"+r.size+r.type;else if(r instanceof ArrayBuffer||ArrayBuffer.isView(r))R="AB"+r.byteLength;else{let s=typeof r===E?X(r):String(r);R=G(JSON.stringify(s))}return n+t+u+m+y+f+o+p+g+R}function Ne(e,t){return t?Date.now()-e>t*1e3:!1}function Pe(e,t){let n=te.get(e);if(n){if(!Ne(n.timestamp,t))return n;te.delete(e)}return null}function De(e,t,n=!1){te.set(e,{data:t,isLoading:n,timestamp:Date.now()})}var He={method:N,strategy:"reject",timeout:3e4,dedupeTime:1e3,defaultResponse:null,headers:{Accept:A+", text/plain, */*","Accept-Encoding":"gzip, deflate, br",[L]:A+";charset=utf-8"},retry:{delay:1e3,maxDelay:3e4,resetTimeout:!0,backoff:1.5,retryOn:[408,409,425,429,500,502,503,504]}};function z(e){let t={...He,...e},n=t.fetcher,a=(n==null?void 0:n.create(t))||null,r=()=>a,u=(s,i)=>typeof s[i]!==C?s[i]:t[i],m=(...s)=>{var i;(i=t.logger)!=null&&i.warn&&t.logger.warn(...s)},y=(s,i)=>{let d=u(i,"method").toUpperCase(),P=d===N||d===ue,l=de(s,u(i,"urlPathParams")),h=u(i,"params"),U=u(i,"body")||u(i,"data"),D;P||(D=U);let S=u(i,"withCredentials")?"include":u(i,"credentials"),x=h?pe(l,h):l,k=x.includes("://")?"":u(i,"baseURL")||u(i,"apiUrl");return D&&typeof D!==v&&!W(D)&&Re(D)&&(D=JSON.stringify(D)),{...i,credentials:S,body:D,method:d,url:k+x}},f=async(s,i)=>{p(s)||m("API ERROR",s),await T(s,i==null?void 0:i.onError),await T(s,t==null?void 0:t.onError)},o=async(s,i,d)=>{let P=p(s),l=u(d,"strategy"),h=u(d,"rejectCancelled");if(!(P&&!h)){if(l==="silent")await new Promise(()=>null);else if(l==="reject")return Promise.reject(s)}return R(i,d,s)},p=s=>s.name===j||s.name===le,g=async(s,i=null)=>{var ae;let d=i||{},P={...t,...d},l=null,h=y(s,P),{timeout:U,cancellable:D,dedupeTime:ne,pollingInterval:S,shouldStopPolling:x,cacheTime:B,cacheKey:k}=P,b;if(k?b=k(h):b=ge(h),B&&b){let I=P.cacheBuster;if(!I||!I(h)){let c=Pe(b,B);if(c)return c.data}}let{retries:Y=0,delay:Ee,backoff:Ce,retryOn:K,shouldRetry:se,maxDelay:Te,resetTimeout:be}=P.retry,O=0,V=0,F=Ee,re=Y>0?Y:0;for(;O<=re;)try{let c={signal:(await ye(h,U,ne,D,!!(U&&(!re||be)))).signal,...h};if(await T(c,d==null?void 0:d.onRequest),await T(c,t==null?void 0:t.onRequest),n!==null&&a!==null?l=await a.request(c):l=await fetch(c.url,c),l instanceof Response&&(l.config=c,l.data=await he(l),!l.ok))throw new _(`${c.url} failed! Status: ${l.status||null}`,c,l);if(await T(l,d==null?void 0:d.onResponse),await T(l,t==null?void 0:t.onResponse),$(h),S&&(!x||!x(l,V))){V++,m(`Polling attempt ${V}...`),await Z(S);continue}let q=R(l,c);if(B&&b){let oe=c.skipCache;(!oe||!oe(q,c))&&De(b,q)}return q}catch(I){let c=I,q=((ae=c==null?void 0:c.response)==null?void 0:ae.status)||(c==null?void 0:c.status)||0;if(O===Y||!(!se||await se(c,O))||!(K!=null&&K.includes(q)))return await f(c,h),$(h),o(c,l,h);m(`Attempt ${O+1} failed. Retry in ${F}ms.`),await Z(F),F*=Ce,F=Math.min(F,Te),O++}return R(l,h)},R=(s,i,d=null)=>{let P=u(i,"defaultResponse");if(!s)return{ok:!1,error:d,data:P,headers:null,config:i};J(d,"response"),J(d,"request"),J(d,"config");let l=s==null?void 0:s.data;return(l==null||typeof l===E&&Object.keys(l).length===0)&&(l=P),u(i,"flattenResponse")&&(s.data=ee(l)),s instanceof Response?{body:s.body,bodyUsed:s.bodyUsed,formData:s.formData,ok:s.ok,redirected:s.redirected,type:s.type,url:s.url,status:s.status,statusText:s.statusText,blob:s.blob.bind(s),json:s.json.bind(s),text:s.text.bind(s),clone:s.clone.bind(s),arrayBuffer:s.arrayBuffer.bind(s),error:d,data:l,headers:me(s.headers),config:i}:s};return{getInstance:r,buildConfig:y,config:e,request:g}}function Ue(e){let t=e.endpoints,n=z(e);function a(){return n.getInstance()}function r(y){return console.error(`Add ${y} to 'endpoints'.`),Promise.resolve(null)}async function u(y,f={}){let o=t[y]||{url:y};return await n.request(o.url,{...o,...f})}let m={config:e,endpoints:t,requestHandler:n,getInstance:a,request:u};return new Proxy(m,{get(y,f){return f in m?m[f]:t[f]?m.request.bind(null,f):r.bind(null,f)}})}async function Se(e,t={}){return z(t).request(e,t)}return Ae(Be);})(); //# sourceMappingURL=index.global.js.map \ No newline at end of file diff --git a/dist/browser/index.global.js.map b/dist/browser/index.global.js.map index ce64d7a..756583b 100644 --- a/dist/browser/index.global.js.map +++ b/dist/browser/index.global.js.map @@ -1 +1 @@ -{"version":3,"sources":["../../src/index.ts","../../src/interceptor-manager.ts","../../src/response-error.ts","../../src/const.ts","../../src/utils.ts","../../src/queue-manager.ts","../../src/response-parser.ts","../../src/hash.ts","../../src/cache-manager.ts","../../src/request-handler.ts","../../src/api-handler.ts"],"sourcesContent":["import { createRequestHandler } from './request-handler';\nimport type { APIResponse, FetchResponse, RequestHandlerConfig } from './types';\n\n/**\n * Simple wrapper for request fetching.\n * It abstracts the creation of RequestHandler, making it easy to perform API requests.\n *\n * @param {string | URL | globalThis.Request} url - Request URL.\n * @param {RequestHandlerConfig} config - Configuration object for the request handler.\n * @returns {Promise>} Response Data.\n */\nexport async function fetchf(\n url: string,\n config: RequestHandlerConfig = {},\n): Promise> {\n return createRequestHandler(config).request(url, null, config);\n}\n\nexport * from './types';\nexport * from './api-handler';\n","type InterceptorFunction = (object: T) => Promise;\n\n/**\n * Applies interceptors to the object. Interceptors can be a single function or an array of functions.\n *\n * @template T - Type of the object.\n * @template I - Type of interceptors.\n *\n * @param {T} object - The object to process.\n * @param {InterceptorFunction | InterceptorFunction[]} [interceptors] - Interceptor function(s).\n *\n * @returns {Promise} - Nothing as the function is non-idempotent.\n */\nexport async function applyInterceptor<\n T extends object,\n I = InterceptorFunction | InterceptorFunction[],\n>(object: T, interceptors?: I): Promise {\n if (!interceptors) {\n return;\n }\n\n if (typeof interceptors === 'function') {\n const value = await interceptors(object);\n\n if (value) {\n Object.assign(object, value);\n }\n } else if (Array.isArray(interceptors)) {\n for (const interceptor of interceptors) {\n const value = await interceptor(object);\n\n if (value) {\n Object.assign(object, value);\n }\n }\n }\n}\n","import type { FetchResponse, RequestConfig } from './types';\n\nexport class ResponseErr extends Error {\n response: FetchResponse;\n request: RequestConfig;\n config: RequestConfig;\n status: number;\n statusText: string;\n\n constructor(\n message: string,\n requestInfo: RequestConfig,\n response: FetchResponse,\n ) {\n super(message);\n\n this.name = 'ResponseError';\n this.message = message;\n this.status = response.status;\n this.statusText = response.statusText;\n this.request = requestInfo;\n this.config = requestInfo;\n this.response = response;\n }\n}\n","export const APPLICATION_JSON = 'application/json';\nexport const CONTENT_TYPE = 'Content-Type';\n\nexport const UNDEFINED = 'undefined';\nexport const OBJECT = 'object';\nexport const STRING = 'string';\n\nexport const ABORT_ERROR = 'AbortError';\nexport const TIMEOUT_ERROR = 'TimeoutError';\nexport const CANCELLED_ERROR = 'CanceledError';\n\nexport const GET = 'GET';\nexport const HEAD = 'HEAD';\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { OBJECT, STRING, UNDEFINED } from './const';\nimport type { HeadersObject, QueryParams, UrlPathParams } from './types';\n\nexport function isSearchParams(data: unknown): boolean {\n return data instanceof URLSearchParams;\n}\n\n/**\n * Determines if a value is a non-null object.\n *\n * @param {any} value - The value to check.\n * @returns {boolean} - True if the value is a non-null object.\n */\nexport function isObject(value: any): value is Record {\n return value !== null && typeof value === OBJECT;\n}\n\n/**\n * Shallowly serializes an object by converting its key-value pairs into a string representation.\n * This function does not recursively serialize nested objects.\n *\n * @param obj - The object to serialize.\n * @returns A string representation of the object's top-level properties.\n */\nexport function shallowSerialize(obj: Record): string {\n let result = '';\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n result += key + ':' + obj[key];\n }\n }\n\n return result;\n}\n\n/**\n * Sorts the keys of an object and returns a new object with sorted keys.\n *\n * This function is optimized for performance by minimizing the number of object operations\n * and using a single pass to create the sorted object.\n *\n * @param {Object} obj - The object to be sorted by keys.\n * @returns {Object} - A new object with keys sorted in ascending order.\n */\nexport function sortObject(obj: Record): object {\n const sortedObj = {} as Record;\n const keys = Object.keys(obj);\n\n keys.sort();\n\n for (let i = 0, len = keys.length; i < len; i++) {\n const key = keys[i];\n sortedObj[key] = obj[key];\n }\n\n return sortedObj;\n}\n\n/**\n * Appends a query string to a URL, ensuring proper handling of existing query parameters.\n *\n * @param baseUrl - The base URL to which the query string will be appended.\n * @param queryString - The encoded query string to append.\n * @returns The URL with the appended query string, or the original URL if no query string is provided.\n */\nfunction appendQueryStringToUrl(baseUrl: string, queryString: string): string {\n if (!queryString) {\n return baseUrl;\n }\n\n return baseUrl.includes('?')\n ? `${baseUrl}&${queryString}`\n : `${baseUrl}?${queryString}`;\n}\n\n/**\n * Appends query parameters to a given URL.\n *\n * @param {string} url - The base URL to which query parameters will be appended.\n * @param {QueryParams} params - An object containing the query parameters to append.\n * @returns {string} - The URL with the appended query parameters.\n */\nexport function appendQueryParams(url: string, params: QueryParams): string {\n if (!params) {\n return url;\n }\n\n // Check if `params` is an instance of URLSearchParams and bail early if it is\n if (isSearchParams(params)) {\n const encodedQueryString = params.toString();\n\n return appendQueryStringToUrl(url, encodedQueryString);\n }\n\n // This is exact copy of what JQ used to do. It works much better than URLSearchParams\n const s: string[] = [];\n const encode = encodeURIComponent;\n const add = (k: string, v: any) => {\n v = typeof v === 'function' ? v() : v;\n v = v === null ? '' : v === undefined ? '' : v;\n s[s.length] = encode(k) + '=' + encode(v);\n };\n\n const buildParams = (prefix: string, obj: any) => {\n let i: number, len: number, key: string;\n\n if (prefix) {\n if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n buildParams(\n prefix + '[' + (typeof obj[i] === OBJECT && obj[i] ? i : '') + ']',\n obj[i],\n );\n }\n } else if (typeof obj === OBJECT && obj !== null) {\n for (key in obj) {\n buildParams(prefix + '[' + key + ']', obj[key]);\n }\n } else {\n add(prefix, obj);\n }\n } else if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n add(obj[i].name, obj[i].value);\n }\n } else {\n for (key in obj) {\n buildParams(key, obj[key]);\n }\n }\n return s;\n };\n\n const queryStringParts = buildParams('', params).join('&');\n\n // Encode special characters as per RFC 3986, https://datatracker.ietf.org/doc/html/rfc3986\n const encodedQueryString = queryStringParts.replace(/%5B%5D/g, '[]'); // Keep '[]' for arrays\n\n return appendQueryStringToUrl(url, encodedQueryString);\n}\n\n/**\n * Replaces dynamic URI parameters in a URL string with values from the provided `urlPathParams` object.\n * Parameters in the URL are denoted by `:`, where `` is a key in `urlPathParams`.\n *\n * @param {string} url - The URL string containing placeholders in the format `:`.\n * @param {Object} urlPathParams - An object containing the parameter values to replace placeholders.\n * @param {string} urlPathParams.paramName - The value to replace the placeholder `:` in the URL.\n * @returns {string} - The URL string with placeholders replaced by corresponding values from `urlPathParams`.\n */\nexport function replaceUrlPathParams(\n url: string,\n urlPathParams: UrlPathParams,\n): string {\n if (!urlPathParams) {\n return url;\n }\n\n return url.replace(/:\\w+/g, (str): string => {\n const word = str.substring(1);\n\n return String(urlPathParams[word] ? urlPathParams[word] : str);\n });\n}\n\n/**\n * Checks if a value is JSON serializable.\n *\n * JSON serializable values include:\n * - Primitive types: string, number, boolean, null\n * - Arrays\n * - Plain objects (i.e., objects without special methods)\n * - Values with a `toJSON` method\n *\n * @param {any} value - The value to check for JSON serializability.\n * @returns {boolean} - Returns `true` if the value is JSON serializable, otherwise `false`.\n */\nexport function isJSONSerializable(value: any): boolean {\n const t = typeof value;\n\n if (t === UNDEFINED || value === null) {\n return false;\n }\n\n if (t === STRING || t === 'number' || t === 'boolean') {\n return true;\n }\n\n if (Array.isArray(value)) {\n return true;\n }\n\n if (Buffer.isBuffer(value)) {\n return false;\n }\n\n if (value instanceof Date) {\n return false;\n }\n\n if (t === OBJECT) {\n const proto = Object.getPrototypeOf(value);\n\n // Check if the prototype is `Object.prototype` or `null` (plain object)\n if (proto === Object.prototype || proto === null) {\n return true;\n }\n\n // Check if the object has a toJSON method\n if (typeof value.toJSON === 'function') {\n return true;\n }\n }\n\n return false;\n}\n\nexport async function delayInvocation(ms: number): Promise {\n return new Promise((resolve) =>\n setTimeout(() => {\n return resolve(true);\n }, ms),\n );\n}\n\n/**\n * Recursively flattens the data object if it meets specific criteria.\n *\n * The method checks if the provided `data` is an object with exactly one property named `data`.\n * If so, it recursively flattens the `data` property. Otherwise, it returns the `data` as-is.\n *\n * @param {any} data - The data to be flattened. Can be of any type, including objects, arrays, or primitives.\n * @returns {any} - The flattened data if the criteria are met; otherwise, the original `data`.\n */\nexport function flattenData(data: any): any {\n if (\n data &&\n typeof data === OBJECT &&\n typeof data.data !== UNDEFINED &&\n Object.keys(data).length === 1\n ) {\n return flattenData(data.data);\n }\n\n return data;\n}\n\n/**\n * Processes headers and returns them as a normalized object.\n *\n * Handles both `Headers` instances and plain objects. Normalizes header keys to lowercase\n * as per RFC 2616 section 4.2.\n *\n * @param headers - The headers to process. Can be an instance of `Headers`, a plain object,\n * or `null`. If `null`, an empty object is returned.\n * @returns {HeadersObject} - A normalized headers object with lowercase keys.\n */\nexport function processHeaders(\n headers?: (HeadersObject & HeadersInit) | null | Headers,\n): HeadersObject {\n if (!headers) {\n return {};\n }\n\n const headersObject: HeadersObject = {};\n\n // Handle Headers object with entries() method\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n headersObject[key] = value;\n });\n } else if (typeof headers === OBJECT && headers !== null) {\n // Handle plain object\n for (const [key, value] of Object.entries(headers)) {\n // Normalize keys to lowercase as per RFC 2616 4.2\n // https://datatracker.ietf.org/doc/html/rfc2616#section-4.2\n headersObject[key.toLowerCase()] = value;\n }\n }\n\n return headersObject;\n}\n\n/**\n * Deletes a property from an object if it exists.\n *\n * @param obj - The object from which to delete the property.\n * @param property - The property to delete from the object.\n */\nexport function deleteProperty>(\n obj: T | null,\n property: keyof T,\n): void {\n if (obj && property in obj) {\n delete obj[property];\n }\n}\n","import { ABORT_ERROR, TIMEOUT_ERROR } from './const';\nimport type { RequestConfig } from './types';\nimport type { QueueItem, RequestsQueue } from './types/queue-manager';\n\n/**\n * Queue Manager is responsible for managing and controlling the flow of concurrent or sequential requests. It handles:\n * - Request Queueing and Deduplication\n * - Request Timeout Handling\n * - Abort Controller Management and Request Cancellation\n * - Concurrency Control and Locking\n * - Request Lifecycle Management\n */\nconst queue: RequestsQueue = new Map();\n\n/**\n * Adds a request to the queue if it's not already being processed within the dedupeTime interval.\n *\n * @param {RequestConfig} config - The request configuration object.\n * @param {number} timeout - Timeout in milliseconds for the request.\n * @param {number} dedupeTime - Deduplication time in milliseconds.\n * @param {boolean} isCancellable - If true, then the previous request with same configuration should be aborted.\n * @param {boolean} isTimeoutEnabled - Whether timeout is enabled.\n * @returns {Promise} - A promise that resolves to an AbortController.\n */\nexport async function addRequest(\n config: RequestConfig,\n timeout: number | undefined,\n dedupeTime: number = 0,\n isCancellable: boolean = false,\n isTimeoutEnabled: boolean = true,\n): Promise {\n const now = Date.now();\n const item = queue.get(config);\n\n if (item) {\n const isCancellable = item[3];\n const previousController = item[0];\n const timeoutId = item[1];\n\n // If the request is already in the queue and within the dedupeTime, reuse the existing controller\n if (!isCancellable && now - item[2] < dedupeTime) {\n return previousController;\n }\n\n // If the request is too old, remove it and proceed to add a new one\n // Abort previous request, if applicable, and continue as usual\n if (isCancellable) {\n previousController.abort(\n new DOMException('Aborted due to new request', ABORT_ERROR),\n );\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n\n const controller = new AbortController();\n\n const timeoutId = isTimeoutEnabled\n ? setTimeout(() => {\n const error = new DOMException(\n `${config.url} aborted due to timeout`,\n TIMEOUT_ERROR,\n );\n\n removeRequest(config, error);\n }, timeout)\n : null;\n\n queue.set(config, [controller, timeoutId, now, isCancellable]);\n\n return controller;\n}\n\n/**\n * Removes a request from the queue and clears its timeout.\n *\n * @param config - The request configuration.\n * @param {boolean} error - Error payload so to force the request to abort.\n */\nexport async function removeRequest(\n config: RequestConfig,\n error: DOMException | null | string = null,\n): Promise {\n const item = queue.get(config);\n\n if (item) {\n const controller = item[0];\n const timeoutId = item[1];\n\n // If the request is not yet aborted, abort it with the provided error\n if (error && !controller.signal.aborted) {\n controller.abort(error);\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n}\n\n/**\n * Gets the AbortController for a request configuration.\n *\n * @param config - The request configuration.\n * @returns {AbortController | undefined} - The AbortController or undefined.\n */\nexport async function getController(\n config: RequestConfig,\n): Promise {\n const item = queue.get(config);\n\n return item?.[0];\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { APPLICATION_JSON, CONTENT_TYPE } from './const';\nimport type { APIResponse } from './types/api-handler';\nimport type { FetchResponse } from './types/request-handler';\n\n/**\n * Parses the response data based on the Content-Type header.\n *\n * @param response - The Response object to parse.\n * @returns A Promise that resolves to the parsed data.\n */\nexport async function parseResponseData(\n response: FetchResponse,\n): Promise {\n // Bail early when body is empty\n if (!response?.body) {\n return null;\n }\n\n const contentType = String(\n (response as Response).headers?.get(CONTENT_TYPE) || '',\n ).split(';')[0]; // Correctly handle charset\n\n let data;\n\n try {\n if (\n contentType.includes(APPLICATION_JSON) ||\n contentType.includes('+json')\n ) {\n data = await response.json(); // Parse JSON response\n } else if (contentType.includes('multipart/form-data')) {\n data = await response.formData(); // Parse as FormData\n } else if (contentType.includes('application/octet-stream')) {\n data = await response.blob(); // Parse as blob\n } else if (contentType.includes('application/x-www-form-urlencoded')) {\n data = await response.formData(); // Handle URL-encoded forms\n } else if (contentType.includes('text/')) {\n data = await response.text(); // Parse as text\n } else {\n try {\n const responseClone = response.clone();\n\n // Handle edge case of no content type being provided... We assume JSON here.\n data = await responseClone.json();\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_e) {\n // Handle streams\n data = await response.text();\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_error) {\n // Parsing failed, fallback to null\n data = null;\n }\n\n return data;\n}\n","const PRIME_MULTIPLIER = 31;\n\n/**\n * Computes a hash value for a given string using the variant of djb2 hash function.\n * This hash function is non-cryptographic and designed for speed.\n * @author Daniel J. Bernstein (of djb2)\n *\n * @param str Input string to hash\n * @returns {string} Hash\n */\nexport function hash(str: string): string {\n let hash = 0;\n\n for (let i = 0, len = str.length; i < len; i++) {\n const char = str.charCodeAt(i);\n hash = (hash * PRIME_MULTIPLIER + char) | 0;\n }\n\n return String(hash);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { hash } from './hash';\nimport { fetchf } from './index';\nimport type { FetcherConfig } from './types/request-handler';\nimport type { CacheEntry } from './types/cache-manager';\nimport { GET, OBJECT, UNDEFINED } from './const';\nimport { shallowSerialize, sortObject } from './utils';\n\nconst cache = new Map>();\n\n/**\n * Generates a cache key for a given URL and fetch options, ensuring that key factors\n * like method, headers, body, and other options are included in the cache key.\n * Headers and other objects are sorted by key to ensure consistent cache keys.\n *\n * @param options - The fetch options that may affect the request. The most important are:\n * @property {string} [method=\"GET\"] - The HTTP method (GET, POST, etc.).\n * @property {HeadersInit} [headers={}] - The request headers.\n * @property {BodyInit | null} [body=\"\"] - The body of the request (only for methods like POST, PUT).\n * @property {RequestMode} [mode=\"cors\"] - The mode for the request (e.g., cors, no-cors, include).\n * @property {RequestCredentials} [credentials=\"include\"] - Whether to include credentials like cookies.\n * @property {RequestCache} [cache=\"default\"] - The cache mode (e.g., default, no-store, reload).\n * @property {RequestRedirect} [redirect=\"follow\"] - How to handle redirects (e.g., follow, error, manual).\n * @property {string} [referrer=\"\"] - The referrer URL to send with the request.\n * @property {string} [integrity=\"\"] - Subresource integrity value (a cryptographic hash for resource validation).\n * @returns {string} - A unique cache key based on the URL and request options. Empty if cache is to be burst.\n *\n * @example\n * const cacheKey = generateCacheKey({\n * url: 'https://api.example.com/data',\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ name: 'Alice' }),\n * mode: 'cors',\n * credentials: 'include',\n * });\n * console.log(cacheKey);\n */\nexport function generateCacheKey(options: FetcherConfig): string {\n const {\n url = '',\n method = GET,\n headers = {},\n body = '',\n mode = 'cors',\n credentials = 'include',\n cache = 'default',\n redirect = 'follow',\n referrer = '',\n integrity = '',\n } = options;\n\n // Bail early if cache should be burst\n if (cache === 'reload') {\n return '';\n }\n\n // Sort headers and body + convert sorted to strings for hashing purposes\n // Native serializer is on avg. 3.5x faster than a Fast Hash or FNV-1a\n const headersString = shallowSerialize(sortObject(headers));\n\n let bodyString = '';\n\n // In majority of cases we do not cache body\n if (body !== null) {\n if (typeof body === 'string') {\n bodyString = hash(body);\n } else if (body instanceof FormData) {\n body.forEach((value, key) => {\n // Append key=value and '&' directly to the result\n bodyString += key + '=' + value + '&';\n });\n bodyString = hash(bodyString);\n } else if (\n (typeof Blob !== UNDEFINED && body instanceof Blob) ||\n (typeof File !== UNDEFINED && body instanceof File)\n ) {\n bodyString = 'BF' + body.size + body.type;\n } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n bodyString = 'AB' + body.byteLength;\n } else {\n const o = typeof body === OBJECT ? sortObject(body) : String(body);\n bodyString = hash(JSON.stringify(o));\n }\n }\n\n // Concatenate all key parts into a cache key string\n // Template literals are apparently slower\n return (\n method +\n url +\n mode +\n credentials +\n cache +\n redirect +\n referrer +\n integrity +\n headersString +\n bodyString\n );\n}\n\n/**\n * Checks if the cache entry is expired based on its timestamp and the maximum stale time.\n *\n * @param {number} timestamp - The timestamp of the cache entry.\n * @param {number} maxStaleTime - The maximum stale time in seconds.\n * @returns {boolean} - Returns true if the cache entry is expired, false otherwise.\n */\nfunction isCacheExpired(timestamp: number, maxStaleTime: number): boolean {\n if (!maxStaleTime) {\n return false;\n }\n\n return Date.now() - timestamp > maxStaleTime * 1000;\n}\n\n/**\n * Retrieves a cache entry if it exists and is not expired.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} cacheTime - Maximum time to cache entry.\n * @returns {CacheEntry | null} - The cache entry if it exists and is not expired, null otherwise.\n */\nexport function getCache(\n key: string,\n cacheTime: number,\n): CacheEntry | null {\n const entry = cache.get(key);\n\n if (entry) {\n if (!isCacheExpired(entry.timestamp, cacheTime)) {\n return entry;\n }\n\n cache.delete(key);\n }\n\n return null;\n}\n\n/**\n * Sets a new cache entry or updates an existing one.\n *\n * @param {string} key Cache key to utilize\n * @param {T} data - The data to be cached.\n * @param {boolean} isLoading - Indicates if the data is currently being fetched.\n */\nexport function setCache(\n key: string,\n data: T,\n isLoading: boolean = false,\n): void {\n cache.set(key, {\n data,\n isLoading,\n timestamp: Date.now(),\n });\n}\n\n/**\n * Revalidates a cache entry by fetching fresh data and updating the cache.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @returns {Promise} - A promise that resolves when the revalidation is complete.\n */\nexport async function revalidate(\n key: string,\n config: FetcherConfig,\n): Promise {\n try {\n // Fetch fresh data\n const newData = await fetchf(config.url, {\n ...config,\n cache: 'reload',\n });\n\n setCache(key, newData);\n } catch (error) {\n console.error(`Error revalidating ${config.url}:`, error);\n\n // Rethrow the error to forward it\n throw error;\n }\n}\n\n/**\n * Invalidates (deletes) a cache entry.\n *\n * @param {string} key Cache key to utilize\n */\nexport function deleteCache(key: string): void {\n cache.delete(key);\n}\n\n/**\n * Mutates a cache entry with new data and optionally revalidates it.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @param {T} newData - The new data to be cached.\n * @param {boolean} revalidateAfter - If true, triggers revalidation after mutation.\n */\nexport function mutate(\n key: string,\n config: FetcherConfig,\n newData: T,\n revalidateAfter: boolean = false,\n): void {\n setCache(key, newData);\n\n if (revalidateAfter) {\n revalidate(key, config);\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {\n RequestHandlerConfig,\n RequestConfig,\n Method,\n RetryOptions,\n FetchResponse,\n ResponseError,\n RequestHandlerReturnType,\n CreatedCustomFetcherInstance,\n FetcherConfig,\n} from './types/request-handler';\nimport type {\n APIResponse,\n BodyPayload,\n QueryParams,\n QueryParamsOrBody,\n} from './types/api-handler';\nimport { applyInterceptor } from './interceptor-manager';\nimport { ResponseErr } from './response-error';\nimport {\n appendQueryParams,\n isJSONSerializable,\n replaceUrlPathParams,\n delayInvocation,\n flattenData,\n processHeaders,\n deleteProperty,\n isSearchParams,\n} from './utils';\nimport { addRequest, removeRequest } from './queue-manager';\nimport {\n ABORT_ERROR,\n APPLICATION_JSON,\n CANCELLED_ERROR,\n CONTENT_TYPE,\n GET,\n HEAD,\n OBJECT,\n STRING,\n UNDEFINED,\n} from './const';\nimport { parseResponseData } from './response-parser';\nimport { generateCacheKey, getCache, setCache } from './cache-manager';\n\nconst defaultConfig: RequestHandlerConfig = {\n method: GET,\n strategy: 'reject',\n timeout: 30000,\n dedupeTime: 1000,\n defaultResponse: null,\n headers: {\n Accept: APPLICATION_JSON + ', text/plain, */*',\n 'Accept-Encoding': 'gzip, deflate, br',\n [CONTENT_TYPE]: APPLICATION_JSON + ';charset=utf-8',\n },\n retry: {\n delay: 1000,\n maxDelay: 30000,\n resetTimeout: true,\n backoff: 1.5,\n\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status\n retryOn: [\n 408, // Request Timeout\n 409, // Conflict\n 425, // Too Early\n 429, // Too Many Requests\n 500, // Internal Server Error\n 502, // Bad Gateway\n 503, // Service Unavailable\n 504, // Gateway Timeout\n ],\n },\n};\n\n/**\n * Create Request Handler\n *\n * @param {RequestHandlerConfig} config - Configuration object for the request handler\n * @returns {Object} An object with methods for handling requests\n */\nexport function createRequestHandler(\n config: RequestHandlerConfig,\n): RequestHandlerReturnType {\n const handlerConfig: RequestHandlerConfig = {\n ...defaultConfig,\n ...config,\n };\n\n /**\n * Immediately create instance of custom fetcher if it is defined\n */\n const customFetcher = handlerConfig.fetcher;\n const requestInstance = customFetcher?.create(handlerConfig) || null;\n\n /**\n * Get Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Provider's instance\n */\n const getInstance = (): CreatedCustomFetcherInstance | null => {\n return requestInstance;\n };\n\n /**\n * Gets a configuration value from `reqConfig`, defaulting to `handlerConfig` if not present.\n *\n * @param {RequestConfig} reqConfig - Request configuration object.\n * @param {keyof RequestConfig} name - Key of the configuration value.\n * @returns {T} - The configuration value.\n */\n const getConfig = (\n reqConfig: RequestConfig,\n name: keyof RequestConfig,\n ): T => {\n return typeof reqConfig[name] !== UNDEFINED\n ? reqConfig[name]\n : handlerConfig[name];\n };\n\n /**\n * Logs messages or errors using the configured logger's `warn` method.\n *\n * @param {...(string | ResponseError)} args - Messages or errors to log.\n */\n const logger = (...args: (string | ResponseError)[]): void => {\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn(...args);\n }\n };\n\n /**\n * Build request configuration\n *\n * @param {string} url - Request url\n * @param {QueryParamsOrBody} data - Query Params in case of GET and HEAD requests, body payload otherwise\n * @param {RequestConfig} reqConfig - Request config passed when making the request\n * @returns {RequestConfig} - Provider's instance\n */\n const buildConfig = (\n url: string,\n data: QueryParamsOrBody,\n reqConfig: RequestConfig,\n ): FetcherConfig => {\n const method = getConfig(\n reqConfig,\n 'method',\n ).toUpperCase() as Method;\n const isGetAlikeMethod = method === GET || method === HEAD;\n\n const dynamicUrl = replaceUrlPathParams(\n url,\n getConfig(reqConfig, 'urlPathParams'),\n );\n\n // The explicitly passed \"params\"\n const explicitParams = getConfig(reqConfig, 'params');\n\n // The explicitly passed \"body\" or \"data\"\n const explicitBodyData: BodyPayload =\n getConfig(reqConfig, 'body') || getConfig(reqConfig, 'data');\n\n // For convenience, in POST requests the body payload is the \"data\"\n // In edge cases we want to use Query Params in the POST requests\n // and use explicitly passed \"body\" or \"data\" from request config\n const shouldTreatDataAsParams =\n data && (isGetAlikeMethod || explicitBodyData) ? true : false;\n\n // Final body data\n let body: RequestConfig['data'];\n\n // Only applicable for request methods 'PUT', 'POST', 'DELETE', and 'PATCH'\n if (!isGetAlikeMethod) {\n body = explicitBodyData || (data as BodyPayload);\n }\n\n // Native fetch compatible settings\n const isWithCredentials = getConfig(reqConfig, 'withCredentials');\n\n const credentials = isWithCredentials\n ? 'include'\n : getConfig(reqConfig, 'credentials');\n\n deleteProperty(reqConfig, 'data');\n deleteProperty(reqConfig, 'withCredentials');\n\n const urlPath =\n explicitParams || shouldTreatDataAsParams\n ? appendQueryParams(dynamicUrl, explicitParams || (data as QueryParams))\n : dynamicUrl;\n const isFullUrl = urlPath.includes('://');\n const baseURL = isFullUrl\n ? ''\n : getConfig(reqConfig, 'baseURL') ||\n getConfig(reqConfig, 'apiUrl');\n\n // Automatically stringify request body, if possible and when not dealing with strings\n if (\n body &&\n typeof body !== STRING &&\n !isSearchParams(body) &&\n isJSONSerializable(body)\n ) {\n body = JSON.stringify(body);\n }\n\n return {\n ...reqConfig,\n credentials,\n body,\n method,\n\n url: baseURL + urlPath,\n };\n };\n\n /**\n * Process global Request Error\n *\n * @param {ResponseError} error Error instance\n * @param {RequestConfig} requestConfig Per endpoint request config\n * @returns {Promise}\n */\n const processError = async (\n error: ResponseError,\n requestConfig: RequestConfig,\n ): Promise => {\n if (!isRequestCancelled(error)) {\n logger('API ERROR', error);\n }\n\n // Local interceptors\n await applyInterceptor(error, requestConfig?.onError);\n\n // Global interceptors\n await applyInterceptor(error, handlerConfig?.onError);\n };\n\n /**\n * Output default response in case of an error, depending on chosen strategy\n *\n * @param {ResponseError} error Error instance\n * @param {FetchResponse} response Response\n * @param {RequestConfig} requestConfig Per endpoint request config\n * @returns {*} Error response\n */\n const outputErrorResponse = async (\n error: ResponseError,\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n ): Promise => {\n const _isRequestCancelled = isRequestCancelled(error);\n const errorHandlingStrategy = getConfig(requestConfig, 'strategy');\n const rejectCancelled = getConfig(\n requestConfig,\n 'rejectCancelled',\n );\n\n // By default cancelled requests aren't rejected (softFail strategy)\n if (!(_isRequestCancelled && !rejectCancelled)) {\n // Hang the promise\n if (errorHandlingStrategy === 'silent') {\n await new Promise(() => null);\n }\n // Reject the promise\n else if (errorHandlingStrategy === 'reject') {\n return Promise.reject(error);\n }\n }\n\n return outputResponse(response, requestConfig, error);\n };\n\n /**\n * Output error response depending on chosen strategy\n *\n * @param {ResponseError} error Error instance\n * @returns {boolean} True if request is aborted\n */\n const isRequestCancelled = (error: ResponseError): boolean => {\n return error.name === ABORT_ERROR || error.name === CANCELLED_ERROR;\n };\n\n /**\n * Handle Request depending on used strategy\n *\n * @param {string} url - Request url\n * @param {QueryParamsOrBody} data - Query Params in case of GET and HEAD requests, body payload otherwise\n * @param {RequestConfig} reqConfig - Request config\n * @throws {ResponseError}\n * @returns {Promise>} Response Data\n */\n const request = async (\n url: string,\n data: QueryParamsOrBody = null,\n reqConfig: RequestConfig | null = null,\n ): Promise> => {\n const _reqConfig = reqConfig || {};\n const mergedConfig = {\n ...handlerConfig,\n ..._reqConfig,\n } as RequestConfig;\n\n let response: FetchResponse | null = null;\n const fetcherConfig = buildConfig(url, data, mergedConfig);\n\n const {\n timeout,\n cancellable,\n dedupeTime,\n pollingInterval,\n shouldStopPolling,\n cacheTime,\n cacheKey,\n } = mergedConfig;\n\n // Prevent performance overhead of cache access\n let _cacheKey: string;\n\n if (cacheKey) {\n _cacheKey = cacheKey(fetcherConfig);\n } else {\n _cacheKey = generateCacheKey(fetcherConfig);\n }\n\n if (cacheTime && _cacheKey) {\n const cacheBuster = mergedConfig.cacheBuster;\n\n if (!cacheBuster || !cacheBuster(fetcherConfig)) {\n const cachedEntry = getCache<\n ResponseData & FetchResponse\n >(_cacheKey, cacheTime);\n\n if (cachedEntry) {\n // Serve stale data from cache\n return cachedEntry.data;\n }\n }\n }\n\n const {\n retries = 0,\n delay,\n backoff,\n retryOn,\n shouldRetry,\n maxDelay,\n resetTimeout,\n } = mergedConfig.retry as Required;\n\n let attempt = 0;\n let pollingAttempt = 0;\n let waitTime: number = delay;\n\n while (attempt <= retries) {\n try {\n // Add the request to the queue. Make sure to handle deduplication, cancellation, timeouts in accordance to retry settings\n const controller = await addRequest(\n fetcherConfig,\n timeout,\n dedupeTime,\n cancellable,\n // Reset timeouts by default or when retries are ON\n !!(timeout && (!retries || resetTimeout)),\n );\n\n // Shallow copy to ensure basic idempotency\n const requestConfig: RequestConfig = {\n signal: controller.signal,\n ...fetcherConfig,\n };\n\n // Local interceptors\n await applyInterceptor(requestConfig, _reqConfig?.onRequest);\n\n // Global interceptors\n await applyInterceptor(requestConfig, handlerConfig?.onRequest);\n\n if (customFetcher !== null && requestInstance !== null) {\n response = await requestInstance.request(requestConfig);\n } else {\n response = (await fetch(\n requestConfig.url as string,\n requestConfig as RequestInit,\n )) as FetchResponse;\n }\n\n // Add more information to response object\n if (response instanceof Response) {\n response.config = requestConfig;\n response.data = await parseResponseData(response);\n\n // Check if the response status is not outside the range 200-299 and if so, output error\n if (!response.ok) {\n throw new ResponseErr(\n `${requestConfig.url} failed! Status: ${response.status || null}`,\n requestConfig,\n response,\n );\n }\n }\n\n // Local interceptors\n await applyInterceptor(response, _reqConfig?.onResponse);\n\n // Global interceptors\n await applyInterceptor(response, handlerConfig?.onResponse);\n\n removeRequest(fetcherConfig);\n\n // Polling logic\n if (\n pollingInterval &&\n (!shouldStopPolling || !shouldStopPolling(response, pollingAttempt))\n ) {\n // Restart the main retry loop\n pollingAttempt++;\n\n logger(`Polling attempt ${pollingAttempt}...`);\n\n await delayInvocation(pollingInterval);\n\n continue;\n }\n\n // If polling is not required, or polling attempts are exhausted\n const output = outputResponse(response, requestConfig) as ResponseData &\n FetchResponse;\n\n if (cacheTime && _cacheKey) {\n const skipCache = requestConfig.skipCache;\n\n if (!skipCache || !skipCache(output, requestConfig)) {\n setCache(_cacheKey, output);\n }\n }\n\n return output;\n } catch (err) {\n const error = err as ResponseErr;\n const status = error?.response?.status || error?.status || 0;\n\n if (\n attempt === retries ||\n !(!shouldRetry || (await shouldRetry(error, attempt))) ||\n !retryOn?.includes(status)\n ) {\n await processError(error, fetcherConfig);\n\n removeRequest(fetcherConfig);\n\n return outputErrorResponse(error, response, fetcherConfig);\n }\n\n logger(`Attempt ${attempt + 1} failed. Retry in ${waitTime}ms.`);\n\n await delayInvocation(waitTime);\n\n waitTime *= backoff;\n waitTime = Math.min(waitTime, maxDelay);\n attempt++;\n }\n }\n\n return outputResponse(response, fetcherConfig) as ResponseData &\n FetchResponse;\n };\n\n /**\n * Output response\n *\n * @param response - Response payload\n * @param {RequestConfig} requestConfig - Request config\n * @param error - whether the response is erroneous\n * @returns {ResponseData | FetchResponse} Response data\n */\n const outputResponse = (\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n error: ResponseError | null = null,\n ): ResponseData | FetchResponse => {\n const defaultResponse = getConfig(requestConfig, 'defaultResponse');\n const flattenResponse = getConfig(\n requestConfig,\n 'flattenResponse',\n );\n\n if (!response) {\n return flattenResponse\n ? defaultResponse\n : {\n error,\n headers: null,\n data: defaultResponse,\n config: requestConfig,\n };\n }\n\n // Clean up the error object\n deleteProperty(error, 'response');\n deleteProperty(error, 'request');\n deleteProperty(error, 'config');\n\n let data = response?.data;\n\n // Set the default response if the provided data is an empty object\n if (\n data === undefined ||\n data === null ||\n (typeof data === OBJECT && Object.keys(data).length === 0)\n ) {\n data = defaultResponse;\n }\n\n // Return flattened response immediately\n if (flattenResponse) {\n return flattenData(data);\n }\n\n // If it's a custom fetcher, and it does not return any Response instance, it may have its own internal handler\n if (!(response instanceof Response)) {\n return response;\n }\n\n // Native fetch Response extended by extra information\n return {\n body: response.body,\n bodyUsed: response.bodyUsed,\n formData: response.formData,\n ok: response.ok,\n redirected: response.redirected,\n type: response.type,\n url: response.url,\n status: response.status,\n statusText: response.statusText,\n\n blob: response.blob.bind(response),\n json: response.json.bind(response),\n text: response.text.bind(response),\n clone: response.clone.bind(response),\n arrayBuffer: response.arrayBuffer.bind(response),\n\n // Extend with extra information\n error,\n data,\n headers: processHeaders(response.headers),\n config: requestConfig,\n };\n };\n\n return {\n getInstance,\n buildConfig,\n config,\n request,\n };\n}\n","import type {\n RequestConfig,\n FetchResponse,\n CreatedCustomFetcherInstance,\n} from './types/request-handler';\nimport type {\n ApiHandlerConfig,\n ApiHandlerMethods,\n ApiHandlerReturnType,\n APIResponse,\n QueryParamsOrBody,\n UrlPathParams,\n} from './types/api-handler';\nimport { createRequestHandler } from './request-handler';\n\n/**\n * Creates an instance of API Handler.\n * It creates an API fetcher function using native fetch() or a custom fetcher if it is passed as \"fetcher\".\n * @url https://github.com/MattCCC/fetchff\n *\n * @param {Object} config - Configuration object for the API fetcher.\n * @param {string} config.apiUrl - The base URL for the API.\n * @param {Object} config.endpoints - An object containing endpoint definitions.\n * @param {number} config.timeout - You can set the timeout for particular request in milliseconds.\n * @param {number} config.cancellable - If true, the ongoing previous requests will be automatically cancelled.\n * @param {number} config.rejectCancelled - If true and request is set to cancellable, a cancelled request promise will be rejected. By default, instead of rejecting the promise, defaultResponse is returned.\n * @param {number} config.timeout - Request timeout\n * @param {number} config.dedupeTime - Time window, in milliseconds, during which identical requests are deduplicated (treated as single request).\n * @param {string} config.strategy - Error Handling Strategy\n * @param {string} config.flattenResponse - Whether to flatten response \"data\" object within \"data\" one\n * @param {*} config.defaultResponse - Default response when there is no data or when endpoint fails depending on the chosen strategy. It's \"null\" by default\n * @param {Object} [config.retry] - Options for retrying requests.\n * @param {number} [config.retry.retries=0] - Number of retry attempts. No retries by default.\n * @param {number} [config.retry.delay=1000] - Initial delay between retries in milliseconds.\n * @param {number} [config.retry.backoff=1.5] - Exponential backoff factor.\n * @param {number[]} [config.retry.retryOn=[502, 504, 408]] - HTTP status codes to retry on.\n * @param {RequestInterceptor|RequestInterceptor[]} [config.onRequest] - Optional request interceptor function or an array of functions.\n * These functions will be called with the request configuration object before the request is made. Can be used to modify or log the request configuration.\n * @param {ResponseInterceptor|ResponseInterceptor[]} [config.onResponse] - Optional response interceptor function or an array of functions.\n * These functions will be called with the response object after the response is received. an be used to modify or log the response data.\n * @param {Function} [config.onError] - Optional callback function for handling errors.\n * @param {Object} [config.headers] - Optional default headers to include in every request.\n * @param {Object} config.fetcher - The Custom Fetcher instance to use for making requests. It should expose create() and request() functions.\n * @param {*} config.logger - Instance of custom logger. Either class or an object similar to \"console\". Console is used by default.\n * @returns API handler functions and endpoints to call\n *\n * @example\n * // Define endpoint paths\n * const endpoints = {\n * getUser: '/user',\n * createPost: '/post',\n * };\n *\n * // Create the API fetcher with configuration\n * const api = createApiFetcher({\n * endpoints,\n * apiUrl: 'https://example.com/api',\n * onError(error) {\n * console.log('Request failed', error);\n * },\n * headers: {\n * 'my-auth-key': 'example-auth-key-32rjjfa',\n * },\n * });\n *\n * // Fetch user data\n * const response = await api.getUser({ userId: 1, ratings: [1, 2] })\n */\nfunction createApiFetcher<\n EndpointsMethods extends object,\n EndpointsCfg = never,\n>(config: ApiHandlerConfig) {\n const endpoints = config.endpoints;\n const requestHandler = createRequestHandler(config);\n\n /**\n * Get Custom Fetcher Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Request Handler's Custom Fetcher Instance\n */\n function getInstance(): CreatedCustomFetcherInstance | null {\n return requestHandler.getInstance();\n }\n\n /**\n * Triggered when trying to use non-existent endpoints\n *\n * @param endpointName Endpoint Name\n * @returns {Promise}\n */\n function handleNonImplemented(endpointName: string): Promise {\n console.error(`Add ${endpointName} to 'endpoints'.`);\n\n return Promise.resolve(null);\n }\n\n /**\n * Handle Single API Request\n * It considers settings in following order: per-request settings, global per-endpoint settings, global settings.\n *\n * @param {string} endpointName - The name of the API endpoint to call.\n * @param {QueryParamsOrBody} [data={}] - Query parameters to include in the request.\n * @param {UrlPathParams} [urlPathParams={}] - URI parameters to include in the request.\n * @param {EndpointConfig} [requestConfig={}] - Additional configuration for the request.\n * @returns {Promise} - A promise that resolves with the response from the API provider.\n */\n async function request(\n endpointName: keyof EndpointsMethods | string,\n data: QueryParamsOrBody = {},\n urlPathParams: UrlPathParams = {},\n requestConfig: RequestConfig = {},\n ): Promise> {\n // Use global per-endpoint settings\n const endpointConfig = endpoints[endpointName as string];\n\n const responseData = await requestHandler.request(\n endpointConfig.url,\n data,\n {\n ...(endpointConfig || {}),\n ...requestConfig,\n urlPathParams,\n },\n );\n\n return responseData;\n }\n\n /**\n * Maps all API requests using native Proxy\n *\n * @param {*} prop Caller\n */\n function get(prop: string) {\n if (prop in apiHandler) {\n return apiHandler[\n prop as unknown as keyof ApiHandlerMethods\n ];\n }\n\n // Prevent handler from triggering non-existent endpoints\n if (!endpoints[prop]) {\n return handleNonImplemented.bind(null, prop);\n }\n\n return apiHandler.request.bind(null, prop);\n }\n\n const apiHandler: ApiHandlerMethods = {\n config,\n endpoints,\n requestHandler,\n getInstance,\n request,\n };\n\n return new Proxy(apiHandler, {\n get: (_target, prop: string) => get(prop),\n }) as ApiHandlerReturnType;\n}\n\nexport { createApiFetcher };\n"],"mappings":"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","names":["src_exports","__export","createApiFetcher","fetchf","applyInterceptor","object","interceptors","value","interceptor","ResponseErr","message","requestInfo","response","__publicField","APPLICATION_JSON","CONTENT_TYPE","UNDEFINED","OBJECT","STRING","ABORT_ERROR","TIMEOUT_ERROR","CANCELLED_ERROR","GET","HEAD","isSearchParams","data","shallowSerialize","obj","result","key","sortObject","sortedObj","keys","i","len","appendQueryStringToUrl","baseUrl","queryString","appendQueryParams","url","params","isSearchParams","encodedQueryString","s","encode","add","k","v","buildParams","prefix","OBJECT","replaceUrlPathParams","urlPathParams","str","word","isJSONSerializable","value","UNDEFINED","STRING","proto","delayInvocation","ms","resolve","flattenData","data","processHeaders","headers","headersObject","deleteProperty","property","queue","addRequest","config","timeout","dedupeTime","isCancellable","isTimeoutEnabled","now","item","previousController","timeoutId","ABORT_ERROR","controller","error","TIMEOUT_ERROR","removeRequest","parseResponseData","response","_a","contentType","CONTENT_TYPE","data","APPLICATION_JSON","_e","_error","hash","str","i","len","char","cache","generateCacheKey","options","url","method","GET","headers","body","mode","credentials","redirect","referrer","integrity","headersString","shallowSerialize","sortObject","bodyString","hash","value","key","UNDEFINED","o","OBJECT","isCacheExpired","timestamp","maxStaleTime","getCache","cacheTime","entry","setCache","data","isLoading","defaultConfig","GET","APPLICATION_JSON","CONTENT_TYPE","createRequestHandler","config","handlerConfig","__spreadValues","customFetcher","requestInstance","getInstance","getConfig","reqConfig","name","UNDEFINED","logger","args","_a","buildConfig","url","data","method","isGetAlikeMethod","HEAD","dynamicUrl","replaceUrlPathParams","explicitParams","explicitBodyData","shouldTreatDataAsParams","body","credentials","deleteProperty","urlPath","appendQueryParams","baseURL","STRING","isSearchParams","isJSONSerializable","__spreadProps","processError","error","requestConfig","isRequestCancelled","applyInterceptor","outputErrorResponse","response","_isRequestCancelled","errorHandlingStrategy","rejectCancelled","outputResponse","ABORT_ERROR","CANCELLED_ERROR","request","_reqConfig","mergedConfig","fetcherConfig","timeout","cancellable","dedupeTime","pollingInterval","shouldStopPolling","cacheTime","cacheKey","_cacheKey","generateCacheKey","cacheBuster","cachedEntry","getCache","retries","delay","backoff","retryOn","shouldRetry","maxDelay","resetTimeout","attempt","pollingAttempt","waitTime","controller","addRequest","parseResponseData","ResponseErr","removeRequest","delayInvocation","output","skipCache","setCache","err","status","defaultResponse","flattenResponse","OBJECT","flattenData","processHeaders","createApiFetcher","config","endpoints","requestHandler","createRequestHandler","getInstance","handleNonImplemented","endpointName","request","data","urlPathParams","requestConfig","endpointConfig","__spreadProps","__spreadValues","get","prop","apiHandler","_target","fetchf","url","config","createRequestHandler"]} \ No newline at end of file +{"version":3,"sources":["../../src/index.ts","../../src/interceptor-manager.ts","../../src/response-error.ts","../../src/constants.ts","../../src/utils.ts","../../src/queue-manager.ts","../../src/response-parser.ts","../../src/hash.ts","../../src/cache-manager.ts","../../src/request-handler.ts","../../src/api-handler.ts"],"sourcesContent":["import { createRequestHandler } from './request-handler';\nimport type {\n DefaultResponse,\n FetchResponse,\n RequestHandlerConfig,\n} from './types';\n\n/**\n * Simple wrapper for request fetching.\n * It abstracts the creation of RequestHandler, making it easy to perform API requests.\n *\n * @param {string | URL | globalThis.Request} url - Request URL.\n * @param {RequestHandlerConfig} config - Configuration object for the request handler.\n * @returns {Promise>} Response Data.\n */\nexport async function fetchf(\n url: string,\n config: RequestHandlerConfig = {},\n): Promise> {\n return createRequestHandler(config).request(url, config);\n}\n\nexport * from './types';\nexport * from './api-handler';\n","type InterceptorFunction = (object: T) => Promise;\n\n/**\n * Applies interceptors to the object. Interceptors can be a single function or an array of functions.\n *\n * @template T - Type of the object.\n * @template I - Type of interceptors.\n *\n * @param {T} object - The object to process.\n * @param {InterceptorFunction | InterceptorFunction[]} [interceptors] - Interceptor function(s).\n *\n * @returns {Promise} - Nothing as the function is non-idempotent.\n */\nexport async function applyInterceptor<\n T extends object,\n I = InterceptorFunction | InterceptorFunction[],\n>(object: T, interceptors?: I): Promise {\n if (!interceptors) {\n return;\n }\n\n if (typeof interceptors === 'function') {\n const value = await interceptors(object);\n\n if (value) {\n Object.assign(object, value);\n }\n } else if (Array.isArray(interceptors)) {\n for (const interceptor of interceptors) {\n const value = await interceptor(object);\n\n if (value) {\n Object.assign(object, value);\n }\n }\n }\n}\n","import type { FetchResponse, RequestConfig } from './types';\n\nexport class ResponseErr extends Error {\n response: FetchResponse;\n request: RequestConfig;\n config: RequestConfig;\n status: number;\n statusText: string;\n\n constructor(\n message: string,\n requestInfo: RequestConfig,\n response: FetchResponse,\n ) {\n super(message);\n\n this.name = 'ResponseError';\n this.message = message;\n this.status = response.status;\n this.statusText = response.statusText;\n this.request = requestInfo;\n this.config = requestInfo;\n this.response = response;\n }\n}\n","export const APPLICATION_CONTENT_TYPE = 'application/';\n\nexport const APPLICATION_JSON = APPLICATION_CONTENT_TYPE + 'json';\nexport const CONTENT_TYPE = 'Content-Type';\n\nexport const UNDEFINED = 'undefined';\nexport const OBJECT = 'object';\nexport const STRING = 'string';\n\nexport const ABORT_ERROR = 'AbortError';\nexport const TIMEOUT_ERROR = 'TimeoutError';\nexport const CANCELLED_ERROR = 'CanceledError';\n\nexport const GET = 'GET';\nexport const HEAD = 'HEAD';\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { OBJECT, STRING, UNDEFINED } from './constants';\nimport type {\n DefaultUrlParams,\n HeadersObject,\n QueryParams,\n UrlPathParams,\n} from './types';\n\nexport function isSearchParams(data: unknown): boolean {\n return data instanceof URLSearchParams;\n}\n\n/**\n * Determines if a value is a non-null object.\n *\n * @param {any} value - The value to check.\n * @returns {boolean} - True if the value is a non-null object.\n */\nexport function isObject(value: any): value is Record {\n return value !== null && typeof value === OBJECT;\n}\n\n/**\n * Shallowly serializes an object by converting its key-value pairs into a string representation.\n * This function does not recursively serialize nested objects.\n *\n * @param obj - The object to serialize.\n * @returns A string representation of the object's top-level properties.\n */\nexport function shallowSerialize(obj: Record): string {\n let result = '';\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n result += key + ':' + obj[key];\n }\n }\n\n return result;\n}\n\n/**\n * Sorts the keys of an object and returns a new object with sorted keys.\n *\n * This function is optimized for performance by minimizing the number of object operations\n * and using a single pass to create the sorted object.\n *\n * @param {Object} obj - The object to be sorted by keys.\n * @returns {Object} - A new object with keys sorted in ascending order.\n */\nexport function sortObject(obj: Record): object {\n const sortedObj = {} as Record;\n const keys = Object.keys(obj);\n\n keys.sort();\n\n for (let i = 0, len = keys.length; i < len; i++) {\n const key = keys[i];\n sortedObj[key] = obj[key];\n }\n\n return sortedObj;\n}\n\n/**\n * Appends a query string to a URL, ensuring proper handling of existing query parameters.\n *\n * @param baseUrl - The base URL to which the query string will be appended.\n * @param queryString - The encoded query string to append.\n * @returns The URL with the appended query string, or the original URL if no query string is provided.\n */\nfunction appendQueryStringToUrl(baseUrl: string, queryString: string): string {\n if (!queryString) {\n return baseUrl;\n }\n\n return baseUrl.includes('?')\n ? `${baseUrl}&${queryString}`\n : `${baseUrl}?${queryString}`;\n}\n\n/**\n * Appends query parameters to a given URL.\n *\n * @param {string} url - The base URL to which query parameters will be appended.\n * @param {QueryParams} params - An object containing the query parameters to append.\n * @returns {string} - The URL with the appended query parameters.\n */\nexport function appendQueryParams(url: string, params: QueryParams): string {\n if (!params) {\n return url;\n }\n\n // Check if `params` is an instance of URLSearchParams and bail early if it is\n if (isSearchParams(params)) {\n const encodedQueryString = params.toString();\n\n return appendQueryStringToUrl(url, encodedQueryString);\n }\n\n // This is exact copy of what JQ used to do. It works much better than URLSearchParams\n const s: string[] = [];\n const encode = encodeURIComponent;\n const add = (k: string, v: any) => {\n v = typeof v === 'function' ? v() : v;\n v = v === null ? '' : v === undefined ? '' : v;\n s[s.length] = encode(k) + '=' + encode(v);\n };\n\n const buildParams = (prefix: string, obj: any) => {\n let i: number, len: number, key: string;\n\n if (prefix) {\n if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n buildParams(\n prefix + '[' + (typeof obj[i] === OBJECT && obj[i] ? i : '') + ']',\n obj[i],\n );\n }\n } else if (typeof obj === OBJECT && obj !== null) {\n for (key in obj) {\n buildParams(prefix + '[' + key + ']', obj[key]);\n }\n } else {\n add(prefix, obj);\n }\n } else if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n add(obj[i].name, obj[i].value);\n }\n } else {\n for (key in obj) {\n buildParams(key, obj[key]);\n }\n }\n return s;\n };\n\n const queryStringParts = buildParams('', params).join('&');\n\n // Encode special characters as per RFC 3986, https://datatracker.ietf.org/doc/html/rfc3986\n const encodedQueryString = queryStringParts.replace(/%5B%5D/g, '[]'); // Keep '[]' for arrays\n\n return appendQueryStringToUrl(url, encodedQueryString);\n}\n\n/**\n * Replaces dynamic URI parameters in a URL string with values from the provided `urlPathParams` object.\n * Parameters in the URL are denoted by `:`, where `` is a key in `urlPathParams`.\n *\n * @param {string} url - The URL string containing placeholders in the format `:`.\n * @param {Object} urlPathParams - An object containing the parameter values to replace placeholders.\n * @param {string} urlPathParams.paramName - The value to replace the placeholder `:` in the URL.\n * @returns {string} - The URL string with placeholders replaced by corresponding values from `urlPathParams`.\n */\nexport function replaceUrlPathParams(\n url: string,\n urlPathParams: UrlPathParams,\n): string {\n if (!urlPathParams) {\n return url;\n }\n\n return url.replace(/:\\w+/g, (str): string => {\n const word = str.substring(1);\n\n if ((urlPathParams as DefaultUrlParams)[word]) {\n return String((urlPathParams as DefaultUrlParams)[word]);\n }\n\n return str;\n });\n}\n\n/**\n * Checks if a value is JSON serializable.\n *\n * JSON serializable values include:\n * - Primitive types: string, number, boolean, null\n * - Arrays\n * - Plain objects (i.e., objects without special methods)\n * - Values with a `toJSON` method\n *\n * @param {any} value - The value to check for JSON serializability.\n * @returns {boolean} - Returns `true` if the value is JSON serializable, otherwise `false`.\n */\nexport function isJSONSerializable(value: any): boolean {\n const t = typeof value;\n\n if (t === UNDEFINED || value === null) {\n return false;\n }\n\n if (t === STRING || t === 'number' || t === 'boolean') {\n return true;\n }\n\n if (Array.isArray(value)) {\n return true;\n }\n\n if (Buffer.isBuffer(value)) {\n return false;\n }\n\n if (value instanceof Date) {\n return false;\n }\n\n if (t === OBJECT) {\n const proto = Object.getPrototypeOf(value);\n\n // Check if the prototype is `Object.prototype` or `null` (plain object)\n if (proto === Object.prototype || proto === null) {\n return true;\n }\n\n // Check if the object has a toJSON method\n if (typeof value.toJSON === 'function') {\n return true;\n }\n }\n\n return false;\n}\n\nexport async function delayInvocation(ms: number): Promise {\n return new Promise((resolve) =>\n setTimeout(() => {\n return resolve(true);\n }, ms),\n );\n}\n\n/**\n * Recursively flattens the data object if it meets specific criteria.\n *\n * The method checks if the provided `data` is an object with exactly one property named `data`.\n * If so, it recursively flattens the `data` property. Otherwise, it returns the `data` as-is.\n *\n * @param {any} data - The data to be flattened. Can be of any type, including objects, arrays, or primitives.\n * @returns {any} - The flattened data if the criteria are met; otherwise, the original `data`.\n */\nexport function flattenData(data: any): any {\n if (\n data &&\n typeof data === OBJECT &&\n typeof data.data !== UNDEFINED &&\n Object.keys(data).length === 1\n ) {\n return flattenData(data.data);\n }\n\n return data;\n}\n\n/**\n * Processes headers and returns them as a normalized object.\n *\n * Handles both `Headers` instances and plain objects. Normalizes header keys to lowercase\n * as per RFC 2616 section 4.2.\n *\n * @param headers - The headers to process. Can be an instance of `Headers`, a plain object,\n * or `null`. If `null`, an empty object is returned.\n * @returns {HeadersObject} - A normalized headers object with lowercase keys.\n */\nexport function processHeaders(\n headers?: (HeadersObject & HeadersInit) | null | Headers,\n): HeadersObject {\n if (!headers) {\n return {};\n }\n\n const headersObject: HeadersObject = {};\n\n // Handle Headers object with entries() method\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n headersObject[key] = value;\n });\n } else if (typeof headers === OBJECT && headers !== null) {\n // Handle plain object\n for (const [key, value] of Object.entries(headers)) {\n // Normalize keys to lowercase as per RFC 2616 4.2\n // https://datatracker.ietf.org/doc/html/rfc2616#section-4.2\n headersObject[key.toLowerCase()] = value;\n }\n }\n\n return headersObject;\n}\n\n/**\n * Deletes a property from an object if it exists.\n *\n * @param obj - The object from which to delete the property.\n * @param property - The property to delete from the object.\n */\nexport function deleteProperty>(\n obj: T | null,\n property: keyof T,\n): void {\n if (obj && property in obj) {\n delete obj[property];\n }\n}\n","import { ABORT_ERROR, TIMEOUT_ERROR } from './constants';\nimport type { RequestConfig } from './types';\nimport type { QueueItem, RequestsQueue } from './types/queue-manager';\n\n/**\n * Queue Manager is responsible for managing and controlling the flow of concurrent or sequential requests. It handles:\n * - Request Queueing and Deduplication\n * - Request Timeout Handling\n * - Abort Controller Management and Request Cancellation\n * - Concurrency Control and Locking\n * - Request Lifecycle Management\n */\nconst queue: RequestsQueue = new Map();\n\n/**\n * Adds a request to the queue if it's not already being processed within the dedupeTime interval.\n *\n * @param {RequestConfig} config - The request configuration object.\n * @param {number} timeout - Timeout in milliseconds for the request.\n * @param {number} dedupeTime - Deduplication time in milliseconds.\n * @param {boolean} isCancellable - If true, then the previous request with same configuration should be aborted.\n * @param {boolean} isTimeoutEnabled - Whether timeout is enabled.\n * @returns {Promise} - A promise that resolves to an AbortController.\n */\nexport async function addRequest(\n config: RequestConfig,\n timeout: number | undefined,\n dedupeTime: number = 0,\n isCancellable: boolean = false,\n isTimeoutEnabled: boolean = true,\n): Promise {\n const now = Date.now();\n const item = queue.get(config);\n\n if (item) {\n const isCancellable = item[3];\n const previousController = item[0];\n const timeoutId = item[1];\n\n // If the request is already in the queue and within the dedupeTime, reuse the existing controller\n if (!isCancellable && now - item[2] < dedupeTime) {\n return previousController;\n }\n\n // If the request is too old, remove it and proceed to add a new one\n // Abort previous request, if applicable, and continue as usual\n if (isCancellable) {\n previousController.abort(\n new DOMException('Aborted due to new request', ABORT_ERROR),\n );\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n\n const controller = new AbortController();\n\n const timeoutId = isTimeoutEnabled\n ? setTimeout(() => {\n const error = new DOMException(\n `${config.url} aborted due to timeout`,\n TIMEOUT_ERROR,\n );\n\n removeRequest(config, error);\n }, timeout)\n : null;\n\n queue.set(config, [controller, timeoutId, now, isCancellable]);\n\n return controller;\n}\n\n/**\n * Removes a request from the queue and clears its timeout.\n *\n * @param config - The request configuration.\n * @param {boolean} error - Error payload so to force the request to abort.\n */\nexport async function removeRequest(\n config: RequestConfig,\n error: DOMException | null | string = null,\n): Promise {\n const item = queue.get(config);\n\n if (item) {\n const controller = item[0];\n const timeoutId = item[1];\n\n // If the request is not yet aborted, abort it with the provided error\n if (error && !controller.signal.aborted) {\n controller.abort(error);\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n}\n\n/**\n * Gets the AbortController for a request configuration.\n *\n * @param config - The request configuration.\n * @returns {AbortController | undefined} - The AbortController or undefined.\n */\nexport async function getController(\n config: RequestConfig,\n): Promise {\n const item = queue.get(config);\n\n return item?.[0];\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n APPLICATION_CONTENT_TYPE,\n APPLICATION_JSON,\n CONTENT_TYPE,\n} from './constants';\nimport type { DefaultResponse, FetchResponse } from './types/request-handler';\n\n/**\n * Parses the response data based on the Content-Type header.\n *\n * @param response - The Response object to parse.\n * @returns A Promise that resolves to the parsed data.\n */\nexport async function parseResponseData(\n response: FetchResponse,\n): Promise {\n // Bail early when body is empty\n if (!response?.body) {\n return null;\n }\n\n const contentType = String(\n (response as Response).headers?.get(CONTENT_TYPE) || '',\n ).split(';')[0]; // Correctly handle charset\n\n let data;\n\n try {\n if (\n contentType.includes(APPLICATION_JSON) ||\n contentType.includes('+json')\n ) {\n data = await response.json(); // Parse JSON response\n } else if (contentType.includes('multipart/form-data')) {\n data = await response.formData(); // Parse as FormData\n } else if (\n contentType.includes(APPLICATION_CONTENT_TYPE + 'octet-stream')\n ) {\n data = await response.blob(); // Parse as blob\n } else if (\n contentType.includes(APPLICATION_CONTENT_TYPE + 'x-www-form-urlencoded')\n ) {\n data = await response.formData(); // Handle URL-encoded forms\n } else if (contentType.includes('text/')) {\n data = await response.text(); // Parse as text\n } else {\n try {\n const responseClone = response.clone();\n\n // Handle edge case of no content type being provided... We assume JSON here.\n data = await responseClone.json();\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_e) {\n // Handle streams\n data = await response.text();\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_error) {\n // Parsing failed, fallback to null\n data = null;\n }\n\n return data;\n}\n","const PRIME_MULTIPLIER = 31;\n\n/**\n * Computes a hash value for a given string using the variant of djb2 hash function.\n * This hash function is non-cryptographic and designed for speed.\n * @author Daniel J. Bernstein (of djb2)\n *\n * @param str Input string to hash\n * @returns {string} Hash\n */\nexport function hash(str: string): string {\n let hash = 0;\n\n for (let i = 0, len = str.length; i < len; i++) {\n const char = str.charCodeAt(i);\n hash = (hash * PRIME_MULTIPLIER + char) | 0;\n }\n\n return String(hash);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { hash } from './hash';\nimport { fetchf } from './index';\nimport type { FetcherConfig } from './types/request-handler';\nimport type { CacheEntry } from './types/cache-manager';\nimport { GET, OBJECT, UNDEFINED } from './constants';\nimport { shallowSerialize, sortObject } from './utils';\n\nconst cache = new Map>();\n\n/**\n * Generates a cache key for a given URL and fetch options, ensuring that key factors\n * like method, headers, body, and other options are included in the cache key.\n * Headers and other objects are sorted by key to ensure consistent cache keys.\n *\n * @param options - The fetch options that may affect the request. The most important are:\n * @property {string} [method=\"GET\"] - The HTTP method (GET, POST, etc.).\n * @property {HeadersInit} [headers={}] - The request headers.\n * @property {BodyInit | null} [body=\"\"] - The body of the request (only for methods like POST, PUT).\n * @property {RequestMode} [mode=\"cors\"] - The mode for the request (e.g., cors, no-cors, include).\n * @property {RequestCredentials} [credentials=\"include\"] - Whether to include credentials like cookies.\n * @property {RequestCache} [cache=\"default\"] - The cache mode (e.g., default, no-store, reload).\n * @property {RequestRedirect} [redirect=\"follow\"] - How to handle redirects (e.g., follow, error, manual).\n * @property {string} [referrer=\"\"] - The referrer URL to send with the request.\n * @property {string} [integrity=\"\"] - Subresource integrity value (a cryptographic hash for resource validation).\n * @returns {string} - A unique cache key based on the URL and request options. Empty if cache is to be burst.\n *\n * @example\n * const cacheKey = generateCacheKey({\n * url: 'https://api.example.com/data',\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ name: 'Alice' }),\n * mode: 'cors',\n * credentials: 'include',\n * });\n * console.log(cacheKey);\n */\nexport function generateCacheKey(options: FetcherConfig): string {\n const {\n url = '',\n method = GET,\n headers = {},\n body = '',\n mode = 'cors',\n credentials = 'include',\n cache = 'default',\n redirect = 'follow',\n referrer = '',\n integrity = '',\n } = options;\n\n // Bail early if cache should be burst\n if (cache === 'reload') {\n return '';\n }\n\n // Sort headers and body + convert sorted to strings for hashing purposes\n // Native serializer is on avg. 3.5x faster than a Fast Hash or FNV-1a\n const headersString = shallowSerialize(sortObject(headers));\n\n let bodyString = '';\n\n // In majority of cases we do not cache body\n if (body !== null) {\n if (typeof body === 'string') {\n bodyString = hash(body);\n } else if (body instanceof FormData) {\n body.forEach((value, key) => {\n // Append key=value and '&' directly to the result\n bodyString += key + '=' + value + '&';\n });\n bodyString = hash(bodyString);\n } else if (\n (typeof Blob !== UNDEFINED && body instanceof Blob) ||\n (typeof File !== UNDEFINED && body instanceof File)\n ) {\n bodyString = 'BF' + body.size + body.type;\n } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n bodyString = 'AB' + body.byteLength;\n } else {\n const o = typeof body === OBJECT ? sortObject(body) : String(body);\n bodyString = hash(JSON.stringify(o));\n }\n }\n\n // Concatenate all key parts into a cache key string\n // Template literals are apparently slower\n return (\n method +\n url +\n mode +\n credentials +\n cache +\n redirect +\n referrer +\n integrity +\n headersString +\n bodyString\n );\n}\n\n/**\n * Checks if the cache entry is expired based on its timestamp and the maximum stale time.\n *\n * @param {number} timestamp - The timestamp of the cache entry.\n * @param {number} maxStaleTime - The maximum stale time in seconds.\n * @returns {boolean} - Returns true if the cache entry is expired, false otherwise.\n */\nfunction isCacheExpired(timestamp: number, maxStaleTime: number): boolean {\n if (!maxStaleTime) {\n return false;\n }\n\n return Date.now() - timestamp > maxStaleTime * 1000;\n}\n\n/**\n * Retrieves a cache entry if it exists and is not expired.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} cacheTime - Maximum time to cache entry.\n * @returns {CacheEntry | null} - The cache entry if it exists and is not expired, null otherwise.\n */\nexport function getCache(\n key: string,\n cacheTime: number,\n): CacheEntry | null {\n const entry = cache.get(key);\n\n if (entry) {\n if (!isCacheExpired(entry.timestamp, cacheTime)) {\n return entry;\n }\n\n cache.delete(key);\n }\n\n return null;\n}\n\n/**\n * Sets a new cache entry or updates an existing one.\n *\n * @param {string} key Cache key to utilize\n * @param {T} data - The data to be cached.\n * @param {boolean} isLoading - Indicates if the data is currently being fetched.\n */\nexport function setCache(\n key: string,\n data: T,\n isLoading: boolean = false,\n): void {\n cache.set(key, {\n data,\n isLoading,\n timestamp: Date.now(),\n });\n}\n\n/**\n * Revalidates a cache entry by fetching fresh data and updating the cache.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @returns {Promise} - A promise that resolves when the revalidation is complete.\n */\nexport async function revalidate(\n key: string,\n config: FetcherConfig,\n): Promise {\n try {\n // Fetch fresh data\n const newData = await fetchf(config.url, {\n ...config,\n cache: 'reload',\n });\n\n setCache(key, newData);\n } catch (error) {\n console.error(`Error revalidating ${config.url}:`, error);\n\n // Rethrow the error to forward it\n throw error;\n }\n}\n\n/**\n * Invalidates (deletes) a cache entry.\n *\n * @param {string} key Cache key to utilize\n */\nexport function deleteCache(key: string): void {\n cache.delete(key);\n}\n\n/**\n * Mutates a cache entry with new data and optionally revalidates it.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @param {T} newData - The new data to be cached.\n * @param {boolean} revalidateAfter - If true, triggers revalidation after mutation.\n */\nexport function mutate(\n key: string,\n config: FetcherConfig,\n newData: T,\n revalidateAfter: boolean = false,\n): void {\n setCache(key, newData);\n\n if (revalidateAfter) {\n revalidate(key, config);\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {\n DefaultResponse,\n RequestHandlerConfig,\n RequestConfig,\n Method,\n RetryOptions,\n FetchResponse,\n ResponseError,\n RequestHandlerReturnType,\n CreatedCustomFetcherInstance,\n FetcherConfig,\n} from './types/request-handler';\nimport type {\n BodyPayload,\n DefaultParams,\n DefaultPayload,\n DefaultUrlParams,\n QueryParams,\n} from './types/api-handler';\nimport { applyInterceptor } from './interceptor-manager';\nimport { ResponseErr } from './response-error';\nimport {\n appendQueryParams,\n isJSONSerializable,\n replaceUrlPathParams,\n delayInvocation,\n flattenData,\n processHeaders,\n deleteProperty,\n isSearchParams,\n} from './utils';\nimport { addRequest, removeRequest } from './queue-manager';\nimport {\n ABORT_ERROR,\n APPLICATION_JSON,\n CANCELLED_ERROR,\n CONTENT_TYPE,\n GET,\n HEAD,\n OBJECT,\n STRING,\n UNDEFINED,\n} from './constants';\nimport { parseResponseData } from './response-parser';\nimport { generateCacheKey, getCache, setCache } from './cache-manager';\n\nconst defaultConfig: RequestHandlerConfig = {\n method: GET,\n strategy: 'reject',\n timeout: 30000,\n dedupeTime: 1000,\n defaultResponse: null,\n headers: {\n Accept: APPLICATION_JSON + ', text/plain, */*',\n 'Accept-Encoding': 'gzip, deflate, br',\n [CONTENT_TYPE]: APPLICATION_JSON + ';charset=utf-8',\n },\n retry: {\n delay: 1000,\n maxDelay: 30000,\n resetTimeout: true,\n backoff: 1.5,\n\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status\n retryOn: [\n 408, // Request Timeout\n 409, // Conflict\n 425, // Too Early\n 429, // Too Many Requests\n 500, // Internal Server Error\n 502, // Bad Gateway\n 503, // Service Unavailable\n 504, // Gateway Timeout\n ],\n },\n};\n\n/**\n * Create Request Handler\n *\n * @param {RequestHandlerConfig} config - Configuration object for the request handler\n * @returns {Object} An object with methods for handling requests\n */\nexport function createRequestHandler(\n config: RequestHandlerConfig,\n): RequestHandlerReturnType {\n const handlerConfig: RequestHandlerConfig = {\n ...defaultConfig,\n ...config,\n };\n\n /**\n * Immediately create instance of custom fetcher if it is defined\n */\n const customFetcher = handlerConfig.fetcher;\n const requestInstance = customFetcher?.create(handlerConfig) || null;\n\n /**\n * Get Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Provider's instance\n */\n const getInstance = (): CreatedCustomFetcherInstance | null => {\n return requestInstance;\n };\n\n /**\n * Gets a configuration value from `reqConfig`, defaulting to `handlerConfig` if not present.\n *\n * @param {RequestConfig} reqConfig - Request configuration object.\n * @param {keyof RequestConfig} name - Key of the configuration value.\n * @returns {T} - The configuration value.\n */\n const getConfig = (\n reqConfig: RequestConfig,\n name: keyof RequestConfig,\n ): T => {\n return typeof reqConfig[name] !== UNDEFINED\n ? reqConfig[name]\n : handlerConfig[name];\n };\n\n /**\n * Logs messages or errors using the configured logger's `warn` method.\n *\n * @param {...(string | ResponseError)} args - Messages or errors to log.\n */\n const logger = (...args: (string | ResponseError)[]): void => {\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn(...args);\n }\n };\n\n /**\n * Build request configuration\n *\n * @param {string} url - Request url\n * @param {RequestConfig} reqConfig - Request config passed when making the request\n * @returns {RequestConfig} - Provider's instance\n */\n const buildConfig = (\n url: string,\n reqConfig: RequestConfig,\n ): FetcherConfig => {\n const method = getConfig(\n reqConfig,\n 'method',\n ).toUpperCase() as Method;\n const isGetAlikeMethod = method === GET || method === HEAD;\n\n const dynamicUrl = replaceUrlPathParams(\n url,\n getConfig(reqConfig, 'urlPathParams'),\n );\n\n // The explicitly passed \"params\"\n const explicitParams = getConfig(reqConfig, 'params');\n\n // The explicitly passed \"body\" or \"data\"\n const explicitBodyData: BodyPayload =\n getConfig(reqConfig, 'body') || getConfig(reqConfig, 'data');\n\n // Final body data\n let body: RequestConfig['data'];\n\n // Only applicable for request methods 'PUT', 'POST', 'DELETE', and 'PATCH'\n if (!isGetAlikeMethod) {\n body = explicitBodyData;\n }\n\n // Native fetch compatible settings\n const isWithCredentials = getConfig(reqConfig, 'withCredentials');\n\n const credentials = isWithCredentials\n ? 'include'\n : getConfig(reqConfig, 'credentials');\n\n const urlPath = explicitParams\n ? appendQueryParams(dynamicUrl, explicitParams)\n : dynamicUrl;\n const isFullUrl = urlPath.includes('://');\n const baseURL = isFullUrl\n ? ''\n : getConfig(reqConfig, 'baseURL') ||\n getConfig(reqConfig, 'apiUrl');\n\n // Automatically stringify request body, if possible and when not dealing with strings\n if (\n body &&\n typeof body !== STRING &&\n !isSearchParams(body) &&\n isJSONSerializable(body)\n ) {\n body = JSON.stringify(body);\n }\n\n return {\n ...reqConfig,\n credentials,\n body,\n method,\n\n url: baseURL + urlPath,\n };\n };\n\n /**\n * Process global Request Error\n *\n * @param {ResponseError} error Error instance\n * @param {RequestConfig} requestConfig Per endpoint request config\n * @returns {Promise}\n */\n const processError = async (\n error: ResponseError,\n requestConfig: RequestConfig,\n ): Promise => {\n if (!isRequestCancelled(error)) {\n logger('API ERROR', error);\n }\n\n // Local interceptors\n await applyInterceptor(error, requestConfig?.onError);\n\n // Global interceptors\n await applyInterceptor(error, handlerConfig?.onError);\n };\n\n /**\n * Output default response in case of an error, depending on chosen strategy\n *\n * @param {ResponseError} error - Error instance\n * @param {FetchResponse | null} response - Response. It may be \"null\" in case of request being aborted.\n * @param {RequestConfig} requestConfig - Per endpoint request config\n * @returns {FetchResponse} Response together with the error object\n */\n const outputErrorResponse = async (\n error: ResponseError,\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n ): Promise => {\n const _isRequestCancelled = isRequestCancelled(error);\n const errorHandlingStrategy = getConfig(requestConfig, 'strategy');\n const rejectCancelled = getConfig(\n requestConfig,\n 'rejectCancelled',\n );\n\n // By default cancelled requests aren't rejected (softFail strategy)\n if (!(_isRequestCancelled && !rejectCancelled)) {\n // Hang the promise\n if (errorHandlingStrategy === 'silent') {\n await new Promise(() => null);\n }\n // Reject the promise\n else if (errorHandlingStrategy === 'reject') {\n return Promise.reject(error);\n }\n }\n\n return outputResponse(response, requestConfig, error);\n };\n\n /**\n * Output error response depending on chosen strategy\n *\n * @param {ResponseError} error Error instance\n * @returns {boolean} True if request is aborted\n */\n const isRequestCancelled = (error: ResponseError): boolean => {\n return error.name === ABORT_ERROR || error.name === CANCELLED_ERROR;\n };\n\n /**\n * Handle Request depending on used strategy\n *\n * @param {string} url - Request url\n * @param {RequestConfig} reqConfig - Request config\n * @throws {ResponseError}\n * @returns {Promise>} Response Data\n */\n const request = async <\n ResponseData = DefaultResponse,\n QueryParams = DefaultParams,\n PathParams = DefaultUrlParams,\n RequestBody = DefaultPayload,\n >(\n url: string,\n reqConfig: RequestConfig<\n ResponseData,\n QueryParams,\n PathParams,\n RequestBody\n > | null = null,\n ): Promise> => {\n const _reqConfig = reqConfig || {};\n const mergedConfig = {\n ...handlerConfig,\n ..._reqConfig,\n } as RequestConfig;\n\n let response: FetchResponse | null = null;\n const fetcherConfig = buildConfig(url, mergedConfig);\n\n const {\n timeout,\n cancellable,\n dedupeTime,\n pollingInterval,\n shouldStopPolling,\n cacheTime,\n cacheKey,\n } = mergedConfig;\n\n // Prevent performance overhead of cache access\n let _cacheKey: string;\n\n if (cacheKey) {\n _cacheKey = cacheKey(fetcherConfig);\n } else {\n _cacheKey = generateCacheKey(fetcherConfig);\n }\n\n if (cacheTime && _cacheKey) {\n const cacheBuster = mergedConfig.cacheBuster;\n\n if (!cacheBuster || !cacheBuster(fetcherConfig)) {\n const cachedEntry = getCache>(\n _cacheKey,\n cacheTime,\n );\n\n if (cachedEntry) {\n // Serve stale data from cache\n return cachedEntry.data;\n }\n }\n }\n\n const {\n retries = 0,\n delay,\n backoff,\n retryOn,\n shouldRetry,\n maxDelay,\n resetTimeout,\n } = mergedConfig.retry as Required;\n\n let attempt = 0;\n let pollingAttempt = 0;\n let waitTime: number = delay;\n const _retries = retries > 0 ? retries : 0;\n\n while (attempt <= _retries) {\n try {\n // Add the request to the queue. Make sure to handle deduplication, cancellation, timeouts in accordance to retry settings\n const controller = await addRequest(\n fetcherConfig,\n timeout,\n dedupeTime,\n cancellable,\n // Reset timeouts by default or when retries are ON\n !!(timeout && (!_retries || resetTimeout)),\n );\n\n // Shallow copy to ensure basic idempotency\n const requestConfig: RequestConfig = {\n signal: controller.signal,\n ...fetcherConfig,\n };\n\n // Local interceptors\n await applyInterceptor(requestConfig, _reqConfig?.onRequest);\n\n // Global interceptors\n await applyInterceptor(requestConfig, handlerConfig?.onRequest);\n\n if (customFetcher !== null && requestInstance !== null) {\n response = await requestInstance.request(requestConfig);\n } else {\n response = (await fetch(\n requestConfig.url as string,\n requestConfig as RequestInit,\n )) as FetchResponse;\n }\n\n // Add more information to response object\n if (response instanceof Response) {\n response.config = requestConfig;\n response.data = await parseResponseData(response);\n\n // Check if the response status is not outside the range 200-299 and if so, output error\n if (!response.ok) {\n throw new ResponseErr(\n `${requestConfig.url} failed! Status: ${response.status || null}`,\n requestConfig,\n response,\n );\n }\n }\n\n // Local interceptors\n await applyInterceptor(response, _reqConfig?.onResponse);\n\n // Global interceptors\n await applyInterceptor(response, handlerConfig?.onResponse);\n\n removeRequest(fetcherConfig);\n\n // Polling logic\n if (\n pollingInterval &&\n (!shouldStopPolling || !shouldStopPolling(response, pollingAttempt))\n ) {\n // Restart the main retry loop\n pollingAttempt++;\n\n logger(`Polling attempt ${pollingAttempt}...`);\n\n await delayInvocation(pollingInterval);\n\n continue;\n }\n\n // If polling is not required, or polling attempts are exhausted\n const output = outputResponse(response, requestConfig);\n\n if (cacheTime && _cacheKey) {\n const skipCache = requestConfig.skipCache;\n\n if (!skipCache || !skipCache(output, requestConfig)) {\n setCache(_cacheKey, output);\n }\n }\n\n return output;\n } catch (err) {\n const error = err as ResponseErr;\n const status = error?.response?.status || error?.status || 0;\n\n if (\n attempt === retries ||\n !(!shouldRetry || (await shouldRetry(error, attempt))) ||\n !retryOn?.includes(status)\n ) {\n await processError(error, fetcherConfig);\n\n removeRequest(fetcherConfig);\n\n return outputErrorResponse(\n error,\n response,\n fetcherConfig,\n );\n }\n\n logger(`Attempt ${attempt + 1} failed. Retry in ${waitTime}ms.`);\n\n await delayInvocation(waitTime);\n\n waitTime *= backoff;\n waitTime = Math.min(waitTime, maxDelay);\n attempt++;\n }\n }\n\n return outputResponse(response, fetcherConfig);\n };\n\n /**\n * Output response\n *\n * @param Response. It may be \"null\" in case of request being aborted.\n * @param {RequestConfig} requestConfig - Request config\n * @param error - whether the response is erroneous\n * @returns {FetchResponse} Response data\n */\n const outputResponse = (\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n error: ResponseError | null = null,\n ): FetchResponse => {\n const defaultResponse = getConfig(requestConfig, 'defaultResponse');\n\n // This may happen when request is cancelled.\n if (!response) {\n return {\n ok: false,\n // Enhance the response with extra information\n error,\n data: defaultResponse,\n headers: null,\n config: requestConfig,\n } as unknown as FetchResponse;\n }\n\n // Clean up the error object\n deleteProperty(error, 'response');\n deleteProperty(error, 'request');\n deleteProperty(error, 'config');\n\n let data = response?.data;\n\n // Set the default response if the provided data is an empty object\n if (\n data === undefined ||\n data === null ||\n (typeof data === OBJECT && Object.keys(data).length === 0)\n ) {\n data = defaultResponse;\n }\n\n // Return flattened response immediately\n const flattenResponse = getConfig(\n requestConfig,\n 'flattenResponse',\n );\n\n if (flattenResponse) {\n response.data = flattenData(data);\n }\n\n // If it's a custom fetcher, and it does not return any Response instance, it may have its own internal handler\n if (!(response instanceof Response)) {\n return response;\n }\n\n // Native fetch Response extended by extra information\n return {\n body: response.body,\n bodyUsed: response.bodyUsed,\n formData: response.formData,\n ok: response.ok,\n redirected: response.redirected,\n type: response.type,\n url: response.url,\n status: response.status,\n statusText: response.statusText,\n\n blob: response.blob.bind(response),\n json: response.json.bind(response),\n text: response.text.bind(response),\n clone: response.clone.bind(response),\n arrayBuffer: response.arrayBuffer.bind(response),\n\n // Enhance the response with extra information\n error,\n data,\n headers: processHeaders(response.headers),\n config: requestConfig,\n };\n };\n\n return {\n getInstance,\n buildConfig,\n config,\n request,\n };\n}\n","import type {\n RequestConfig,\n FetchResponse,\n DefaultResponse,\n CreatedCustomFetcherInstance,\n} from './types/request-handler';\nimport type {\n ApiHandlerConfig,\n ApiHandlerDefaultMethods,\n ApiHandlerMethods,\n DefaultPayload,\n FallbackValue,\n FinalParams,\n FinalResponse,\n QueryParams,\n RequestConfigUrlRequired,\n UrlPathParams,\n} from './types/api-handler';\nimport { createRequestHandler } from './request-handler';\n\n/**\n * Creates an instance of API Handler.\n * It creates an API fetcher function using native fetch() or a custom fetcher if it is passed as \"fetcher\".\n * @url https://github.com/MattCCC/fetchff\n *\n * @param {Object} config - Configuration object for the API fetcher.\n * @param {string} config.apiUrl - The base URL for the API.\n * @param {Object} config.endpoints - An object containing endpoint definitions.\n * @param {number} config.timeout - You can set the timeout for particular request in milliseconds.\n * @param {number} config.cancellable - If true, the ongoing previous requests will be automatically cancelled.\n * @param {number} config.rejectCancelled - If true and request is set to cancellable, a cancelled request promise will be rejected. By default, instead of rejecting the promise, defaultResponse is returned.\n * @param {number} config.timeout - Request timeout\n * @param {number} config.dedupeTime - Time window, in milliseconds, during which identical requests are deduplicated (treated as single request).\n * @param {string} config.strategy - Error Handling Strategy\n * @param {string} config.flattenResponse - Whether to flatten response \"data\" object within \"data\". It works only if the response structure includes a single data property.\n * @param {*} config.defaultResponse - Default response when there is no data or when endpoint fails depending on the chosen strategy. It's \"null\" by default\n * @param {Object} [config.retry] - Options for retrying requests.\n * @param {number} [config.retry.retries=0] - Number of retry attempts. No retries by default.\n * @param {number} [config.retry.delay=1000] - Initial delay between retries in milliseconds.\n * @param {number} [config.retry.backoff=1.5] - Exponential backoff factor.\n * @param {number[]} [config.retry.retryOn=[502, 504, 408]] - HTTP status codes to retry on.\n * @param {RequestInterceptor|RequestInterceptor[]} [config.onRequest] - Optional request interceptor function or an array of functions.\n * These functions will be called with the request configuration object before the request is made. Can be used to modify or log the request configuration.\n * @param {ResponseInterceptor|ResponseInterceptor[]} [config.onResponse] - Optional response interceptor function or an array of functions.\n * These functions will be called with the response object after the response is received. an be used to modify or log the response data.\n * @param {Function} [config.onError] - Optional callback function for handling errors.\n * @param {Object} [config.headers] - Optional default headers to include in every request.\n * @param {Object} config.fetcher - The Custom Fetcher instance to use for making requests. It should expose create() and request() functions.\n * @param {*} config.logger - Instance of custom logger. Either class or an object similar to \"console\". Console is used by default.\n * @returns API handler functions and endpoints to call\n *\n * @example\n * // Define endpoint paths\n * const endpoints = {\n * getUser: '/user',\n * createPost: '/post',\n * };\n *\n * // Create the API fetcher with configuration\n * const api = createApiFetcher({\n * endpoints,\n * apiUrl: 'https://example.com/api',\n * onError(error) {\n * console.log('Request failed', error);\n * },\n * headers: {\n * 'my-auth-key': 'example-auth-key-32rjjfa',\n * },\n * });\n *\n * // Fetch user data\n * const response = await api.getUser({ userId: 1, ratings: [1, 2] })\n */\nfunction createApiFetcher<\n EndpointsMethods extends object,\n EndpointsSettings = never,\n>(config: ApiHandlerConfig) {\n const endpoints = config.endpoints;\n const requestHandler = createRequestHandler(config);\n\n /**\n * Get Custom Fetcher Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Request Handler's Custom Fetcher Instance\n */\n function getInstance(): CreatedCustomFetcherInstance | null {\n return requestHandler.getInstance();\n }\n\n /**\n * Triggered when trying to use non-existent endpoints\n *\n * @param endpointName Endpoint Name\n * @returns {Promise}\n */\n function handleNonImplemented(endpointName: string): Promise {\n console.error(`Add ${endpointName} to 'endpoints'.`);\n\n return Promise.resolve(null);\n }\n\n /**\n * Handle Single API Request\n * It considers settings in following order: per-request settings, global per-endpoint settings, global settings.\n *\n * @param {keyof EndpointsMethods | string} endpointName - The name of the API endpoint to call.\n * @param {EndpointConfig} [requestConfig={}] - Additional configuration for the request.\n * @returns {Promise>} - A promise that resolves with the response from the API provider.\n */\n async function request<\n ResponseData = never,\n QueryParams_ = never,\n UrlParams = never,\n RequestBody = never,\n >(\n endpointName: keyof EndpointsMethods | string,\n requestConfig: RequestConfig<\n FinalResponse,\n FinalParams,\n FinalParams,\n FallbackValue\n > = {},\n ): Promise>> {\n // Use global per-endpoint settings\n const endpointConfig =\n endpoints[endpointName] ||\n ({ url: endpointName as string } as RequestConfigUrlRequired);\n\n const responseData = await requestHandler.request<\n FinalResponse,\n FinalParams,\n FinalParams,\n FallbackValue\n >(endpointConfig.url, {\n ...endpointConfig,\n ...requestConfig,\n });\n\n return responseData;\n }\n\n const apiHandler: ApiHandlerDefaultMethods = {\n config,\n endpoints,\n requestHandler,\n getInstance,\n request,\n };\n\n /**\n * Maps all API requests using native Proxy\n *\n * @param {*} prop Caller\n */\n return new Proxy>(\n apiHandler as ApiHandlerMethods,\n {\n get(_target, prop: string) {\n if (prop in apiHandler) {\n return apiHandler[prop as unknown as keyof typeof apiHandler];\n }\n\n // Prevent handler from triggering non-existent endpoints\n if (endpoints[prop]) {\n return apiHandler.request.bind(null, prop);\n }\n\n return handleNonImplemented.bind(null, prop);\n },\n },\n );\n}\n\nexport { createApiFetcher };\n"],"mappings":"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","names":["src_exports","__export","createApiFetcher","fetchf","applyInterceptor","object","interceptors","value","interceptor","ResponseErr","message","requestInfo","response","__publicField","APPLICATION_CONTENT_TYPE","APPLICATION_JSON","CONTENT_TYPE","UNDEFINED","OBJECT","STRING","ABORT_ERROR","TIMEOUT_ERROR","CANCELLED_ERROR","GET","HEAD","isSearchParams","data","shallowSerialize","obj","result","key","sortObject","sortedObj","keys","i","len","appendQueryStringToUrl","baseUrl","queryString","appendQueryParams","url","params","isSearchParams","encodedQueryString","s","encode","add","k","v","buildParams","prefix","OBJECT","replaceUrlPathParams","urlPathParams","str","word","isJSONSerializable","value","UNDEFINED","STRING","proto","delayInvocation","ms","resolve","flattenData","data","processHeaders","headers","headersObject","deleteProperty","property","queue","addRequest","config","timeout","dedupeTime","isCancellable","isTimeoutEnabled","now","item","previousController","timeoutId","ABORT_ERROR","controller","error","TIMEOUT_ERROR","removeRequest","parseResponseData","response","_a","contentType","CONTENT_TYPE","data","APPLICATION_JSON","APPLICATION_CONTENT_TYPE","_e","_error","hash","str","i","len","char","cache","generateCacheKey","options","url","method","GET","headers","body","mode","credentials","redirect","referrer","integrity","headersString","shallowSerialize","sortObject","bodyString","hash","value","key","UNDEFINED","o","OBJECT","isCacheExpired","timestamp","maxStaleTime","getCache","cacheTime","entry","setCache","data","isLoading","defaultConfig","GET","APPLICATION_JSON","CONTENT_TYPE","createRequestHandler","config","handlerConfig","customFetcher","requestInstance","getInstance","getConfig","reqConfig","name","UNDEFINED","logger","args","_a","buildConfig","url","method","isGetAlikeMethod","HEAD","dynamicUrl","replaceUrlPathParams","explicitParams","explicitBodyData","body","credentials","urlPath","appendQueryParams","baseURL","STRING","isSearchParams","isJSONSerializable","processError","error","requestConfig","isRequestCancelled","applyInterceptor","outputErrorResponse","response","_isRequestCancelled","errorHandlingStrategy","rejectCancelled","outputResponse","ABORT_ERROR","CANCELLED_ERROR","request","_reqConfig","mergedConfig","fetcherConfig","timeout","cancellable","dedupeTime","pollingInterval","shouldStopPolling","cacheTime","cacheKey","_cacheKey","generateCacheKey","cacheBuster","cachedEntry","getCache","retries","delay","backoff","retryOn","shouldRetry","maxDelay","resetTimeout","attempt","pollingAttempt","waitTime","_retries","addRequest","parseResponseData","ResponseErr","removeRequest","delayInvocation","output","skipCache","setCache","err","status","defaultResponse","deleteProperty","data","OBJECT","flattenData","processHeaders","createApiFetcher","config","endpoints","requestHandler","createRequestHandler","getInstance","handleNonImplemented","endpointName","request","requestConfig","endpointConfig","apiHandler","_target","prop","fetchf","url","config","createRequestHandler"]} \ No newline at end of file diff --git a/dist/browser/index.mjs b/dist/browser/index.mjs index 098d2e3..ad1b42d 100644 --- a/dist/browser/index.mjs +++ b/dist/browser/index.mjs @@ -1,2 +1,2 @@ -var De=Object.defineProperty,Ie=Object.defineProperties;var Ae=Object.getOwnPropertyDescriptors;var ce=Object.getOwnPropertySymbols;var Fe=Object.prototype.hasOwnProperty,qe=Object.prototype.propertyIsEnumerable;var Z=(e,t,n)=>t in e?De(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,C=(e,t)=>{for(var n in t||(t={}))Fe.call(t,n)&&Z(e,n,t[n]);if(ce)for(var n of ce(t))qe.call(t,n)&&Z(e,n,t[n]);return e},B=(e,t)=>Ie(e,Ae(t));var A=(e,t,n)=>Z(e,typeof t!="symbol"?t+"":t,n);async function O(e,t){if(t){if(typeof t=="function"){let n=await t(e);n&&Object.assign(e,n)}else if(Array.isArray(t))for(let n of t){let a=await n(e);a&&Object.assign(e,a)}}}var _=class extends Error{constructor(n,a,o){super(n);A(this,"response");A(this,"request");A(this,"config");A(this,"status");A(this,"statusText");this.name="ResponseError",this.message=n,this.status=o.status,this.statusText=o.statusText,this.request=a,this.config=a,this.response=o}};var M="application/json",L="Content-Type",x="undefined",b="object",j="string",J="AbortError",le="TimeoutError",ue="CanceledError",U="GET",fe="HEAD";function ee(e){return e instanceof URLSearchParams}function de(e){let t="";for(let n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t+=n+":"+e[n]);return t}function te(e){let t={},n=Object.keys(e);n.sort();for(let a=0,o=n.length;a{s=typeof s=="function"?s():s,s=s===null||s===void 0?"":s,n[n.length]=a(l)+"="+a(s)},i=(l,s)=>{let d,g,y;if(l)if(Array.isArray(s))for(d=0,g=s.length;d{let a=n.substring(1);return String(t[a]?t[a]:n)}):e}function me(e){let t=typeof e;if(t===x||e===null)return!1;if(t===j||t==="number"||t==="boolean"||Array.isArray(e))return!0;if(Buffer.isBuffer(e)||e instanceof Date)return!1;if(t===b){let n=Object.getPrototypeOf(e);if(n===Object.prototype||n===null||typeof e.toJSON=="function")return!0}return!1}async function ne(e){return new Promise(t=>setTimeout(()=>t(!0),e))}function re(e){return e&&typeof e===b&&typeof e.data!==x&&Object.keys(e).length===1?re(e.data):e}function he(e){if(!e)return{};let t={};if(e instanceof Headers)e.forEach((n,a)=>{t[a]=n});else if(typeof e===b&&e!==null)for(let[n,a]of Object.entries(e))t[n.toLowerCase()]=a;return t}function F(e,t){e&&t in e&&delete e[t]}var k=new Map;async function ge(e,t,n=0,a=!1,o=!0){let i=Date.now(),h=k.get(e);if(h){let s=h[3],d=h[0],g=h[1];if(!s&&i-h[2]{let s=new DOMException(`${e.url} aborted due to timeout`,le);$(e,s)},t):null;return k.set(e,[m,l,i,a]),m}async function $(e,t=null){let n=k.get(e);if(n){let a=n[0],o=n[1];t&&!a.signal.aborted&&a.abort(t),o!==null&&clearTimeout(o),k.delete(e)}}async function Pe(e){var a;if(!(e!=null&&e.body))return null;let t=String(((a=e.headers)==null?void 0:a.get(L))||"").split(";")[0],n;try{if(t.includes(M)||t.includes("+json"))n=await e.json();else if(t.includes("multipart/form-data"))n=await e.formData();else if(t.includes("application/octet-stream"))n=await e.blob();else if(t.includes("application/x-www-form-urlencoded"))n=await e.formData();else if(t.includes("text/"))n=await e.text();else try{n=await e.clone().json()}catch(o){n=await e.text()}}catch(o){n=null}return n}function v(e){let t=0;for(let n=0,a=e.length;n{y+=u+"="+r+"&"}),y=v(y);else if(typeof Blob!==x&&o instanceof Blob||typeof File!==x&&o instanceof File)y="BF"+o.size+o.type;else if(o instanceof ArrayBuffer||ArrayBuffer.isView(o))y="AB"+o.byteLength;else{let r=typeof o===b?te(o):String(o);y=v(JSON.stringify(r))}return n+t+i+h+m+l+s+d+g+y}function He(e,t){return t?Date.now()-e>t*1e3:!1}function Ce(e,t){let n=se.get(e);if(n){if(!He(n.timestamp,t))return n;se.delete(e)}return null}function Te(e,t,n=!1){se.set(e,{data:t,isLoading:n,timestamp:Date.now()})}var Se={method:U,strategy:"reject",timeout:3e4,dedupeTime:1e3,defaultResponse:null,headers:{Accept:M+", text/plain, */*","Accept-Encoding":"gzip, deflate, br",[L]:M+";charset=utf-8"},retry:{delay:1e3,maxDelay:3e4,resetTimeout:!0,backoff:1.5,retryOn:[408,409,425,429,500,502,503,504]}};function G(e){let t=C(C({},Se),e),n=t.fetcher,a=(n==null?void 0:n.create(t))||null,o=()=>a,i=(r,u)=>typeof r[u]!==x?r[u]:t[u],h=(...r)=>{var u;(u=t.logger)!=null&&u.warn&&t.logger.warn(...r)},m=(r,u,f)=>{let R=i(f,"method").toUpperCase(),P=R===U||R===fe,c=Re(r,i(f,"urlPathParams")),E=i(f,"params"),q=i(f,"body")||i(f,"data"),z=!!(u&&(P||q)),T;P||(T=q||u);let Q=i(f,"withCredentials")?"include":i(f,"credentials");F(f,"data"),F(f,"withCredentials");let D=E||z?ye(c,E||u):c,w=D.includes("://")?"":i(f,"baseURL")||i(f,"apiUrl");return T&&typeof T!==j&&!ee(T)&&me(T)&&(T=JSON.stringify(T)),B(C({},f),{credentials:Q,body:T,method:R,url:w+D})},l=async(r,u)=>{d(r)||h("API ERROR",r),await O(r,u==null?void 0:u.onError),await O(r,t==null?void 0:t.onError)},s=async(r,u,f)=>{let R=d(r),P=i(f,"strategy"),c=i(f,"rejectCancelled");if(!(R&&!c)){if(P==="silent")await new Promise(()=>null);else if(P==="reject")return Promise.reject(r)}return y(u,f,r)},d=r=>r.name===J||r.name===ue,g=async(r,u=null,f=null)=>{var ae;let R=f||{},P=C(C({},t),R),c=null,E=m(r,u,P),{timeout:q,cancellable:z,dedupeTime:T,pollingInterval:K,shouldStopPolling:Q,cacheTime:D,cacheKey:Y}=P,w;if(Y?w=Y(E):w=Ee(E),D&&w){let I=P.cacheBuster;if(!I||!I(E)){let p=Ce(w,D);if(p)return p.data}}let{retries:W=0,delay:be,backoff:we,retryOn:V,shouldRetry:oe,maxDelay:xe,resetTimeout:Oe}=P.retry,H=0,X=0,N=be;for(;H<=W;)try{let I=await ge(E,q,T,z,!!(q&&(!W||Oe))),p=C({signal:I.signal},E);if(await O(p,R==null?void 0:R.onRequest),await O(p,t==null?void 0:t.onRequest),n!==null&&a!==null?c=await a.request(p):c=await fetch(p.url,p),c instanceof Response&&(c.config=p,c.data=await Pe(c),!c.ok))throw new _(`${p.url} failed! Status: ${c.status||null}`,p,c);if(await O(c,R==null?void 0:R.onResponse),await O(c,t==null?void 0:t.onResponse),$(E),K&&(!Q||!Q(c,X))){X++,h(`Polling attempt ${X}...`),await ne(K);continue}let S=y(c,p);if(D&&w){let ie=p.skipCache;(!ie||!ie(S,p))&&Te(w,S)}return S}catch(I){let p=I,S=((ae=p==null?void 0:p.response)==null?void 0:ae.status)||(p==null?void 0:p.status)||0;if(H===W||!(!oe||await oe(p,H))||!(V!=null&&V.includes(S)))return await l(p,E),$(E),s(p,c,E);h(`Attempt ${H+1} failed. Retry in ${N}ms.`),await ne(N),N*=we,N=Math.min(N,xe),H++}return y(c,E)},y=(r,u,f=null)=>{let R=i(u,"defaultResponse"),P=i(u,"flattenResponse");if(!r)return P?R:{error:f,headers:null,data:R,config:u};F(f,"response"),F(f,"request"),F(f,"config");let c=r==null?void 0:r.data;return(c==null||typeof c===b&&Object.keys(c).length===0)&&(c=R),P?re(c):r instanceof Response?{body:r.body,bodyUsed:r.bodyUsed,formData:r.formData,ok:r.ok,redirected:r.redirected,type:r.type,url:r.url,status:r.status,statusText:r.statusText,blob:r.blob.bind(r),json:r.json.bind(r),text:r.text.bind(r),clone:r.clone.bind(r),arrayBuffer:r.arrayBuffer.bind(r),error:f,data:c,headers:he(r.headers),config:u}:r};return{getInstance:o,buildConfig:m,config:e,request:g}}function ft(e){let t=e.endpoints,n=G(e);function a(){return n.getInstance()}function o(l){return console.error(`Add ${l} to 'endpoints'.`),Promise.resolve(null)}async function i(l,s={},d={},g={}){let y=t[l];return await n.request(y.url,s,B(C(C({},y||{}),g),{urlPathParams:d}))}function h(l){return l in m?m[l]:t[l]?m.request.bind(null,l):o.bind(null,l)}let m={config:e,endpoints:t,requestHandler:n,getInstance:a,request:i};return new Proxy(m,{get:(l,s)=>h(s)})}async function Ne(e,t={}){return G(t).request(e,null,t)}export{ft as createApiFetcher,Ne as fetchf}; +var be=Object.defineProperty;var we=(e,t,n)=>t in e?be(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var w=(e,t,n)=>we(e,typeof t!="symbol"?t+"":t,n);async function T(e,t){if(t){if(typeof t=="function"){let n=await t(e);n&&Object.assign(e,n)}else if(Array.isArray(t))for(let n of t){let o=await n(e);o&&Object.assign(e,o)}}}var M=class extends Error{constructor(n,o,r){super(n);w(this,"response");w(this,"request");w(this,"config");w(this,"status");w(this,"statusText");this.name="ResponseError",this.message=n,this.status=r.status,this.statusText=r.statusText,this.request=o,this.config=o,this.response=r}};var _="application/",A=_+"json",Q="Content-Type",C="undefined",E="object",L="string",v="AbortError",oe="TimeoutError",ie="CanceledError",N="GET",le="HEAD";function V(e){return e instanceof URLSearchParams}function ce(e){let t="";for(let n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t+=n+":"+e[n]);return t}function W(e){let t={},n=Object.keys(e);n.sort();for(let o=0,r=n.length;o{a=typeof a=="function"?a():a,a=a===null||a===void 0?"":a,n[n.length]=o(f)+"="+o(a)},u=(f,a)=>{let p,g,R;if(f)if(Array.isArray(a))for(p=0,g=a.length;p{let o=n.substring(1);return t[o]?String(t[o]):n}):e}function de(e){let t=typeof e;if(t===C||e===null)return!1;if(t===L||t==="number"||t==="boolean"||Array.isArray(e))return!0;if(Buffer.isBuffer(e)||e instanceof Date)return!1;if(t===E){let n=Object.getPrototypeOf(e);if(n===Object.prototype||n===null||typeof e.toJSON=="function")return!0}return!1}async function X(e){return new Promise(t=>setTimeout(()=>t(!0),e))}function Z(e){return e&&typeof e===E&&typeof e.data!==C&&Object.keys(e).length===1?Z(e.data):e}function Re(e){if(!e)return{};let t={};if(e instanceof Headers)e.forEach((n,o)=>{t[o]=n});else if(typeof e===E&&e!==null)for(let[n,o]of Object.entries(e))t[n.toLowerCase()]=o;return t}function j(e,t){e&&t in e&&delete e[t]}var H=new Map;async function me(e,t,n=0,o=!1,r=!0){let u=Date.now(),m=H.get(e);if(m){let a=m[3],p=m[0],g=m[1];if(!a&&u-m[2]{let a=new DOMException(`${e.url} aborted due to timeout`,oe);J(e,a)},t):null;return H.set(e,[y,f,u,o]),y}async function J(e,t=null){let n=H.get(e);if(n){let o=n[0],r=n[1];t&&!o.signal.aborted&&o.abort(t),r!==null&&clearTimeout(r),H.delete(e)}}async function ye(e){var o;if(!(e!=null&&e.body))return null;let t=String(((o=e.headers)==null?void 0:o.get(Q))||"").split(";")[0],n;try{if(t.includes(A)||t.includes("+json"))n=await e.json();else if(t.includes("multipart/form-data"))n=await e.formData();else if(t.includes(_+"octet-stream"))n=await e.blob();else if(t.includes(_+"x-www-form-urlencoded"))n=await e.formData();else if(t.includes("text/"))n=await e.text();else try{n=await e.clone().json()}catch(r){n=await e.text()}}catch(r){n=null}return n}function $(e){let t=0;for(let n=0,o=e.length;n{R+=i+"="+s+"&"}),R=$(R);else if(typeof Blob!==C&&r instanceof Blob||typeof File!==C&&r instanceof File)R="BF"+r.size+r.type;else if(r instanceof ArrayBuffer||ArrayBuffer.isView(r))R="AB"+r.byteLength;else{let s=typeof r===E?W(r):String(r);R=$(JSON.stringify(s))}return n+t+u+m+y+f+a+p+g+R}function xe(e,t){return t?Date.now()-e>t*1e3:!1}function ge(e,t){let n=ee.get(e);if(n){if(!xe(n.timestamp,t))return n;ee.delete(e)}return null}function Pe(e,t,n=!1){ee.set(e,{data:t,isLoading:n,timestamp:Date.now()})}var Oe={method:N,strategy:"reject",timeout:3e4,dedupeTime:1e3,defaultResponse:null,headers:{Accept:A+", text/plain, */*","Accept-Encoding":"gzip, deflate, br",[Q]:A+";charset=utf-8"},retry:{delay:1e3,maxDelay:3e4,resetTimeout:!0,backoff:1.5,retryOn:[408,409,425,429,500,502,503,504]}};function G(e){let t={...Oe,...e},n=t.fetcher,o=(n==null?void 0:n.create(t))||null,r=()=>o,u=(s,i)=>typeof s[i]!==C?s[i]:t[i],m=(...s)=>{var i;(i=t.logger)!=null&&i.warn&&t.logger.warn(...s)},y=(s,i)=>{let d=u(i,"method").toUpperCase(),P=d===N||d===le,l=pe(s,u(i,"urlPathParams")),h=u(i,"params"),U=u(i,"body")||u(i,"data"),D;P||(D=U);let S=u(i,"withCredentials")?"include":u(i,"credentials"),x=h?fe(l,h):l,k=x.includes("://")?"":u(i,"baseURL")||u(i,"apiUrl");return D&&typeof D!==L&&!V(D)&&de(D)&&(D=JSON.stringify(D)),{...i,credentials:S,body:D,method:d,url:k+x}},f=async(s,i)=>{p(s)||m("API ERROR",s),await T(s,i==null?void 0:i.onError),await T(s,t==null?void 0:t.onError)},a=async(s,i,d)=>{let P=p(s),l=u(d,"strategy"),h=u(d,"rejectCancelled");if(!(P&&!h)){if(l==="silent")await new Promise(()=>null);else if(l==="reject")return Promise.reject(s)}return R(i,d,s)},p=s=>s.name===v||s.name===ie,g=async(s,i=null)=>{var re;let d=i||{},P={...t,...d},l=null,h=y(s,P),{timeout:U,cancellable:D,dedupeTime:te,pollingInterval:S,shouldStopPolling:x,cacheTime:B,cacheKey:k}=P,b;if(k?b=k(h):b=he(h),B&&b){let I=P.cacheBuster;if(!I||!I(h)){let c=ge(b,B);if(c)return c.data}}let{retries:z=0,delay:De,backoff:Ee,retryOn:Y,shouldRetry:ne,maxDelay:Ce,resetTimeout:Te}=P.retry,O=0,K=0,F=De,se=z>0?z:0;for(;O<=se;)try{let c={signal:(await me(h,U,te,D,!!(U&&(!se||Te)))).signal,...h};if(await T(c,d==null?void 0:d.onRequest),await T(c,t==null?void 0:t.onRequest),n!==null&&o!==null?l=await o.request(c):l=await fetch(c.url,c),l instanceof Response&&(l.config=c,l.data=await ye(l),!l.ok))throw new M(`${c.url} failed! Status: ${l.status||null}`,c,l);if(await T(l,d==null?void 0:d.onResponse),await T(l,t==null?void 0:t.onResponse),J(h),S&&(!x||!x(l,K))){K++,m(`Polling attempt ${K}...`),await X(S);continue}let q=R(l,c);if(B&&b){let ae=c.skipCache;(!ae||!ae(q,c))&&Pe(b,q)}return q}catch(I){let c=I,q=((re=c==null?void 0:c.response)==null?void 0:re.status)||(c==null?void 0:c.status)||0;if(O===z||!(!ne||await ne(c,O))||!(Y!=null&&Y.includes(q)))return await f(c,h),J(h),a(c,l,h);m(`Attempt ${O+1} failed. Retry in ${F}ms.`),await X(F),F*=Ee,F=Math.min(F,Ce),O++}return R(l,h)},R=(s,i,d=null)=>{let P=u(i,"defaultResponse");if(!s)return{ok:!1,error:d,data:P,headers:null,config:i};j(d,"response"),j(d,"request"),j(d,"config");let l=s==null?void 0:s.data;return(l==null||typeof l===E&&Object.keys(l).length===0)&&(l=P),u(i,"flattenResponse")&&(s.data=Z(l)),s instanceof Response?{body:s.body,bodyUsed:s.bodyUsed,formData:s.formData,ok:s.ok,redirected:s.redirected,type:s.type,url:s.url,status:s.status,statusText:s.statusText,blob:s.blob.bind(s),json:s.json.bind(s),text:s.text.bind(s),clone:s.clone.bind(s),arrayBuffer:s.arrayBuffer.bind(s),error:d,data:l,headers:Re(s.headers),config:i}:s};return{getInstance:r,buildConfig:y,config:e,request:g}}function Ze(e){let t=e.endpoints,n=G(e);function o(){return n.getInstance()}function r(y){return console.error(`Add ${y} to 'endpoints'.`),Promise.resolve(null)}async function u(y,f={}){let a=t[y]||{url:y};return await n.request(a.url,{...a,...f})}let m={config:e,endpoints:t,requestHandler:n,getInstance:o,request:u};return new Proxy(m,{get(y,f){return f in m?m[f]:t[f]?m.request.bind(null,f):r.bind(null,f)}})}async function nt(e,t={}){return G(t).request(e,t)}export{Ze as createApiFetcher,nt as fetchf}; //# sourceMappingURL=index.mjs.map \ No newline at end of file diff --git a/dist/browser/index.mjs.map b/dist/browser/index.mjs.map index f4ae9c2..a84a0a0 100644 --- a/dist/browser/index.mjs.map +++ b/dist/browser/index.mjs.map @@ -1 +1 @@ -{"version":3,"sources":["../../src/interceptor-manager.ts","../../src/response-error.ts","../../src/const.ts","../../src/utils.ts","../../src/queue-manager.ts","../../src/response-parser.ts","../../src/hash.ts","../../src/cache-manager.ts","../../src/request-handler.ts","../../src/api-handler.ts","../../src/index.ts"],"sourcesContent":["type InterceptorFunction = (object: T) => Promise;\n\n/**\n * Applies interceptors to the object. Interceptors can be a single function or an array of functions.\n *\n * @template T - Type of the object.\n * @template I - Type of interceptors.\n *\n * @param {T} object - The object to process.\n * @param {InterceptorFunction | InterceptorFunction[]} [interceptors] - Interceptor function(s).\n *\n * @returns {Promise} - Nothing as the function is non-idempotent.\n */\nexport async function applyInterceptor<\n T extends object,\n I = InterceptorFunction | InterceptorFunction[],\n>(object: T, interceptors?: I): Promise {\n if (!interceptors) {\n return;\n }\n\n if (typeof interceptors === 'function') {\n const value = await interceptors(object);\n\n if (value) {\n Object.assign(object, value);\n }\n } else if (Array.isArray(interceptors)) {\n for (const interceptor of interceptors) {\n const value = await interceptor(object);\n\n if (value) {\n Object.assign(object, value);\n }\n }\n }\n}\n","import type { FetchResponse, RequestConfig } from './types';\n\nexport class ResponseErr extends Error {\n response: FetchResponse;\n request: RequestConfig;\n config: RequestConfig;\n status: number;\n statusText: string;\n\n constructor(\n message: string,\n requestInfo: RequestConfig,\n response: FetchResponse,\n ) {\n super(message);\n\n this.name = 'ResponseError';\n this.message = message;\n this.status = response.status;\n this.statusText = response.statusText;\n this.request = requestInfo;\n this.config = requestInfo;\n this.response = response;\n }\n}\n","export const APPLICATION_JSON = 'application/json';\nexport const CONTENT_TYPE = 'Content-Type';\n\nexport const UNDEFINED = 'undefined';\nexport const OBJECT = 'object';\nexport const STRING = 'string';\n\nexport const ABORT_ERROR = 'AbortError';\nexport const TIMEOUT_ERROR = 'TimeoutError';\nexport const CANCELLED_ERROR = 'CanceledError';\n\nexport const GET = 'GET';\nexport const HEAD = 'HEAD';\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { OBJECT, STRING, UNDEFINED } from './const';\nimport type { HeadersObject, QueryParams, UrlPathParams } from './types';\n\nexport function isSearchParams(data: unknown): boolean {\n return data instanceof URLSearchParams;\n}\n\n/**\n * Determines if a value is a non-null object.\n *\n * @param {any} value - The value to check.\n * @returns {boolean} - True if the value is a non-null object.\n */\nexport function isObject(value: any): value is Record {\n return value !== null && typeof value === OBJECT;\n}\n\n/**\n * Shallowly serializes an object by converting its key-value pairs into a string representation.\n * This function does not recursively serialize nested objects.\n *\n * @param obj - The object to serialize.\n * @returns A string representation of the object's top-level properties.\n */\nexport function shallowSerialize(obj: Record): string {\n let result = '';\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n result += key + ':' + obj[key];\n }\n }\n\n return result;\n}\n\n/**\n * Sorts the keys of an object and returns a new object with sorted keys.\n *\n * This function is optimized for performance by minimizing the number of object operations\n * and using a single pass to create the sorted object.\n *\n * @param {Object} obj - The object to be sorted by keys.\n * @returns {Object} - A new object with keys sorted in ascending order.\n */\nexport function sortObject(obj: Record): object {\n const sortedObj = {} as Record;\n const keys = Object.keys(obj);\n\n keys.sort();\n\n for (let i = 0, len = keys.length; i < len; i++) {\n const key = keys[i];\n sortedObj[key] = obj[key];\n }\n\n return sortedObj;\n}\n\n/**\n * Appends a query string to a URL, ensuring proper handling of existing query parameters.\n *\n * @param baseUrl - The base URL to which the query string will be appended.\n * @param queryString - The encoded query string to append.\n * @returns The URL with the appended query string, or the original URL if no query string is provided.\n */\nfunction appendQueryStringToUrl(baseUrl: string, queryString: string): string {\n if (!queryString) {\n return baseUrl;\n }\n\n return baseUrl.includes('?')\n ? `${baseUrl}&${queryString}`\n : `${baseUrl}?${queryString}`;\n}\n\n/**\n * Appends query parameters to a given URL.\n *\n * @param {string} url - The base URL to which query parameters will be appended.\n * @param {QueryParams} params - An object containing the query parameters to append.\n * @returns {string} - The URL with the appended query parameters.\n */\nexport function appendQueryParams(url: string, params: QueryParams): string {\n if (!params) {\n return url;\n }\n\n // Check if `params` is an instance of URLSearchParams and bail early if it is\n if (isSearchParams(params)) {\n const encodedQueryString = params.toString();\n\n return appendQueryStringToUrl(url, encodedQueryString);\n }\n\n // This is exact copy of what JQ used to do. It works much better than URLSearchParams\n const s: string[] = [];\n const encode = encodeURIComponent;\n const add = (k: string, v: any) => {\n v = typeof v === 'function' ? v() : v;\n v = v === null ? '' : v === undefined ? '' : v;\n s[s.length] = encode(k) + '=' + encode(v);\n };\n\n const buildParams = (prefix: string, obj: any) => {\n let i: number, len: number, key: string;\n\n if (prefix) {\n if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n buildParams(\n prefix + '[' + (typeof obj[i] === OBJECT && obj[i] ? i : '') + ']',\n obj[i],\n );\n }\n } else if (typeof obj === OBJECT && obj !== null) {\n for (key in obj) {\n buildParams(prefix + '[' + key + ']', obj[key]);\n }\n } else {\n add(prefix, obj);\n }\n } else if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n add(obj[i].name, obj[i].value);\n }\n } else {\n for (key in obj) {\n buildParams(key, obj[key]);\n }\n }\n return s;\n };\n\n const queryStringParts = buildParams('', params).join('&');\n\n // Encode special characters as per RFC 3986, https://datatracker.ietf.org/doc/html/rfc3986\n const encodedQueryString = queryStringParts.replace(/%5B%5D/g, '[]'); // Keep '[]' for arrays\n\n return appendQueryStringToUrl(url, encodedQueryString);\n}\n\n/**\n * Replaces dynamic URI parameters in a URL string with values from the provided `urlPathParams` object.\n * Parameters in the URL are denoted by `:`, where `` is a key in `urlPathParams`.\n *\n * @param {string} url - The URL string containing placeholders in the format `:`.\n * @param {Object} urlPathParams - An object containing the parameter values to replace placeholders.\n * @param {string} urlPathParams.paramName - The value to replace the placeholder `:` in the URL.\n * @returns {string} - The URL string with placeholders replaced by corresponding values from `urlPathParams`.\n */\nexport function replaceUrlPathParams(\n url: string,\n urlPathParams: UrlPathParams,\n): string {\n if (!urlPathParams) {\n return url;\n }\n\n return url.replace(/:\\w+/g, (str): string => {\n const word = str.substring(1);\n\n return String(urlPathParams[word] ? urlPathParams[word] : str);\n });\n}\n\n/**\n * Checks if a value is JSON serializable.\n *\n * JSON serializable values include:\n * - Primitive types: string, number, boolean, null\n * - Arrays\n * - Plain objects (i.e., objects without special methods)\n * - Values with a `toJSON` method\n *\n * @param {any} value - The value to check for JSON serializability.\n * @returns {boolean} - Returns `true` if the value is JSON serializable, otherwise `false`.\n */\nexport function isJSONSerializable(value: any): boolean {\n const t = typeof value;\n\n if (t === UNDEFINED || value === null) {\n return false;\n }\n\n if (t === STRING || t === 'number' || t === 'boolean') {\n return true;\n }\n\n if (Array.isArray(value)) {\n return true;\n }\n\n if (Buffer.isBuffer(value)) {\n return false;\n }\n\n if (value instanceof Date) {\n return false;\n }\n\n if (t === OBJECT) {\n const proto = Object.getPrototypeOf(value);\n\n // Check if the prototype is `Object.prototype` or `null` (plain object)\n if (proto === Object.prototype || proto === null) {\n return true;\n }\n\n // Check if the object has a toJSON method\n if (typeof value.toJSON === 'function') {\n return true;\n }\n }\n\n return false;\n}\n\nexport async function delayInvocation(ms: number): Promise {\n return new Promise((resolve) =>\n setTimeout(() => {\n return resolve(true);\n }, ms),\n );\n}\n\n/**\n * Recursively flattens the data object if it meets specific criteria.\n *\n * The method checks if the provided `data` is an object with exactly one property named `data`.\n * If so, it recursively flattens the `data` property. Otherwise, it returns the `data` as-is.\n *\n * @param {any} data - The data to be flattened. Can be of any type, including objects, arrays, or primitives.\n * @returns {any} - The flattened data if the criteria are met; otherwise, the original `data`.\n */\nexport function flattenData(data: any): any {\n if (\n data &&\n typeof data === OBJECT &&\n typeof data.data !== UNDEFINED &&\n Object.keys(data).length === 1\n ) {\n return flattenData(data.data);\n }\n\n return data;\n}\n\n/**\n * Processes headers and returns them as a normalized object.\n *\n * Handles both `Headers` instances and plain objects. Normalizes header keys to lowercase\n * as per RFC 2616 section 4.2.\n *\n * @param headers - The headers to process. Can be an instance of `Headers`, a plain object,\n * or `null`. If `null`, an empty object is returned.\n * @returns {HeadersObject} - A normalized headers object with lowercase keys.\n */\nexport function processHeaders(\n headers?: (HeadersObject & HeadersInit) | null | Headers,\n): HeadersObject {\n if (!headers) {\n return {};\n }\n\n const headersObject: HeadersObject = {};\n\n // Handle Headers object with entries() method\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n headersObject[key] = value;\n });\n } else if (typeof headers === OBJECT && headers !== null) {\n // Handle plain object\n for (const [key, value] of Object.entries(headers)) {\n // Normalize keys to lowercase as per RFC 2616 4.2\n // https://datatracker.ietf.org/doc/html/rfc2616#section-4.2\n headersObject[key.toLowerCase()] = value;\n }\n }\n\n return headersObject;\n}\n\n/**\n * Deletes a property from an object if it exists.\n *\n * @param obj - The object from which to delete the property.\n * @param property - The property to delete from the object.\n */\nexport function deleteProperty>(\n obj: T | null,\n property: keyof T,\n): void {\n if (obj && property in obj) {\n delete obj[property];\n }\n}\n","import { ABORT_ERROR, TIMEOUT_ERROR } from './const';\nimport type { RequestConfig } from './types';\nimport type { QueueItem, RequestsQueue } from './types/queue-manager';\n\n/**\n * Queue Manager is responsible for managing and controlling the flow of concurrent or sequential requests. It handles:\n * - Request Queueing and Deduplication\n * - Request Timeout Handling\n * - Abort Controller Management and Request Cancellation\n * - Concurrency Control and Locking\n * - Request Lifecycle Management\n */\nconst queue: RequestsQueue = new Map();\n\n/**\n * Adds a request to the queue if it's not already being processed within the dedupeTime interval.\n *\n * @param {RequestConfig} config - The request configuration object.\n * @param {number} timeout - Timeout in milliseconds for the request.\n * @param {number} dedupeTime - Deduplication time in milliseconds.\n * @param {boolean} isCancellable - If true, then the previous request with same configuration should be aborted.\n * @param {boolean} isTimeoutEnabled - Whether timeout is enabled.\n * @returns {Promise} - A promise that resolves to an AbortController.\n */\nexport async function addRequest(\n config: RequestConfig,\n timeout: number | undefined,\n dedupeTime: number = 0,\n isCancellable: boolean = false,\n isTimeoutEnabled: boolean = true,\n): Promise {\n const now = Date.now();\n const item = queue.get(config);\n\n if (item) {\n const isCancellable = item[3];\n const previousController = item[0];\n const timeoutId = item[1];\n\n // If the request is already in the queue and within the dedupeTime, reuse the existing controller\n if (!isCancellable && now - item[2] < dedupeTime) {\n return previousController;\n }\n\n // If the request is too old, remove it and proceed to add a new one\n // Abort previous request, if applicable, and continue as usual\n if (isCancellable) {\n previousController.abort(\n new DOMException('Aborted due to new request', ABORT_ERROR),\n );\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n\n const controller = new AbortController();\n\n const timeoutId = isTimeoutEnabled\n ? setTimeout(() => {\n const error = new DOMException(\n `${config.url} aborted due to timeout`,\n TIMEOUT_ERROR,\n );\n\n removeRequest(config, error);\n }, timeout)\n : null;\n\n queue.set(config, [controller, timeoutId, now, isCancellable]);\n\n return controller;\n}\n\n/**\n * Removes a request from the queue and clears its timeout.\n *\n * @param config - The request configuration.\n * @param {boolean} error - Error payload so to force the request to abort.\n */\nexport async function removeRequest(\n config: RequestConfig,\n error: DOMException | null | string = null,\n): Promise {\n const item = queue.get(config);\n\n if (item) {\n const controller = item[0];\n const timeoutId = item[1];\n\n // If the request is not yet aborted, abort it with the provided error\n if (error && !controller.signal.aborted) {\n controller.abort(error);\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n}\n\n/**\n * Gets the AbortController for a request configuration.\n *\n * @param config - The request configuration.\n * @returns {AbortController | undefined} - The AbortController or undefined.\n */\nexport async function getController(\n config: RequestConfig,\n): Promise {\n const item = queue.get(config);\n\n return item?.[0];\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { APPLICATION_JSON, CONTENT_TYPE } from './const';\nimport type { APIResponse } from './types/api-handler';\nimport type { FetchResponse } from './types/request-handler';\n\n/**\n * Parses the response data based on the Content-Type header.\n *\n * @param response - The Response object to parse.\n * @returns A Promise that resolves to the parsed data.\n */\nexport async function parseResponseData(\n response: FetchResponse,\n): Promise {\n // Bail early when body is empty\n if (!response?.body) {\n return null;\n }\n\n const contentType = String(\n (response as Response).headers?.get(CONTENT_TYPE) || '',\n ).split(';')[0]; // Correctly handle charset\n\n let data;\n\n try {\n if (\n contentType.includes(APPLICATION_JSON) ||\n contentType.includes('+json')\n ) {\n data = await response.json(); // Parse JSON response\n } else if (contentType.includes('multipart/form-data')) {\n data = await response.formData(); // Parse as FormData\n } else if (contentType.includes('application/octet-stream')) {\n data = await response.blob(); // Parse as blob\n } else if (contentType.includes('application/x-www-form-urlencoded')) {\n data = await response.formData(); // Handle URL-encoded forms\n } else if (contentType.includes('text/')) {\n data = await response.text(); // Parse as text\n } else {\n try {\n const responseClone = response.clone();\n\n // Handle edge case of no content type being provided... We assume JSON here.\n data = await responseClone.json();\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_e) {\n // Handle streams\n data = await response.text();\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_error) {\n // Parsing failed, fallback to null\n data = null;\n }\n\n return data;\n}\n","const PRIME_MULTIPLIER = 31;\n\n/**\n * Computes a hash value for a given string using the variant of djb2 hash function.\n * This hash function is non-cryptographic and designed for speed.\n * @author Daniel J. Bernstein (of djb2)\n *\n * @param str Input string to hash\n * @returns {string} Hash\n */\nexport function hash(str: string): string {\n let hash = 0;\n\n for (let i = 0, len = str.length; i < len; i++) {\n const char = str.charCodeAt(i);\n hash = (hash * PRIME_MULTIPLIER + char) | 0;\n }\n\n return String(hash);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { hash } from './hash';\nimport { fetchf } from './index';\nimport type { FetcherConfig } from './types/request-handler';\nimport type { CacheEntry } from './types/cache-manager';\nimport { GET, OBJECT, UNDEFINED } from './const';\nimport { shallowSerialize, sortObject } from './utils';\n\nconst cache = new Map>();\n\n/**\n * Generates a cache key for a given URL and fetch options, ensuring that key factors\n * like method, headers, body, and other options are included in the cache key.\n * Headers and other objects are sorted by key to ensure consistent cache keys.\n *\n * @param options - The fetch options that may affect the request. The most important are:\n * @property {string} [method=\"GET\"] - The HTTP method (GET, POST, etc.).\n * @property {HeadersInit} [headers={}] - The request headers.\n * @property {BodyInit | null} [body=\"\"] - The body of the request (only for methods like POST, PUT).\n * @property {RequestMode} [mode=\"cors\"] - The mode for the request (e.g., cors, no-cors, include).\n * @property {RequestCredentials} [credentials=\"include\"] - Whether to include credentials like cookies.\n * @property {RequestCache} [cache=\"default\"] - The cache mode (e.g., default, no-store, reload).\n * @property {RequestRedirect} [redirect=\"follow\"] - How to handle redirects (e.g., follow, error, manual).\n * @property {string} [referrer=\"\"] - The referrer URL to send with the request.\n * @property {string} [integrity=\"\"] - Subresource integrity value (a cryptographic hash for resource validation).\n * @returns {string} - A unique cache key based on the URL and request options. Empty if cache is to be burst.\n *\n * @example\n * const cacheKey = generateCacheKey({\n * url: 'https://api.example.com/data',\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ name: 'Alice' }),\n * mode: 'cors',\n * credentials: 'include',\n * });\n * console.log(cacheKey);\n */\nexport function generateCacheKey(options: FetcherConfig): string {\n const {\n url = '',\n method = GET,\n headers = {},\n body = '',\n mode = 'cors',\n credentials = 'include',\n cache = 'default',\n redirect = 'follow',\n referrer = '',\n integrity = '',\n } = options;\n\n // Bail early if cache should be burst\n if (cache === 'reload') {\n return '';\n }\n\n // Sort headers and body + convert sorted to strings for hashing purposes\n // Native serializer is on avg. 3.5x faster than a Fast Hash or FNV-1a\n const headersString = shallowSerialize(sortObject(headers));\n\n let bodyString = '';\n\n // In majority of cases we do not cache body\n if (body !== null) {\n if (typeof body === 'string') {\n bodyString = hash(body);\n } else if (body instanceof FormData) {\n body.forEach((value, key) => {\n // Append key=value and '&' directly to the result\n bodyString += key + '=' + value + '&';\n });\n bodyString = hash(bodyString);\n } else if (\n (typeof Blob !== UNDEFINED && body instanceof Blob) ||\n (typeof File !== UNDEFINED && body instanceof File)\n ) {\n bodyString = 'BF' + body.size + body.type;\n } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n bodyString = 'AB' + body.byteLength;\n } else {\n const o = typeof body === OBJECT ? sortObject(body) : String(body);\n bodyString = hash(JSON.stringify(o));\n }\n }\n\n // Concatenate all key parts into a cache key string\n // Template literals are apparently slower\n return (\n method +\n url +\n mode +\n credentials +\n cache +\n redirect +\n referrer +\n integrity +\n headersString +\n bodyString\n );\n}\n\n/**\n * Checks if the cache entry is expired based on its timestamp and the maximum stale time.\n *\n * @param {number} timestamp - The timestamp of the cache entry.\n * @param {number} maxStaleTime - The maximum stale time in seconds.\n * @returns {boolean} - Returns true if the cache entry is expired, false otherwise.\n */\nfunction isCacheExpired(timestamp: number, maxStaleTime: number): boolean {\n if (!maxStaleTime) {\n return false;\n }\n\n return Date.now() - timestamp > maxStaleTime * 1000;\n}\n\n/**\n * Retrieves a cache entry if it exists and is not expired.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} cacheTime - Maximum time to cache entry.\n * @returns {CacheEntry | null} - The cache entry if it exists and is not expired, null otherwise.\n */\nexport function getCache(\n key: string,\n cacheTime: number,\n): CacheEntry | null {\n const entry = cache.get(key);\n\n if (entry) {\n if (!isCacheExpired(entry.timestamp, cacheTime)) {\n return entry;\n }\n\n cache.delete(key);\n }\n\n return null;\n}\n\n/**\n * Sets a new cache entry or updates an existing one.\n *\n * @param {string} key Cache key to utilize\n * @param {T} data - The data to be cached.\n * @param {boolean} isLoading - Indicates if the data is currently being fetched.\n */\nexport function setCache(\n key: string,\n data: T,\n isLoading: boolean = false,\n): void {\n cache.set(key, {\n data,\n isLoading,\n timestamp: Date.now(),\n });\n}\n\n/**\n * Revalidates a cache entry by fetching fresh data and updating the cache.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @returns {Promise} - A promise that resolves when the revalidation is complete.\n */\nexport async function revalidate(\n key: string,\n config: FetcherConfig,\n): Promise {\n try {\n // Fetch fresh data\n const newData = await fetchf(config.url, {\n ...config,\n cache: 'reload',\n });\n\n setCache(key, newData);\n } catch (error) {\n console.error(`Error revalidating ${config.url}:`, error);\n\n // Rethrow the error to forward it\n throw error;\n }\n}\n\n/**\n * Invalidates (deletes) a cache entry.\n *\n * @param {string} key Cache key to utilize\n */\nexport function deleteCache(key: string): void {\n cache.delete(key);\n}\n\n/**\n * Mutates a cache entry with new data and optionally revalidates it.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @param {T} newData - The new data to be cached.\n * @param {boolean} revalidateAfter - If true, triggers revalidation after mutation.\n */\nexport function mutate(\n key: string,\n config: FetcherConfig,\n newData: T,\n revalidateAfter: boolean = false,\n): void {\n setCache(key, newData);\n\n if (revalidateAfter) {\n revalidate(key, config);\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {\n RequestHandlerConfig,\n RequestConfig,\n Method,\n RetryOptions,\n FetchResponse,\n ResponseError,\n RequestHandlerReturnType,\n CreatedCustomFetcherInstance,\n FetcherConfig,\n} from './types/request-handler';\nimport type {\n APIResponse,\n BodyPayload,\n QueryParams,\n QueryParamsOrBody,\n} from './types/api-handler';\nimport { applyInterceptor } from './interceptor-manager';\nimport { ResponseErr } from './response-error';\nimport {\n appendQueryParams,\n isJSONSerializable,\n replaceUrlPathParams,\n delayInvocation,\n flattenData,\n processHeaders,\n deleteProperty,\n isSearchParams,\n} from './utils';\nimport { addRequest, removeRequest } from './queue-manager';\nimport {\n ABORT_ERROR,\n APPLICATION_JSON,\n CANCELLED_ERROR,\n CONTENT_TYPE,\n GET,\n HEAD,\n OBJECT,\n STRING,\n UNDEFINED,\n} from './const';\nimport { parseResponseData } from './response-parser';\nimport { generateCacheKey, getCache, setCache } from './cache-manager';\n\nconst defaultConfig: RequestHandlerConfig = {\n method: GET,\n strategy: 'reject',\n timeout: 30000,\n dedupeTime: 1000,\n defaultResponse: null,\n headers: {\n Accept: APPLICATION_JSON + ', text/plain, */*',\n 'Accept-Encoding': 'gzip, deflate, br',\n [CONTENT_TYPE]: APPLICATION_JSON + ';charset=utf-8',\n },\n retry: {\n delay: 1000,\n maxDelay: 30000,\n resetTimeout: true,\n backoff: 1.5,\n\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status\n retryOn: [\n 408, // Request Timeout\n 409, // Conflict\n 425, // Too Early\n 429, // Too Many Requests\n 500, // Internal Server Error\n 502, // Bad Gateway\n 503, // Service Unavailable\n 504, // Gateway Timeout\n ],\n },\n};\n\n/**\n * Create Request Handler\n *\n * @param {RequestHandlerConfig} config - Configuration object for the request handler\n * @returns {Object} An object with methods for handling requests\n */\nexport function createRequestHandler(\n config: RequestHandlerConfig,\n): RequestHandlerReturnType {\n const handlerConfig: RequestHandlerConfig = {\n ...defaultConfig,\n ...config,\n };\n\n /**\n * Immediately create instance of custom fetcher if it is defined\n */\n const customFetcher = handlerConfig.fetcher;\n const requestInstance = customFetcher?.create(handlerConfig) || null;\n\n /**\n * Get Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Provider's instance\n */\n const getInstance = (): CreatedCustomFetcherInstance | null => {\n return requestInstance;\n };\n\n /**\n * Gets a configuration value from `reqConfig`, defaulting to `handlerConfig` if not present.\n *\n * @param {RequestConfig} reqConfig - Request configuration object.\n * @param {keyof RequestConfig} name - Key of the configuration value.\n * @returns {T} - The configuration value.\n */\n const getConfig = (\n reqConfig: RequestConfig,\n name: keyof RequestConfig,\n ): T => {\n return typeof reqConfig[name] !== UNDEFINED\n ? reqConfig[name]\n : handlerConfig[name];\n };\n\n /**\n * Logs messages or errors using the configured logger's `warn` method.\n *\n * @param {...(string | ResponseError)} args - Messages or errors to log.\n */\n const logger = (...args: (string | ResponseError)[]): void => {\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn(...args);\n }\n };\n\n /**\n * Build request configuration\n *\n * @param {string} url - Request url\n * @param {QueryParamsOrBody} data - Query Params in case of GET and HEAD requests, body payload otherwise\n * @param {RequestConfig} reqConfig - Request config passed when making the request\n * @returns {RequestConfig} - Provider's instance\n */\n const buildConfig = (\n url: string,\n data: QueryParamsOrBody,\n reqConfig: RequestConfig,\n ): FetcherConfig => {\n const method = getConfig(\n reqConfig,\n 'method',\n ).toUpperCase() as Method;\n const isGetAlikeMethod = method === GET || method === HEAD;\n\n const dynamicUrl = replaceUrlPathParams(\n url,\n getConfig(reqConfig, 'urlPathParams'),\n );\n\n // The explicitly passed \"params\"\n const explicitParams = getConfig(reqConfig, 'params');\n\n // The explicitly passed \"body\" or \"data\"\n const explicitBodyData: BodyPayload =\n getConfig(reqConfig, 'body') || getConfig(reqConfig, 'data');\n\n // For convenience, in POST requests the body payload is the \"data\"\n // In edge cases we want to use Query Params in the POST requests\n // and use explicitly passed \"body\" or \"data\" from request config\n const shouldTreatDataAsParams =\n data && (isGetAlikeMethod || explicitBodyData) ? true : false;\n\n // Final body data\n let body: RequestConfig['data'];\n\n // Only applicable for request methods 'PUT', 'POST', 'DELETE', and 'PATCH'\n if (!isGetAlikeMethod) {\n body = explicitBodyData || (data as BodyPayload);\n }\n\n // Native fetch compatible settings\n const isWithCredentials = getConfig(reqConfig, 'withCredentials');\n\n const credentials = isWithCredentials\n ? 'include'\n : getConfig(reqConfig, 'credentials');\n\n deleteProperty(reqConfig, 'data');\n deleteProperty(reqConfig, 'withCredentials');\n\n const urlPath =\n explicitParams || shouldTreatDataAsParams\n ? appendQueryParams(dynamicUrl, explicitParams || (data as QueryParams))\n : dynamicUrl;\n const isFullUrl = urlPath.includes('://');\n const baseURL = isFullUrl\n ? ''\n : getConfig(reqConfig, 'baseURL') ||\n getConfig(reqConfig, 'apiUrl');\n\n // Automatically stringify request body, if possible and when not dealing with strings\n if (\n body &&\n typeof body !== STRING &&\n !isSearchParams(body) &&\n isJSONSerializable(body)\n ) {\n body = JSON.stringify(body);\n }\n\n return {\n ...reqConfig,\n credentials,\n body,\n method,\n\n url: baseURL + urlPath,\n };\n };\n\n /**\n * Process global Request Error\n *\n * @param {ResponseError} error Error instance\n * @param {RequestConfig} requestConfig Per endpoint request config\n * @returns {Promise}\n */\n const processError = async (\n error: ResponseError,\n requestConfig: RequestConfig,\n ): Promise => {\n if (!isRequestCancelled(error)) {\n logger('API ERROR', error);\n }\n\n // Local interceptors\n await applyInterceptor(error, requestConfig?.onError);\n\n // Global interceptors\n await applyInterceptor(error, handlerConfig?.onError);\n };\n\n /**\n * Output default response in case of an error, depending on chosen strategy\n *\n * @param {ResponseError} error Error instance\n * @param {FetchResponse} response Response\n * @param {RequestConfig} requestConfig Per endpoint request config\n * @returns {*} Error response\n */\n const outputErrorResponse = async (\n error: ResponseError,\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n ): Promise => {\n const _isRequestCancelled = isRequestCancelled(error);\n const errorHandlingStrategy = getConfig(requestConfig, 'strategy');\n const rejectCancelled = getConfig(\n requestConfig,\n 'rejectCancelled',\n );\n\n // By default cancelled requests aren't rejected (softFail strategy)\n if (!(_isRequestCancelled && !rejectCancelled)) {\n // Hang the promise\n if (errorHandlingStrategy === 'silent') {\n await new Promise(() => null);\n }\n // Reject the promise\n else if (errorHandlingStrategy === 'reject') {\n return Promise.reject(error);\n }\n }\n\n return outputResponse(response, requestConfig, error);\n };\n\n /**\n * Output error response depending on chosen strategy\n *\n * @param {ResponseError} error Error instance\n * @returns {boolean} True if request is aborted\n */\n const isRequestCancelled = (error: ResponseError): boolean => {\n return error.name === ABORT_ERROR || error.name === CANCELLED_ERROR;\n };\n\n /**\n * Handle Request depending on used strategy\n *\n * @param {string} url - Request url\n * @param {QueryParamsOrBody} data - Query Params in case of GET and HEAD requests, body payload otherwise\n * @param {RequestConfig} reqConfig - Request config\n * @throws {ResponseError}\n * @returns {Promise>} Response Data\n */\n const request = async (\n url: string,\n data: QueryParamsOrBody = null,\n reqConfig: RequestConfig | null = null,\n ): Promise> => {\n const _reqConfig = reqConfig || {};\n const mergedConfig = {\n ...handlerConfig,\n ..._reqConfig,\n } as RequestConfig;\n\n let response: FetchResponse | null = null;\n const fetcherConfig = buildConfig(url, data, mergedConfig);\n\n const {\n timeout,\n cancellable,\n dedupeTime,\n pollingInterval,\n shouldStopPolling,\n cacheTime,\n cacheKey,\n } = mergedConfig;\n\n // Prevent performance overhead of cache access\n let _cacheKey: string;\n\n if (cacheKey) {\n _cacheKey = cacheKey(fetcherConfig);\n } else {\n _cacheKey = generateCacheKey(fetcherConfig);\n }\n\n if (cacheTime && _cacheKey) {\n const cacheBuster = mergedConfig.cacheBuster;\n\n if (!cacheBuster || !cacheBuster(fetcherConfig)) {\n const cachedEntry = getCache<\n ResponseData & FetchResponse\n >(_cacheKey, cacheTime);\n\n if (cachedEntry) {\n // Serve stale data from cache\n return cachedEntry.data;\n }\n }\n }\n\n const {\n retries = 0,\n delay,\n backoff,\n retryOn,\n shouldRetry,\n maxDelay,\n resetTimeout,\n } = mergedConfig.retry as Required;\n\n let attempt = 0;\n let pollingAttempt = 0;\n let waitTime: number = delay;\n\n while (attempt <= retries) {\n try {\n // Add the request to the queue. Make sure to handle deduplication, cancellation, timeouts in accordance to retry settings\n const controller = await addRequest(\n fetcherConfig,\n timeout,\n dedupeTime,\n cancellable,\n // Reset timeouts by default or when retries are ON\n !!(timeout && (!retries || resetTimeout)),\n );\n\n // Shallow copy to ensure basic idempotency\n const requestConfig: RequestConfig = {\n signal: controller.signal,\n ...fetcherConfig,\n };\n\n // Local interceptors\n await applyInterceptor(requestConfig, _reqConfig?.onRequest);\n\n // Global interceptors\n await applyInterceptor(requestConfig, handlerConfig?.onRequest);\n\n if (customFetcher !== null && requestInstance !== null) {\n response = await requestInstance.request(requestConfig);\n } else {\n response = (await fetch(\n requestConfig.url as string,\n requestConfig as RequestInit,\n )) as FetchResponse;\n }\n\n // Add more information to response object\n if (response instanceof Response) {\n response.config = requestConfig;\n response.data = await parseResponseData(response);\n\n // Check if the response status is not outside the range 200-299 and if so, output error\n if (!response.ok) {\n throw new ResponseErr(\n `${requestConfig.url} failed! Status: ${response.status || null}`,\n requestConfig,\n response,\n );\n }\n }\n\n // Local interceptors\n await applyInterceptor(response, _reqConfig?.onResponse);\n\n // Global interceptors\n await applyInterceptor(response, handlerConfig?.onResponse);\n\n removeRequest(fetcherConfig);\n\n // Polling logic\n if (\n pollingInterval &&\n (!shouldStopPolling || !shouldStopPolling(response, pollingAttempt))\n ) {\n // Restart the main retry loop\n pollingAttempt++;\n\n logger(`Polling attempt ${pollingAttempt}...`);\n\n await delayInvocation(pollingInterval);\n\n continue;\n }\n\n // If polling is not required, or polling attempts are exhausted\n const output = outputResponse(response, requestConfig) as ResponseData &\n FetchResponse;\n\n if (cacheTime && _cacheKey) {\n const skipCache = requestConfig.skipCache;\n\n if (!skipCache || !skipCache(output, requestConfig)) {\n setCache(_cacheKey, output);\n }\n }\n\n return output;\n } catch (err) {\n const error = err as ResponseErr;\n const status = error?.response?.status || error?.status || 0;\n\n if (\n attempt === retries ||\n !(!shouldRetry || (await shouldRetry(error, attempt))) ||\n !retryOn?.includes(status)\n ) {\n await processError(error, fetcherConfig);\n\n removeRequest(fetcherConfig);\n\n return outputErrorResponse(error, response, fetcherConfig);\n }\n\n logger(`Attempt ${attempt + 1} failed. Retry in ${waitTime}ms.`);\n\n await delayInvocation(waitTime);\n\n waitTime *= backoff;\n waitTime = Math.min(waitTime, maxDelay);\n attempt++;\n }\n }\n\n return outputResponse(response, fetcherConfig) as ResponseData &\n FetchResponse;\n };\n\n /**\n * Output response\n *\n * @param response - Response payload\n * @param {RequestConfig} requestConfig - Request config\n * @param error - whether the response is erroneous\n * @returns {ResponseData | FetchResponse} Response data\n */\n const outputResponse = (\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n error: ResponseError | null = null,\n ): ResponseData | FetchResponse => {\n const defaultResponse = getConfig(requestConfig, 'defaultResponse');\n const flattenResponse = getConfig(\n requestConfig,\n 'flattenResponse',\n );\n\n if (!response) {\n return flattenResponse\n ? defaultResponse\n : {\n error,\n headers: null,\n data: defaultResponse,\n config: requestConfig,\n };\n }\n\n // Clean up the error object\n deleteProperty(error, 'response');\n deleteProperty(error, 'request');\n deleteProperty(error, 'config');\n\n let data = response?.data;\n\n // Set the default response if the provided data is an empty object\n if (\n data === undefined ||\n data === null ||\n (typeof data === OBJECT && Object.keys(data).length === 0)\n ) {\n data = defaultResponse;\n }\n\n // Return flattened response immediately\n if (flattenResponse) {\n return flattenData(data);\n }\n\n // If it's a custom fetcher, and it does not return any Response instance, it may have its own internal handler\n if (!(response instanceof Response)) {\n return response;\n }\n\n // Native fetch Response extended by extra information\n return {\n body: response.body,\n bodyUsed: response.bodyUsed,\n formData: response.formData,\n ok: response.ok,\n redirected: response.redirected,\n type: response.type,\n url: response.url,\n status: response.status,\n statusText: response.statusText,\n\n blob: response.blob.bind(response),\n json: response.json.bind(response),\n text: response.text.bind(response),\n clone: response.clone.bind(response),\n arrayBuffer: response.arrayBuffer.bind(response),\n\n // Extend with extra information\n error,\n data,\n headers: processHeaders(response.headers),\n config: requestConfig,\n };\n };\n\n return {\n getInstance,\n buildConfig,\n config,\n request,\n };\n}\n","import type {\n RequestConfig,\n FetchResponse,\n CreatedCustomFetcherInstance,\n} from './types/request-handler';\nimport type {\n ApiHandlerConfig,\n ApiHandlerMethods,\n ApiHandlerReturnType,\n APIResponse,\n QueryParamsOrBody,\n UrlPathParams,\n} from './types/api-handler';\nimport { createRequestHandler } from './request-handler';\n\n/**\n * Creates an instance of API Handler.\n * It creates an API fetcher function using native fetch() or a custom fetcher if it is passed as \"fetcher\".\n * @url https://github.com/MattCCC/fetchff\n *\n * @param {Object} config - Configuration object for the API fetcher.\n * @param {string} config.apiUrl - The base URL for the API.\n * @param {Object} config.endpoints - An object containing endpoint definitions.\n * @param {number} config.timeout - You can set the timeout for particular request in milliseconds.\n * @param {number} config.cancellable - If true, the ongoing previous requests will be automatically cancelled.\n * @param {number} config.rejectCancelled - If true and request is set to cancellable, a cancelled request promise will be rejected. By default, instead of rejecting the promise, defaultResponse is returned.\n * @param {number} config.timeout - Request timeout\n * @param {number} config.dedupeTime - Time window, in milliseconds, during which identical requests are deduplicated (treated as single request).\n * @param {string} config.strategy - Error Handling Strategy\n * @param {string} config.flattenResponse - Whether to flatten response \"data\" object within \"data\" one\n * @param {*} config.defaultResponse - Default response when there is no data or when endpoint fails depending on the chosen strategy. It's \"null\" by default\n * @param {Object} [config.retry] - Options for retrying requests.\n * @param {number} [config.retry.retries=0] - Number of retry attempts. No retries by default.\n * @param {number} [config.retry.delay=1000] - Initial delay between retries in milliseconds.\n * @param {number} [config.retry.backoff=1.5] - Exponential backoff factor.\n * @param {number[]} [config.retry.retryOn=[502, 504, 408]] - HTTP status codes to retry on.\n * @param {RequestInterceptor|RequestInterceptor[]} [config.onRequest] - Optional request interceptor function or an array of functions.\n * These functions will be called with the request configuration object before the request is made. Can be used to modify or log the request configuration.\n * @param {ResponseInterceptor|ResponseInterceptor[]} [config.onResponse] - Optional response interceptor function or an array of functions.\n * These functions will be called with the response object after the response is received. an be used to modify or log the response data.\n * @param {Function} [config.onError] - Optional callback function for handling errors.\n * @param {Object} [config.headers] - Optional default headers to include in every request.\n * @param {Object} config.fetcher - The Custom Fetcher instance to use for making requests. It should expose create() and request() functions.\n * @param {*} config.logger - Instance of custom logger. Either class or an object similar to \"console\". Console is used by default.\n * @returns API handler functions and endpoints to call\n *\n * @example\n * // Define endpoint paths\n * const endpoints = {\n * getUser: '/user',\n * createPost: '/post',\n * };\n *\n * // Create the API fetcher with configuration\n * const api = createApiFetcher({\n * endpoints,\n * apiUrl: 'https://example.com/api',\n * onError(error) {\n * console.log('Request failed', error);\n * },\n * headers: {\n * 'my-auth-key': 'example-auth-key-32rjjfa',\n * },\n * });\n *\n * // Fetch user data\n * const response = await api.getUser({ userId: 1, ratings: [1, 2] })\n */\nfunction createApiFetcher<\n EndpointsMethods extends object,\n EndpointsCfg = never,\n>(config: ApiHandlerConfig) {\n const endpoints = config.endpoints;\n const requestHandler = createRequestHandler(config);\n\n /**\n * Get Custom Fetcher Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Request Handler's Custom Fetcher Instance\n */\n function getInstance(): CreatedCustomFetcherInstance | null {\n return requestHandler.getInstance();\n }\n\n /**\n * Triggered when trying to use non-existent endpoints\n *\n * @param endpointName Endpoint Name\n * @returns {Promise}\n */\n function handleNonImplemented(endpointName: string): Promise {\n console.error(`Add ${endpointName} to 'endpoints'.`);\n\n return Promise.resolve(null);\n }\n\n /**\n * Handle Single API Request\n * It considers settings in following order: per-request settings, global per-endpoint settings, global settings.\n *\n * @param {string} endpointName - The name of the API endpoint to call.\n * @param {QueryParamsOrBody} [data={}] - Query parameters to include in the request.\n * @param {UrlPathParams} [urlPathParams={}] - URI parameters to include in the request.\n * @param {EndpointConfig} [requestConfig={}] - Additional configuration for the request.\n * @returns {Promise} - A promise that resolves with the response from the API provider.\n */\n async function request(\n endpointName: keyof EndpointsMethods | string,\n data: QueryParamsOrBody = {},\n urlPathParams: UrlPathParams = {},\n requestConfig: RequestConfig = {},\n ): Promise> {\n // Use global per-endpoint settings\n const endpointConfig = endpoints[endpointName as string];\n\n const responseData = await requestHandler.request(\n endpointConfig.url,\n data,\n {\n ...(endpointConfig || {}),\n ...requestConfig,\n urlPathParams,\n },\n );\n\n return responseData;\n }\n\n /**\n * Maps all API requests using native Proxy\n *\n * @param {*} prop Caller\n */\n function get(prop: string) {\n if (prop in apiHandler) {\n return apiHandler[\n prop as unknown as keyof ApiHandlerMethods\n ];\n }\n\n // Prevent handler from triggering non-existent endpoints\n if (!endpoints[prop]) {\n return handleNonImplemented.bind(null, prop);\n }\n\n return apiHandler.request.bind(null, prop);\n }\n\n const apiHandler: ApiHandlerMethods = {\n config,\n endpoints,\n requestHandler,\n getInstance,\n request,\n };\n\n return new Proxy(apiHandler, {\n get: (_target, prop: string) => get(prop),\n }) as ApiHandlerReturnType;\n}\n\nexport { createApiFetcher };\n","import { createRequestHandler } from './request-handler';\nimport type { APIResponse, FetchResponse, RequestHandlerConfig } from './types';\n\n/**\n * Simple wrapper for request fetching.\n * It abstracts the creation of RequestHandler, making it easy to perform API requests.\n *\n * @param {string | URL | globalThis.Request} url - Request URL.\n * @param {RequestHandlerConfig} config - Configuration object for the request handler.\n * @returns {Promise>} Response Data.\n */\nexport async function fetchf(\n url: string,\n config: RequestHandlerConfig = {},\n): Promise> {\n return createRequestHandler(config).request(url, null, config);\n}\n\nexport * from './types';\nexport * from './api-handler';\n"],"mappings":"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","names":["applyInterceptor","object","interceptors","value","interceptor","ResponseErr","message","requestInfo","response","__publicField","APPLICATION_JSON","CONTENT_TYPE","UNDEFINED","OBJECT","STRING","ABORT_ERROR","TIMEOUT_ERROR","CANCELLED_ERROR","GET","HEAD","isSearchParams","data","shallowSerialize","obj","result","key","sortObject","sortedObj","keys","i","len","appendQueryStringToUrl","baseUrl","queryString","appendQueryParams","url","params","isSearchParams","encodedQueryString","s","encode","add","k","v","buildParams","prefix","OBJECT","replaceUrlPathParams","urlPathParams","str","word","isJSONSerializable","value","UNDEFINED","STRING","proto","delayInvocation","ms","resolve","flattenData","data","processHeaders","headers","headersObject","deleteProperty","property","queue","addRequest","config","timeout","dedupeTime","isCancellable","isTimeoutEnabled","now","item","previousController","timeoutId","ABORT_ERROR","controller","error","TIMEOUT_ERROR","removeRequest","parseResponseData","response","_a","contentType","CONTENT_TYPE","data","APPLICATION_JSON","_e","_error","hash","str","i","len","char","cache","generateCacheKey","options","url","method","GET","headers","body","mode","credentials","redirect","referrer","integrity","headersString","shallowSerialize","sortObject","bodyString","hash","value","key","UNDEFINED","o","OBJECT","isCacheExpired","timestamp","maxStaleTime","getCache","cacheTime","entry","setCache","data","isLoading","defaultConfig","GET","APPLICATION_JSON","CONTENT_TYPE","createRequestHandler","config","handlerConfig","__spreadValues","customFetcher","requestInstance","getInstance","getConfig","reqConfig","name","UNDEFINED","logger","args","_a","buildConfig","url","data","method","isGetAlikeMethod","HEAD","dynamicUrl","replaceUrlPathParams","explicitParams","explicitBodyData","shouldTreatDataAsParams","body","credentials","deleteProperty","urlPath","appendQueryParams","baseURL","STRING","isSearchParams","isJSONSerializable","__spreadProps","processError","error","requestConfig","isRequestCancelled","applyInterceptor","outputErrorResponse","response","_isRequestCancelled","errorHandlingStrategy","rejectCancelled","outputResponse","ABORT_ERROR","CANCELLED_ERROR","request","_reqConfig","mergedConfig","fetcherConfig","timeout","cancellable","dedupeTime","pollingInterval","shouldStopPolling","cacheTime","cacheKey","_cacheKey","generateCacheKey","cacheBuster","cachedEntry","getCache","retries","delay","backoff","retryOn","shouldRetry","maxDelay","resetTimeout","attempt","pollingAttempt","waitTime","controller","addRequest","parseResponseData","ResponseErr","removeRequest","delayInvocation","output","skipCache","setCache","err","status","defaultResponse","flattenResponse","OBJECT","flattenData","processHeaders","createApiFetcher","config","endpoints","requestHandler","createRequestHandler","getInstance","handleNonImplemented","endpointName","request","data","urlPathParams","requestConfig","endpointConfig","__spreadProps","__spreadValues","get","prop","apiHandler","_target","fetchf","url","config","createRequestHandler"]} \ No newline at end of file +{"version":3,"sources":["../../src/interceptor-manager.ts","../../src/response-error.ts","../../src/constants.ts","../../src/utils.ts","../../src/queue-manager.ts","../../src/response-parser.ts","../../src/hash.ts","../../src/cache-manager.ts","../../src/request-handler.ts","../../src/api-handler.ts","../../src/index.ts"],"sourcesContent":["type InterceptorFunction = (object: T) => Promise;\n\n/**\n * Applies interceptors to the object. Interceptors can be a single function or an array of functions.\n *\n * @template T - Type of the object.\n * @template I - Type of interceptors.\n *\n * @param {T} object - The object to process.\n * @param {InterceptorFunction | InterceptorFunction[]} [interceptors] - Interceptor function(s).\n *\n * @returns {Promise} - Nothing as the function is non-idempotent.\n */\nexport async function applyInterceptor<\n T extends object,\n I = InterceptorFunction | InterceptorFunction[],\n>(object: T, interceptors?: I): Promise {\n if (!interceptors) {\n return;\n }\n\n if (typeof interceptors === 'function') {\n const value = await interceptors(object);\n\n if (value) {\n Object.assign(object, value);\n }\n } else if (Array.isArray(interceptors)) {\n for (const interceptor of interceptors) {\n const value = await interceptor(object);\n\n if (value) {\n Object.assign(object, value);\n }\n }\n }\n}\n","import type { FetchResponse, RequestConfig } from './types';\n\nexport class ResponseErr extends Error {\n response: FetchResponse;\n request: RequestConfig;\n config: RequestConfig;\n status: number;\n statusText: string;\n\n constructor(\n message: string,\n requestInfo: RequestConfig,\n response: FetchResponse,\n ) {\n super(message);\n\n this.name = 'ResponseError';\n this.message = message;\n this.status = response.status;\n this.statusText = response.statusText;\n this.request = requestInfo;\n this.config = requestInfo;\n this.response = response;\n }\n}\n","export const APPLICATION_CONTENT_TYPE = 'application/';\n\nexport const APPLICATION_JSON = APPLICATION_CONTENT_TYPE + 'json';\nexport const CONTENT_TYPE = 'Content-Type';\n\nexport const UNDEFINED = 'undefined';\nexport const OBJECT = 'object';\nexport const STRING = 'string';\n\nexport const ABORT_ERROR = 'AbortError';\nexport const TIMEOUT_ERROR = 'TimeoutError';\nexport const CANCELLED_ERROR = 'CanceledError';\n\nexport const GET = 'GET';\nexport const HEAD = 'HEAD';\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { OBJECT, STRING, UNDEFINED } from './constants';\nimport type {\n DefaultUrlParams,\n HeadersObject,\n QueryParams,\n UrlPathParams,\n} from './types';\n\nexport function isSearchParams(data: unknown): boolean {\n return data instanceof URLSearchParams;\n}\n\n/**\n * Determines if a value is a non-null object.\n *\n * @param {any} value - The value to check.\n * @returns {boolean} - True if the value is a non-null object.\n */\nexport function isObject(value: any): value is Record {\n return value !== null && typeof value === OBJECT;\n}\n\n/**\n * Shallowly serializes an object by converting its key-value pairs into a string representation.\n * This function does not recursively serialize nested objects.\n *\n * @param obj - The object to serialize.\n * @returns A string representation of the object's top-level properties.\n */\nexport function shallowSerialize(obj: Record): string {\n let result = '';\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n result += key + ':' + obj[key];\n }\n }\n\n return result;\n}\n\n/**\n * Sorts the keys of an object and returns a new object with sorted keys.\n *\n * This function is optimized for performance by minimizing the number of object operations\n * and using a single pass to create the sorted object.\n *\n * @param {Object} obj - The object to be sorted by keys.\n * @returns {Object} - A new object with keys sorted in ascending order.\n */\nexport function sortObject(obj: Record): object {\n const sortedObj = {} as Record;\n const keys = Object.keys(obj);\n\n keys.sort();\n\n for (let i = 0, len = keys.length; i < len; i++) {\n const key = keys[i];\n sortedObj[key] = obj[key];\n }\n\n return sortedObj;\n}\n\n/**\n * Appends a query string to a URL, ensuring proper handling of existing query parameters.\n *\n * @param baseUrl - The base URL to which the query string will be appended.\n * @param queryString - The encoded query string to append.\n * @returns The URL with the appended query string, or the original URL if no query string is provided.\n */\nfunction appendQueryStringToUrl(baseUrl: string, queryString: string): string {\n if (!queryString) {\n return baseUrl;\n }\n\n return baseUrl.includes('?')\n ? `${baseUrl}&${queryString}`\n : `${baseUrl}?${queryString}`;\n}\n\n/**\n * Appends query parameters to a given URL.\n *\n * @param {string} url - The base URL to which query parameters will be appended.\n * @param {QueryParams} params - An object containing the query parameters to append.\n * @returns {string} - The URL with the appended query parameters.\n */\nexport function appendQueryParams(url: string, params: QueryParams): string {\n if (!params) {\n return url;\n }\n\n // Check if `params` is an instance of URLSearchParams and bail early if it is\n if (isSearchParams(params)) {\n const encodedQueryString = params.toString();\n\n return appendQueryStringToUrl(url, encodedQueryString);\n }\n\n // This is exact copy of what JQ used to do. It works much better than URLSearchParams\n const s: string[] = [];\n const encode = encodeURIComponent;\n const add = (k: string, v: any) => {\n v = typeof v === 'function' ? v() : v;\n v = v === null ? '' : v === undefined ? '' : v;\n s[s.length] = encode(k) + '=' + encode(v);\n };\n\n const buildParams = (prefix: string, obj: any) => {\n let i: number, len: number, key: string;\n\n if (prefix) {\n if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n buildParams(\n prefix + '[' + (typeof obj[i] === OBJECT && obj[i] ? i : '') + ']',\n obj[i],\n );\n }\n } else if (typeof obj === OBJECT && obj !== null) {\n for (key in obj) {\n buildParams(prefix + '[' + key + ']', obj[key]);\n }\n } else {\n add(prefix, obj);\n }\n } else if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n add(obj[i].name, obj[i].value);\n }\n } else {\n for (key in obj) {\n buildParams(key, obj[key]);\n }\n }\n return s;\n };\n\n const queryStringParts = buildParams('', params).join('&');\n\n // Encode special characters as per RFC 3986, https://datatracker.ietf.org/doc/html/rfc3986\n const encodedQueryString = queryStringParts.replace(/%5B%5D/g, '[]'); // Keep '[]' for arrays\n\n return appendQueryStringToUrl(url, encodedQueryString);\n}\n\n/**\n * Replaces dynamic URI parameters in a URL string with values from the provided `urlPathParams` object.\n * Parameters in the URL are denoted by `:`, where `` is a key in `urlPathParams`.\n *\n * @param {string} url - The URL string containing placeholders in the format `:`.\n * @param {Object} urlPathParams - An object containing the parameter values to replace placeholders.\n * @param {string} urlPathParams.paramName - The value to replace the placeholder `:` in the URL.\n * @returns {string} - The URL string with placeholders replaced by corresponding values from `urlPathParams`.\n */\nexport function replaceUrlPathParams(\n url: string,\n urlPathParams: UrlPathParams,\n): string {\n if (!urlPathParams) {\n return url;\n }\n\n return url.replace(/:\\w+/g, (str): string => {\n const word = str.substring(1);\n\n if ((urlPathParams as DefaultUrlParams)[word]) {\n return String((urlPathParams as DefaultUrlParams)[word]);\n }\n\n return str;\n });\n}\n\n/**\n * Checks if a value is JSON serializable.\n *\n * JSON serializable values include:\n * - Primitive types: string, number, boolean, null\n * - Arrays\n * - Plain objects (i.e., objects without special methods)\n * - Values with a `toJSON` method\n *\n * @param {any} value - The value to check for JSON serializability.\n * @returns {boolean} - Returns `true` if the value is JSON serializable, otherwise `false`.\n */\nexport function isJSONSerializable(value: any): boolean {\n const t = typeof value;\n\n if (t === UNDEFINED || value === null) {\n return false;\n }\n\n if (t === STRING || t === 'number' || t === 'boolean') {\n return true;\n }\n\n if (Array.isArray(value)) {\n return true;\n }\n\n if (Buffer.isBuffer(value)) {\n return false;\n }\n\n if (value instanceof Date) {\n return false;\n }\n\n if (t === OBJECT) {\n const proto = Object.getPrototypeOf(value);\n\n // Check if the prototype is `Object.prototype` or `null` (plain object)\n if (proto === Object.prototype || proto === null) {\n return true;\n }\n\n // Check if the object has a toJSON method\n if (typeof value.toJSON === 'function') {\n return true;\n }\n }\n\n return false;\n}\n\nexport async function delayInvocation(ms: number): Promise {\n return new Promise((resolve) =>\n setTimeout(() => {\n return resolve(true);\n }, ms),\n );\n}\n\n/**\n * Recursively flattens the data object if it meets specific criteria.\n *\n * The method checks if the provided `data` is an object with exactly one property named `data`.\n * If so, it recursively flattens the `data` property. Otherwise, it returns the `data` as-is.\n *\n * @param {any} data - The data to be flattened. Can be of any type, including objects, arrays, or primitives.\n * @returns {any} - The flattened data if the criteria are met; otherwise, the original `data`.\n */\nexport function flattenData(data: any): any {\n if (\n data &&\n typeof data === OBJECT &&\n typeof data.data !== UNDEFINED &&\n Object.keys(data).length === 1\n ) {\n return flattenData(data.data);\n }\n\n return data;\n}\n\n/**\n * Processes headers and returns them as a normalized object.\n *\n * Handles both `Headers` instances and plain objects. Normalizes header keys to lowercase\n * as per RFC 2616 section 4.2.\n *\n * @param headers - The headers to process. Can be an instance of `Headers`, a plain object,\n * or `null`. If `null`, an empty object is returned.\n * @returns {HeadersObject} - A normalized headers object with lowercase keys.\n */\nexport function processHeaders(\n headers?: (HeadersObject & HeadersInit) | null | Headers,\n): HeadersObject {\n if (!headers) {\n return {};\n }\n\n const headersObject: HeadersObject = {};\n\n // Handle Headers object with entries() method\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n headersObject[key] = value;\n });\n } else if (typeof headers === OBJECT && headers !== null) {\n // Handle plain object\n for (const [key, value] of Object.entries(headers)) {\n // Normalize keys to lowercase as per RFC 2616 4.2\n // https://datatracker.ietf.org/doc/html/rfc2616#section-4.2\n headersObject[key.toLowerCase()] = value;\n }\n }\n\n return headersObject;\n}\n\n/**\n * Deletes a property from an object if it exists.\n *\n * @param obj - The object from which to delete the property.\n * @param property - The property to delete from the object.\n */\nexport function deleteProperty>(\n obj: T | null,\n property: keyof T,\n): void {\n if (obj && property in obj) {\n delete obj[property];\n }\n}\n","import { ABORT_ERROR, TIMEOUT_ERROR } from './constants';\nimport type { RequestConfig } from './types';\nimport type { QueueItem, RequestsQueue } from './types/queue-manager';\n\n/**\n * Queue Manager is responsible for managing and controlling the flow of concurrent or sequential requests. It handles:\n * - Request Queueing and Deduplication\n * - Request Timeout Handling\n * - Abort Controller Management and Request Cancellation\n * - Concurrency Control and Locking\n * - Request Lifecycle Management\n */\nconst queue: RequestsQueue = new Map();\n\n/**\n * Adds a request to the queue if it's not already being processed within the dedupeTime interval.\n *\n * @param {RequestConfig} config - The request configuration object.\n * @param {number} timeout - Timeout in milliseconds for the request.\n * @param {number} dedupeTime - Deduplication time in milliseconds.\n * @param {boolean} isCancellable - If true, then the previous request with same configuration should be aborted.\n * @param {boolean} isTimeoutEnabled - Whether timeout is enabled.\n * @returns {Promise} - A promise that resolves to an AbortController.\n */\nexport async function addRequest(\n config: RequestConfig,\n timeout: number | undefined,\n dedupeTime: number = 0,\n isCancellable: boolean = false,\n isTimeoutEnabled: boolean = true,\n): Promise {\n const now = Date.now();\n const item = queue.get(config);\n\n if (item) {\n const isCancellable = item[3];\n const previousController = item[0];\n const timeoutId = item[1];\n\n // If the request is already in the queue and within the dedupeTime, reuse the existing controller\n if (!isCancellable && now - item[2] < dedupeTime) {\n return previousController;\n }\n\n // If the request is too old, remove it and proceed to add a new one\n // Abort previous request, if applicable, and continue as usual\n if (isCancellable) {\n previousController.abort(\n new DOMException('Aborted due to new request', ABORT_ERROR),\n );\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n\n const controller = new AbortController();\n\n const timeoutId = isTimeoutEnabled\n ? setTimeout(() => {\n const error = new DOMException(\n `${config.url} aborted due to timeout`,\n TIMEOUT_ERROR,\n );\n\n removeRequest(config, error);\n }, timeout)\n : null;\n\n queue.set(config, [controller, timeoutId, now, isCancellable]);\n\n return controller;\n}\n\n/**\n * Removes a request from the queue and clears its timeout.\n *\n * @param config - The request configuration.\n * @param {boolean} error - Error payload so to force the request to abort.\n */\nexport async function removeRequest(\n config: RequestConfig,\n error: DOMException | null | string = null,\n): Promise {\n const item = queue.get(config);\n\n if (item) {\n const controller = item[0];\n const timeoutId = item[1];\n\n // If the request is not yet aborted, abort it with the provided error\n if (error && !controller.signal.aborted) {\n controller.abort(error);\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n}\n\n/**\n * Gets the AbortController for a request configuration.\n *\n * @param config - The request configuration.\n * @returns {AbortController | undefined} - The AbortController or undefined.\n */\nexport async function getController(\n config: RequestConfig,\n): Promise {\n const item = queue.get(config);\n\n return item?.[0];\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n APPLICATION_CONTENT_TYPE,\n APPLICATION_JSON,\n CONTENT_TYPE,\n} from './constants';\nimport type { DefaultResponse, FetchResponse } from './types/request-handler';\n\n/**\n * Parses the response data based on the Content-Type header.\n *\n * @param response - The Response object to parse.\n * @returns A Promise that resolves to the parsed data.\n */\nexport async function parseResponseData(\n response: FetchResponse,\n): Promise {\n // Bail early when body is empty\n if (!response?.body) {\n return null;\n }\n\n const contentType = String(\n (response as Response).headers?.get(CONTENT_TYPE) || '',\n ).split(';')[0]; // Correctly handle charset\n\n let data;\n\n try {\n if (\n contentType.includes(APPLICATION_JSON) ||\n contentType.includes('+json')\n ) {\n data = await response.json(); // Parse JSON response\n } else if (contentType.includes('multipart/form-data')) {\n data = await response.formData(); // Parse as FormData\n } else if (\n contentType.includes(APPLICATION_CONTENT_TYPE + 'octet-stream')\n ) {\n data = await response.blob(); // Parse as blob\n } else if (\n contentType.includes(APPLICATION_CONTENT_TYPE + 'x-www-form-urlencoded')\n ) {\n data = await response.formData(); // Handle URL-encoded forms\n } else if (contentType.includes('text/')) {\n data = await response.text(); // Parse as text\n } else {\n try {\n const responseClone = response.clone();\n\n // Handle edge case of no content type being provided... We assume JSON here.\n data = await responseClone.json();\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_e) {\n // Handle streams\n data = await response.text();\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_error) {\n // Parsing failed, fallback to null\n data = null;\n }\n\n return data;\n}\n","const PRIME_MULTIPLIER = 31;\n\n/**\n * Computes a hash value for a given string using the variant of djb2 hash function.\n * This hash function is non-cryptographic and designed for speed.\n * @author Daniel J. Bernstein (of djb2)\n *\n * @param str Input string to hash\n * @returns {string} Hash\n */\nexport function hash(str: string): string {\n let hash = 0;\n\n for (let i = 0, len = str.length; i < len; i++) {\n const char = str.charCodeAt(i);\n hash = (hash * PRIME_MULTIPLIER + char) | 0;\n }\n\n return String(hash);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { hash } from './hash';\nimport { fetchf } from './index';\nimport type { FetcherConfig } from './types/request-handler';\nimport type { CacheEntry } from './types/cache-manager';\nimport { GET, OBJECT, UNDEFINED } from './constants';\nimport { shallowSerialize, sortObject } from './utils';\n\nconst cache = new Map>();\n\n/**\n * Generates a cache key for a given URL and fetch options, ensuring that key factors\n * like method, headers, body, and other options are included in the cache key.\n * Headers and other objects are sorted by key to ensure consistent cache keys.\n *\n * @param options - The fetch options that may affect the request. The most important are:\n * @property {string} [method=\"GET\"] - The HTTP method (GET, POST, etc.).\n * @property {HeadersInit} [headers={}] - The request headers.\n * @property {BodyInit | null} [body=\"\"] - The body of the request (only for methods like POST, PUT).\n * @property {RequestMode} [mode=\"cors\"] - The mode for the request (e.g., cors, no-cors, include).\n * @property {RequestCredentials} [credentials=\"include\"] - Whether to include credentials like cookies.\n * @property {RequestCache} [cache=\"default\"] - The cache mode (e.g., default, no-store, reload).\n * @property {RequestRedirect} [redirect=\"follow\"] - How to handle redirects (e.g., follow, error, manual).\n * @property {string} [referrer=\"\"] - The referrer URL to send with the request.\n * @property {string} [integrity=\"\"] - Subresource integrity value (a cryptographic hash for resource validation).\n * @returns {string} - A unique cache key based on the URL and request options. Empty if cache is to be burst.\n *\n * @example\n * const cacheKey = generateCacheKey({\n * url: 'https://api.example.com/data',\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ name: 'Alice' }),\n * mode: 'cors',\n * credentials: 'include',\n * });\n * console.log(cacheKey);\n */\nexport function generateCacheKey(options: FetcherConfig): string {\n const {\n url = '',\n method = GET,\n headers = {},\n body = '',\n mode = 'cors',\n credentials = 'include',\n cache = 'default',\n redirect = 'follow',\n referrer = '',\n integrity = '',\n } = options;\n\n // Bail early if cache should be burst\n if (cache === 'reload') {\n return '';\n }\n\n // Sort headers and body + convert sorted to strings for hashing purposes\n // Native serializer is on avg. 3.5x faster than a Fast Hash or FNV-1a\n const headersString = shallowSerialize(sortObject(headers));\n\n let bodyString = '';\n\n // In majority of cases we do not cache body\n if (body !== null) {\n if (typeof body === 'string') {\n bodyString = hash(body);\n } else if (body instanceof FormData) {\n body.forEach((value, key) => {\n // Append key=value and '&' directly to the result\n bodyString += key + '=' + value + '&';\n });\n bodyString = hash(bodyString);\n } else if (\n (typeof Blob !== UNDEFINED && body instanceof Blob) ||\n (typeof File !== UNDEFINED && body instanceof File)\n ) {\n bodyString = 'BF' + body.size + body.type;\n } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n bodyString = 'AB' + body.byteLength;\n } else {\n const o = typeof body === OBJECT ? sortObject(body) : String(body);\n bodyString = hash(JSON.stringify(o));\n }\n }\n\n // Concatenate all key parts into a cache key string\n // Template literals are apparently slower\n return (\n method +\n url +\n mode +\n credentials +\n cache +\n redirect +\n referrer +\n integrity +\n headersString +\n bodyString\n );\n}\n\n/**\n * Checks if the cache entry is expired based on its timestamp and the maximum stale time.\n *\n * @param {number} timestamp - The timestamp of the cache entry.\n * @param {number} maxStaleTime - The maximum stale time in seconds.\n * @returns {boolean} - Returns true if the cache entry is expired, false otherwise.\n */\nfunction isCacheExpired(timestamp: number, maxStaleTime: number): boolean {\n if (!maxStaleTime) {\n return false;\n }\n\n return Date.now() - timestamp > maxStaleTime * 1000;\n}\n\n/**\n * Retrieves a cache entry if it exists and is not expired.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} cacheTime - Maximum time to cache entry.\n * @returns {CacheEntry | null} - The cache entry if it exists and is not expired, null otherwise.\n */\nexport function getCache(\n key: string,\n cacheTime: number,\n): CacheEntry | null {\n const entry = cache.get(key);\n\n if (entry) {\n if (!isCacheExpired(entry.timestamp, cacheTime)) {\n return entry;\n }\n\n cache.delete(key);\n }\n\n return null;\n}\n\n/**\n * Sets a new cache entry or updates an existing one.\n *\n * @param {string} key Cache key to utilize\n * @param {T} data - The data to be cached.\n * @param {boolean} isLoading - Indicates if the data is currently being fetched.\n */\nexport function setCache(\n key: string,\n data: T,\n isLoading: boolean = false,\n): void {\n cache.set(key, {\n data,\n isLoading,\n timestamp: Date.now(),\n });\n}\n\n/**\n * Revalidates a cache entry by fetching fresh data and updating the cache.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @returns {Promise} - A promise that resolves when the revalidation is complete.\n */\nexport async function revalidate(\n key: string,\n config: FetcherConfig,\n): Promise {\n try {\n // Fetch fresh data\n const newData = await fetchf(config.url, {\n ...config,\n cache: 'reload',\n });\n\n setCache(key, newData);\n } catch (error) {\n console.error(`Error revalidating ${config.url}:`, error);\n\n // Rethrow the error to forward it\n throw error;\n }\n}\n\n/**\n * Invalidates (deletes) a cache entry.\n *\n * @param {string} key Cache key to utilize\n */\nexport function deleteCache(key: string): void {\n cache.delete(key);\n}\n\n/**\n * Mutates a cache entry with new data and optionally revalidates it.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @param {T} newData - The new data to be cached.\n * @param {boolean} revalidateAfter - If true, triggers revalidation after mutation.\n */\nexport function mutate(\n key: string,\n config: FetcherConfig,\n newData: T,\n revalidateAfter: boolean = false,\n): void {\n setCache(key, newData);\n\n if (revalidateAfter) {\n revalidate(key, config);\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {\n DefaultResponse,\n RequestHandlerConfig,\n RequestConfig,\n Method,\n RetryOptions,\n FetchResponse,\n ResponseError,\n RequestHandlerReturnType,\n CreatedCustomFetcherInstance,\n FetcherConfig,\n} from './types/request-handler';\nimport type {\n BodyPayload,\n DefaultParams,\n DefaultPayload,\n DefaultUrlParams,\n QueryParams,\n} from './types/api-handler';\nimport { applyInterceptor } from './interceptor-manager';\nimport { ResponseErr } from './response-error';\nimport {\n appendQueryParams,\n isJSONSerializable,\n replaceUrlPathParams,\n delayInvocation,\n flattenData,\n processHeaders,\n deleteProperty,\n isSearchParams,\n} from './utils';\nimport { addRequest, removeRequest } from './queue-manager';\nimport {\n ABORT_ERROR,\n APPLICATION_JSON,\n CANCELLED_ERROR,\n CONTENT_TYPE,\n GET,\n HEAD,\n OBJECT,\n STRING,\n UNDEFINED,\n} from './constants';\nimport { parseResponseData } from './response-parser';\nimport { generateCacheKey, getCache, setCache } from './cache-manager';\n\nconst defaultConfig: RequestHandlerConfig = {\n method: GET,\n strategy: 'reject',\n timeout: 30000,\n dedupeTime: 1000,\n defaultResponse: null,\n headers: {\n Accept: APPLICATION_JSON + ', text/plain, */*',\n 'Accept-Encoding': 'gzip, deflate, br',\n [CONTENT_TYPE]: APPLICATION_JSON + ';charset=utf-8',\n },\n retry: {\n delay: 1000,\n maxDelay: 30000,\n resetTimeout: true,\n backoff: 1.5,\n\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status\n retryOn: [\n 408, // Request Timeout\n 409, // Conflict\n 425, // Too Early\n 429, // Too Many Requests\n 500, // Internal Server Error\n 502, // Bad Gateway\n 503, // Service Unavailable\n 504, // Gateway Timeout\n ],\n },\n};\n\n/**\n * Create Request Handler\n *\n * @param {RequestHandlerConfig} config - Configuration object for the request handler\n * @returns {Object} An object with methods for handling requests\n */\nexport function createRequestHandler(\n config: RequestHandlerConfig,\n): RequestHandlerReturnType {\n const handlerConfig: RequestHandlerConfig = {\n ...defaultConfig,\n ...config,\n };\n\n /**\n * Immediately create instance of custom fetcher if it is defined\n */\n const customFetcher = handlerConfig.fetcher;\n const requestInstance = customFetcher?.create(handlerConfig) || null;\n\n /**\n * Get Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Provider's instance\n */\n const getInstance = (): CreatedCustomFetcherInstance | null => {\n return requestInstance;\n };\n\n /**\n * Gets a configuration value from `reqConfig`, defaulting to `handlerConfig` if not present.\n *\n * @param {RequestConfig} reqConfig - Request configuration object.\n * @param {keyof RequestConfig} name - Key of the configuration value.\n * @returns {T} - The configuration value.\n */\n const getConfig = (\n reqConfig: RequestConfig,\n name: keyof RequestConfig,\n ): T => {\n return typeof reqConfig[name] !== UNDEFINED\n ? reqConfig[name]\n : handlerConfig[name];\n };\n\n /**\n * Logs messages or errors using the configured logger's `warn` method.\n *\n * @param {...(string | ResponseError)} args - Messages or errors to log.\n */\n const logger = (...args: (string | ResponseError)[]): void => {\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn(...args);\n }\n };\n\n /**\n * Build request configuration\n *\n * @param {string} url - Request url\n * @param {RequestConfig} reqConfig - Request config passed when making the request\n * @returns {RequestConfig} - Provider's instance\n */\n const buildConfig = (\n url: string,\n reqConfig: RequestConfig,\n ): FetcherConfig => {\n const method = getConfig(\n reqConfig,\n 'method',\n ).toUpperCase() as Method;\n const isGetAlikeMethod = method === GET || method === HEAD;\n\n const dynamicUrl = replaceUrlPathParams(\n url,\n getConfig(reqConfig, 'urlPathParams'),\n );\n\n // The explicitly passed \"params\"\n const explicitParams = getConfig(reqConfig, 'params');\n\n // The explicitly passed \"body\" or \"data\"\n const explicitBodyData: BodyPayload =\n getConfig(reqConfig, 'body') || getConfig(reqConfig, 'data');\n\n // Final body data\n let body: RequestConfig['data'];\n\n // Only applicable for request methods 'PUT', 'POST', 'DELETE', and 'PATCH'\n if (!isGetAlikeMethod) {\n body = explicitBodyData;\n }\n\n // Native fetch compatible settings\n const isWithCredentials = getConfig(reqConfig, 'withCredentials');\n\n const credentials = isWithCredentials\n ? 'include'\n : getConfig(reqConfig, 'credentials');\n\n const urlPath = explicitParams\n ? appendQueryParams(dynamicUrl, explicitParams)\n : dynamicUrl;\n const isFullUrl = urlPath.includes('://');\n const baseURL = isFullUrl\n ? ''\n : getConfig(reqConfig, 'baseURL') ||\n getConfig(reqConfig, 'apiUrl');\n\n // Automatically stringify request body, if possible and when not dealing with strings\n if (\n body &&\n typeof body !== STRING &&\n !isSearchParams(body) &&\n isJSONSerializable(body)\n ) {\n body = JSON.stringify(body);\n }\n\n return {\n ...reqConfig,\n credentials,\n body,\n method,\n\n url: baseURL + urlPath,\n };\n };\n\n /**\n * Process global Request Error\n *\n * @param {ResponseError} error Error instance\n * @param {RequestConfig} requestConfig Per endpoint request config\n * @returns {Promise}\n */\n const processError = async (\n error: ResponseError,\n requestConfig: RequestConfig,\n ): Promise => {\n if (!isRequestCancelled(error)) {\n logger('API ERROR', error);\n }\n\n // Local interceptors\n await applyInterceptor(error, requestConfig?.onError);\n\n // Global interceptors\n await applyInterceptor(error, handlerConfig?.onError);\n };\n\n /**\n * Output default response in case of an error, depending on chosen strategy\n *\n * @param {ResponseError} error - Error instance\n * @param {FetchResponse | null} response - Response. It may be \"null\" in case of request being aborted.\n * @param {RequestConfig} requestConfig - Per endpoint request config\n * @returns {FetchResponse} Response together with the error object\n */\n const outputErrorResponse = async (\n error: ResponseError,\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n ): Promise => {\n const _isRequestCancelled = isRequestCancelled(error);\n const errorHandlingStrategy = getConfig(requestConfig, 'strategy');\n const rejectCancelled = getConfig(\n requestConfig,\n 'rejectCancelled',\n );\n\n // By default cancelled requests aren't rejected (softFail strategy)\n if (!(_isRequestCancelled && !rejectCancelled)) {\n // Hang the promise\n if (errorHandlingStrategy === 'silent') {\n await new Promise(() => null);\n }\n // Reject the promise\n else if (errorHandlingStrategy === 'reject') {\n return Promise.reject(error);\n }\n }\n\n return outputResponse(response, requestConfig, error);\n };\n\n /**\n * Output error response depending on chosen strategy\n *\n * @param {ResponseError} error Error instance\n * @returns {boolean} True if request is aborted\n */\n const isRequestCancelled = (error: ResponseError): boolean => {\n return error.name === ABORT_ERROR || error.name === CANCELLED_ERROR;\n };\n\n /**\n * Handle Request depending on used strategy\n *\n * @param {string} url - Request url\n * @param {RequestConfig} reqConfig - Request config\n * @throws {ResponseError}\n * @returns {Promise>} Response Data\n */\n const request = async <\n ResponseData = DefaultResponse,\n QueryParams = DefaultParams,\n PathParams = DefaultUrlParams,\n RequestBody = DefaultPayload,\n >(\n url: string,\n reqConfig: RequestConfig<\n ResponseData,\n QueryParams,\n PathParams,\n RequestBody\n > | null = null,\n ): Promise> => {\n const _reqConfig = reqConfig || {};\n const mergedConfig = {\n ...handlerConfig,\n ..._reqConfig,\n } as RequestConfig;\n\n let response: FetchResponse | null = null;\n const fetcherConfig = buildConfig(url, mergedConfig);\n\n const {\n timeout,\n cancellable,\n dedupeTime,\n pollingInterval,\n shouldStopPolling,\n cacheTime,\n cacheKey,\n } = mergedConfig;\n\n // Prevent performance overhead of cache access\n let _cacheKey: string;\n\n if (cacheKey) {\n _cacheKey = cacheKey(fetcherConfig);\n } else {\n _cacheKey = generateCacheKey(fetcherConfig);\n }\n\n if (cacheTime && _cacheKey) {\n const cacheBuster = mergedConfig.cacheBuster;\n\n if (!cacheBuster || !cacheBuster(fetcherConfig)) {\n const cachedEntry = getCache>(\n _cacheKey,\n cacheTime,\n );\n\n if (cachedEntry) {\n // Serve stale data from cache\n return cachedEntry.data;\n }\n }\n }\n\n const {\n retries = 0,\n delay,\n backoff,\n retryOn,\n shouldRetry,\n maxDelay,\n resetTimeout,\n } = mergedConfig.retry as Required;\n\n let attempt = 0;\n let pollingAttempt = 0;\n let waitTime: number = delay;\n const _retries = retries > 0 ? retries : 0;\n\n while (attempt <= _retries) {\n try {\n // Add the request to the queue. Make sure to handle deduplication, cancellation, timeouts in accordance to retry settings\n const controller = await addRequest(\n fetcherConfig,\n timeout,\n dedupeTime,\n cancellable,\n // Reset timeouts by default or when retries are ON\n !!(timeout && (!_retries || resetTimeout)),\n );\n\n // Shallow copy to ensure basic idempotency\n const requestConfig: RequestConfig = {\n signal: controller.signal,\n ...fetcherConfig,\n };\n\n // Local interceptors\n await applyInterceptor(requestConfig, _reqConfig?.onRequest);\n\n // Global interceptors\n await applyInterceptor(requestConfig, handlerConfig?.onRequest);\n\n if (customFetcher !== null && requestInstance !== null) {\n response = await requestInstance.request(requestConfig);\n } else {\n response = (await fetch(\n requestConfig.url as string,\n requestConfig as RequestInit,\n )) as FetchResponse;\n }\n\n // Add more information to response object\n if (response instanceof Response) {\n response.config = requestConfig;\n response.data = await parseResponseData(response);\n\n // Check if the response status is not outside the range 200-299 and if so, output error\n if (!response.ok) {\n throw new ResponseErr(\n `${requestConfig.url} failed! Status: ${response.status || null}`,\n requestConfig,\n response,\n );\n }\n }\n\n // Local interceptors\n await applyInterceptor(response, _reqConfig?.onResponse);\n\n // Global interceptors\n await applyInterceptor(response, handlerConfig?.onResponse);\n\n removeRequest(fetcherConfig);\n\n // Polling logic\n if (\n pollingInterval &&\n (!shouldStopPolling || !shouldStopPolling(response, pollingAttempt))\n ) {\n // Restart the main retry loop\n pollingAttempt++;\n\n logger(`Polling attempt ${pollingAttempt}...`);\n\n await delayInvocation(pollingInterval);\n\n continue;\n }\n\n // If polling is not required, or polling attempts are exhausted\n const output = outputResponse(response, requestConfig);\n\n if (cacheTime && _cacheKey) {\n const skipCache = requestConfig.skipCache;\n\n if (!skipCache || !skipCache(output, requestConfig)) {\n setCache(_cacheKey, output);\n }\n }\n\n return output;\n } catch (err) {\n const error = err as ResponseErr;\n const status = error?.response?.status || error?.status || 0;\n\n if (\n attempt === retries ||\n !(!shouldRetry || (await shouldRetry(error, attempt))) ||\n !retryOn?.includes(status)\n ) {\n await processError(error, fetcherConfig);\n\n removeRequest(fetcherConfig);\n\n return outputErrorResponse(\n error,\n response,\n fetcherConfig,\n );\n }\n\n logger(`Attempt ${attempt + 1} failed. Retry in ${waitTime}ms.`);\n\n await delayInvocation(waitTime);\n\n waitTime *= backoff;\n waitTime = Math.min(waitTime, maxDelay);\n attempt++;\n }\n }\n\n return outputResponse(response, fetcherConfig);\n };\n\n /**\n * Output response\n *\n * @param Response. It may be \"null\" in case of request being aborted.\n * @param {RequestConfig} requestConfig - Request config\n * @param error - whether the response is erroneous\n * @returns {FetchResponse} Response data\n */\n const outputResponse = (\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n error: ResponseError | null = null,\n ): FetchResponse => {\n const defaultResponse = getConfig(requestConfig, 'defaultResponse');\n\n // This may happen when request is cancelled.\n if (!response) {\n return {\n ok: false,\n // Enhance the response with extra information\n error,\n data: defaultResponse,\n headers: null,\n config: requestConfig,\n } as unknown as FetchResponse;\n }\n\n // Clean up the error object\n deleteProperty(error, 'response');\n deleteProperty(error, 'request');\n deleteProperty(error, 'config');\n\n let data = response?.data;\n\n // Set the default response if the provided data is an empty object\n if (\n data === undefined ||\n data === null ||\n (typeof data === OBJECT && Object.keys(data).length === 0)\n ) {\n data = defaultResponse;\n }\n\n // Return flattened response immediately\n const flattenResponse = getConfig(\n requestConfig,\n 'flattenResponse',\n );\n\n if (flattenResponse) {\n response.data = flattenData(data);\n }\n\n // If it's a custom fetcher, and it does not return any Response instance, it may have its own internal handler\n if (!(response instanceof Response)) {\n return response;\n }\n\n // Native fetch Response extended by extra information\n return {\n body: response.body,\n bodyUsed: response.bodyUsed,\n formData: response.formData,\n ok: response.ok,\n redirected: response.redirected,\n type: response.type,\n url: response.url,\n status: response.status,\n statusText: response.statusText,\n\n blob: response.blob.bind(response),\n json: response.json.bind(response),\n text: response.text.bind(response),\n clone: response.clone.bind(response),\n arrayBuffer: response.arrayBuffer.bind(response),\n\n // Enhance the response with extra information\n error,\n data,\n headers: processHeaders(response.headers),\n config: requestConfig,\n };\n };\n\n return {\n getInstance,\n buildConfig,\n config,\n request,\n };\n}\n","import type {\n RequestConfig,\n FetchResponse,\n DefaultResponse,\n CreatedCustomFetcherInstance,\n} from './types/request-handler';\nimport type {\n ApiHandlerConfig,\n ApiHandlerDefaultMethods,\n ApiHandlerMethods,\n DefaultPayload,\n FallbackValue,\n FinalParams,\n FinalResponse,\n QueryParams,\n RequestConfigUrlRequired,\n UrlPathParams,\n} from './types/api-handler';\nimport { createRequestHandler } from './request-handler';\n\n/**\n * Creates an instance of API Handler.\n * It creates an API fetcher function using native fetch() or a custom fetcher if it is passed as \"fetcher\".\n * @url https://github.com/MattCCC/fetchff\n *\n * @param {Object} config - Configuration object for the API fetcher.\n * @param {string} config.apiUrl - The base URL for the API.\n * @param {Object} config.endpoints - An object containing endpoint definitions.\n * @param {number} config.timeout - You can set the timeout for particular request in milliseconds.\n * @param {number} config.cancellable - If true, the ongoing previous requests will be automatically cancelled.\n * @param {number} config.rejectCancelled - If true and request is set to cancellable, a cancelled request promise will be rejected. By default, instead of rejecting the promise, defaultResponse is returned.\n * @param {number} config.timeout - Request timeout\n * @param {number} config.dedupeTime - Time window, in milliseconds, during which identical requests are deduplicated (treated as single request).\n * @param {string} config.strategy - Error Handling Strategy\n * @param {string} config.flattenResponse - Whether to flatten response \"data\" object within \"data\". It works only if the response structure includes a single data property.\n * @param {*} config.defaultResponse - Default response when there is no data or when endpoint fails depending on the chosen strategy. It's \"null\" by default\n * @param {Object} [config.retry] - Options for retrying requests.\n * @param {number} [config.retry.retries=0] - Number of retry attempts. No retries by default.\n * @param {number} [config.retry.delay=1000] - Initial delay between retries in milliseconds.\n * @param {number} [config.retry.backoff=1.5] - Exponential backoff factor.\n * @param {number[]} [config.retry.retryOn=[502, 504, 408]] - HTTP status codes to retry on.\n * @param {RequestInterceptor|RequestInterceptor[]} [config.onRequest] - Optional request interceptor function or an array of functions.\n * These functions will be called with the request configuration object before the request is made. Can be used to modify or log the request configuration.\n * @param {ResponseInterceptor|ResponseInterceptor[]} [config.onResponse] - Optional response interceptor function or an array of functions.\n * These functions will be called with the response object after the response is received. an be used to modify or log the response data.\n * @param {Function} [config.onError] - Optional callback function for handling errors.\n * @param {Object} [config.headers] - Optional default headers to include in every request.\n * @param {Object} config.fetcher - The Custom Fetcher instance to use for making requests. It should expose create() and request() functions.\n * @param {*} config.logger - Instance of custom logger. Either class or an object similar to \"console\". Console is used by default.\n * @returns API handler functions and endpoints to call\n *\n * @example\n * // Define endpoint paths\n * const endpoints = {\n * getUser: '/user',\n * createPost: '/post',\n * };\n *\n * // Create the API fetcher with configuration\n * const api = createApiFetcher({\n * endpoints,\n * apiUrl: 'https://example.com/api',\n * onError(error) {\n * console.log('Request failed', error);\n * },\n * headers: {\n * 'my-auth-key': 'example-auth-key-32rjjfa',\n * },\n * });\n *\n * // Fetch user data\n * const response = await api.getUser({ userId: 1, ratings: [1, 2] })\n */\nfunction createApiFetcher<\n EndpointsMethods extends object,\n EndpointsSettings = never,\n>(config: ApiHandlerConfig) {\n const endpoints = config.endpoints;\n const requestHandler = createRequestHandler(config);\n\n /**\n * Get Custom Fetcher Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Request Handler's Custom Fetcher Instance\n */\n function getInstance(): CreatedCustomFetcherInstance | null {\n return requestHandler.getInstance();\n }\n\n /**\n * Triggered when trying to use non-existent endpoints\n *\n * @param endpointName Endpoint Name\n * @returns {Promise}\n */\n function handleNonImplemented(endpointName: string): Promise {\n console.error(`Add ${endpointName} to 'endpoints'.`);\n\n return Promise.resolve(null);\n }\n\n /**\n * Handle Single API Request\n * It considers settings in following order: per-request settings, global per-endpoint settings, global settings.\n *\n * @param {keyof EndpointsMethods | string} endpointName - The name of the API endpoint to call.\n * @param {EndpointConfig} [requestConfig={}] - Additional configuration for the request.\n * @returns {Promise>} - A promise that resolves with the response from the API provider.\n */\n async function request<\n ResponseData = never,\n QueryParams_ = never,\n UrlParams = never,\n RequestBody = never,\n >(\n endpointName: keyof EndpointsMethods | string,\n requestConfig: RequestConfig<\n FinalResponse,\n FinalParams,\n FinalParams,\n FallbackValue\n > = {},\n ): Promise>> {\n // Use global per-endpoint settings\n const endpointConfig =\n endpoints[endpointName] ||\n ({ url: endpointName as string } as RequestConfigUrlRequired);\n\n const responseData = await requestHandler.request<\n FinalResponse,\n FinalParams,\n FinalParams,\n FallbackValue\n >(endpointConfig.url, {\n ...endpointConfig,\n ...requestConfig,\n });\n\n return responseData;\n }\n\n const apiHandler: ApiHandlerDefaultMethods = {\n config,\n endpoints,\n requestHandler,\n getInstance,\n request,\n };\n\n /**\n * Maps all API requests using native Proxy\n *\n * @param {*} prop Caller\n */\n return new Proxy>(\n apiHandler as ApiHandlerMethods,\n {\n get(_target, prop: string) {\n if (prop in apiHandler) {\n return apiHandler[prop as unknown as keyof typeof apiHandler];\n }\n\n // Prevent handler from triggering non-existent endpoints\n if (endpoints[prop]) {\n return apiHandler.request.bind(null, prop);\n }\n\n return handleNonImplemented.bind(null, prop);\n },\n },\n );\n}\n\nexport { createApiFetcher };\n","import { createRequestHandler } from './request-handler';\nimport type {\n DefaultResponse,\n FetchResponse,\n RequestHandlerConfig,\n} from './types';\n\n/**\n * Simple wrapper for request fetching.\n * It abstracts the creation of RequestHandler, making it easy to perform API requests.\n *\n * @param {string | URL | globalThis.Request} url - Request URL.\n * @param {RequestHandlerConfig} config - Configuration object for the request handler.\n * @returns {Promise>} Response Data.\n */\nexport async function fetchf(\n url: string,\n config: RequestHandlerConfig = {},\n): Promise> {\n return createRequestHandler(config).request(url, config);\n}\n\nexport * from './types';\nexport * from './api-handler';\n"],"mappings":"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","names":["applyInterceptor","object","interceptors","value","interceptor","ResponseErr","message","requestInfo","response","__publicField","APPLICATION_CONTENT_TYPE","APPLICATION_JSON","CONTENT_TYPE","UNDEFINED","OBJECT","STRING","ABORT_ERROR","TIMEOUT_ERROR","CANCELLED_ERROR","GET","HEAD","isSearchParams","data","shallowSerialize","obj","result","key","sortObject","sortedObj","keys","i","len","appendQueryStringToUrl","baseUrl","queryString","appendQueryParams","url","params","isSearchParams","encodedQueryString","s","encode","add","k","v","buildParams","prefix","OBJECT","replaceUrlPathParams","urlPathParams","str","word","isJSONSerializable","value","UNDEFINED","STRING","proto","delayInvocation","ms","resolve","flattenData","data","processHeaders","headers","headersObject","deleteProperty","property","queue","addRequest","config","timeout","dedupeTime","isCancellable","isTimeoutEnabled","now","item","previousController","timeoutId","ABORT_ERROR","controller","error","TIMEOUT_ERROR","removeRequest","parseResponseData","response","_a","contentType","CONTENT_TYPE","data","APPLICATION_JSON","APPLICATION_CONTENT_TYPE","_e","_error","hash","str","i","len","char","cache","generateCacheKey","options","url","method","GET","headers","body","mode","credentials","redirect","referrer","integrity","headersString","shallowSerialize","sortObject","bodyString","hash","value","key","UNDEFINED","o","OBJECT","isCacheExpired","timestamp","maxStaleTime","getCache","cacheTime","entry","setCache","data","isLoading","defaultConfig","GET","APPLICATION_JSON","CONTENT_TYPE","createRequestHandler","config","handlerConfig","customFetcher","requestInstance","getInstance","getConfig","reqConfig","name","UNDEFINED","logger","args","_a","buildConfig","url","method","isGetAlikeMethod","HEAD","dynamicUrl","replaceUrlPathParams","explicitParams","explicitBodyData","body","credentials","urlPath","appendQueryParams","baseURL","STRING","isSearchParams","isJSONSerializable","processError","error","requestConfig","isRequestCancelled","applyInterceptor","outputErrorResponse","response","_isRequestCancelled","errorHandlingStrategy","rejectCancelled","outputResponse","ABORT_ERROR","CANCELLED_ERROR","request","_reqConfig","mergedConfig","fetcherConfig","timeout","cancellable","dedupeTime","pollingInterval","shouldStopPolling","cacheTime","cacheKey","_cacheKey","generateCacheKey","cacheBuster","cachedEntry","getCache","retries","delay","backoff","retryOn","shouldRetry","maxDelay","resetTimeout","attempt","pollingAttempt","waitTime","_retries","addRequest","parseResponseData","ResponseErr","removeRequest","delayInvocation","output","skipCache","setCache","err","status","defaultResponse","deleteProperty","data","OBJECT","flattenData","processHeaders","createApiFetcher","config","endpoints","requestHandler","createRequestHandler","getInstance","handleNonImplemented","endpointName","request","requestConfig","endpointConfig","apiHandler","_target","prop","fetchf","url","config","createRequestHandler"]} \ No newline at end of file diff --git a/dist/node/index.js b/dist/node/index.js index 8bd09d2..45ccec3 100644 --- a/dist/node/index.js +++ b/dist/node/index.js @@ -1,2 +1,2 @@ -"use strict";var _=Object.defineProperty,Fe=Object.defineProperties,qe=Object.getOwnPropertyDescriptor,He=Object.getOwnPropertyDescriptors,Ne=Object.getOwnPropertyNames,le=Object.getOwnPropertySymbols;var ue=Object.prototype.hasOwnProperty,Se=Object.prototype.propertyIsEnumerable;var ee=(e,t,n)=>t in e?_(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,C=(e,t)=>{for(var n in t||(t={}))ue.call(t,n)&&ee(e,n,t[n]);if(le)for(var n of le(t))Se.call(t,n)&&ee(e,n,t[n]);return e},B=(e,t)=>Fe(e,He(t));var Be=(e,t)=>{for(var n in t)_(e,n,{get:t[n],enumerable:!0})},Me=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of Ne(t))!ue.call(e,s)&&s!==n&&_(e,s,{get:()=>t[s],enumerable:!(o=qe(t,s))||o.enumerable});return e};var Ue=e=>Me(_({},"__esModule",{value:!0}),e);var A=(e,t,n)=>ee(e,typeof t!="symbol"?t+"":t,n);var Le={};Be(Le,{createApiFetcher:()=>_e,fetchf:()=>xe});module.exports=Ue(Le);async function O(e,t){if(t){if(typeof t=="function"){let n=await t(e);n&&Object.assign(e,n)}else if(Array.isArray(t))for(let n of t){let o=await n(e);o&&Object.assign(e,o)}}}var L=class extends Error{constructor(n,o,s){super(n);A(this,"response");A(this,"request");A(this,"config");A(this,"status");A(this,"statusText");this.name="ResponseError",this.message=n,this.status=s.status,this.statusText=s.statusText,this.request=o,this.config=o,this.response=s}};var M="application/json",j="Content-Type",x="undefined",b="object",J="string",$="AbortError",fe="TimeoutError",pe="CanceledError",U="GET",de="HEAD";function te(e){return e instanceof URLSearchParams}function Re(e){let t="";for(let n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t+=n+":"+e[n]);return t}function ne(e){let t={},n=Object.keys(e);n.sort();for(let o=0,s=n.length;o{a=typeof a=="function"?a():a,a=a===null||a===void 0?"":a,n[n.length]=o(l)+"="+o(a)},i=(l,a)=>{let d,g,y;if(l)if(Array.isArray(a))for(d=0,g=a.length;d{let o=n.substring(1);return String(t[o]?t[o]:n)}):e}function ge(e){let t=typeof e;if(t===x||e===null)return!1;if(t===J||t==="number"||t==="boolean"||Array.isArray(e))return!0;if(Buffer.isBuffer(e)||e instanceof Date)return!1;if(t===b){let n=Object.getPrototypeOf(e);if(n===Object.prototype||n===null||typeof e.toJSON=="function")return!0}return!1}async function re(e){return new Promise(t=>setTimeout(()=>t(!0),e))}function se(e){return e&&typeof e===b&&typeof e.data!==x&&Object.keys(e).length===1?se(e.data):e}function Pe(e){if(!e)return{};let t={};if(e instanceof Headers)e.forEach((n,o)=>{t[o]=n});else if(typeof e===b&&e!==null)for(let[n,o]of Object.entries(e))t[n.toLowerCase()]=o;return t}function F(e,t){e&&t in e&&delete e[t]}var k=new Map;async function Ee(e,t,n=0,o=!1,s=!0){let i=Date.now(),h=k.get(e);if(h){let a=h[3],d=h[0],g=h[1];if(!a&&i-h[2]{let a=new DOMException(`${e.url} aborted due to timeout`,fe);v(e,a)},t):null;return k.set(e,[m,l,i,o]),m}async function v(e,t=null){let n=k.get(e);if(n){let o=n[0],s=n[1];t&&!o.signal.aborted&&o.abort(t),s!==null&&clearTimeout(s),k.delete(e)}}async function Ce(e){var o;if(!(e!=null&&e.body))return null;let t=String(((o=e.headers)==null?void 0:o.get(j))||"").split(";")[0],n;try{if(t.includes(M)||t.includes("+json"))n=await e.json();else if(t.includes("multipart/form-data"))n=await e.formData();else if(t.includes("application/octet-stream"))n=await e.blob();else if(t.includes("application/x-www-form-urlencoded"))n=await e.formData();else if(t.includes("text/"))n=await e.text();else try{n=await e.clone().json()}catch(s){n=await e.text()}}catch(s){n=null}return n}function G(e){let t=0;for(let n=0,o=e.length;n{y+=u+"="+r+"&"}),y=G(y);else if(typeof Blob!==x&&s instanceof Blob||typeof File!==x&&s instanceof File)y="BF"+s.size+s.type;else if(s instanceof ArrayBuffer||ArrayBuffer.isView(s))y="AB"+s.byteLength;else{let r=typeof s===b?ne(s):String(s);y=G(JSON.stringify(r))}return n+t+i+h+m+l+a+d+g+y}function ke(e,t){return t?Date.now()-e>t*1e3:!1}function be(e,t){let n=oe.get(e);if(n){if(!ke(n.timestamp,t))return n;oe.delete(e)}return null}function we(e,t,n=!1){oe.set(e,{data:t,isLoading:n,timestamp:Date.now()})}var Qe={method:U,strategy:"reject",timeout:3e4,dedupeTime:1e3,defaultResponse:null,headers:{Accept:M+", text/plain, */*","Accept-Encoding":"gzip, deflate, br",[j]:M+";charset=utf-8"},retry:{delay:1e3,maxDelay:3e4,resetTimeout:!0,backoff:1.5,retryOn:[408,409,425,429,500,502,503,504]}};function z(e){let t=C(C({},Qe),e),n=t.fetcher,o=(n==null?void 0:n.create(t))||null,s=()=>o,i=(r,u)=>typeof r[u]!==x?r[u]:t[u],h=(...r)=>{var u;(u=t.logger)!=null&&u.warn&&t.logger.warn(...r)},m=(r,u,f)=>{let R=i(f,"method").toUpperCase(),P=R===U||R===de,c=he(r,i(f,"urlPathParams")),E=i(f,"params"),q=i(f,"body")||i(f,"data"),K=!!(u&&(P||q)),T;P||(T=q||u);let Q=i(f,"withCredentials")?"include":i(f,"credentials");F(f,"data"),F(f,"withCredentials");let D=E||K?me(c,E||u):c,w=D.includes("://")?"":i(f,"baseURL")||i(f,"apiUrl");return T&&typeof T!==J&&!te(T)&&ge(T)&&(T=JSON.stringify(T)),B(C({},f),{credentials:Q,body:T,method:R,url:w+D})},l=async(r,u)=>{d(r)||h("API ERROR",r),await O(r,u==null?void 0:u.onError),await O(r,t==null?void 0:t.onError)},a=async(r,u,f)=>{let R=d(r),P=i(f,"strategy"),c=i(f,"rejectCancelled");if(!(R&&!c)){if(P==="silent")await new Promise(()=>null);else if(P==="reject")return Promise.reject(r)}return y(u,f,r)},d=r=>r.name===$||r.name===pe,g=async(r,u=null,f=null)=>{var ie;let R=f||{},P=C(C({},t),R),c=null,E=m(r,u,P),{timeout:q,cancellable:K,dedupeTime:T,pollingInterval:Y,shouldStopPolling:Q,cacheTime:D,cacheKey:W}=P,w;if(W?w=W(E):w=Te(E),D&&w){let I=P.cacheBuster;if(!I||!I(E)){let p=be(w,D);if(p)return p.data}}let{retries:V=0,delay:Oe,backoff:De,retryOn:X,shouldRetry:ae,maxDelay:Ie,resetTimeout:Ae}=P.retry,H=0,Z=0,N=Oe;for(;H<=V;)try{let I=await Ee(E,q,T,K,!!(q&&(!V||Ae))),p=C({signal:I.signal},E);if(await O(p,R==null?void 0:R.onRequest),await O(p,t==null?void 0:t.onRequest),n!==null&&o!==null?c=await o.request(p):c=await fetch(p.url,p),c instanceof Response&&(c.config=p,c.data=await Ce(c),!c.ok))throw new L(`${p.url} failed! Status: ${c.status||null}`,p,c);if(await O(c,R==null?void 0:R.onResponse),await O(c,t==null?void 0:t.onResponse),v(E),Y&&(!Q||!Q(c,Z))){Z++,h(`Polling attempt ${Z}...`),await re(Y);continue}let S=y(c,p);if(D&&w){let ce=p.skipCache;(!ce||!ce(S,p))&&we(w,S)}return S}catch(I){let p=I,S=((ie=p==null?void 0:p.response)==null?void 0:ie.status)||(p==null?void 0:p.status)||0;if(H===V||!(!ae||await ae(p,H))||!(X!=null&&X.includes(S)))return await l(p,E),v(E),a(p,c,E);h(`Attempt ${H+1} failed. Retry in ${N}ms.`),await re(N),N*=De,N=Math.min(N,Ie),H++}return y(c,E)},y=(r,u,f=null)=>{let R=i(u,"defaultResponse"),P=i(u,"flattenResponse");if(!r)return P?R:{error:f,headers:null,data:R,config:u};F(f,"response"),F(f,"request"),F(f,"config");let c=r==null?void 0:r.data;return(c==null||typeof c===b&&Object.keys(c).length===0)&&(c=R),P?se(c):r instanceof Response?{body:r.body,bodyUsed:r.bodyUsed,formData:r.formData,ok:r.ok,redirected:r.redirected,type:r.type,url:r.url,status:r.status,statusText:r.statusText,blob:r.blob.bind(r),json:r.json.bind(r),text:r.text.bind(r),clone:r.clone.bind(r),arrayBuffer:r.arrayBuffer.bind(r),error:f,data:c,headers:Pe(r.headers),config:u}:r};return{getInstance:s,buildConfig:m,config:e,request:g}}function _e(e){let t=e.endpoints,n=z(e);function o(){return n.getInstance()}function s(l){return console.error(`Add ${l} to 'endpoints'.`),Promise.resolve(null)}async function i(l,a={},d={},g={}){let y=t[l];return await n.request(y.url,a,B(C(C({},y||{}),g),{urlPathParams:d}))}function h(l){return l in m?m[l]:t[l]?m.request.bind(null,l):s.bind(null,l)}let m={config:e,endpoints:t,requestHandler:n,getInstance:o,request:i};return new Proxy(m,{get:(l,a)=>h(a)})}async function xe(e,t={}){return z(t).request(e,null,t)}0&&(module.exports={createApiFetcher,fetchf}); +"use strict";var Y=Object.defineProperty;var Te=Object.getOwnPropertyDescriptor;var be=Object.getOwnPropertyNames;var we=Object.prototype.hasOwnProperty;var Oe=(e,t)=>{for(var n in t)Y(e,n,{get:t[n],enumerable:!0})},xe=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of be(t))!we.call(e,a)&&a!==n&&Y(e,a,{get:()=>t[a],enumerable:!(s=Te(t,a))||s.enumerable});return e};var qe=e=>xe(Y({},"__esModule",{value:!0}),e);var He={};Oe(He,{createApiFetcher:()=>Ae,fetchf:()=>Ne});module.exports=qe(He);async function T(e,t){if(t){if(typeof t=="function"){let n=await t(e);n&&Object.assign(e,n)}else if(Array.isArray(t))for(let n of t){let s=await n(e);s&&Object.assign(e,s)}}}var B=class extends Error{response;request;config;status;statusText;constructor(t,n,s){super(t),this.name="ResponseError",this.message=t,this.status=s.status,this.statusText=s.statusText,this.request=n,this.config=n,this.response=s}};var k="application/",I=k+"json",M="Content-Type",E="undefined",C="object",Q="string",L="AbortError",se="TimeoutError",oe="CanceledError",A="GET",ae="HEAD";function K(e){return e instanceof URLSearchParams}function le(e){let t="";for(let n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t+=n+":"+e[n]);return t}function V(e){let t={},n=Object.keys(e);n.sort();for(let s=0,a=n.length;s{o=typeof o=="function"?o():o,o=o===null||o===void 0?"":o,n[n.length]=s(c)+"="+s(o)},l=(c,o)=>{let f,h,d;if(c)if(Array.isArray(o))for(f=0,h=o.length;f{let s=n.substring(1);return t[s]?String(t[s]):n}):e}function fe(e){let t=typeof e;if(t===E||e===null)return!1;if(t===Q||t==="number"||t==="boolean"||Array.isArray(e))return!0;if(Buffer.isBuffer(e)||e instanceof Date)return!1;if(t===C){let n=Object.getPrototypeOf(e);if(n===Object.prototype||n===null||typeof e.toJSON=="function")return!0}return!1}async function W(e){return new Promise(t=>setTimeout(()=>t(!0),e))}function X(e){return e&&typeof e===C&&typeof e.data!==E&&Object.keys(e).length===1?X(e.data):e}function pe(e){if(!e)return{};let t={};if(e instanceof Headers)e.forEach((n,s)=>{t[s]=n});else if(typeof e===C&&e!==null)for(let[n,s]of Object.entries(e))t[n.toLowerCase()]=s;return t}function v(e,t){e&&t in e&&delete e[t]}var N=new Map;async function de(e,t,n=0,s=!1,a=!0){let l=Date.now(),R=N.get(e);if(R){let o=R[3],f=R[0],h=R[1];if(!o&&l-R[2]{let o=new DOMException(`${e.url} aborted due to timeout`,se);j(e,o)},t):null;return N.set(e,[y,c,l,s]),y}async function j(e,t=null){let n=N.get(e);if(n){let s=n[0],a=n[1];t&&!s.signal.aborted&&s.abort(t),a!==null&&clearTimeout(a),N.delete(e)}}async function Re(e){if(!e?.body)return null;let t=String(e.headers?.get(M)||"").split(";")[0],n;try{if(t.includes(I)||t.includes("+json"))n=await e.json();else if(t.includes("multipart/form-data"))n=await e.formData();else if(t.includes(k+"octet-stream"))n=await e.blob();else if(t.includes(k+"x-www-form-urlencoded"))n=await e.formData();else if(t.includes("text/"))n=await e.text();else try{n=await e.clone().json()}catch{n=await e.text()}}catch{n=null}return n}function J(e){let t=0;for(let n=0,s=e.length;n{d+=u+"="+r+"&"}),d=J(d);else if(typeof Blob!==E&&a instanceof Blob||typeof File!==E&&a instanceof File)d="BF"+a.size+a.type;else if(a instanceof ArrayBuffer||ArrayBuffer.isView(a))d="AB"+a.byteLength;else{let r=typeof a===C?V(a):String(a);d=J(JSON.stringify(r))}return n+t+l+R+y+c+o+f+h+d}function Fe(e,t){return t?Date.now()-e>t*1e3:!1}function ye(e,t){let n=Z.get(e);if(n){if(!Fe(n.timestamp,t))return n;Z.delete(e)}return null}function ge(e,t,n=!1){Z.set(e,{data:t,isLoading:n,timestamp:Date.now()})}var Ie={method:A,strategy:"reject",timeout:3e4,dedupeTime:1e3,defaultResponse:null,headers:{Accept:I+", text/plain, */*","Accept-Encoding":"gzip, deflate, br",[M]:I+";charset=utf-8"},retry:{delay:1e3,maxDelay:3e4,resetTimeout:!0,backoff:1.5,retryOn:[408,409,425,429,500,502,503,504]}};function $(e){let t={...Ie,...e},n=t.fetcher,s=n?.create(t)||null,a=()=>s,l=(r,u)=>typeof r[u]!==E?r[u]:t[u],R=(...r)=>{t.logger?.warn&&t.logger.warn(...r)},y=(r,u)=>{let m=l(u,"method").toUpperCase(),P=m===A||m===ae,i=ce(r,l(u,"urlPathParams")),g=l(u,"params"),H=l(u,"body")||l(u,"data"),D;P||(D=H);let U=l(u,"withCredentials")?"include":l(u,"credentials"),w=g?ue(i,g):i,_=w.includes("://")?"":l(u,"baseURL")||l(u,"apiUrl");return D&&typeof D!==Q&&!K(D)&&fe(D)&&(D=JSON.stringify(D)),{...u,credentials:U,body:D,method:m,url:_+w}},c=async(r,u)=>{f(r)||R("API ERROR",r),await T(r,u?.onError),await T(r,t?.onError)},o=async(r,u,m)=>{let P=f(r),i=l(m,"strategy"),g=l(m,"rejectCancelled");if(!(P&&!g)){if(i==="silent")await new Promise(()=>null);else if(i==="reject")return Promise.reject(r)}return d(u,m,r)},f=r=>r.name===L||r.name===oe,h=async(r,u=null)=>{let m=u||{},P={...t,...m},i=null,g=y(r,P),{timeout:H,cancellable:D,dedupeTime:ee,pollingInterval:U,shouldStopPolling:w,cacheTime:S,cacheKey:_}=P,b;if(_?b=_(g):b=me(g),S&&b){let q=P.cacheBuster;if(!q||!q(g)){let p=ye(b,S);if(p)return p.data}}let{retries:G=0,delay:he,backoff:Pe,retryOn:De,shouldRetry:te,maxDelay:Ce,resetTimeout:Ee}=P.retry,O=0,z=0,x=he,ne=G>0?G:0;for(;O<=ne;)try{let p={signal:(await de(g,H,ee,D,!!(H&&(!ne||Ee)))).signal,...g};if(await T(p,m?.onRequest),await T(p,t?.onRequest),n!==null&&s!==null?i=await s.request(p):i=await fetch(p.url,p),i instanceof Response&&(i.config=p,i.data=await Re(i),!i.ok))throw new B(`${p.url} failed! Status: ${i.status||null}`,p,i);if(await T(i,m?.onResponse),await T(i,t?.onResponse),j(g),U&&(!w||!w(i,z))){z++,R(`Polling attempt ${z}...`),await W(U);continue}let F=d(i,p);if(S&&b){let re=p.skipCache;(!re||!re(F,p))&&ge(b,F)}return F}catch(q){let p=q,F=p?.response?.status||p?.status||0;if(O===G||!(!te||await te(p,O))||!De?.includes(F))return await c(p,g),j(g),o(p,i,g);R(`Attempt ${O+1} failed. Retry in ${x}ms.`),await W(x),x*=Pe,x=Math.min(x,Ce),O++}return d(i,g)},d=(r,u,m=null)=>{let P=l(u,"defaultResponse");if(!r)return{ok:!1,error:m,data:P,headers:null,config:u};v(m,"response"),v(m,"request"),v(m,"config");let i=r?.data;return(i==null||typeof i===C&&Object.keys(i).length===0)&&(i=P),l(u,"flattenResponse")&&(r.data=X(i)),r instanceof Response?{body:r.body,bodyUsed:r.bodyUsed,formData:r.formData,ok:r.ok,redirected:r.redirected,type:r.type,url:r.url,status:r.status,statusText:r.statusText,blob:r.blob.bind(r),json:r.json.bind(r),text:r.text.bind(r),clone:r.clone.bind(r),arrayBuffer:r.arrayBuffer.bind(r),error:m,data:i,headers:pe(r.headers),config:u}:r};return{getInstance:a,buildConfig:y,config:e,request:h}}function Ae(e){let t=e.endpoints,n=$(e);function s(){return n.getInstance()}function a(y){return console.error(`Add ${y} to 'endpoints'.`),Promise.resolve(null)}async function l(y,c={}){let o=t[y]||{url:y};return await n.request(o.url,{...o,...c})}let R={config:e,endpoints:t,requestHandler:n,getInstance:s,request:l};return new Proxy(R,{get(y,c){return c in R?R[c]:t[c]?R.request.bind(null,c):a.bind(null,c)}})}async function Ne(e,t={}){return $(t).request(e,t)}0&&(module.exports={createApiFetcher,fetchf}); //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/node/index.js.map b/dist/node/index.js.map index 00dc167..72e205d 100644 --- a/dist/node/index.js.map +++ b/dist/node/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../../src/index.ts","../../src/interceptor-manager.ts","../../src/response-error.ts","../../src/const.ts","../../src/utils.ts","../../src/queue-manager.ts","../../src/response-parser.ts","../../src/hash.ts","../../src/cache-manager.ts","../../src/request-handler.ts","../../src/api-handler.ts"],"sourcesContent":["import { createRequestHandler } from './request-handler';\nimport type { APIResponse, FetchResponse, RequestHandlerConfig } from './types';\n\n/**\n * Simple wrapper for request fetching.\n * It abstracts the creation of RequestHandler, making it easy to perform API requests.\n *\n * @param {string | URL | globalThis.Request} url - Request URL.\n * @param {RequestHandlerConfig} config - Configuration object for the request handler.\n * @returns {Promise>} Response Data.\n */\nexport async function fetchf(\n url: string,\n config: RequestHandlerConfig = {},\n): Promise> {\n return createRequestHandler(config).request(url, null, config);\n}\n\nexport * from './types';\nexport * from './api-handler';\n","type InterceptorFunction = (object: T) => Promise;\n\n/**\n * Applies interceptors to the object. Interceptors can be a single function or an array of functions.\n *\n * @template T - Type of the object.\n * @template I - Type of interceptors.\n *\n * @param {T} object - The object to process.\n * @param {InterceptorFunction | InterceptorFunction[]} [interceptors] - Interceptor function(s).\n *\n * @returns {Promise} - Nothing as the function is non-idempotent.\n */\nexport async function applyInterceptor<\n T extends object,\n I = InterceptorFunction | InterceptorFunction[],\n>(object: T, interceptors?: I): Promise {\n if (!interceptors) {\n return;\n }\n\n if (typeof interceptors === 'function') {\n const value = await interceptors(object);\n\n if (value) {\n Object.assign(object, value);\n }\n } else if (Array.isArray(interceptors)) {\n for (const interceptor of interceptors) {\n const value = await interceptor(object);\n\n if (value) {\n Object.assign(object, value);\n }\n }\n }\n}\n","import type { FetchResponse, RequestConfig } from './types';\n\nexport class ResponseErr extends Error {\n response: FetchResponse;\n request: RequestConfig;\n config: RequestConfig;\n status: number;\n statusText: string;\n\n constructor(\n message: string,\n requestInfo: RequestConfig,\n response: FetchResponse,\n ) {\n super(message);\n\n this.name = 'ResponseError';\n this.message = message;\n this.status = response.status;\n this.statusText = response.statusText;\n this.request = requestInfo;\n this.config = requestInfo;\n this.response = response;\n }\n}\n","export const APPLICATION_JSON = 'application/json';\nexport const CONTENT_TYPE = 'Content-Type';\n\nexport const UNDEFINED = 'undefined';\nexport const OBJECT = 'object';\nexport const STRING = 'string';\n\nexport const ABORT_ERROR = 'AbortError';\nexport const TIMEOUT_ERROR = 'TimeoutError';\nexport const CANCELLED_ERROR = 'CanceledError';\n\nexport const GET = 'GET';\nexport const HEAD = 'HEAD';\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { OBJECT, STRING, UNDEFINED } from './const';\nimport type { HeadersObject, QueryParams, UrlPathParams } from './types';\n\nexport function isSearchParams(data: unknown): boolean {\n return data instanceof URLSearchParams;\n}\n\n/**\n * Determines if a value is a non-null object.\n *\n * @param {any} value - The value to check.\n * @returns {boolean} - True if the value is a non-null object.\n */\nexport function isObject(value: any): value is Record {\n return value !== null && typeof value === OBJECT;\n}\n\n/**\n * Shallowly serializes an object by converting its key-value pairs into a string representation.\n * This function does not recursively serialize nested objects.\n *\n * @param obj - The object to serialize.\n * @returns A string representation of the object's top-level properties.\n */\nexport function shallowSerialize(obj: Record): string {\n let result = '';\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n result += key + ':' + obj[key];\n }\n }\n\n return result;\n}\n\n/**\n * Sorts the keys of an object and returns a new object with sorted keys.\n *\n * This function is optimized for performance by minimizing the number of object operations\n * and using a single pass to create the sorted object.\n *\n * @param {Object} obj - The object to be sorted by keys.\n * @returns {Object} - A new object with keys sorted in ascending order.\n */\nexport function sortObject(obj: Record): object {\n const sortedObj = {} as Record;\n const keys = Object.keys(obj);\n\n keys.sort();\n\n for (let i = 0, len = keys.length; i < len; i++) {\n const key = keys[i];\n sortedObj[key] = obj[key];\n }\n\n return sortedObj;\n}\n\n/**\n * Appends a query string to a URL, ensuring proper handling of existing query parameters.\n *\n * @param baseUrl - The base URL to which the query string will be appended.\n * @param queryString - The encoded query string to append.\n * @returns The URL with the appended query string, or the original URL if no query string is provided.\n */\nfunction appendQueryStringToUrl(baseUrl: string, queryString: string): string {\n if (!queryString) {\n return baseUrl;\n }\n\n return baseUrl.includes('?')\n ? `${baseUrl}&${queryString}`\n : `${baseUrl}?${queryString}`;\n}\n\n/**\n * Appends query parameters to a given URL.\n *\n * @param {string} url - The base URL to which query parameters will be appended.\n * @param {QueryParams} params - An object containing the query parameters to append.\n * @returns {string} - The URL with the appended query parameters.\n */\nexport function appendQueryParams(url: string, params: QueryParams): string {\n if (!params) {\n return url;\n }\n\n // Check if `params` is an instance of URLSearchParams and bail early if it is\n if (isSearchParams(params)) {\n const encodedQueryString = params.toString();\n\n return appendQueryStringToUrl(url, encodedQueryString);\n }\n\n // This is exact copy of what JQ used to do. It works much better than URLSearchParams\n const s: string[] = [];\n const encode = encodeURIComponent;\n const add = (k: string, v: any) => {\n v = typeof v === 'function' ? v() : v;\n v = v === null ? '' : v === undefined ? '' : v;\n s[s.length] = encode(k) + '=' + encode(v);\n };\n\n const buildParams = (prefix: string, obj: any) => {\n let i: number, len: number, key: string;\n\n if (prefix) {\n if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n buildParams(\n prefix + '[' + (typeof obj[i] === OBJECT && obj[i] ? i : '') + ']',\n obj[i],\n );\n }\n } else if (typeof obj === OBJECT && obj !== null) {\n for (key in obj) {\n buildParams(prefix + '[' + key + ']', obj[key]);\n }\n } else {\n add(prefix, obj);\n }\n } else if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n add(obj[i].name, obj[i].value);\n }\n } else {\n for (key in obj) {\n buildParams(key, obj[key]);\n }\n }\n return s;\n };\n\n const queryStringParts = buildParams('', params).join('&');\n\n // Encode special characters as per RFC 3986, https://datatracker.ietf.org/doc/html/rfc3986\n const encodedQueryString = queryStringParts.replace(/%5B%5D/g, '[]'); // Keep '[]' for arrays\n\n return appendQueryStringToUrl(url, encodedQueryString);\n}\n\n/**\n * Replaces dynamic URI parameters in a URL string with values from the provided `urlPathParams` object.\n * Parameters in the URL are denoted by `:`, where `` is a key in `urlPathParams`.\n *\n * @param {string} url - The URL string containing placeholders in the format `:`.\n * @param {Object} urlPathParams - An object containing the parameter values to replace placeholders.\n * @param {string} urlPathParams.paramName - The value to replace the placeholder `:` in the URL.\n * @returns {string} - The URL string with placeholders replaced by corresponding values from `urlPathParams`.\n */\nexport function replaceUrlPathParams(\n url: string,\n urlPathParams: UrlPathParams,\n): string {\n if (!urlPathParams) {\n return url;\n }\n\n return url.replace(/:\\w+/g, (str): string => {\n const word = str.substring(1);\n\n return String(urlPathParams[word] ? urlPathParams[word] : str);\n });\n}\n\n/**\n * Checks if a value is JSON serializable.\n *\n * JSON serializable values include:\n * - Primitive types: string, number, boolean, null\n * - Arrays\n * - Plain objects (i.e., objects without special methods)\n * - Values with a `toJSON` method\n *\n * @param {any} value - The value to check for JSON serializability.\n * @returns {boolean} - Returns `true` if the value is JSON serializable, otherwise `false`.\n */\nexport function isJSONSerializable(value: any): boolean {\n const t = typeof value;\n\n if (t === UNDEFINED || value === null) {\n return false;\n }\n\n if (t === STRING || t === 'number' || t === 'boolean') {\n return true;\n }\n\n if (Array.isArray(value)) {\n return true;\n }\n\n if (Buffer.isBuffer(value)) {\n return false;\n }\n\n if (value instanceof Date) {\n return false;\n }\n\n if (t === OBJECT) {\n const proto = Object.getPrototypeOf(value);\n\n // Check if the prototype is `Object.prototype` or `null` (plain object)\n if (proto === Object.prototype || proto === null) {\n return true;\n }\n\n // Check if the object has a toJSON method\n if (typeof value.toJSON === 'function') {\n return true;\n }\n }\n\n return false;\n}\n\nexport async function delayInvocation(ms: number): Promise {\n return new Promise((resolve) =>\n setTimeout(() => {\n return resolve(true);\n }, ms),\n );\n}\n\n/**\n * Recursively flattens the data object if it meets specific criteria.\n *\n * The method checks if the provided `data` is an object with exactly one property named `data`.\n * If so, it recursively flattens the `data` property. Otherwise, it returns the `data` as-is.\n *\n * @param {any} data - The data to be flattened. Can be of any type, including objects, arrays, or primitives.\n * @returns {any} - The flattened data if the criteria are met; otherwise, the original `data`.\n */\nexport function flattenData(data: any): any {\n if (\n data &&\n typeof data === OBJECT &&\n typeof data.data !== UNDEFINED &&\n Object.keys(data).length === 1\n ) {\n return flattenData(data.data);\n }\n\n return data;\n}\n\n/**\n * Processes headers and returns them as a normalized object.\n *\n * Handles both `Headers` instances and plain objects. Normalizes header keys to lowercase\n * as per RFC 2616 section 4.2.\n *\n * @param headers - The headers to process. Can be an instance of `Headers`, a plain object,\n * or `null`. If `null`, an empty object is returned.\n * @returns {HeadersObject} - A normalized headers object with lowercase keys.\n */\nexport function processHeaders(\n headers?: (HeadersObject & HeadersInit) | null | Headers,\n): HeadersObject {\n if (!headers) {\n return {};\n }\n\n const headersObject: HeadersObject = {};\n\n // Handle Headers object with entries() method\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n headersObject[key] = value;\n });\n } else if (typeof headers === OBJECT && headers !== null) {\n // Handle plain object\n for (const [key, value] of Object.entries(headers)) {\n // Normalize keys to lowercase as per RFC 2616 4.2\n // https://datatracker.ietf.org/doc/html/rfc2616#section-4.2\n headersObject[key.toLowerCase()] = value;\n }\n }\n\n return headersObject;\n}\n\n/**\n * Deletes a property from an object if it exists.\n *\n * @param obj - The object from which to delete the property.\n * @param property - The property to delete from the object.\n */\nexport function deleteProperty>(\n obj: T | null,\n property: keyof T,\n): void {\n if (obj && property in obj) {\n delete obj[property];\n }\n}\n","import { ABORT_ERROR, TIMEOUT_ERROR } from './const';\nimport type { RequestConfig } from './types';\nimport type { QueueItem, RequestsQueue } from './types/queue-manager';\n\n/**\n * Queue Manager is responsible for managing and controlling the flow of concurrent or sequential requests. It handles:\n * - Request Queueing and Deduplication\n * - Request Timeout Handling\n * - Abort Controller Management and Request Cancellation\n * - Concurrency Control and Locking\n * - Request Lifecycle Management\n */\nconst queue: RequestsQueue = new Map();\n\n/**\n * Adds a request to the queue if it's not already being processed within the dedupeTime interval.\n *\n * @param {RequestConfig} config - The request configuration object.\n * @param {number} timeout - Timeout in milliseconds for the request.\n * @param {number} dedupeTime - Deduplication time in milliseconds.\n * @param {boolean} isCancellable - If true, then the previous request with same configuration should be aborted.\n * @param {boolean} isTimeoutEnabled - Whether timeout is enabled.\n * @returns {Promise} - A promise that resolves to an AbortController.\n */\nexport async function addRequest(\n config: RequestConfig,\n timeout: number | undefined,\n dedupeTime: number = 0,\n isCancellable: boolean = false,\n isTimeoutEnabled: boolean = true,\n): Promise {\n const now = Date.now();\n const item = queue.get(config);\n\n if (item) {\n const isCancellable = item[3];\n const previousController = item[0];\n const timeoutId = item[1];\n\n // If the request is already in the queue and within the dedupeTime, reuse the existing controller\n if (!isCancellable && now - item[2] < dedupeTime) {\n return previousController;\n }\n\n // If the request is too old, remove it and proceed to add a new one\n // Abort previous request, if applicable, and continue as usual\n if (isCancellable) {\n previousController.abort(\n new DOMException('Aborted due to new request', ABORT_ERROR),\n );\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n\n const controller = new AbortController();\n\n const timeoutId = isTimeoutEnabled\n ? setTimeout(() => {\n const error = new DOMException(\n `${config.url} aborted due to timeout`,\n TIMEOUT_ERROR,\n );\n\n removeRequest(config, error);\n }, timeout)\n : null;\n\n queue.set(config, [controller, timeoutId, now, isCancellable]);\n\n return controller;\n}\n\n/**\n * Removes a request from the queue and clears its timeout.\n *\n * @param config - The request configuration.\n * @param {boolean} error - Error payload so to force the request to abort.\n */\nexport async function removeRequest(\n config: RequestConfig,\n error: DOMException | null | string = null,\n): Promise {\n const item = queue.get(config);\n\n if (item) {\n const controller = item[0];\n const timeoutId = item[1];\n\n // If the request is not yet aborted, abort it with the provided error\n if (error && !controller.signal.aborted) {\n controller.abort(error);\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n}\n\n/**\n * Gets the AbortController for a request configuration.\n *\n * @param config - The request configuration.\n * @returns {AbortController | undefined} - The AbortController or undefined.\n */\nexport async function getController(\n config: RequestConfig,\n): Promise {\n const item = queue.get(config);\n\n return item?.[0];\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { APPLICATION_JSON, CONTENT_TYPE } from './const';\nimport type { APIResponse } from './types/api-handler';\nimport type { FetchResponse } from './types/request-handler';\n\n/**\n * Parses the response data based on the Content-Type header.\n *\n * @param response - The Response object to parse.\n * @returns A Promise that resolves to the parsed data.\n */\nexport async function parseResponseData(\n response: FetchResponse,\n): Promise {\n // Bail early when body is empty\n if (!response?.body) {\n return null;\n }\n\n const contentType = String(\n (response as Response).headers?.get(CONTENT_TYPE) || '',\n ).split(';')[0]; // Correctly handle charset\n\n let data;\n\n try {\n if (\n contentType.includes(APPLICATION_JSON) ||\n contentType.includes('+json')\n ) {\n data = await response.json(); // Parse JSON response\n } else if (contentType.includes('multipart/form-data')) {\n data = await response.formData(); // Parse as FormData\n } else if (contentType.includes('application/octet-stream')) {\n data = await response.blob(); // Parse as blob\n } else if (contentType.includes('application/x-www-form-urlencoded')) {\n data = await response.formData(); // Handle URL-encoded forms\n } else if (contentType.includes('text/')) {\n data = await response.text(); // Parse as text\n } else {\n try {\n const responseClone = response.clone();\n\n // Handle edge case of no content type being provided... We assume JSON here.\n data = await responseClone.json();\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_e) {\n // Handle streams\n data = await response.text();\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_error) {\n // Parsing failed, fallback to null\n data = null;\n }\n\n return data;\n}\n","const PRIME_MULTIPLIER = 31;\n\n/**\n * Computes a hash value for a given string using the variant of djb2 hash function.\n * This hash function is non-cryptographic and designed for speed.\n * @author Daniel J. Bernstein (of djb2)\n *\n * @param str Input string to hash\n * @returns {string} Hash\n */\nexport function hash(str: string): string {\n let hash = 0;\n\n for (let i = 0, len = str.length; i < len; i++) {\n const char = str.charCodeAt(i);\n hash = (hash * PRIME_MULTIPLIER + char) | 0;\n }\n\n return String(hash);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { hash } from './hash';\nimport { fetchf } from './index';\nimport type { FetcherConfig } from './types/request-handler';\nimport type { CacheEntry } from './types/cache-manager';\nimport { GET, OBJECT, UNDEFINED } from './const';\nimport { shallowSerialize, sortObject } from './utils';\n\nconst cache = new Map>();\n\n/**\n * Generates a cache key for a given URL and fetch options, ensuring that key factors\n * like method, headers, body, and other options are included in the cache key.\n * Headers and other objects are sorted by key to ensure consistent cache keys.\n *\n * @param options - The fetch options that may affect the request. The most important are:\n * @property {string} [method=\"GET\"] - The HTTP method (GET, POST, etc.).\n * @property {HeadersInit} [headers={}] - The request headers.\n * @property {BodyInit | null} [body=\"\"] - The body of the request (only for methods like POST, PUT).\n * @property {RequestMode} [mode=\"cors\"] - The mode for the request (e.g., cors, no-cors, include).\n * @property {RequestCredentials} [credentials=\"include\"] - Whether to include credentials like cookies.\n * @property {RequestCache} [cache=\"default\"] - The cache mode (e.g., default, no-store, reload).\n * @property {RequestRedirect} [redirect=\"follow\"] - How to handle redirects (e.g., follow, error, manual).\n * @property {string} [referrer=\"\"] - The referrer URL to send with the request.\n * @property {string} [integrity=\"\"] - Subresource integrity value (a cryptographic hash for resource validation).\n * @returns {string} - A unique cache key based on the URL and request options. Empty if cache is to be burst.\n *\n * @example\n * const cacheKey = generateCacheKey({\n * url: 'https://api.example.com/data',\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ name: 'Alice' }),\n * mode: 'cors',\n * credentials: 'include',\n * });\n * console.log(cacheKey);\n */\nexport function generateCacheKey(options: FetcherConfig): string {\n const {\n url = '',\n method = GET,\n headers = {},\n body = '',\n mode = 'cors',\n credentials = 'include',\n cache = 'default',\n redirect = 'follow',\n referrer = '',\n integrity = '',\n } = options;\n\n // Bail early if cache should be burst\n if (cache === 'reload') {\n return '';\n }\n\n // Sort headers and body + convert sorted to strings for hashing purposes\n // Native serializer is on avg. 3.5x faster than a Fast Hash or FNV-1a\n const headersString = shallowSerialize(sortObject(headers));\n\n let bodyString = '';\n\n // In majority of cases we do not cache body\n if (body !== null) {\n if (typeof body === 'string') {\n bodyString = hash(body);\n } else if (body instanceof FormData) {\n body.forEach((value, key) => {\n // Append key=value and '&' directly to the result\n bodyString += key + '=' + value + '&';\n });\n bodyString = hash(bodyString);\n } else if (\n (typeof Blob !== UNDEFINED && body instanceof Blob) ||\n (typeof File !== UNDEFINED && body instanceof File)\n ) {\n bodyString = 'BF' + body.size + body.type;\n } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n bodyString = 'AB' + body.byteLength;\n } else {\n const o = typeof body === OBJECT ? sortObject(body) : String(body);\n bodyString = hash(JSON.stringify(o));\n }\n }\n\n // Concatenate all key parts into a cache key string\n // Template literals are apparently slower\n return (\n method +\n url +\n mode +\n credentials +\n cache +\n redirect +\n referrer +\n integrity +\n headersString +\n bodyString\n );\n}\n\n/**\n * Checks if the cache entry is expired based on its timestamp and the maximum stale time.\n *\n * @param {number} timestamp - The timestamp of the cache entry.\n * @param {number} maxStaleTime - The maximum stale time in seconds.\n * @returns {boolean} - Returns true if the cache entry is expired, false otherwise.\n */\nfunction isCacheExpired(timestamp: number, maxStaleTime: number): boolean {\n if (!maxStaleTime) {\n return false;\n }\n\n return Date.now() - timestamp > maxStaleTime * 1000;\n}\n\n/**\n * Retrieves a cache entry if it exists and is not expired.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} cacheTime - Maximum time to cache entry.\n * @returns {CacheEntry | null} - The cache entry if it exists and is not expired, null otherwise.\n */\nexport function getCache(\n key: string,\n cacheTime: number,\n): CacheEntry | null {\n const entry = cache.get(key);\n\n if (entry) {\n if (!isCacheExpired(entry.timestamp, cacheTime)) {\n return entry;\n }\n\n cache.delete(key);\n }\n\n return null;\n}\n\n/**\n * Sets a new cache entry or updates an existing one.\n *\n * @param {string} key Cache key to utilize\n * @param {T} data - The data to be cached.\n * @param {boolean} isLoading - Indicates if the data is currently being fetched.\n */\nexport function setCache(\n key: string,\n data: T,\n isLoading: boolean = false,\n): void {\n cache.set(key, {\n data,\n isLoading,\n timestamp: Date.now(),\n });\n}\n\n/**\n * Revalidates a cache entry by fetching fresh data and updating the cache.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @returns {Promise} - A promise that resolves when the revalidation is complete.\n */\nexport async function revalidate(\n key: string,\n config: FetcherConfig,\n): Promise {\n try {\n // Fetch fresh data\n const newData = await fetchf(config.url, {\n ...config,\n cache: 'reload',\n });\n\n setCache(key, newData);\n } catch (error) {\n console.error(`Error revalidating ${config.url}:`, error);\n\n // Rethrow the error to forward it\n throw error;\n }\n}\n\n/**\n * Invalidates (deletes) a cache entry.\n *\n * @param {string} key Cache key to utilize\n */\nexport function deleteCache(key: string): void {\n cache.delete(key);\n}\n\n/**\n * Mutates a cache entry with new data and optionally revalidates it.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @param {T} newData - The new data to be cached.\n * @param {boolean} revalidateAfter - If true, triggers revalidation after mutation.\n */\nexport function mutate(\n key: string,\n config: FetcherConfig,\n newData: T,\n revalidateAfter: boolean = false,\n): void {\n setCache(key, newData);\n\n if (revalidateAfter) {\n revalidate(key, config);\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {\n RequestHandlerConfig,\n RequestConfig,\n Method,\n RetryOptions,\n FetchResponse,\n ResponseError,\n RequestHandlerReturnType,\n CreatedCustomFetcherInstance,\n FetcherConfig,\n} from './types/request-handler';\nimport type {\n APIResponse,\n BodyPayload,\n QueryParams,\n QueryParamsOrBody,\n} from './types/api-handler';\nimport { applyInterceptor } from './interceptor-manager';\nimport { ResponseErr } from './response-error';\nimport {\n appendQueryParams,\n isJSONSerializable,\n replaceUrlPathParams,\n delayInvocation,\n flattenData,\n processHeaders,\n deleteProperty,\n isSearchParams,\n} from './utils';\nimport { addRequest, removeRequest } from './queue-manager';\nimport {\n ABORT_ERROR,\n APPLICATION_JSON,\n CANCELLED_ERROR,\n CONTENT_TYPE,\n GET,\n HEAD,\n OBJECT,\n STRING,\n UNDEFINED,\n} from './const';\nimport { parseResponseData } from './response-parser';\nimport { generateCacheKey, getCache, setCache } from './cache-manager';\n\nconst defaultConfig: RequestHandlerConfig = {\n method: GET,\n strategy: 'reject',\n timeout: 30000,\n dedupeTime: 1000,\n defaultResponse: null,\n headers: {\n Accept: APPLICATION_JSON + ', text/plain, */*',\n 'Accept-Encoding': 'gzip, deflate, br',\n [CONTENT_TYPE]: APPLICATION_JSON + ';charset=utf-8',\n },\n retry: {\n delay: 1000,\n maxDelay: 30000,\n resetTimeout: true,\n backoff: 1.5,\n\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status\n retryOn: [\n 408, // Request Timeout\n 409, // Conflict\n 425, // Too Early\n 429, // Too Many Requests\n 500, // Internal Server Error\n 502, // Bad Gateway\n 503, // Service Unavailable\n 504, // Gateway Timeout\n ],\n },\n};\n\n/**\n * Create Request Handler\n *\n * @param {RequestHandlerConfig} config - Configuration object for the request handler\n * @returns {Object} An object with methods for handling requests\n */\nexport function createRequestHandler(\n config: RequestHandlerConfig,\n): RequestHandlerReturnType {\n const handlerConfig: RequestHandlerConfig = {\n ...defaultConfig,\n ...config,\n };\n\n /**\n * Immediately create instance of custom fetcher if it is defined\n */\n const customFetcher = handlerConfig.fetcher;\n const requestInstance = customFetcher?.create(handlerConfig) || null;\n\n /**\n * Get Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Provider's instance\n */\n const getInstance = (): CreatedCustomFetcherInstance | null => {\n return requestInstance;\n };\n\n /**\n * Gets a configuration value from `reqConfig`, defaulting to `handlerConfig` if not present.\n *\n * @param {RequestConfig} reqConfig - Request configuration object.\n * @param {keyof RequestConfig} name - Key of the configuration value.\n * @returns {T} - The configuration value.\n */\n const getConfig = (\n reqConfig: RequestConfig,\n name: keyof RequestConfig,\n ): T => {\n return typeof reqConfig[name] !== UNDEFINED\n ? reqConfig[name]\n : handlerConfig[name];\n };\n\n /**\n * Logs messages or errors using the configured logger's `warn` method.\n *\n * @param {...(string | ResponseError)} args - Messages or errors to log.\n */\n const logger = (...args: (string | ResponseError)[]): void => {\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn(...args);\n }\n };\n\n /**\n * Build request configuration\n *\n * @param {string} url - Request url\n * @param {QueryParamsOrBody} data - Query Params in case of GET and HEAD requests, body payload otherwise\n * @param {RequestConfig} reqConfig - Request config passed when making the request\n * @returns {RequestConfig} - Provider's instance\n */\n const buildConfig = (\n url: string,\n data: QueryParamsOrBody,\n reqConfig: RequestConfig,\n ): FetcherConfig => {\n const method = getConfig(\n reqConfig,\n 'method',\n ).toUpperCase() as Method;\n const isGetAlikeMethod = method === GET || method === HEAD;\n\n const dynamicUrl = replaceUrlPathParams(\n url,\n getConfig(reqConfig, 'urlPathParams'),\n );\n\n // The explicitly passed \"params\"\n const explicitParams = getConfig(reqConfig, 'params');\n\n // The explicitly passed \"body\" or \"data\"\n const explicitBodyData: BodyPayload =\n getConfig(reqConfig, 'body') || getConfig(reqConfig, 'data');\n\n // For convenience, in POST requests the body payload is the \"data\"\n // In edge cases we want to use Query Params in the POST requests\n // and use explicitly passed \"body\" or \"data\" from request config\n const shouldTreatDataAsParams =\n data && (isGetAlikeMethod || explicitBodyData) ? true : false;\n\n // Final body data\n let body: RequestConfig['data'];\n\n // Only applicable for request methods 'PUT', 'POST', 'DELETE', and 'PATCH'\n if (!isGetAlikeMethod) {\n body = explicitBodyData || (data as BodyPayload);\n }\n\n // Native fetch compatible settings\n const isWithCredentials = getConfig(reqConfig, 'withCredentials');\n\n const credentials = isWithCredentials\n ? 'include'\n : getConfig(reqConfig, 'credentials');\n\n deleteProperty(reqConfig, 'data');\n deleteProperty(reqConfig, 'withCredentials');\n\n const urlPath =\n explicitParams || shouldTreatDataAsParams\n ? appendQueryParams(dynamicUrl, explicitParams || (data as QueryParams))\n : dynamicUrl;\n const isFullUrl = urlPath.includes('://');\n const baseURL = isFullUrl\n ? ''\n : getConfig(reqConfig, 'baseURL') ||\n getConfig(reqConfig, 'apiUrl');\n\n // Automatically stringify request body, if possible and when not dealing with strings\n if (\n body &&\n typeof body !== STRING &&\n !isSearchParams(body) &&\n isJSONSerializable(body)\n ) {\n body = JSON.stringify(body);\n }\n\n return {\n ...reqConfig,\n credentials,\n body,\n method,\n\n url: baseURL + urlPath,\n };\n };\n\n /**\n * Process global Request Error\n *\n * @param {ResponseError} error Error instance\n * @param {RequestConfig} requestConfig Per endpoint request config\n * @returns {Promise}\n */\n const processError = async (\n error: ResponseError,\n requestConfig: RequestConfig,\n ): Promise => {\n if (!isRequestCancelled(error)) {\n logger('API ERROR', error);\n }\n\n // Local interceptors\n await applyInterceptor(error, requestConfig?.onError);\n\n // Global interceptors\n await applyInterceptor(error, handlerConfig?.onError);\n };\n\n /**\n * Output default response in case of an error, depending on chosen strategy\n *\n * @param {ResponseError} error Error instance\n * @param {FetchResponse} response Response\n * @param {RequestConfig} requestConfig Per endpoint request config\n * @returns {*} Error response\n */\n const outputErrorResponse = async (\n error: ResponseError,\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n ): Promise => {\n const _isRequestCancelled = isRequestCancelled(error);\n const errorHandlingStrategy = getConfig(requestConfig, 'strategy');\n const rejectCancelled = getConfig(\n requestConfig,\n 'rejectCancelled',\n );\n\n // By default cancelled requests aren't rejected (softFail strategy)\n if (!(_isRequestCancelled && !rejectCancelled)) {\n // Hang the promise\n if (errorHandlingStrategy === 'silent') {\n await new Promise(() => null);\n }\n // Reject the promise\n else if (errorHandlingStrategy === 'reject') {\n return Promise.reject(error);\n }\n }\n\n return outputResponse(response, requestConfig, error);\n };\n\n /**\n * Output error response depending on chosen strategy\n *\n * @param {ResponseError} error Error instance\n * @returns {boolean} True if request is aborted\n */\n const isRequestCancelled = (error: ResponseError): boolean => {\n return error.name === ABORT_ERROR || error.name === CANCELLED_ERROR;\n };\n\n /**\n * Handle Request depending on used strategy\n *\n * @param {string} url - Request url\n * @param {QueryParamsOrBody} data - Query Params in case of GET and HEAD requests, body payload otherwise\n * @param {RequestConfig} reqConfig - Request config\n * @throws {ResponseError}\n * @returns {Promise>} Response Data\n */\n const request = async (\n url: string,\n data: QueryParamsOrBody = null,\n reqConfig: RequestConfig | null = null,\n ): Promise> => {\n const _reqConfig = reqConfig || {};\n const mergedConfig = {\n ...handlerConfig,\n ..._reqConfig,\n } as RequestConfig;\n\n let response: FetchResponse | null = null;\n const fetcherConfig = buildConfig(url, data, mergedConfig);\n\n const {\n timeout,\n cancellable,\n dedupeTime,\n pollingInterval,\n shouldStopPolling,\n cacheTime,\n cacheKey,\n } = mergedConfig;\n\n // Prevent performance overhead of cache access\n let _cacheKey: string;\n\n if (cacheKey) {\n _cacheKey = cacheKey(fetcherConfig);\n } else {\n _cacheKey = generateCacheKey(fetcherConfig);\n }\n\n if (cacheTime && _cacheKey) {\n const cacheBuster = mergedConfig.cacheBuster;\n\n if (!cacheBuster || !cacheBuster(fetcherConfig)) {\n const cachedEntry = getCache<\n ResponseData & FetchResponse\n >(_cacheKey, cacheTime);\n\n if (cachedEntry) {\n // Serve stale data from cache\n return cachedEntry.data;\n }\n }\n }\n\n const {\n retries = 0,\n delay,\n backoff,\n retryOn,\n shouldRetry,\n maxDelay,\n resetTimeout,\n } = mergedConfig.retry as Required;\n\n let attempt = 0;\n let pollingAttempt = 0;\n let waitTime: number = delay;\n\n while (attempt <= retries) {\n try {\n // Add the request to the queue. Make sure to handle deduplication, cancellation, timeouts in accordance to retry settings\n const controller = await addRequest(\n fetcherConfig,\n timeout,\n dedupeTime,\n cancellable,\n // Reset timeouts by default or when retries are ON\n !!(timeout && (!retries || resetTimeout)),\n );\n\n // Shallow copy to ensure basic idempotency\n const requestConfig: RequestConfig = {\n signal: controller.signal,\n ...fetcherConfig,\n };\n\n // Local interceptors\n await applyInterceptor(requestConfig, _reqConfig?.onRequest);\n\n // Global interceptors\n await applyInterceptor(requestConfig, handlerConfig?.onRequest);\n\n if (customFetcher !== null && requestInstance !== null) {\n response = await requestInstance.request(requestConfig);\n } else {\n response = (await fetch(\n requestConfig.url as string,\n requestConfig as RequestInit,\n )) as FetchResponse;\n }\n\n // Add more information to response object\n if (response instanceof Response) {\n response.config = requestConfig;\n response.data = await parseResponseData(response);\n\n // Check if the response status is not outside the range 200-299 and if so, output error\n if (!response.ok) {\n throw new ResponseErr(\n `${requestConfig.url} failed! Status: ${response.status || null}`,\n requestConfig,\n response,\n );\n }\n }\n\n // Local interceptors\n await applyInterceptor(response, _reqConfig?.onResponse);\n\n // Global interceptors\n await applyInterceptor(response, handlerConfig?.onResponse);\n\n removeRequest(fetcherConfig);\n\n // Polling logic\n if (\n pollingInterval &&\n (!shouldStopPolling || !shouldStopPolling(response, pollingAttempt))\n ) {\n // Restart the main retry loop\n pollingAttempt++;\n\n logger(`Polling attempt ${pollingAttempt}...`);\n\n await delayInvocation(pollingInterval);\n\n continue;\n }\n\n // If polling is not required, or polling attempts are exhausted\n const output = outputResponse(response, requestConfig) as ResponseData &\n FetchResponse;\n\n if (cacheTime && _cacheKey) {\n const skipCache = requestConfig.skipCache;\n\n if (!skipCache || !skipCache(output, requestConfig)) {\n setCache(_cacheKey, output);\n }\n }\n\n return output;\n } catch (err) {\n const error = err as ResponseErr;\n const status = error?.response?.status || error?.status || 0;\n\n if (\n attempt === retries ||\n !(!shouldRetry || (await shouldRetry(error, attempt))) ||\n !retryOn?.includes(status)\n ) {\n await processError(error, fetcherConfig);\n\n removeRequest(fetcherConfig);\n\n return outputErrorResponse(error, response, fetcherConfig);\n }\n\n logger(`Attempt ${attempt + 1} failed. Retry in ${waitTime}ms.`);\n\n await delayInvocation(waitTime);\n\n waitTime *= backoff;\n waitTime = Math.min(waitTime, maxDelay);\n attempt++;\n }\n }\n\n return outputResponse(response, fetcherConfig) as ResponseData &\n FetchResponse;\n };\n\n /**\n * Output response\n *\n * @param response - Response payload\n * @param {RequestConfig} requestConfig - Request config\n * @param error - whether the response is erroneous\n * @returns {ResponseData | FetchResponse} Response data\n */\n const outputResponse = (\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n error: ResponseError | null = null,\n ): ResponseData | FetchResponse => {\n const defaultResponse = getConfig(requestConfig, 'defaultResponse');\n const flattenResponse = getConfig(\n requestConfig,\n 'flattenResponse',\n );\n\n if (!response) {\n return flattenResponse\n ? defaultResponse\n : {\n error,\n headers: null,\n data: defaultResponse,\n config: requestConfig,\n };\n }\n\n // Clean up the error object\n deleteProperty(error, 'response');\n deleteProperty(error, 'request');\n deleteProperty(error, 'config');\n\n let data = response?.data;\n\n // Set the default response if the provided data is an empty object\n if (\n data === undefined ||\n data === null ||\n (typeof data === OBJECT && Object.keys(data).length === 0)\n ) {\n data = defaultResponse;\n }\n\n // Return flattened response immediately\n if (flattenResponse) {\n return flattenData(data);\n }\n\n // If it's a custom fetcher, and it does not return any Response instance, it may have its own internal handler\n if (!(response instanceof Response)) {\n return response;\n }\n\n // Native fetch Response extended by extra information\n return {\n body: response.body,\n bodyUsed: response.bodyUsed,\n formData: response.formData,\n ok: response.ok,\n redirected: response.redirected,\n type: response.type,\n url: response.url,\n status: response.status,\n statusText: response.statusText,\n\n blob: response.blob.bind(response),\n json: response.json.bind(response),\n text: response.text.bind(response),\n clone: response.clone.bind(response),\n arrayBuffer: response.arrayBuffer.bind(response),\n\n // Extend with extra information\n error,\n data,\n headers: processHeaders(response.headers),\n config: requestConfig,\n };\n };\n\n return {\n getInstance,\n buildConfig,\n config,\n request,\n };\n}\n","import type {\n RequestConfig,\n FetchResponse,\n CreatedCustomFetcherInstance,\n} from './types/request-handler';\nimport type {\n ApiHandlerConfig,\n ApiHandlerMethods,\n ApiHandlerReturnType,\n APIResponse,\n QueryParamsOrBody,\n UrlPathParams,\n} from './types/api-handler';\nimport { createRequestHandler } from './request-handler';\n\n/**\n * Creates an instance of API Handler.\n * It creates an API fetcher function using native fetch() or a custom fetcher if it is passed as \"fetcher\".\n * @url https://github.com/MattCCC/fetchff\n *\n * @param {Object} config - Configuration object for the API fetcher.\n * @param {string} config.apiUrl - The base URL for the API.\n * @param {Object} config.endpoints - An object containing endpoint definitions.\n * @param {number} config.timeout - You can set the timeout for particular request in milliseconds.\n * @param {number} config.cancellable - If true, the ongoing previous requests will be automatically cancelled.\n * @param {number} config.rejectCancelled - If true and request is set to cancellable, a cancelled request promise will be rejected. By default, instead of rejecting the promise, defaultResponse is returned.\n * @param {number} config.timeout - Request timeout\n * @param {number} config.dedupeTime - Time window, in milliseconds, during which identical requests are deduplicated (treated as single request).\n * @param {string} config.strategy - Error Handling Strategy\n * @param {string} config.flattenResponse - Whether to flatten response \"data\" object within \"data\" one\n * @param {*} config.defaultResponse - Default response when there is no data or when endpoint fails depending on the chosen strategy. It's \"null\" by default\n * @param {Object} [config.retry] - Options for retrying requests.\n * @param {number} [config.retry.retries=0] - Number of retry attempts. No retries by default.\n * @param {number} [config.retry.delay=1000] - Initial delay between retries in milliseconds.\n * @param {number} [config.retry.backoff=1.5] - Exponential backoff factor.\n * @param {number[]} [config.retry.retryOn=[502, 504, 408]] - HTTP status codes to retry on.\n * @param {RequestInterceptor|RequestInterceptor[]} [config.onRequest] - Optional request interceptor function or an array of functions.\n * These functions will be called with the request configuration object before the request is made. Can be used to modify or log the request configuration.\n * @param {ResponseInterceptor|ResponseInterceptor[]} [config.onResponse] - Optional response interceptor function or an array of functions.\n * These functions will be called with the response object after the response is received. an be used to modify or log the response data.\n * @param {Function} [config.onError] - Optional callback function for handling errors.\n * @param {Object} [config.headers] - Optional default headers to include in every request.\n * @param {Object} config.fetcher - The Custom Fetcher instance to use for making requests. It should expose create() and request() functions.\n * @param {*} config.logger - Instance of custom logger. Either class or an object similar to \"console\". Console is used by default.\n * @returns API handler functions and endpoints to call\n *\n * @example\n * // Define endpoint paths\n * const endpoints = {\n * getUser: '/user',\n * createPost: '/post',\n * };\n *\n * // Create the API fetcher with configuration\n * const api = createApiFetcher({\n * endpoints,\n * apiUrl: 'https://example.com/api',\n * onError(error) {\n * console.log('Request failed', error);\n * },\n * headers: {\n * 'my-auth-key': 'example-auth-key-32rjjfa',\n * },\n * });\n *\n * // Fetch user data\n * const response = await api.getUser({ userId: 1, ratings: [1, 2] })\n */\nfunction createApiFetcher<\n EndpointsMethods extends object,\n EndpointsCfg = never,\n>(config: ApiHandlerConfig) {\n const endpoints = config.endpoints;\n const requestHandler = createRequestHandler(config);\n\n /**\n * Get Custom Fetcher Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Request Handler's Custom Fetcher Instance\n */\n function getInstance(): CreatedCustomFetcherInstance | null {\n return requestHandler.getInstance();\n }\n\n /**\n * Triggered when trying to use non-existent endpoints\n *\n * @param endpointName Endpoint Name\n * @returns {Promise}\n */\n function handleNonImplemented(endpointName: string): Promise {\n console.error(`Add ${endpointName} to 'endpoints'.`);\n\n return Promise.resolve(null);\n }\n\n /**\n * Handle Single API Request\n * It considers settings in following order: per-request settings, global per-endpoint settings, global settings.\n *\n * @param {string} endpointName - The name of the API endpoint to call.\n * @param {QueryParamsOrBody} [data={}] - Query parameters to include in the request.\n * @param {UrlPathParams} [urlPathParams={}] - URI parameters to include in the request.\n * @param {EndpointConfig} [requestConfig={}] - Additional configuration for the request.\n * @returns {Promise} - A promise that resolves with the response from the API provider.\n */\n async function request(\n endpointName: keyof EndpointsMethods | string,\n data: QueryParamsOrBody = {},\n urlPathParams: UrlPathParams = {},\n requestConfig: RequestConfig = {},\n ): Promise> {\n // Use global per-endpoint settings\n const endpointConfig = endpoints[endpointName as string];\n\n const responseData = await requestHandler.request(\n endpointConfig.url,\n data,\n {\n ...(endpointConfig || {}),\n ...requestConfig,\n urlPathParams,\n },\n );\n\n return responseData;\n }\n\n /**\n * Maps all API requests using native Proxy\n *\n * @param {*} prop Caller\n */\n function get(prop: string) {\n if (prop in apiHandler) {\n return apiHandler[\n prop as unknown as keyof ApiHandlerMethods\n ];\n }\n\n // Prevent handler from triggering non-existent endpoints\n if (!endpoints[prop]) {\n return handleNonImplemented.bind(null, prop);\n }\n\n return apiHandler.request.bind(null, prop);\n }\n\n const apiHandler: ApiHandlerMethods = {\n config,\n endpoints,\n requestHandler,\n getInstance,\n request,\n };\n\n return new Proxy(apiHandler, {\n get: (_target, prop: string) => get(prop),\n }) as ApiHandlerReturnType;\n}\n\nexport { createApiFetcher };\n"],"mappings":"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","names":["src_exports","__export","createApiFetcher","fetchf","__toCommonJS","applyInterceptor","object","interceptors","value","interceptor","ResponseErr","message","requestInfo","response","__publicField","APPLICATION_JSON","CONTENT_TYPE","UNDEFINED","OBJECT","STRING","ABORT_ERROR","TIMEOUT_ERROR","CANCELLED_ERROR","GET","HEAD","isSearchParams","data","shallowSerialize","obj","result","key","sortObject","sortedObj","keys","i","len","appendQueryStringToUrl","baseUrl","queryString","appendQueryParams","url","params","isSearchParams","encodedQueryString","s","encode","add","k","v","buildParams","prefix","OBJECT","replaceUrlPathParams","urlPathParams","str","word","isJSONSerializable","value","UNDEFINED","STRING","proto","delayInvocation","ms","resolve","flattenData","data","processHeaders","headers","headersObject","deleteProperty","property","queue","addRequest","config","timeout","dedupeTime","isCancellable","isTimeoutEnabled","now","item","previousController","timeoutId","ABORT_ERROR","controller","error","TIMEOUT_ERROR","removeRequest","parseResponseData","response","_a","contentType","CONTENT_TYPE","data","APPLICATION_JSON","_e","_error","hash","str","i","len","char","cache","generateCacheKey","options","url","method","GET","headers","body","mode","credentials","redirect","referrer","integrity","headersString","shallowSerialize","sortObject","bodyString","hash","value","key","UNDEFINED","o","OBJECT","isCacheExpired","timestamp","maxStaleTime","getCache","cacheTime","entry","setCache","data","isLoading","defaultConfig","GET","APPLICATION_JSON","CONTENT_TYPE","createRequestHandler","config","handlerConfig","__spreadValues","customFetcher","requestInstance","getInstance","getConfig","reqConfig","name","UNDEFINED","logger","args","_a","buildConfig","url","data","method","isGetAlikeMethod","HEAD","dynamicUrl","replaceUrlPathParams","explicitParams","explicitBodyData","shouldTreatDataAsParams","body","credentials","deleteProperty","urlPath","appendQueryParams","baseURL","STRING","isSearchParams","isJSONSerializable","__spreadProps","processError","error","requestConfig","isRequestCancelled","applyInterceptor","outputErrorResponse","response","_isRequestCancelled","errorHandlingStrategy","rejectCancelled","outputResponse","ABORT_ERROR","CANCELLED_ERROR","request","_reqConfig","mergedConfig","fetcherConfig","timeout","cancellable","dedupeTime","pollingInterval","shouldStopPolling","cacheTime","cacheKey","_cacheKey","generateCacheKey","cacheBuster","cachedEntry","getCache","retries","delay","backoff","retryOn","shouldRetry","maxDelay","resetTimeout","attempt","pollingAttempt","waitTime","controller","addRequest","parseResponseData","ResponseErr","removeRequest","delayInvocation","output","skipCache","setCache","err","status","defaultResponse","flattenResponse","OBJECT","flattenData","processHeaders","createApiFetcher","config","endpoints","requestHandler","createRequestHandler","getInstance","handleNonImplemented","endpointName","request","data","urlPathParams","requestConfig","endpointConfig","__spreadProps","__spreadValues","get","prop","apiHandler","_target","fetchf","url","config","createRequestHandler"]} \ No newline at end of file +{"version":3,"sources":["../../src/index.ts","../../src/interceptor-manager.ts","../../src/response-error.ts","../../src/constants.ts","../../src/utils.ts","../../src/queue-manager.ts","../../src/response-parser.ts","../../src/hash.ts","../../src/cache-manager.ts","../../src/request-handler.ts","../../src/api-handler.ts"],"sourcesContent":["import { createRequestHandler } from './request-handler';\nimport type {\n DefaultResponse,\n FetchResponse,\n RequestHandlerConfig,\n} from './types';\n\n/**\n * Simple wrapper for request fetching.\n * It abstracts the creation of RequestHandler, making it easy to perform API requests.\n *\n * @param {string | URL | globalThis.Request} url - Request URL.\n * @param {RequestHandlerConfig} config - Configuration object for the request handler.\n * @returns {Promise>} Response Data.\n */\nexport async function fetchf(\n url: string,\n config: RequestHandlerConfig = {},\n): Promise> {\n return createRequestHandler(config).request(url, config);\n}\n\nexport * from './types';\nexport * from './api-handler';\n","type InterceptorFunction = (object: T) => Promise;\n\n/**\n * Applies interceptors to the object. Interceptors can be a single function or an array of functions.\n *\n * @template T - Type of the object.\n * @template I - Type of interceptors.\n *\n * @param {T} object - The object to process.\n * @param {InterceptorFunction | InterceptorFunction[]} [interceptors] - Interceptor function(s).\n *\n * @returns {Promise} - Nothing as the function is non-idempotent.\n */\nexport async function applyInterceptor<\n T extends object,\n I = InterceptorFunction | InterceptorFunction[],\n>(object: T, interceptors?: I): Promise {\n if (!interceptors) {\n return;\n }\n\n if (typeof interceptors === 'function') {\n const value = await interceptors(object);\n\n if (value) {\n Object.assign(object, value);\n }\n } else if (Array.isArray(interceptors)) {\n for (const interceptor of interceptors) {\n const value = await interceptor(object);\n\n if (value) {\n Object.assign(object, value);\n }\n }\n }\n}\n","import type { FetchResponse, RequestConfig } from './types';\n\nexport class ResponseErr extends Error {\n response: FetchResponse;\n request: RequestConfig;\n config: RequestConfig;\n status: number;\n statusText: string;\n\n constructor(\n message: string,\n requestInfo: RequestConfig,\n response: FetchResponse,\n ) {\n super(message);\n\n this.name = 'ResponseError';\n this.message = message;\n this.status = response.status;\n this.statusText = response.statusText;\n this.request = requestInfo;\n this.config = requestInfo;\n this.response = response;\n }\n}\n","export const APPLICATION_CONTENT_TYPE = 'application/';\n\nexport const APPLICATION_JSON = APPLICATION_CONTENT_TYPE + 'json';\nexport const CONTENT_TYPE = 'Content-Type';\n\nexport const UNDEFINED = 'undefined';\nexport const OBJECT = 'object';\nexport const STRING = 'string';\n\nexport const ABORT_ERROR = 'AbortError';\nexport const TIMEOUT_ERROR = 'TimeoutError';\nexport const CANCELLED_ERROR = 'CanceledError';\n\nexport const GET = 'GET';\nexport const HEAD = 'HEAD';\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { OBJECT, STRING, UNDEFINED } from './constants';\nimport type {\n DefaultUrlParams,\n HeadersObject,\n QueryParams,\n UrlPathParams,\n} from './types';\n\nexport function isSearchParams(data: unknown): boolean {\n return data instanceof URLSearchParams;\n}\n\n/**\n * Determines if a value is a non-null object.\n *\n * @param {any} value - The value to check.\n * @returns {boolean} - True if the value is a non-null object.\n */\nexport function isObject(value: any): value is Record {\n return value !== null && typeof value === OBJECT;\n}\n\n/**\n * Shallowly serializes an object by converting its key-value pairs into a string representation.\n * This function does not recursively serialize nested objects.\n *\n * @param obj - The object to serialize.\n * @returns A string representation of the object's top-level properties.\n */\nexport function shallowSerialize(obj: Record): string {\n let result = '';\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n result += key + ':' + obj[key];\n }\n }\n\n return result;\n}\n\n/**\n * Sorts the keys of an object and returns a new object with sorted keys.\n *\n * This function is optimized for performance by minimizing the number of object operations\n * and using a single pass to create the sorted object.\n *\n * @param {Object} obj - The object to be sorted by keys.\n * @returns {Object} - A new object with keys sorted in ascending order.\n */\nexport function sortObject(obj: Record): object {\n const sortedObj = {} as Record;\n const keys = Object.keys(obj);\n\n keys.sort();\n\n for (let i = 0, len = keys.length; i < len; i++) {\n const key = keys[i];\n sortedObj[key] = obj[key];\n }\n\n return sortedObj;\n}\n\n/**\n * Appends a query string to a URL, ensuring proper handling of existing query parameters.\n *\n * @param baseUrl - The base URL to which the query string will be appended.\n * @param queryString - The encoded query string to append.\n * @returns The URL with the appended query string, or the original URL if no query string is provided.\n */\nfunction appendQueryStringToUrl(baseUrl: string, queryString: string): string {\n if (!queryString) {\n return baseUrl;\n }\n\n return baseUrl.includes('?')\n ? `${baseUrl}&${queryString}`\n : `${baseUrl}?${queryString}`;\n}\n\n/**\n * Appends query parameters to a given URL.\n *\n * @param {string} url - The base URL to which query parameters will be appended.\n * @param {QueryParams} params - An object containing the query parameters to append.\n * @returns {string} - The URL with the appended query parameters.\n */\nexport function appendQueryParams(url: string, params: QueryParams): string {\n if (!params) {\n return url;\n }\n\n // Check if `params` is an instance of URLSearchParams and bail early if it is\n if (isSearchParams(params)) {\n const encodedQueryString = params.toString();\n\n return appendQueryStringToUrl(url, encodedQueryString);\n }\n\n // This is exact copy of what JQ used to do. It works much better than URLSearchParams\n const s: string[] = [];\n const encode = encodeURIComponent;\n const add = (k: string, v: any) => {\n v = typeof v === 'function' ? v() : v;\n v = v === null ? '' : v === undefined ? '' : v;\n s[s.length] = encode(k) + '=' + encode(v);\n };\n\n const buildParams = (prefix: string, obj: any) => {\n let i: number, len: number, key: string;\n\n if (prefix) {\n if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n buildParams(\n prefix + '[' + (typeof obj[i] === OBJECT && obj[i] ? i : '') + ']',\n obj[i],\n );\n }\n } else if (typeof obj === OBJECT && obj !== null) {\n for (key in obj) {\n buildParams(prefix + '[' + key + ']', obj[key]);\n }\n } else {\n add(prefix, obj);\n }\n } else if (Array.isArray(obj)) {\n for (i = 0, len = obj.length; i < len; i++) {\n add(obj[i].name, obj[i].value);\n }\n } else {\n for (key in obj) {\n buildParams(key, obj[key]);\n }\n }\n return s;\n };\n\n const queryStringParts = buildParams('', params).join('&');\n\n // Encode special characters as per RFC 3986, https://datatracker.ietf.org/doc/html/rfc3986\n const encodedQueryString = queryStringParts.replace(/%5B%5D/g, '[]'); // Keep '[]' for arrays\n\n return appendQueryStringToUrl(url, encodedQueryString);\n}\n\n/**\n * Replaces dynamic URI parameters in a URL string with values from the provided `urlPathParams` object.\n * Parameters in the URL are denoted by `:`, where `` is a key in `urlPathParams`.\n *\n * @param {string} url - The URL string containing placeholders in the format `:`.\n * @param {Object} urlPathParams - An object containing the parameter values to replace placeholders.\n * @param {string} urlPathParams.paramName - The value to replace the placeholder `:` in the URL.\n * @returns {string} - The URL string with placeholders replaced by corresponding values from `urlPathParams`.\n */\nexport function replaceUrlPathParams(\n url: string,\n urlPathParams: UrlPathParams,\n): string {\n if (!urlPathParams) {\n return url;\n }\n\n return url.replace(/:\\w+/g, (str): string => {\n const word = str.substring(1);\n\n if ((urlPathParams as DefaultUrlParams)[word]) {\n return String((urlPathParams as DefaultUrlParams)[word]);\n }\n\n return str;\n });\n}\n\n/**\n * Checks if a value is JSON serializable.\n *\n * JSON serializable values include:\n * - Primitive types: string, number, boolean, null\n * - Arrays\n * - Plain objects (i.e., objects without special methods)\n * - Values with a `toJSON` method\n *\n * @param {any} value - The value to check for JSON serializability.\n * @returns {boolean} - Returns `true` if the value is JSON serializable, otherwise `false`.\n */\nexport function isJSONSerializable(value: any): boolean {\n const t = typeof value;\n\n if (t === UNDEFINED || value === null) {\n return false;\n }\n\n if (t === STRING || t === 'number' || t === 'boolean') {\n return true;\n }\n\n if (Array.isArray(value)) {\n return true;\n }\n\n if (Buffer.isBuffer(value)) {\n return false;\n }\n\n if (value instanceof Date) {\n return false;\n }\n\n if (t === OBJECT) {\n const proto = Object.getPrototypeOf(value);\n\n // Check if the prototype is `Object.prototype` or `null` (plain object)\n if (proto === Object.prototype || proto === null) {\n return true;\n }\n\n // Check if the object has a toJSON method\n if (typeof value.toJSON === 'function') {\n return true;\n }\n }\n\n return false;\n}\n\nexport async function delayInvocation(ms: number): Promise {\n return new Promise((resolve) =>\n setTimeout(() => {\n return resolve(true);\n }, ms),\n );\n}\n\n/**\n * Recursively flattens the data object if it meets specific criteria.\n *\n * The method checks if the provided `data` is an object with exactly one property named `data`.\n * If so, it recursively flattens the `data` property. Otherwise, it returns the `data` as-is.\n *\n * @param {any} data - The data to be flattened. Can be of any type, including objects, arrays, or primitives.\n * @returns {any} - The flattened data if the criteria are met; otherwise, the original `data`.\n */\nexport function flattenData(data: any): any {\n if (\n data &&\n typeof data === OBJECT &&\n typeof data.data !== UNDEFINED &&\n Object.keys(data).length === 1\n ) {\n return flattenData(data.data);\n }\n\n return data;\n}\n\n/**\n * Processes headers and returns them as a normalized object.\n *\n * Handles both `Headers` instances and plain objects. Normalizes header keys to lowercase\n * as per RFC 2616 section 4.2.\n *\n * @param headers - The headers to process. Can be an instance of `Headers`, a plain object,\n * or `null`. If `null`, an empty object is returned.\n * @returns {HeadersObject} - A normalized headers object with lowercase keys.\n */\nexport function processHeaders(\n headers?: (HeadersObject & HeadersInit) | null | Headers,\n): HeadersObject {\n if (!headers) {\n return {};\n }\n\n const headersObject: HeadersObject = {};\n\n // Handle Headers object with entries() method\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n headersObject[key] = value;\n });\n } else if (typeof headers === OBJECT && headers !== null) {\n // Handle plain object\n for (const [key, value] of Object.entries(headers)) {\n // Normalize keys to lowercase as per RFC 2616 4.2\n // https://datatracker.ietf.org/doc/html/rfc2616#section-4.2\n headersObject[key.toLowerCase()] = value;\n }\n }\n\n return headersObject;\n}\n\n/**\n * Deletes a property from an object if it exists.\n *\n * @param obj - The object from which to delete the property.\n * @param property - The property to delete from the object.\n */\nexport function deleteProperty>(\n obj: T | null,\n property: keyof T,\n): void {\n if (obj && property in obj) {\n delete obj[property];\n }\n}\n","import { ABORT_ERROR, TIMEOUT_ERROR } from './constants';\nimport type { RequestConfig } from './types';\nimport type { QueueItem, RequestsQueue } from './types/queue-manager';\n\n/**\n * Queue Manager is responsible for managing and controlling the flow of concurrent or sequential requests. It handles:\n * - Request Queueing and Deduplication\n * - Request Timeout Handling\n * - Abort Controller Management and Request Cancellation\n * - Concurrency Control and Locking\n * - Request Lifecycle Management\n */\nconst queue: RequestsQueue = new Map();\n\n/**\n * Adds a request to the queue if it's not already being processed within the dedupeTime interval.\n *\n * @param {RequestConfig} config - The request configuration object.\n * @param {number} timeout - Timeout in milliseconds for the request.\n * @param {number} dedupeTime - Deduplication time in milliseconds.\n * @param {boolean} isCancellable - If true, then the previous request with same configuration should be aborted.\n * @param {boolean} isTimeoutEnabled - Whether timeout is enabled.\n * @returns {Promise} - A promise that resolves to an AbortController.\n */\nexport async function addRequest(\n config: RequestConfig,\n timeout: number | undefined,\n dedupeTime: number = 0,\n isCancellable: boolean = false,\n isTimeoutEnabled: boolean = true,\n): Promise {\n const now = Date.now();\n const item = queue.get(config);\n\n if (item) {\n const isCancellable = item[3];\n const previousController = item[0];\n const timeoutId = item[1];\n\n // If the request is already in the queue and within the dedupeTime, reuse the existing controller\n if (!isCancellable && now - item[2] < dedupeTime) {\n return previousController;\n }\n\n // If the request is too old, remove it and proceed to add a new one\n // Abort previous request, if applicable, and continue as usual\n if (isCancellable) {\n previousController.abort(\n new DOMException('Aborted due to new request', ABORT_ERROR),\n );\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n\n const controller = new AbortController();\n\n const timeoutId = isTimeoutEnabled\n ? setTimeout(() => {\n const error = new DOMException(\n `${config.url} aborted due to timeout`,\n TIMEOUT_ERROR,\n );\n\n removeRequest(config, error);\n }, timeout)\n : null;\n\n queue.set(config, [controller, timeoutId, now, isCancellable]);\n\n return controller;\n}\n\n/**\n * Removes a request from the queue and clears its timeout.\n *\n * @param config - The request configuration.\n * @param {boolean} error - Error payload so to force the request to abort.\n */\nexport async function removeRequest(\n config: RequestConfig,\n error: DOMException | null | string = null,\n): Promise {\n const item = queue.get(config);\n\n if (item) {\n const controller = item[0];\n const timeoutId = item[1];\n\n // If the request is not yet aborted, abort it with the provided error\n if (error && !controller.signal.aborted) {\n controller.abort(error);\n }\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n queue.delete(config);\n }\n}\n\n/**\n * Gets the AbortController for a request configuration.\n *\n * @param config - The request configuration.\n * @returns {AbortController | undefined} - The AbortController or undefined.\n */\nexport async function getController(\n config: RequestConfig,\n): Promise {\n const item = queue.get(config);\n\n return item?.[0];\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n APPLICATION_CONTENT_TYPE,\n APPLICATION_JSON,\n CONTENT_TYPE,\n} from './constants';\nimport type { DefaultResponse, FetchResponse } from './types/request-handler';\n\n/**\n * Parses the response data based on the Content-Type header.\n *\n * @param response - The Response object to parse.\n * @returns A Promise that resolves to the parsed data.\n */\nexport async function parseResponseData(\n response: FetchResponse,\n): Promise {\n // Bail early when body is empty\n if (!response?.body) {\n return null;\n }\n\n const contentType = String(\n (response as Response).headers?.get(CONTENT_TYPE) || '',\n ).split(';')[0]; // Correctly handle charset\n\n let data;\n\n try {\n if (\n contentType.includes(APPLICATION_JSON) ||\n contentType.includes('+json')\n ) {\n data = await response.json(); // Parse JSON response\n } else if (contentType.includes('multipart/form-data')) {\n data = await response.formData(); // Parse as FormData\n } else if (\n contentType.includes(APPLICATION_CONTENT_TYPE + 'octet-stream')\n ) {\n data = await response.blob(); // Parse as blob\n } else if (\n contentType.includes(APPLICATION_CONTENT_TYPE + 'x-www-form-urlencoded')\n ) {\n data = await response.formData(); // Handle URL-encoded forms\n } else if (contentType.includes('text/')) {\n data = await response.text(); // Parse as text\n } else {\n try {\n const responseClone = response.clone();\n\n // Handle edge case of no content type being provided... We assume JSON here.\n data = await responseClone.json();\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_e) {\n // Handle streams\n data = await response.text();\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_error) {\n // Parsing failed, fallback to null\n data = null;\n }\n\n return data;\n}\n","const PRIME_MULTIPLIER = 31;\n\n/**\n * Computes a hash value for a given string using the variant of djb2 hash function.\n * This hash function is non-cryptographic and designed for speed.\n * @author Daniel J. Bernstein (of djb2)\n *\n * @param str Input string to hash\n * @returns {string} Hash\n */\nexport function hash(str: string): string {\n let hash = 0;\n\n for (let i = 0, len = str.length; i < len; i++) {\n const char = str.charCodeAt(i);\n hash = (hash * PRIME_MULTIPLIER + char) | 0;\n }\n\n return String(hash);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { hash } from './hash';\nimport { fetchf } from './index';\nimport type { FetcherConfig } from './types/request-handler';\nimport type { CacheEntry } from './types/cache-manager';\nimport { GET, OBJECT, UNDEFINED } from './constants';\nimport { shallowSerialize, sortObject } from './utils';\n\nconst cache = new Map>();\n\n/**\n * Generates a cache key for a given URL and fetch options, ensuring that key factors\n * like method, headers, body, and other options are included in the cache key.\n * Headers and other objects are sorted by key to ensure consistent cache keys.\n *\n * @param options - The fetch options that may affect the request. The most important are:\n * @property {string} [method=\"GET\"] - The HTTP method (GET, POST, etc.).\n * @property {HeadersInit} [headers={}] - The request headers.\n * @property {BodyInit | null} [body=\"\"] - The body of the request (only for methods like POST, PUT).\n * @property {RequestMode} [mode=\"cors\"] - The mode for the request (e.g., cors, no-cors, include).\n * @property {RequestCredentials} [credentials=\"include\"] - Whether to include credentials like cookies.\n * @property {RequestCache} [cache=\"default\"] - The cache mode (e.g., default, no-store, reload).\n * @property {RequestRedirect} [redirect=\"follow\"] - How to handle redirects (e.g., follow, error, manual).\n * @property {string} [referrer=\"\"] - The referrer URL to send with the request.\n * @property {string} [integrity=\"\"] - Subresource integrity value (a cryptographic hash for resource validation).\n * @returns {string} - A unique cache key based on the URL and request options. Empty if cache is to be burst.\n *\n * @example\n * const cacheKey = generateCacheKey({\n * url: 'https://api.example.com/data',\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ name: 'Alice' }),\n * mode: 'cors',\n * credentials: 'include',\n * });\n * console.log(cacheKey);\n */\nexport function generateCacheKey(options: FetcherConfig): string {\n const {\n url = '',\n method = GET,\n headers = {},\n body = '',\n mode = 'cors',\n credentials = 'include',\n cache = 'default',\n redirect = 'follow',\n referrer = '',\n integrity = '',\n } = options;\n\n // Bail early if cache should be burst\n if (cache === 'reload') {\n return '';\n }\n\n // Sort headers and body + convert sorted to strings for hashing purposes\n // Native serializer is on avg. 3.5x faster than a Fast Hash or FNV-1a\n const headersString = shallowSerialize(sortObject(headers));\n\n let bodyString = '';\n\n // In majority of cases we do not cache body\n if (body !== null) {\n if (typeof body === 'string') {\n bodyString = hash(body);\n } else if (body instanceof FormData) {\n body.forEach((value, key) => {\n // Append key=value and '&' directly to the result\n bodyString += key + '=' + value + '&';\n });\n bodyString = hash(bodyString);\n } else if (\n (typeof Blob !== UNDEFINED && body instanceof Blob) ||\n (typeof File !== UNDEFINED && body instanceof File)\n ) {\n bodyString = 'BF' + body.size + body.type;\n } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n bodyString = 'AB' + body.byteLength;\n } else {\n const o = typeof body === OBJECT ? sortObject(body) : String(body);\n bodyString = hash(JSON.stringify(o));\n }\n }\n\n // Concatenate all key parts into a cache key string\n // Template literals are apparently slower\n return (\n method +\n url +\n mode +\n credentials +\n cache +\n redirect +\n referrer +\n integrity +\n headersString +\n bodyString\n );\n}\n\n/**\n * Checks if the cache entry is expired based on its timestamp and the maximum stale time.\n *\n * @param {number} timestamp - The timestamp of the cache entry.\n * @param {number} maxStaleTime - The maximum stale time in seconds.\n * @returns {boolean} - Returns true if the cache entry is expired, false otherwise.\n */\nfunction isCacheExpired(timestamp: number, maxStaleTime: number): boolean {\n if (!maxStaleTime) {\n return false;\n }\n\n return Date.now() - timestamp > maxStaleTime * 1000;\n}\n\n/**\n * Retrieves a cache entry if it exists and is not expired.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} cacheTime - Maximum time to cache entry.\n * @returns {CacheEntry | null} - The cache entry if it exists and is not expired, null otherwise.\n */\nexport function getCache(\n key: string,\n cacheTime: number,\n): CacheEntry | null {\n const entry = cache.get(key);\n\n if (entry) {\n if (!isCacheExpired(entry.timestamp, cacheTime)) {\n return entry;\n }\n\n cache.delete(key);\n }\n\n return null;\n}\n\n/**\n * Sets a new cache entry or updates an existing one.\n *\n * @param {string} key Cache key to utilize\n * @param {T} data - The data to be cached.\n * @param {boolean} isLoading - Indicates if the data is currently being fetched.\n */\nexport function setCache(\n key: string,\n data: T,\n isLoading: boolean = false,\n): void {\n cache.set(key, {\n data,\n isLoading,\n timestamp: Date.now(),\n });\n}\n\n/**\n * Revalidates a cache entry by fetching fresh data and updating the cache.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @returns {Promise} - A promise that resolves when the revalidation is complete.\n */\nexport async function revalidate(\n key: string,\n config: FetcherConfig,\n): Promise {\n try {\n // Fetch fresh data\n const newData = await fetchf(config.url, {\n ...config,\n cache: 'reload',\n });\n\n setCache(key, newData);\n } catch (error) {\n console.error(`Error revalidating ${config.url}:`, error);\n\n // Rethrow the error to forward it\n throw error;\n }\n}\n\n/**\n * Invalidates (deletes) a cache entry.\n *\n * @param {string} key Cache key to utilize\n */\nexport function deleteCache(key: string): void {\n cache.delete(key);\n}\n\n/**\n * Mutates a cache entry with new data and optionally revalidates it.\n *\n * @param {string} key Cache key to utilize\n * @param {FetcherConfig} config - The request configuration object.\n * @param {T} newData - The new data to be cached.\n * @param {boolean} revalidateAfter - If true, triggers revalidation after mutation.\n */\nexport function mutate(\n key: string,\n config: FetcherConfig,\n newData: T,\n revalidateAfter: boolean = false,\n): void {\n setCache(key, newData);\n\n if (revalidateAfter) {\n revalidate(key, config);\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {\n DefaultResponse,\n RequestHandlerConfig,\n RequestConfig,\n Method,\n RetryOptions,\n FetchResponse,\n ResponseError,\n RequestHandlerReturnType,\n CreatedCustomFetcherInstance,\n FetcherConfig,\n} from './types/request-handler';\nimport type {\n BodyPayload,\n DefaultParams,\n DefaultPayload,\n DefaultUrlParams,\n QueryParams,\n} from './types/api-handler';\nimport { applyInterceptor } from './interceptor-manager';\nimport { ResponseErr } from './response-error';\nimport {\n appendQueryParams,\n isJSONSerializable,\n replaceUrlPathParams,\n delayInvocation,\n flattenData,\n processHeaders,\n deleteProperty,\n isSearchParams,\n} from './utils';\nimport { addRequest, removeRequest } from './queue-manager';\nimport {\n ABORT_ERROR,\n APPLICATION_JSON,\n CANCELLED_ERROR,\n CONTENT_TYPE,\n GET,\n HEAD,\n OBJECT,\n STRING,\n UNDEFINED,\n} from './constants';\nimport { parseResponseData } from './response-parser';\nimport { generateCacheKey, getCache, setCache } from './cache-manager';\n\nconst defaultConfig: RequestHandlerConfig = {\n method: GET,\n strategy: 'reject',\n timeout: 30000,\n dedupeTime: 1000,\n defaultResponse: null,\n headers: {\n Accept: APPLICATION_JSON + ', text/plain, */*',\n 'Accept-Encoding': 'gzip, deflate, br',\n [CONTENT_TYPE]: APPLICATION_JSON + ';charset=utf-8',\n },\n retry: {\n delay: 1000,\n maxDelay: 30000,\n resetTimeout: true,\n backoff: 1.5,\n\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status\n retryOn: [\n 408, // Request Timeout\n 409, // Conflict\n 425, // Too Early\n 429, // Too Many Requests\n 500, // Internal Server Error\n 502, // Bad Gateway\n 503, // Service Unavailable\n 504, // Gateway Timeout\n ],\n },\n};\n\n/**\n * Create Request Handler\n *\n * @param {RequestHandlerConfig} config - Configuration object for the request handler\n * @returns {Object} An object with methods for handling requests\n */\nexport function createRequestHandler(\n config: RequestHandlerConfig,\n): RequestHandlerReturnType {\n const handlerConfig: RequestHandlerConfig = {\n ...defaultConfig,\n ...config,\n };\n\n /**\n * Immediately create instance of custom fetcher if it is defined\n */\n const customFetcher = handlerConfig.fetcher;\n const requestInstance = customFetcher?.create(handlerConfig) || null;\n\n /**\n * Get Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Provider's instance\n */\n const getInstance = (): CreatedCustomFetcherInstance | null => {\n return requestInstance;\n };\n\n /**\n * Gets a configuration value from `reqConfig`, defaulting to `handlerConfig` if not present.\n *\n * @param {RequestConfig} reqConfig - Request configuration object.\n * @param {keyof RequestConfig} name - Key of the configuration value.\n * @returns {T} - The configuration value.\n */\n const getConfig = (\n reqConfig: RequestConfig,\n name: keyof RequestConfig,\n ): T => {\n return typeof reqConfig[name] !== UNDEFINED\n ? reqConfig[name]\n : handlerConfig[name];\n };\n\n /**\n * Logs messages or errors using the configured logger's `warn` method.\n *\n * @param {...(string | ResponseError)} args - Messages or errors to log.\n */\n const logger = (...args: (string | ResponseError)[]): void => {\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn(...args);\n }\n };\n\n /**\n * Build request configuration\n *\n * @param {string} url - Request url\n * @param {RequestConfig} reqConfig - Request config passed when making the request\n * @returns {RequestConfig} - Provider's instance\n */\n const buildConfig = (\n url: string,\n reqConfig: RequestConfig,\n ): FetcherConfig => {\n const method = getConfig(\n reqConfig,\n 'method',\n ).toUpperCase() as Method;\n const isGetAlikeMethod = method === GET || method === HEAD;\n\n const dynamicUrl = replaceUrlPathParams(\n url,\n getConfig(reqConfig, 'urlPathParams'),\n );\n\n // The explicitly passed \"params\"\n const explicitParams = getConfig(reqConfig, 'params');\n\n // The explicitly passed \"body\" or \"data\"\n const explicitBodyData: BodyPayload =\n getConfig(reqConfig, 'body') || getConfig(reqConfig, 'data');\n\n // Final body data\n let body: RequestConfig['data'];\n\n // Only applicable for request methods 'PUT', 'POST', 'DELETE', and 'PATCH'\n if (!isGetAlikeMethod) {\n body = explicitBodyData;\n }\n\n // Native fetch compatible settings\n const isWithCredentials = getConfig(reqConfig, 'withCredentials');\n\n const credentials = isWithCredentials\n ? 'include'\n : getConfig(reqConfig, 'credentials');\n\n const urlPath = explicitParams\n ? appendQueryParams(dynamicUrl, explicitParams)\n : dynamicUrl;\n const isFullUrl = urlPath.includes('://');\n const baseURL = isFullUrl\n ? ''\n : getConfig(reqConfig, 'baseURL') ||\n getConfig(reqConfig, 'apiUrl');\n\n // Automatically stringify request body, if possible and when not dealing with strings\n if (\n body &&\n typeof body !== STRING &&\n !isSearchParams(body) &&\n isJSONSerializable(body)\n ) {\n body = JSON.stringify(body);\n }\n\n return {\n ...reqConfig,\n credentials,\n body,\n method,\n\n url: baseURL + urlPath,\n };\n };\n\n /**\n * Process global Request Error\n *\n * @param {ResponseError} error Error instance\n * @param {RequestConfig} requestConfig Per endpoint request config\n * @returns {Promise}\n */\n const processError = async (\n error: ResponseError,\n requestConfig: RequestConfig,\n ): Promise => {\n if (!isRequestCancelled(error)) {\n logger('API ERROR', error);\n }\n\n // Local interceptors\n await applyInterceptor(error, requestConfig?.onError);\n\n // Global interceptors\n await applyInterceptor(error, handlerConfig?.onError);\n };\n\n /**\n * Output default response in case of an error, depending on chosen strategy\n *\n * @param {ResponseError} error - Error instance\n * @param {FetchResponse | null} response - Response. It may be \"null\" in case of request being aborted.\n * @param {RequestConfig} requestConfig - Per endpoint request config\n * @returns {FetchResponse} Response together with the error object\n */\n const outputErrorResponse = async (\n error: ResponseError,\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n ): Promise => {\n const _isRequestCancelled = isRequestCancelled(error);\n const errorHandlingStrategy = getConfig(requestConfig, 'strategy');\n const rejectCancelled = getConfig(\n requestConfig,\n 'rejectCancelled',\n );\n\n // By default cancelled requests aren't rejected (softFail strategy)\n if (!(_isRequestCancelled && !rejectCancelled)) {\n // Hang the promise\n if (errorHandlingStrategy === 'silent') {\n await new Promise(() => null);\n }\n // Reject the promise\n else if (errorHandlingStrategy === 'reject') {\n return Promise.reject(error);\n }\n }\n\n return outputResponse(response, requestConfig, error);\n };\n\n /**\n * Output error response depending on chosen strategy\n *\n * @param {ResponseError} error Error instance\n * @returns {boolean} True if request is aborted\n */\n const isRequestCancelled = (error: ResponseError): boolean => {\n return error.name === ABORT_ERROR || error.name === CANCELLED_ERROR;\n };\n\n /**\n * Handle Request depending on used strategy\n *\n * @param {string} url - Request url\n * @param {RequestConfig} reqConfig - Request config\n * @throws {ResponseError}\n * @returns {Promise>} Response Data\n */\n const request = async <\n ResponseData = DefaultResponse,\n QueryParams = DefaultParams,\n PathParams = DefaultUrlParams,\n RequestBody = DefaultPayload,\n >(\n url: string,\n reqConfig: RequestConfig<\n ResponseData,\n QueryParams,\n PathParams,\n RequestBody\n > | null = null,\n ): Promise> => {\n const _reqConfig = reqConfig || {};\n const mergedConfig = {\n ...handlerConfig,\n ..._reqConfig,\n } as RequestConfig;\n\n let response: FetchResponse | null = null;\n const fetcherConfig = buildConfig(url, mergedConfig);\n\n const {\n timeout,\n cancellable,\n dedupeTime,\n pollingInterval,\n shouldStopPolling,\n cacheTime,\n cacheKey,\n } = mergedConfig;\n\n // Prevent performance overhead of cache access\n let _cacheKey: string;\n\n if (cacheKey) {\n _cacheKey = cacheKey(fetcherConfig);\n } else {\n _cacheKey = generateCacheKey(fetcherConfig);\n }\n\n if (cacheTime && _cacheKey) {\n const cacheBuster = mergedConfig.cacheBuster;\n\n if (!cacheBuster || !cacheBuster(fetcherConfig)) {\n const cachedEntry = getCache>(\n _cacheKey,\n cacheTime,\n );\n\n if (cachedEntry) {\n // Serve stale data from cache\n return cachedEntry.data;\n }\n }\n }\n\n const {\n retries = 0,\n delay,\n backoff,\n retryOn,\n shouldRetry,\n maxDelay,\n resetTimeout,\n } = mergedConfig.retry as Required;\n\n let attempt = 0;\n let pollingAttempt = 0;\n let waitTime: number = delay;\n const _retries = retries > 0 ? retries : 0;\n\n while (attempt <= _retries) {\n try {\n // Add the request to the queue. Make sure to handle deduplication, cancellation, timeouts in accordance to retry settings\n const controller = await addRequest(\n fetcherConfig,\n timeout,\n dedupeTime,\n cancellable,\n // Reset timeouts by default or when retries are ON\n !!(timeout && (!_retries || resetTimeout)),\n );\n\n // Shallow copy to ensure basic idempotency\n const requestConfig: RequestConfig = {\n signal: controller.signal,\n ...fetcherConfig,\n };\n\n // Local interceptors\n await applyInterceptor(requestConfig, _reqConfig?.onRequest);\n\n // Global interceptors\n await applyInterceptor(requestConfig, handlerConfig?.onRequest);\n\n if (customFetcher !== null && requestInstance !== null) {\n response = await requestInstance.request(requestConfig);\n } else {\n response = (await fetch(\n requestConfig.url as string,\n requestConfig as RequestInit,\n )) as FetchResponse;\n }\n\n // Add more information to response object\n if (response instanceof Response) {\n response.config = requestConfig;\n response.data = await parseResponseData(response);\n\n // Check if the response status is not outside the range 200-299 and if so, output error\n if (!response.ok) {\n throw new ResponseErr(\n `${requestConfig.url} failed! Status: ${response.status || null}`,\n requestConfig,\n response,\n );\n }\n }\n\n // Local interceptors\n await applyInterceptor(response, _reqConfig?.onResponse);\n\n // Global interceptors\n await applyInterceptor(response, handlerConfig?.onResponse);\n\n removeRequest(fetcherConfig);\n\n // Polling logic\n if (\n pollingInterval &&\n (!shouldStopPolling || !shouldStopPolling(response, pollingAttempt))\n ) {\n // Restart the main retry loop\n pollingAttempt++;\n\n logger(`Polling attempt ${pollingAttempt}...`);\n\n await delayInvocation(pollingInterval);\n\n continue;\n }\n\n // If polling is not required, or polling attempts are exhausted\n const output = outputResponse(response, requestConfig);\n\n if (cacheTime && _cacheKey) {\n const skipCache = requestConfig.skipCache;\n\n if (!skipCache || !skipCache(output, requestConfig)) {\n setCache(_cacheKey, output);\n }\n }\n\n return output;\n } catch (err) {\n const error = err as ResponseErr;\n const status = error?.response?.status || error?.status || 0;\n\n if (\n attempt === retries ||\n !(!shouldRetry || (await shouldRetry(error, attempt))) ||\n !retryOn?.includes(status)\n ) {\n await processError(error, fetcherConfig);\n\n removeRequest(fetcherConfig);\n\n return outputErrorResponse(\n error,\n response,\n fetcherConfig,\n );\n }\n\n logger(`Attempt ${attempt + 1} failed. Retry in ${waitTime}ms.`);\n\n await delayInvocation(waitTime);\n\n waitTime *= backoff;\n waitTime = Math.min(waitTime, maxDelay);\n attempt++;\n }\n }\n\n return outputResponse(response, fetcherConfig);\n };\n\n /**\n * Output response\n *\n * @param Response. It may be \"null\" in case of request being aborted.\n * @param {RequestConfig} requestConfig - Request config\n * @param error - whether the response is erroneous\n * @returns {FetchResponse} Response data\n */\n const outputResponse = (\n response: FetchResponse | null,\n requestConfig: RequestConfig,\n error: ResponseError | null = null,\n ): FetchResponse => {\n const defaultResponse = getConfig(requestConfig, 'defaultResponse');\n\n // This may happen when request is cancelled.\n if (!response) {\n return {\n ok: false,\n // Enhance the response with extra information\n error,\n data: defaultResponse,\n headers: null,\n config: requestConfig,\n } as unknown as FetchResponse;\n }\n\n // Clean up the error object\n deleteProperty(error, 'response');\n deleteProperty(error, 'request');\n deleteProperty(error, 'config');\n\n let data = response?.data;\n\n // Set the default response if the provided data is an empty object\n if (\n data === undefined ||\n data === null ||\n (typeof data === OBJECT && Object.keys(data).length === 0)\n ) {\n data = defaultResponse;\n }\n\n // Return flattened response immediately\n const flattenResponse = getConfig(\n requestConfig,\n 'flattenResponse',\n );\n\n if (flattenResponse) {\n response.data = flattenData(data);\n }\n\n // If it's a custom fetcher, and it does not return any Response instance, it may have its own internal handler\n if (!(response instanceof Response)) {\n return response;\n }\n\n // Native fetch Response extended by extra information\n return {\n body: response.body,\n bodyUsed: response.bodyUsed,\n formData: response.formData,\n ok: response.ok,\n redirected: response.redirected,\n type: response.type,\n url: response.url,\n status: response.status,\n statusText: response.statusText,\n\n blob: response.blob.bind(response),\n json: response.json.bind(response),\n text: response.text.bind(response),\n clone: response.clone.bind(response),\n arrayBuffer: response.arrayBuffer.bind(response),\n\n // Enhance the response with extra information\n error,\n data,\n headers: processHeaders(response.headers),\n config: requestConfig,\n };\n };\n\n return {\n getInstance,\n buildConfig,\n config,\n request,\n };\n}\n","import type {\n RequestConfig,\n FetchResponse,\n DefaultResponse,\n CreatedCustomFetcherInstance,\n} from './types/request-handler';\nimport type {\n ApiHandlerConfig,\n ApiHandlerDefaultMethods,\n ApiHandlerMethods,\n DefaultPayload,\n FallbackValue,\n FinalParams,\n FinalResponse,\n QueryParams,\n RequestConfigUrlRequired,\n UrlPathParams,\n} from './types/api-handler';\nimport { createRequestHandler } from './request-handler';\n\n/**\n * Creates an instance of API Handler.\n * It creates an API fetcher function using native fetch() or a custom fetcher if it is passed as \"fetcher\".\n * @url https://github.com/MattCCC/fetchff\n *\n * @param {Object} config - Configuration object for the API fetcher.\n * @param {string} config.apiUrl - The base URL for the API.\n * @param {Object} config.endpoints - An object containing endpoint definitions.\n * @param {number} config.timeout - You can set the timeout for particular request in milliseconds.\n * @param {number} config.cancellable - If true, the ongoing previous requests will be automatically cancelled.\n * @param {number} config.rejectCancelled - If true and request is set to cancellable, a cancelled request promise will be rejected. By default, instead of rejecting the promise, defaultResponse is returned.\n * @param {number} config.timeout - Request timeout\n * @param {number} config.dedupeTime - Time window, in milliseconds, during which identical requests are deduplicated (treated as single request).\n * @param {string} config.strategy - Error Handling Strategy\n * @param {string} config.flattenResponse - Whether to flatten response \"data\" object within \"data\". It works only if the response structure includes a single data property.\n * @param {*} config.defaultResponse - Default response when there is no data or when endpoint fails depending on the chosen strategy. It's \"null\" by default\n * @param {Object} [config.retry] - Options for retrying requests.\n * @param {number} [config.retry.retries=0] - Number of retry attempts. No retries by default.\n * @param {number} [config.retry.delay=1000] - Initial delay between retries in milliseconds.\n * @param {number} [config.retry.backoff=1.5] - Exponential backoff factor.\n * @param {number[]} [config.retry.retryOn=[502, 504, 408]] - HTTP status codes to retry on.\n * @param {RequestInterceptor|RequestInterceptor[]} [config.onRequest] - Optional request interceptor function or an array of functions.\n * These functions will be called with the request configuration object before the request is made. Can be used to modify or log the request configuration.\n * @param {ResponseInterceptor|ResponseInterceptor[]} [config.onResponse] - Optional response interceptor function or an array of functions.\n * These functions will be called with the response object after the response is received. an be used to modify or log the response data.\n * @param {Function} [config.onError] - Optional callback function for handling errors.\n * @param {Object} [config.headers] - Optional default headers to include in every request.\n * @param {Object} config.fetcher - The Custom Fetcher instance to use for making requests. It should expose create() and request() functions.\n * @param {*} config.logger - Instance of custom logger. Either class or an object similar to \"console\". Console is used by default.\n * @returns API handler functions and endpoints to call\n *\n * @example\n * // Define endpoint paths\n * const endpoints = {\n * getUser: '/user',\n * createPost: '/post',\n * };\n *\n * // Create the API fetcher with configuration\n * const api = createApiFetcher({\n * endpoints,\n * apiUrl: 'https://example.com/api',\n * onError(error) {\n * console.log('Request failed', error);\n * },\n * headers: {\n * 'my-auth-key': 'example-auth-key-32rjjfa',\n * },\n * });\n *\n * // Fetch user data\n * const response = await api.getUser({ userId: 1, ratings: [1, 2] })\n */\nfunction createApiFetcher<\n EndpointsMethods extends object,\n EndpointsSettings = never,\n>(config: ApiHandlerConfig) {\n const endpoints = config.endpoints;\n const requestHandler = createRequestHandler(config);\n\n /**\n * Get Custom Fetcher Provider Instance\n *\n * @returns {CreatedCustomFetcherInstance | null} Request Handler's Custom Fetcher Instance\n */\n function getInstance(): CreatedCustomFetcherInstance | null {\n return requestHandler.getInstance();\n }\n\n /**\n * Triggered when trying to use non-existent endpoints\n *\n * @param endpointName Endpoint Name\n * @returns {Promise}\n */\n function handleNonImplemented(endpointName: string): Promise {\n console.error(`Add ${endpointName} to 'endpoints'.`);\n\n return Promise.resolve(null);\n }\n\n /**\n * Handle Single API Request\n * It considers settings in following order: per-request settings, global per-endpoint settings, global settings.\n *\n * @param {keyof EndpointsMethods | string} endpointName - The name of the API endpoint to call.\n * @param {EndpointConfig} [requestConfig={}] - Additional configuration for the request.\n * @returns {Promise>} - A promise that resolves with the response from the API provider.\n */\n async function request<\n ResponseData = never,\n QueryParams_ = never,\n UrlParams = never,\n RequestBody = never,\n >(\n endpointName: keyof EndpointsMethods | string,\n requestConfig: RequestConfig<\n FinalResponse,\n FinalParams,\n FinalParams,\n FallbackValue\n > = {},\n ): Promise>> {\n // Use global per-endpoint settings\n const endpointConfig =\n endpoints[endpointName] ||\n ({ url: endpointName as string } as RequestConfigUrlRequired);\n\n const responseData = await requestHandler.request<\n FinalResponse,\n FinalParams,\n FinalParams,\n FallbackValue\n >(endpointConfig.url, {\n ...endpointConfig,\n ...requestConfig,\n });\n\n return responseData;\n }\n\n const apiHandler: ApiHandlerDefaultMethods = {\n config,\n endpoints,\n requestHandler,\n getInstance,\n request,\n };\n\n /**\n * Maps all API requests using native Proxy\n *\n * @param {*} prop Caller\n */\n return new Proxy>(\n apiHandler as ApiHandlerMethods,\n {\n get(_target, prop: string) {\n if (prop in apiHandler) {\n return apiHandler[prop as unknown as keyof typeof apiHandler];\n }\n\n // Prevent handler from triggering non-existent endpoints\n if (endpoints[prop]) {\n return apiHandler.request.bind(null, prop);\n }\n\n return handleNonImplemented.bind(null, prop);\n },\n },\n );\n}\n\nexport { createApiFetcher };\n"],"mappings":"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","names":["src_exports","__export","createApiFetcher","fetchf","__toCommonJS","applyInterceptor","object","interceptors","value","interceptor","ResponseErr","message","requestInfo","response","APPLICATION_CONTENT_TYPE","APPLICATION_JSON","CONTENT_TYPE","UNDEFINED","OBJECT","STRING","ABORT_ERROR","TIMEOUT_ERROR","CANCELLED_ERROR","GET","HEAD","isSearchParams","data","shallowSerialize","obj","result","key","sortObject","sortedObj","keys","i","len","appendQueryStringToUrl","baseUrl","queryString","appendQueryParams","url","params","isSearchParams","encodedQueryString","s","encode","add","k","v","buildParams","prefix","OBJECT","replaceUrlPathParams","urlPathParams","str","word","isJSONSerializable","value","UNDEFINED","STRING","proto","delayInvocation","ms","resolve","flattenData","data","processHeaders","headers","headersObject","deleteProperty","property","queue","addRequest","config","timeout","dedupeTime","isCancellable","isTimeoutEnabled","now","item","previousController","timeoutId","ABORT_ERROR","controller","error","TIMEOUT_ERROR","removeRequest","parseResponseData","response","contentType","CONTENT_TYPE","data","APPLICATION_JSON","APPLICATION_CONTENT_TYPE","hash","str","i","len","char","cache","generateCacheKey","options","url","method","GET","headers","body","mode","credentials","redirect","referrer","integrity","headersString","shallowSerialize","sortObject","bodyString","hash","value","key","UNDEFINED","o","OBJECT","isCacheExpired","timestamp","maxStaleTime","getCache","cacheTime","entry","setCache","data","isLoading","defaultConfig","GET","APPLICATION_JSON","CONTENT_TYPE","createRequestHandler","config","handlerConfig","customFetcher","requestInstance","getInstance","getConfig","reqConfig","name","UNDEFINED","logger","args","buildConfig","url","method","isGetAlikeMethod","HEAD","dynamicUrl","replaceUrlPathParams","explicitParams","explicitBodyData","body","credentials","urlPath","appendQueryParams","baseURL","STRING","isSearchParams","isJSONSerializable","processError","error","requestConfig","isRequestCancelled","applyInterceptor","outputErrorResponse","response","_isRequestCancelled","errorHandlingStrategy","rejectCancelled","outputResponse","ABORT_ERROR","CANCELLED_ERROR","request","_reqConfig","mergedConfig","fetcherConfig","timeout","cancellable","dedupeTime","pollingInterval","shouldStopPolling","cacheTime","cacheKey","_cacheKey","generateCacheKey","cacheBuster","cachedEntry","getCache","retries","delay","backoff","retryOn","shouldRetry","maxDelay","resetTimeout","attempt","pollingAttempt","waitTime","_retries","addRequest","parseResponseData","ResponseErr","removeRequest","delayInvocation","output","skipCache","setCache","err","status","defaultResponse","deleteProperty","data","OBJECT","flattenData","processHeaders","createApiFetcher","config","endpoints","requestHandler","createRequestHandler","getInstance","handleNonImplemented","endpointName","request","requestConfig","endpointConfig","apiHandler","_target","prop","fetchf","url","config","createRequestHandler"]} \ No newline at end of file