diff --git a/dist/browser/index.global.js b/dist/browser/index.global.js index 06e4931..9a27b8c 100644 --- a/dist/browser/index.global.js +++ b/dist/browser/index.global.js @@ -1,2 +1,2 @@ -"use strict";var fetchff=(()=>{var F=Object.defineProperty,re=Object.defineProperties,oe=Object.getOwnPropertyDescriptor,ae=Object.getOwnPropertyDescriptors,ie=Object.getOwnPropertyNames,W=Object.getOwnPropertySymbols;var Z=Object.prototype.hasOwnProperty,le=Object.prototype.propertyIsEnumerable;var k=(t,e,n)=>e in t?F(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,m=(t,e)=>{for(var n in e||(e={}))Z.call(e,n)&&k(t,n,e[n]);if(W)for(var n of W(e))le.call(e,n)&&k(t,n,e[n]);return t},w=(t,e)=>re(t,ae(e));var ue=(t,e)=>{for(var n in e)F(t,n,{get:e[n],enumerable:!0})},ce=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let l of ie(e))!Z.call(t,l)&&l!==n&&F(t,l,{get:()=>e[l],enumerable:!(o=oe(e,l))||o.enumerable});return t};var pe=t=>ce(F({},"__esModule",{value:!0}),t);var C=(t,e,n)=>k(t,typeof e!="symbol"?e+"":e,n);var me={};ue(me,{createApiFetcher:()=>fe,fetchf:()=>Re});async function L(t,e){if(!e)return t;let n=Array.isArray(e)?e:[e],o=m({},t);for(let l of n)o=await l(o);return o}async function $(t,e){if(!e)return t;let n=Array.isArray(e)?e:[e],o=t;for(let l of n)o=await l(o);return o}var U=class extends Error{constructor(n,o,l){super(n);C(this,"response");C(this,"request");C(this,"config");C(this,"status");C(this,"statusText");this.name="ResponseError",this.message=n,this.status=l.status,this.statusText=l.statusText,this.request=o,this.config=o,this.response=l}};function K(t,e){if(!e)return t;if(e instanceof URLSearchParams){let p=e.toString();return t.includes("?")?`${t}&${p}`:p?`${t}?${p}`:t}let n=[],o=function(p,r){r=typeof r=="function"?r():r,r=r===null||r===void 0?"":r,n[n.length]=encodeURIComponent(p)+"="+encodeURIComponent(r)},l=(p,r)=>{let d,g,s;if(p)if(Array.isArray(r))for(d=0,g=r.length;d{let o=n.substring(1);return String(e[o]?e[o]:n)}):t}function X(t){if(t==null)return!1;let e=typeof t;if(e==="string"||e==="number"||e==="boolean")return!0;if(e!=="object")return!1;if(Array.isArray(t))return!0;if(Buffer.isBuffer(t)||t instanceof Date)return!1;let n=Object.getPrototypeOf(t);return n===Object.prototype||n===null||typeof t.toJSON=="function"}async function v(t){return new Promise(e=>setTimeout(()=>e(!0),t))}function B(t){return t&&typeof t=="object"&&typeof t.data!="undefined"&&Object.keys(t).length===1?B(t.data):t}function ee(t){if(!t)return{};let e={};if(t instanceof Headers)t.forEach((n,o)=>{e[o]=n});else if(typeof t=="object"&&t!==null)for(let[n,o]of Object.entries(t))e[n.toLowerCase()]=o;return e}function D(t,e){t&&e in t&&delete t[e]}var T=new Map;async function te(t,e,n=0,o=!1,l=!0){let b=Date.now(),R=T.get(t);if(R){if(!R.isCancellable&&b-R.timestamp{let d=new DOMException(`${t.url} aborted due to timeout`,"TimeoutError");S(t,d)},e):null;return T.set(t,{controller:p,timeoutId:r,timestamp:b,isCancellable:o}),p}async function S(t,e=null){let n=T.get(t);n&&(e&&!n.controller.signal.aborted&&n.controller.abort(e),n.timeoutId!==null&&clearTimeout(n.timeoutId),T.delete(t))}var E="application/json",Q="Content-Type";async function ne(t){var o;if(!(t!=null&&t.body))return null;let e=String(((o=t.headers)==null?void 0:o.get(Q))||"").split(";")[0],n;try{if(e.includes(E)||e.includes("+json"))n=await t.json();else if(e.includes("multipart/form-data"))n=await t.formData();else if(e.includes("application/octet-stream"))n=await t.blob();else if(e.includes("application/x-www-form-urlencoded"))n=await t.formData();else if(e.includes("text/"))n=await t.text();else try{n=await t.clone().json()}catch(l){n=await t.text()}}catch(l){n=null}return n}var de={method:"GET",strategy:"reject",timeout:3e4,rejectCancelled:!1,dedupeTime:1e3,withCredentials:!1,flattenResponse:!1,defaultResponse:null,logger:null,fetcher:null,baseURL:"",retry:{retries:0,delay:1e3,maxDelay:3e4,resetTimeout:!0,backoff:1.5,retryOn:[408,409,425,429,500,502,503,504],shouldRetry:async()=>!0}};function M(t){let e=m(w(m({},de),{baseURL:t.apiUrl||""}),t),n=()=>e.fetcher!==null,o=n()?e.fetcher.create(w(m({},t),{baseURL:e.baseURL,timeout:e.timeout})):null,l=()=>o,b=(s,c,a)=>{let u=(a.method||e.method).toUpperCase(),P=u==="GET"||u==="HEAD",i=V(s,a.urlPathParams||e.urlPathParams||null),A=a.params||e.params,O=a.body||a.data||e.body||e.data,H=!!(c&&(P||O)),y;if(P||(y=O||c),n())return w(m({},a),{method:u,url:i,params:H?c:A,data:y});let N=(typeof a.withCredentials!="undefined"?a.withCredentials:e.withCredentials)?"include":a.credentials||e.credentials||void 0;D(a,"data"),D(a,"withCredentials");let q=A||H?K(i,A||c):i,j=q.includes("://")?"":a.baseURL||e.baseURL;return y&&typeof y!="string"&&!(y instanceof URLSearchParams)&&X(y)&&(y=JSON.stringify(y)),w(m({},a),{credentials:N,body:y,method:u,url:j+q,headers:m(m({Accept:E+", text/plain, */*","Accept-Encoding":"gzip, deflate, br",[Q]:E+";charset=utf-8"},e.headers||{}),a.headers||{})})},R=(s,c)=>{var a;r(s)||((a=e.logger)!=null&&a.warn&&e.logger.warn("API ERROR",s),c.onError&&c.onError(s),e.onError&&e.onError(s))},p=async(s,c,a)=>{let u=r(s),P=a.strategy||e.strategy,i=typeof a.rejectCancelled!="undefined"?a.rejectCancelled:e.rejectCancelled;if(!(u&&!i)){if(P==="silent")await new Promise(()=>null);else if(P==="reject")return Promise.reject(s)}return g(c,a,s)},r=s=>s.name==="AbortError"||s.name==="CanceledError",d=async(s,c=null,a=null)=>{var z,G;let u=null,i=b(s,c,a||{}),A=typeof i.timeout!="undefined"?i.timeout:e.timeout,O=typeof i.cancellable!="undefined"?i.cancellable:e.cancellable,H=typeof i.dedupeTime!="undefined"?i.dedupeTime:e.dedupeTime,{retries:y,delay:_,backoff:N,retryOn:q,shouldRetry:J,maxDelay:j,resetTimeout:se}=m(m({},e.retry),(i==null?void 0:i.retry)||{}),x=0,I=_;for(;x<=y;)try{let h=(await te(i,A,H,O,A>0&&(!y||se))).signal,f=m({signal:h},i);if(f=await L(f,f==null?void 0:f.onRequest),f=await L(f,e==null?void 0:e.onRequest),n())u=await o.request(f);else if(u=await fetch(f.url,f),u.config=f,u.data=await ne(u),!u.ok)throw new U(`${f.url} failed! Status: ${u.status||null}`,f,u);return u=await $(u,f==null?void 0:f.onResponse),u=await $(u,e==null?void 0:e.onResponse),S(i),g(u,f)}catch(Y){let h=Y,f=((z=h==null?void 0:h.response)==null?void 0:z.status)||(h==null?void 0:h.status)||0;if(x===y||!await J(h,x)||!(q!=null&&q.includes(f)))return R(h,i),S(i),p(h,u,i);(G=e.logger)!=null&&G.warn&&e.logger.warn(`Attempt ${x+1} failed. Retrying in ${I}ms...`),await v(I),I*=N,I=Math.min(I,j),x++}return g(u,i)},g=(s,c,a=null)=>{let u=typeof c.defaultResponse!="undefined"?c.defaultResponse:e.defaultResponse,P=typeof c.flattenResponse!="undefined"?c.flattenResponse:e.flattenResponse;if(!s)return P?u:{error:a,headers:null,data:u,config:c};D(a,"response"),D(a,"request"),D(a,"config");let i=s==null?void 0:s.data;return(i==null||typeof i=="object"&&Object.keys(i).length===0)&&(i=u),P?B(i):n()?s:{body:s.body,blob:s.blob,json:s.json,text:s.text,clone:s.clone,bodyUsed:s.bodyUsed,arrayBuffer:s.arrayBuffer,formData:s.formData,ok:s.ok,redirected:s.redirected,type:s.type,url:s.url,status:s.status,statusText:s.statusText,error:a,data:i,headers:ee(s.headers),config:c}};return{getInstance:l,buildConfig:b,config:t,request:d}}function fe(t){let e=t.endpoints,n=M(t);function o(){return n.getInstance()}function l(r){return console.error(`${r} endpoint must be added to 'endpoints'.`),Promise.resolve(null)}async function b(r,d={},g={},s={}){let c=e[r];return await n.request(c.url,d,w(m(m({},c||{}),s),{urlPathParams:g}))}function R(r){return r in p?p[r]:e[r]?p.request.bind(null,r):l.bind(null,r)}let p={config:t,endpoints:e,requestHandler:n,getInstance:o,request:b};return new Proxy(p,{get:(r,d)=>R(d)})}async function Re(t,e={}){return M(e).request(t,null,e)}return pe(me);})(); +"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);})(); //# 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 035415b..ce64d7a 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/utils.ts","../../src/queue-manager.ts","../../src/const.ts","../../src/response-parser.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","import type { RequestHandlerConfig, FetchResponse } from './types';\nimport type {\n RequestInterceptor,\n ResponseInterceptor,\n} from './types/interceptor-manager';\n\n/**\n * Applies a series of request interceptors to the provided configuration.\n * @param {RequestHandlerConfig} config - The initial request configuration.\n * @param {RequestInterceptor | RequestInterceptor[]} interceptors - The request interceptor function(s) to apply.\n * @returns {Promise} - The modified request configuration.\n */\nexport async function interceptRequest(\n config: RequestHandlerConfig,\n interceptors?: RequestInterceptor | RequestInterceptor[],\n): Promise {\n if (!interceptors) {\n return config;\n }\n\n const interceptorList = Array.isArray(interceptors)\n ? interceptors\n : [interceptors];\n\n let interceptedConfig = { ...config };\n\n for (const interceptor of interceptorList) {\n interceptedConfig = await interceptor(interceptedConfig);\n }\n\n return interceptedConfig;\n}\n\n/**\n * Applies a series of response interceptors to the provided response.\n * @param {FetchResponse} response - The initial response object.\n * @param {ResponseInterceptor | ResponseInterceptor[]} interceptors - The response interceptor function(s) to apply.\n * @returns {Promise>} - The modified response object.\n */\nexport async function interceptResponse(\n response: FetchResponse,\n interceptors?: ResponseInterceptor | ResponseInterceptor[],\n): Promise> {\n if (!interceptors) {\n return response;\n }\n\n const interceptorList = Array.isArray(interceptors)\n ? interceptors\n : [interceptors];\n\n let interceptedResponse = response;\n\n for (const interceptor of interceptorList) {\n interceptedResponse = await interceptor(interceptedResponse);\n }\n\n return interceptedResponse;\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","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { HeadersObject, QueryParams, UrlPathParams } from './types';\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 (params instanceof URLSearchParams) {\n const encodedQueryString = params.toString();\n\n return url.includes('?')\n ? `${url}&${encodedQueryString}`\n : encodedQueryString\n ? `${url}?${encodedQueryString}`\n : url;\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 add = function (k: string, v: any) {\n v = typeof v === 'function' ? v() : v;\n v = v === null ? '' : v === undefined ? '' : v;\n s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(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 +\n '[' +\n (typeof obj[i] === 'object' && obj[i] ? i : '') +\n ']',\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 url.includes('?')\n ? `${url}&${encodedQueryString}`\n : encodedQueryString\n ? `${url}?${encodedQueryString}`\n : url;\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(/:[a-zA-Z]+/gi, (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 if (value === undefined || value === null) {\n return false;\n }\n\n const t = typeof value;\n if (t === 'string' || t === 'number' || t === 'boolean') {\n return true;\n }\n\n if (t !== 'object') {\n return false; // bigint, function, symbol, undefined\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 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 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 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,\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 // If the request is already in the queue and within the dedupeTime, reuse the existing controller\n if (!item.isCancellable && now - item.timestamp < dedupeTime) {\n return item.controller;\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 (item.isCancellable) {\n item.controller.abort(\n new DOMException('Aborted due to new request', 'AbortError'),\n );\n }\n\n if (item.timeoutId !== null) {\n clearTimeout(item.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 'TimeoutError',\n );\n\n removeRequest(config, error);\n }, timeout)\n : null;\n\n queue.set(config, { controller, timeoutId, timestamp: 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 // If the request is not yet aborted, abort it with the provided error\n if (error && !item.controller.signal.aborted) {\n item.controller.abort(error);\n }\n\n if (item.timeoutId !== null) {\n clearTimeout(item.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?.controller;\n}\n","export const APPLICATION_JSON = 'application/json';\nexport const CONTENT_TYPE = 'Content-Type';\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","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {\n RequestHandlerConfig,\n RequestConfig,\n FetcherInstance,\n Method,\n RetryOptions,\n FetchResponse,\n ResponseError,\n RequestHandlerReturnType,\n} from './types/request-handler';\nimport type {\n APIResponse,\n BodyPayload,\n QueryParams,\n QueryParamsOrBody,\n} from './types/api-handler';\nimport { interceptRequest, interceptResponse } from './interceptor-manager';\nimport { ResponseErr } from './response-error';\nimport {\n appendQueryParams,\n isJSONSerializable,\n replaceUrlPathParams,\n delayInvocation,\n flattenData,\n processHeaders,\n deleteProperty,\n} from './utils';\nimport { addRequest, removeRequest } from './queue-manager';\nimport { APPLICATION_JSON, CONTENT_TYPE } from './const';\nimport { parseResponseData } from './response-parser';\n\nconst defaultConfig: RequestHandlerConfig = {\n method: 'GET',\n strategy: 'reject',\n timeout: 30000,\n rejectCancelled: false,\n dedupeTime: 1000,\n withCredentials: false,\n flattenResponse: false,\n defaultResponse: null,\n logger: null,\n fetcher: null,\n baseURL: '',\n retry: {\n retries: 0,\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 shouldRetry: async () => true,\n },\n};\n\n/**\n * Create a Request Handler\n *\n * @param {RequestHandlerConfig} config - Configuration object for the request handler\n * @returns {Object} An object with methods for handling requests\n */\nfunction createRequestHandler(\n config: RequestHandlerConfig,\n): RequestHandlerReturnType {\n const handlerConfig: RequestHandlerConfig = {\n ...defaultConfig,\n baseURL: config.apiUrl || '',\n ...config,\n };\n\n /**\n * Detects if a custom fetcher is utilized\n *\n * @returns {boolean} True if it's a custom fetcher\n */\n const isCustomFetcher = (): boolean => {\n return handlerConfig.fetcher !== null;\n };\n\n const requestInstance = isCustomFetcher()\n ? (handlerConfig.fetcher as any).create({\n ...config,\n baseURL: handlerConfig.baseURL,\n timeout: handlerConfig.timeout,\n })\n : null;\n\n /**\n * Get Provider Instance\n *\n * @returns {FetcherInstance} Provider's instance\n */\n const getInstance = (): FetcherInstance => {\n return requestInstance;\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 ): RequestConfig => {\n const method = (\n reqConfig.method || (handlerConfig.method as string)\n ).toUpperCase() as Method;\n const isGetAlikeMethod = method === 'GET' || method === 'HEAD';\n\n const dynamicUrl = replaceUrlPathParams(\n url,\n reqConfig.urlPathParams || handlerConfig.urlPathParams || null,\n );\n\n // The explicitly passed \"params\"\n const explicitParams = reqConfig.params || handlerConfig.params;\n\n // The explicitly passed \"body\" or \"data\"\n const explicitBodyData =\n reqConfig.body ||\n reqConfig.data ||\n handlerConfig.body ||\n handlerConfig.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 if (isCustomFetcher()) {\n return {\n ...reqConfig,\n method,\n url: dynamicUrl,\n params: shouldTreatDataAsParams\n ? (data as QueryParams)\n : explicitParams,\n data: body,\n };\n }\n\n // Native fetch\n const isWithCredentials =\n typeof reqConfig.withCredentials !== 'undefined'\n ? reqConfig.withCredentials\n : handlerConfig.withCredentials;\n\n const credentials = isWithCredentials\n ? 'include'\n : reqConfig.credentials || handlerConfig.credentials || undefined;\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 ? '' : reqConfig.baseURL || handlerConfig.baseURL;\n\n // Automatically stringify request body, if possible and when not dealing with strings\n if (\n body &&\n typeof body !== 'string' &&\n !(body instanceof URLSearchParams) &&\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 // Add sensible defaults\n headers: {\n Accept: APPLICATION_JSON + ', text/plain, */*',\n 'Accept-Encoding': 'gzip, deflate, br',\n [CONTENT_TYPE]: APPLICATION_JSON + ';charset=utf-8',\n ...(handlerConfig.headers || {}),\n ...(reqConfig.headers || {}),\n },\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 {void}\n */\n const processError = (\n error: ResponseError,\n requestConfig: RequestConfig,\n ): void => {\n if (isRequestCancelled(error)) {\n return;\n }\n\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn('API ERROR', error);\n }\n\n // Invoke per request \"onError\" interceptor\n if (requestConfig.onError) {\n requestConfig.onError(error);\n }\n\n // Invoke global \"onError\" interceptor\n if (handlerConfig.onError) {\n handlerConfig.onError(error);\n }\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 =\n requestConfig.strategy || handlerConfig.strategy;\n const rejectCancelled =\n typeof requestConfig.rejectCancelled !== 'undefined'\n ? requestConfig.rejectCancelled\n : handlerConfig.rejectCancelled;\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 === 'AbortError' || error.name === 'CanceledError';\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 let response: FetchResponse | null = null;\n const _reqConfig = reqConfig || {};\n const fetcherConfig = buildConfig(url, data, _reqConfig);\n\n const timeout =\n typeof fetcherConfig.timeout !== 'undefined'\n ? fetcherConfig.timeout\n : (handlerConfig.timeout as number);\n const isCancellable =\n typeof fetcherConfig.cancellable !== 'undefined'\n ? fetcherConfig.cancellable\n : handlerConfig.cancellable;\n const dedupeTime =\n typeof fetcherConfig.dedupeTime !== 'undefined'\n ? fetcherConfig.dedupeTime\n : handlerConfig.dedupeTime;\n\n const {\n retries,\n delay,\n backoff,\n retryOn,\n shouldRetry,\n maxDelay,\n resetTimeout,\n } = {\n ...handlerConfig.retry,\n ...(fetcherConfig?.retry || {}),\n } as Required;\n\n let attempt = 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 isCancellable,\n // Reset timeouts by default or when retries are ON\n timeout > 0 && (!retries || resetTimeout),\n );\n const signal = controller.signal;\n\n let requestConfig: RequestConfig = {\n signal,\n ...fetcherConfig,\n };\n\n // Local interceptors\n requestConfig = await interceptRequest(\n requestConfig,\n requestConfig?.onRequest,\n );\n\n // Global interceptors\n requestConfig = await interceptRequest(\n requestConfig,\n handlerConfig?.onRequest,\n );\n\n if (isCustomFetcher()) {\n response = (await (requestInstance as any).request(\n requestConfig,\n )) as FetchResponse;\n } else {\n response = (await fetch(\n requestConfig.url as string,\n requestConfig as RequestInit,\n )) as FetchResponse;\n\n // Add more information to response object\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 response = await interceptResponse(response, requestConfig?.onResponse);\n\n // Global interceptors\n response = await interceptResponse(response, handlerConfig?.onResponse);\n\n removeRequest(fetcherConfig);\n\n return outputResponse(response, requestConfig) as ResponseData &\n FetchResponse;\n } catch (err) {\n const error = err as ResponseError;\n const status = error?.response?.status || (error as any)?.status || 0;\n\n if (\n attempt === retries ||\n !(await shouldRetry(error, attempt)) ||\n !retryOn?.includes(status)\n ) {\n processError(error, fetcherConfig);\n\n removeRequest(fetcherConfig);\n\n return outputErrorResponse(error, response, fetcherConfig);\n }\n\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn(\n `Attempt ${attempt + 1} failed. Retrying in ${waitTime}ms...`,\n );\n }\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 =\n typeof requestConfig.defaultResponse !== 'undefined'\n ? requestConfig.defaultResponse\n : handlerConfig.defaultResponse;\n const flattenResponse =\n typeof requestConfig.flattenResponse !== 'undefined'\n ? requestConfig.flattenResponse\n : handlerConfig.flattenResponse;\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 (isCustomFetcher()) {\n return response;\n }\n\n return {\n // Native fetch()\n body: response.body,\n blob: response.blob,\n json: response.json,\n text: response.text,\n clone: response.clone,\n bodyUsed: response.bodyUsed,\n arrayBuffer: response.arrayBuffer,\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 // 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\nexport { createRequestHandler };\n","import type {\n FetcherInstance,\n RequestConfig,\n FetchResponse,\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 *\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 {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 Fetcher Provider Instance\n *\n * @returns {FetcherInstance} Request Handler's Fetcher instance\n */\n function getInstance(): FetcherInstance {\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(`${endpointName} endpoint must be added 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","interceptRequest","config","interceptors","interceptorList","interceptedConfig","__spreadValues","interceptor","interceptResponse","response","interceptedResponse","ResponseErr","message","requestInfo","response","__publicField","appendQueryParams","url","params","encodedQueryString","s","add","k","v","buildParams","prefix","obj","i","len","key","replaceUrlPathParams","urlPathParams","str","word","isJSONSerializable","value","t","proto","delayInvocation","ms","resolve","flattenData","data","processHeaders","headers","headersObject","deleteProperty","property","queue","addRequest","config","timeout","dedupeTime","isCancellable","isTimeoutEnabled","now","item","controller","timeoutId","error","removeRequest","APPLICATION_JSON","CONTENT_TYPE","parseResponseData","response","_a","contentType","CONTENT_TYPE","data","APPLICATION_JSON","_e","_error","defaultConfig","createRequestHandler","config","handlerConfig","__spreadValues","__spreadProps","isCustomFetcher","requestInstance","getInstance","buildConfig","url","data","reqConfig","method","isGetAlikeMethod","dynamicUrl","replaceUrlPathParams","explicitParams","explicitBodyData","shouldTreatDataAsParams","body","credentials","deleteProperty","urlPath","appendQueryParams","baseURL","isJSONSerializable","APPLICATION_JSON","CONTENT_TYPE","processError","error","requestConfig","_a","isRequestCancelled","outputErrorResponse","response","_isRequestCancelled","errorHandlingStrategy","rejectCancelled","outputResponse","request","_b","fetcherConfig","timeout","isCancellable","dedupeTime","retries","delay","backoff","retryOn","shouldRetry","maxDelay","resetTimeout","attempt","waitTime","signal","addRequest","interceptRequest","parseResponseData","ResponseErr","interceptResponse","removeRequest","err","status","delayInvocation","defaultResponse","flattenResponse","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/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 diff --git a/dist/browser/index.mjs b/dist/browser/index.mjs index 7def355..098d2e3 100644 --- a/dist/browser/index.mjs +++ b/dist/browser/index.mjs @@ -1,2 +1,2 @@ -var ne=Object.defineProperty,se=Object.defineProperties;var re=Object.getOwnPropertyDescriptors;var Y=Object.getOwnPropertySymbols;var oe=Object.prototype.hasOwnProperty,ae=Object.prototype.propertyIsEnumerable;var j=(t,e,n)=>e in t?ne(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,m=(t,e)=>{for(var n in e||(e={}))oe.call(e,n)&&j(t,n,e[n]);if(Y)for(var n of Y(e))ae.call(e,n)&&j(t,n,e[n]);return t},w=(t,e)=>se(t,re(e));var C=(t,e,n)=>j(t,typeof e!="symbol"?e+"":e,n);async function k(t,e){if(!e)return t;let n=Array.isArray(e)?e:[e],a=m({},t);for(let u of n)a=await u(a);return a}async function L(t,e){if(!e)return t;let n=Array.isArray(e)?e:[e],a=t;for(let u of n)a=await u(a);return a}var F=class extends Error{constructor(n,a,u){super(n);C(this,"response");C(this,"request");C(this,"config");C(this,"status");C(this,"statusText");this.name="ResponseError",this.message=n,this.status=u.status,this.statusText=u.statusText,this.request=a,this.config=a,this.response=u}};function W(t,e){if(!e)return t;if(e instanceof URLSearchParams){let p=e.toString();return t.includes("?")?`${t}&${p}`:p?`${t}?${p}`:t}let n=[],a=function(p,r){r=typeof r=="function"?r():r,r=r===null||r===void 0?"":r,n[n.length]=encodeURIComponent(p)+"="+encodeURIComponent(r)},u=(p,r)=>{let d,g,s;if(p)if(Array.isArray(r))for(d=0,g=r.length;d{let a=n.substring(1);return String(e[a]?e[a]:n)}):t}function K(t){if(t==null)return!1;let e=typeof t;if(e==="string"||e==="number"||e==="boolean")return!0;if(e!=="object")return!1;if(Array.isArray(t))return!0;if(Buffer.isBuffer(t)||t instanceof Date)return!1;let n=Object.getPrototypeOf(t);return n===Object.prototype||n===null||typeof t.toJSON=="function"}async function V(t){return new Promise(e=>setTimeout(()=>e(!0),t))}function $(t){return t&&typeof t=="object"&&typeof t.data!="undefined"&&Object.keys(t).length===1?$(t.data):t}function X(t){if(!t)return{};let e={};if(t instanceof Headers)t.forEach((n,a)=>{e[a]=n});else if(typeof t=="object"&&t!==null)for(let[n,a]of Object.entries(t))e[n.toLowerCase()]=a;return e}function D(t,e){t&&e in t&&delete t[e]}var T=new Map;async function v(t,e,n=0,a=!1,u=!0){let b=Date.now(),R=T.get(t);if(R){if(!R.isCancellable&&b-R.timestamp{let d=new DOMException(`${t.url} aborted due to timeout`,"TimeoutError");U(t,d)},e):null;return T.set(t,{controller:p,timeoutId:r,timestamp:b,isCancellable:a}),p}async function U(t,e=null){let n=T.get(t);n&&(e&&!n.controller.signal.aborted&&n.controller.abort(e),n.timeoutId!==null&&clearTimeout(n.timeoutId),T.delete(t))}var E="application/json",S="Content-Type";async function ee(t){var a;if(!(t!=null&&t.body))return null;let e=String(((a=t.headers)==null?void 0:a.get(S))||"").split(";")[0],n;try{if(e.includes(E)||e.includes("+json"))n=await t.json();else if(e.includes("multipart/form-data"))n=await t.formData();else if(e.includes("application/octet-stream"))n=await t.blob();else if(e.includes("application/x-www-form-urlencoded"))n=await t.formData();else if(e.includes("text/"))n=await t.text();else try{n=await t.clone().json()}catch(u){n=await t.text()}}catch(u){n=null}return n}var ie={method:"GET",strategy:"reject",timeout:3e4,rejectCancelled:!1,dedupeTime:1e3,withCredentials:!1,flattenResponse:!1,defaultResponse:null,logger:null,fetcher:null,baseURL:"",retry:{retries:0,delay:1e3,maxDelay:3e4,resetTimeout:!0,backoff:1.5,retryOn:[408,409,425,429,500,502,503,504],shouldRetry:async()=>!0}};function Q(t){let e=m(w(m({},ie),{baseURL:t.apiUrl||""}),t),n=()=>e.fetcher!==null,a=n()?e.fetcher.create(w(m({},t),{baseURL:e.baseURL,timeout:e.timeout})):null,u=()=>a,b=(s,c,o)=>{let l=(o.method||e.method).toUpperCase(),P=l==="GET"||l==="HEAD",i=Z(s,o.urlPathParams||e.urlPathParams||null),A=o.params||e.params,O=o.body||o.data||e.body||e.data,H=!!(c&&(P||O)),y;if(P||(y=O||c),n())return w(m({},o),{method:l,url:i,params:H?c:A,data:y});let M=(typeof o.withCredentials!="undefined"?o.withCredentials:e.withCredentials)?"include":o.credentials||e.credentials||void 0;D(o,"data"),D(o,"withCredentials");let q=A||H?W(i,A||c):i,N=q.includes("://")?"":o.baseURL||e.baseURL;return y&&typeof y!="string"&&!(y instanceof URLSearchParams)&&K(y)&&(y=JSON.stringify(y)),w(m({},o),{credentials:M,body:y,method:l,url:N+q,headers:m(m({Accept:E+", text/plain, */*","Accept-Encoding":"gzip, deflate, br",[S]:E+";charset=utf-8"},e.headers||{}),o.headers||{})})},R=(s,c)=>{var o;r(s)||((o=e.logger)!=null&&o.warn&&e.logger.warn("API ERROR",s),c.onError&&c.onError(s),e.onError&&e.onError(s))},p=async(s,c,o)=>{let l=r(s),P=o.strategy||e.strategy,i=typeof o.rejectCancelled!="undefined"?o.rejectCancelled:e.rejectCancelled;if(!(l&&!i)){if(P==="silent")await new Promise(()=>null);else if(P==="reject")return Promise.reject(s)}return g(c,o,s)},r=s=>s.name==="AbortError"||s.name==="CanceledError",d=async(s,c=null,o=null)=>{var J,z;let l=null,i=b(s,c,o||{}),A=typeof i.timeout!="undefined"?i.timeout:e.timeout,O=typeof i.cancellable!="undefined"?i.cancellable:e.cancellable,H=typeof i.dedupeTime!="undefined"?i.dedupeTime:e.dedupeTime,{retries:y,delay:B,backoff:M,retryOn:q,shouldRetry:_,maxDelay:N,resetTimeout:te}=m(m({},e.retry),(i==null?void 0:i.retry)||{}),x=0,I=B;for(;x<=y;)try{let h=(await v(i,A,H,O,A>0&&(!y||te))).signal,f=m({signal:h},i);if(f=await k(f,f==null?void 0:f.onRequest),f=await k(f,e==null?void 0:e.onRequest),n())l=await a.request(f);else if(l=await fetch(f.url,f),l.config=f,l.data=await ee(l),!l.ok)throw new F(`${f.url} failed! Status: ${l.status||null}`,f,l);return l=await L(l,f==null?void 0:f.onResponse),l=await L(l,e==null?void 0:e.onResponse),U(i),g(l,f)}catch(G){let h=G,f=((J=h==null?void 0:h.response)==null?void 0:J.status)||(h==null?void 0:h.status)||0;if(x===y||!await _(h,x)||!(q!=null&&q.includes(f)))return R(h,i),U(i),p(h,l,i);(z=e.logger)!=null&&z.warn&&e.logger.warn(`Attempt ${x+1} failed. Retrying in ${I}ms...`),await V(I),I*=M,I=Math.min(I,N),x++}return g(l,i)},g=(s,c,o=null)=>{let l=typeof c.defaultResponse!="undefined"?c.defaultResponse:e.defaultResponse,P=typeof c.flattenResponse!="undefined"?c.flattenResponse:e.flattenResponse;if(!s)return P?l:{error:o,headers:null,data:l,config:c};D(o,"response"),D(o,"request"),D(o,"config");let i=s==null?void 0:s.data;return(i==null||typeof i=="object"&&Object.keys(i).length===0)&&(i=l),P?$(i):n()?s:{body:s.body,blob:s.blob,json:s.json,text:s.text,clone:s.clone,bodyUsed:s.bodyUsed,arrayBuffer:s.arrayBuffer,formData:s.formData,ok:s.ok,redirected:s.redirected,type:s.type,url:s.url,status:s.status,statusText:s.statusText,error:o,data:i,headers:X(s.headers),config:c}};return{getInstance:u,buildConfig:b,config:t,request:d}}function Ee(t){let e=t.endpoints,n=Q(t);function a(){return n.getInstance()}function u(r){return console.error(`${r} endpoint must be added to 'endpoints'.`),Promise.resolve(null)}async function b(r,d={},g={},s={}){let c=e[r];return await n.request(c.url,d,w(m(m({},c||{}),s),{urlPathParams:g}))}function R(r){return r in p?p[r]:e[r]?p.request.bind(null,r):u.bind(null,r)}let p={config:t,endpoints:e,requestHandler:n,getInstance:a,request:b};return new Proxy(p,{get:(r,d)=>R(d)})}async function Ue(t,e={}){return Q(e).request(t,null,e)}export{Ee as createApiFetcher,Ue as fetchf}; +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}; //# 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 71079a0..f4ae9c2 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/utils.ts","../../src/queue-manager.ts","../../src/const.ts","../../src/response-parser.ts","../../src/request-handler.ts","../../src/api-handler.ts","../../src/index.ts"],"sourcesContent":["import type { RequestHandlerConfig, FetchResponse } from './types';\nimport type {\n RequestInterceptor,\n ResponseInterceptor,\n} from './types/interceptor-manager';\n\n/**\n * Applies a series of request interceptors to the provided configuration.\n * @param {RequestHandlerConfig} config - The initial request configuration.\n * @param {RequestInterceptor | RequestInterceptor[]} interceptors - The request interceptor function(s) to apply.\n * @returns {Promise} - The modified request configuration.\n */\nexport async function interceptRequest(\n config: RequestHandlerConfig,\n interceptors?: RequestInterceptor | RequestInterceptor[],\n): Promise {\n if (!interceptors) {\n return config;\n }\n\n const interceptorList = Array.isArray(interceptors)\n ? interceptors\n : [interceptors];\n\n let interceptedConfig = { ...config };\n\n for (const interceptor of interceptorList) {\n interceptedConfig = await interceptor(interceptedConfig);\n }\n\n return interceptedConfig;\n}\n\n/**\n * Applies a series of response interceptors to the provided response.\n * @param {FetchResponse} response - The initial response object.\n * @param {ResponseInterceptor | ResponseInterceptor[]} interceptors - The response interceptor function(s) to apply.\n * @returns {Promise>} - The modified response object.\n */\nexport async function interceptResponse(\n response: FetchResponse,\n interceptors?: ResponseInterceptor | ResponseInterceptor[],\n): Promise> {\n if (!interceptors) {\n return response;\n }\n\n const interceptorList = Array.isArray(interceptors)\n ? interceptors\n : [interceptors];\n\n let interceptedResponse = response;\n\n for (const interceptor of interceptorList) {\n interceptedResponse = await interceptor(interceptedResponse);\n }\n\n return interceptedResponse;\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","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { HeadersObject, QueryParams, UrlPathParams } from './types';\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 (params instanceof URLSearchParams) {\n const encodedQueryString = params.toString();\n\n return url.includes('?')\n ? `${url}&${encodedQueryString}`\n : encodedQueryString\n ? `${url}?${encodedQueryString}`\n : url;\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 add = function (k: string, v: any) {\n v = typeof v === 'function' ? v() : v;\n v = v === null ? '' : v === undefined ? '' : v;\n s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(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 +\n '[' +\n (typeof obj[i] === 'object' && obj[i] ? i : '') +\n ']',\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 url.includes('?')\n ? `${url}&${encodedQueryString}`\n : encodedQueryString\n ? `${url}?${encodedQueryString}`\n : url;\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(/:[a-zA-Z]+/gi, (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 if (value === undefined || value === null) {\n return false;\n }\n\n const t = typeof value;\n if (t === 'string' || t === 'number' || t === 'boolean') {\n return true;\n }\n\n if (t !== 'object') {\n return false; // bigint, function, symbol, undefined\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 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 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 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,\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 // If the request is already in the queue and within the dedupeTime, reuse the existing controller\n if (!item.isCancellable && now - item.timestamp < dedupeTime) {\n return item.controller;\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 (item.isCancellable) {\n item.controller.abort(\n new DOMException('Aborted due to new request', 'AbortError'),\n );\n }\n\n if (item.timeoutId !== null) {\n clearTimeout(item.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 'TimeoutError',\n );\n\n removeRequest(config, error);\n }, timeout)\n : null;\n\n queue.set(config, { controller, timeoutId, timestamp: 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 // If the request is not yet aborted, abort it with the provided error\n if (error && !item.controller.signal.aborted) {\n item.controller.abort(error);\n }\n\n if (item.timeoutId !== null) {\n clearTimeout(item.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?.controller;\n}\n","export const APPLICATION_JSON = 'application/json';\nexport const CONTENT_TYPE = 'Content-Type';\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","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {\n RequestHandlerConfig,\n RequestConfig,\n FetcherInstance,\n Method,\n RetryOptions,\n FetchResponse,\n ResponseError,\n RequestHandlerReturnType,\n} from './types/request-handler';\nimport type {\n APIResponse,\n BodyPayload,\n QueryParams,\n QueryParamsOrBody,\n} from './types/api-handler';\nimport { interceptRequest, interceptResponse } from './interceptor-manager';\nimport { ResponseErr } from './response-error';\nimport {\n appendQueryParams,\n isJSONSerializable,\n replaceUrlPathParams,\n delayInvocation,\n flattenData,\n processHeaders,\n deleteProperty,\n} from './utils';\nimport { addRequest, removeRequest } from './queue-manager';\nimport { APPLICATION_JSON, CONTENT_TYPE } from './const';\nimport { parseResponseData } from './response-parser';\n\nconst defaultConfig: RequestHandlerConfig = {\n method: 'GET',\n strategy: 'reject',\n timeout: 30000,\n rejectCancelled: false,\n dedupeTime: 1000,\n withCredentials: false,\n flattenResponse: false,\n defaultResponse: null,\n logger: null,\n fetcher: null,\n baseURL: '',\n retry: {\n retries: 0,\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 shouldRetry: async () => true,\n },\n};\n\n/**\n * Create a Request Handler\n *\n * @param {RequestHandlerConfig} config - Configuration object for the request handler\n * @returns {Object} An object with methods for handling requests\n */\nfunction createRequestHandler(\n config: RequestHandlerConfig,\n): RequestHandlerReturnType {\n const handlerConfig: RequestHandlerConfig = {\n ...defaultConfig,\n baseURL: config.apiUrl || '',\n ...config,\n };\n\n /**\n * Detects if a custom fetcher is utilized\n *\n * @returns {boolean} True if it's a custom fetcher\n */\n const isCustomFetcher = (): boolean => {\n return handlerConfig.fetcher !== null;\n };\n\n const requestInstance = isCustomFetcher()\n ? (handlerConfig.fetcher as any).create({\n ...config,\n baseURL: handlerConfig.baseURL,\n timeout: handlerConfig.timeout,\n })\n : null;\n\n /**\n * Get Provider Instance\n *\n * @returns {FetcherInstance} Provider's instance\n */\n const getInstance = (): FetcherInstance => {\n return requestInstance;\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 ): RequestConfig => {\n const method = (\n reqConfig.method || (handlerConfig.method as string)\n ).toUpperCase() as Method;\n const isGetAlikeMethod = method === 'GET' || method === 'HEAD';\n\n const dynamicUrl = replaceUrlPathParams(\n url,\n reqConfig.urlPathParams || handlerConfig.urlPathParams || null,\n );\n\n // The explicitly passed \"params\"\n const explicitParams = reqConfig.params || handlerConfig.params;\n\n // The explicitly passed \"body\" or \"data\"\n const explicitBodyData =\n reqConfig.body ||\n reqConfig.data ||\n handlerConfig.body ||\n handlerConfig.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 if (isCustomFetcher()) {\n return {\n ...reqConfig,\n method,\n url: dynamicUrl,\n params: shouldTreatDataAsParams\n ? (data as QueryParams)\n : explicitParams,\n data: body,\n };\n }\n\n // Native fetch\n const isWithCredentials =\n typeof reqConfig.withCredentials !== 'undefined'\n ? reqConfig.withCredentials\n : handlerConfig.withCredentials;\n\n const credentials = isWithCredentials\n ? 'include'\n : reqConfig.credentials || handlerConfig.credentials || undefined;\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 ? '' : reqConfig.baseURL || handlerConfig.baseURL;\n\n // Automatically stringify request body, if possible and when not dealing with strings\n if (\n body &&\n typeof body !== 'string' &&\n !(body instanceof URLSearchParams) &&\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 // Add sensible defaults\n headers: {\n Accept: APPLICATION_JSON + ', text/plain, */*',\n 'Accept-Encoding': 'gzip, deflate, br',\n [CONTENT_TYPE]: APPLICATION_JSON + ';charset=utf-8',\n ...(handlerConfig.headers || {}),\n ...(reqConfig.headers || {}),\n },\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 {void}\n */\n const processError = (\n error: ResponseError,\n requestConfig: RequestConfig,\n ): void => {\n if (isRequestCancelled(error)) {\n return;\n }\n\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn('API ERROR', error);\n }\n\n // Invoke per request \"onError\" interceptor\n if (requestConfig.onError) {\n requestConfig.onError(error);\n }\n\n // Invoke global \"onError\" interceptor\n if (handlerConfig.onError) {\n handlerConfig.onError(error);\n }\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 =\n requestConfig.strategy || handlerConfig.strategy;\n const rejectCancelled =\n typeof requestConfig.rejectCancelled !== 'undefined'\n ? requestConfig.rejectCancelled\n : handlerConfig.rejectCancelled;\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 === 'AbortError' || error.name === 'CanceledError';\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 let response: FetchResponse | null = null;\n const _reqConfig = reqConfig || {};\n const fetcherConfig = buildConfig(url, data, _reqConfig);\n\n const timeout =\n typeof fetcherConfig.timeout !== 'undefined'\n ? fetcherConfig.timeout\n : (handlerConfig.timeout as number);\n const isCancellable =\n typeof fetcherConfig.cancellable !== 'undefined'\n ? fetcherConfig.cancellable\n : handlerConfig.cancellable;\n const dedupeTime =\n typeof fetcherConfig.dedupeTime !== 'undefined'\n ? fetcherConfig.dedupeTime\n : handlerConfig.dedupeTime;\n\n const {\n retries,\n delay,\n backoff,\n retryOn,\n shouldRetry,\n maxDelay,\n resetTimeout,\n } = {\n ...handlerConfig.retry,\n ...(fetcherConfig?.retry || {}),\n } as Required;\n\n let attempt = 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 isCancellable,\n // Reset timeouts by default or when retries are ON\n timeout > 0 && (!retries || resetTimeout),\n );\n const signal = controller.signal;\n\n let requestConfig: RequestConfig = {\n signal,\n ...fetcherConfig,\n };\n\n // Local interceptors\n requestConfig = await interceptRequest(\n requestConfig,\n requestConfig?.onRequest,\n );\n\n // Global interceptors\n requestConfig = await interceptRequest(\n requestConfig,\n handlerConfig?.onRequest,\n );\n\n if (isCustomFetcher()) {\n response = (await (requestInstance as any).request(\n requestConfig,\n )) as FetchResponse;\n } else {\n response = (await fetch(\n requestConfig.url as string,\n requestConfig as RequestInit,\n )) as FetchResponse;\n\n // Add more information to response object\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 response = await interceptResponse(response, requestConfig?.onResponse);\n\n // Global interceptors\n response = await interceptResponse(response, handlerConfig?.onResponse);\n\n removeRequest(fetcherConfig);\n\n return outputResponse(response, requestConfig) as ResponseData &\n FetchResponse;\n } catch (err) {\n const error = err as ResponseError;\n const status = error?.response?.status || (error as any)?.status || 0;\n\n if (\n attempt === retries ||\n !(await shouldRetry(error, attempt)) ||\n !retryOn?.includes(status)\n ) {\n processError(error, fetcherConfig);\n\n removeRequest(fetcherConfig);\n\n return outputErrorResponse(error, response, fetcherConfig);\n }\n\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn(\n `Attempt ${attempt + 1} failed. Retrying in ${waitTime}ms...`,\n );\n }\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 =\n typeof requestConfig.defaultResponse !== 'undefined'\n ? requestConfig.defaultResponse\n : handlerConfig.defaultResponse;\n const flattenResponse =\n typeof requestConfig.flattenResponse !== 'undefined'\n ? requestConfig.flattenResponse\n : handlerConfig.flattenResponse;\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 (isCustomFetcher()) {\n return response;\n }\n\n return {\n // Native fetch()\n body: response.body,\n blob: response.blob,\n json: response.json,\n text: response.text,\n clone: response.clone,\n bodyUsed: response.bodyUsed,\n arrayBuffer: response.arrayBuffer,\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 // 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\nexport { createRequestHandler };\n","import type {\n FetcherInstance,\n RequestConfig,\n FetchResponse,\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 *\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 {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 Fetcher Provider Instance\n *\n * @returns {FetcherInstance} Request Handler's Fetcher instance\n */\n function getInstance(): FetcherInstance {\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(`${endpointName} endpoint must be added 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":["interceptRequest","config","interceptors","interceptorList","interceptedConfig","__spreadValues","interceptor","interceptResponse","response","interceptedResponse","ResponseErr","message","requestInfo","response","__publicField","appendQueryParams","url","params","encodedQueryString","s","add","k","v","buildParams","prefix","obj","i","len","key","replaceUrlPathParams","urlPathParams","str","word","isJSONSerializable","value","t","proto","delayInvocation","ms","resolve","flattenData","data","processHeaders","headers","headersObject","deleteProperty","property","queue","addRequest","config","timeout","dedupeTime","isCancellable","isTimeoutEnabled","now","item","controller","timeoutId","error","removeRequest","APPLICATION_JSON","CONTENT_TYPE","parseResponseData","response","_a","contentType","CONTENT_TYPE","data","APPLICATION_JSON","_e","_error","defaultConfig","createRequestHandler","config","handlerConfig","__spreadValues","__spreadProps","isCustomFetcher","requestInstance","getInstance","buildConfig","url","data","reqConfig","method","isGetAlikeMethod","dynamicUrl","replaceUrlPathParams","explicitParams","explicitBodyData","shouldTreatDataAsParams","body","credentials","deleteProperty","urlPath","appendQueryParams","baseURL","isJSONSerializable","APPLICATION_JSON","CONTENT_TYPE","processError","error","requestConfig","_a","isRequestCancelled","outputErrorResponse","response","_isRequestCancelled","errorHandlingStrategy","rejectCancelled","outputResponse","request","_b","fetcherConfig","timeout","isCancellable","dedupeTime","retries","delay","backoff","retryOn","shouldRetry","maxDelay","resetTimeout","attempt","waitTime","signal","addRequest","interceptRequest","parseResponseData","ResponseErr","interceptResponse","removeRequest","err","status","delayInvocation","defaultResponse","flattenResponse","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/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 diff --git a/dist/node/index.js b/dist/node/index.js index a1f7d3b..8bd09d2 100644 --- a/dist/node/index.js +++ b/dist/node/index.js @@ -1,2 +1,2 @@ -"use strict";var F=Object.defineProperty,re=Object.defineProperties,oe=Object.getOwnPropertyDescriptor,ae=Object.getOwnPropertyDescriptors,ie=Object.getOwnPropertyNames,W=Object.getOwnPropertySymbols;var Z=Object.prototype.hasOwnProperty,le=Object.prototype.propertyIsEnumerable;var k=(t,e,n)=>e in t?F(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,m=(t,e)=>{for(var n in e||(e={}))Z.call(e,n)&&k(t,n,e[n]);if(W)for(var n of W(e))le.call(e,n)&&k(t,n,e[n]);return t},w=(t,e)=>re(t,ae(e));var ue=(t,e)=>{for(var n in e)F(t,n,{get:e[n],enumerable:!0})},ce=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let l of ie(e))!Z.call(t,l)&&l!==n&&F(t,l,{get:()=>e[l],enumerable:!(o=oe(e,l))||o.enumerable});return t};var pe=t=>ce(F({},"__esModule",{value:!0}),t);var C=(t,e,n)=>k(t,typeof e!="symbol"?e+"":e,n);var me={};ue(me,{createApiFetcher:()=>fe,fetchf:()=>Re});module.exports=pe(me);async function L(t,e){if(!e)return t;let n=Array.isArray(e)?e:[e],o=m({},t);for(let l of n)o=await l(o);return o}async function $(t,e){if(!e)return t;let n=Array.isArray(e)?e:[e],o=t;for(let l of n)o=await l(o);return o}var U=class extends Error{constructor(n,o,l){super(n);C(this,"response");C(this,"request");C(this,"config");C(this,"status");C(this,"statusText");this.name="ResponseError",this.message=n,this.status=l.status,this.statusText=l.statusText,this.request=o,this.config=o,this.response=l}};function K(t,e){if(!e)return t;if(e instanceof URLSearchParams){let p=e.toString();return t.includes("?")?`${t}&${p}`:p?`${t}?${p}`:t}let n=[],o=function(p,r){r=typeof r=="function"?r():r,r=r===null||r===void 0?"":r,n[n.length]=encodeURIComponent(p)+"="+encodeURIComponent(r)},l=(p,r)=>{let d,g,s;if(p)if(Array.isArray(r))for(d=0,g=r.length;d{let o=n.substring(1);return String(e[o]?e[o]:n)}):t}function X(t){if(t==null)return!1;let e=typeof t;if(e==="string"||e==="number"||e==="boolean")return!0;if(e!=="object")return!1;if(Array.isArray(t))return!0;if(Buffer.isBuffer(t)||t instanceof Date)return!1;let n=Object.getPrototypeOf(t);return n===Object.prototype||n===null||typeof t.toJSON=="function"}async function v(t){return new Promise(e=>setTimeout(()=>e(!0),t))}function B(t){return t&&typeof t=="object"&&typeof t.data!="undefined"&&Object.keys(t).length===1?B(t.data):t}function ee(t){if(!t)return{};let e={};if(t instanceof Headers)t.forEach((n,o)=>{e[o]=n});else if(typeof t=="object"&&t!==null)for(let[n,o]of Object.entries(t))e[n.toLowerCase()]=o;return e}function D(t,e){t&&e in t&&delete t[e]}var T=new Map;async function te(t,e,n=0,o=!1,l=!0){let b=Date.now(),R=T.get(t);if(R){if(!R.isCancellable&&b-R.timestamp{let d=new DOMException(`${t.url} aborted due to timeout`,"TimeoutError");S(t,d)},e):null;return T.set(t,{controller:p,timeoutId:r,timestamp:b,isCancellable:o}),p}async function S(t,e=null){let n=T.get(t);n&&(e&&!n.controller.signal.aborted&&n.controller.abort(e),n.timeoutId!==null&&clearTimeout(n.timeoutId),T.delete(t))}var E="application/json",Q="Content-Type";async function ne(t){var o;if(!(t!=null&&t.body))return null;let e=String(((o=t.headers)==null?void 0:o.get(Q))||"").split(";")[0],n;try{if(e.includes(E)||e.includes("+json"))n=await t.json();else if(e.includes("multipart/form-data"))n=await t.formData();else if(e.includes("application/octet-stream"))n=await t.blob();else if(e.includes("application/x-www-form-urlencoded"))n=await t.formData();else if(e.includes("text/"))n=await t.text();else try{n=await t.clone().json()}catch(l){n=await t.text()}}catch(l){n=null}return n}var de={method:"GET",strategy:"reject",timeout:3e4,rejectCancelled:!1,dedupeTime:1e3,withCredentials:!1,flattenResponse:!1,defaultResponse:null,logger:null,fetcher:null,baseURL:"",retry:{retries:0,delay:1e3,maxDelay:3e4,resetTimeout:!0,backoff:1.5,retryOn:[408,409,425,429,500,502,503,504],shouldRetry:async()=>!0}};function M(t){let e=m(w(m({},de),{baseURL:t.apiUrl||""}),t),n=()=>e.fetcher!==null,o=n()?e.fetcher.create(w(m({},t),{baseURL:e.baseURL,timeout:e.timeout})):null,l=()=>o,b=(s,c,a)=>{let u=(a.method||e.method).toUpperCase(),P=u==="GET"||u==="HEAD",i=V(s,a.urlPathParams||e.urlPathParams||null),A=a.params||e.params,O=a.body||a.data||e.body||e.data,H=!!(c&&(P||O)),y;if(P||(y=O||c),n())return w(m({},a),{method:u,url:i,params:H?c:A,data:y});let N=(typeof a.withCredentials!="undefined"?a.withCredentials:e.withCredentials)?"include":a.credentials||e.credentials||void 0;D(a,"data"),D(a,"withCredentials");let q=A||H?K(i,A||c):i,j=q.includes("://")?"":a.baseURL||e.baseURL;return y&&typeof y!="string"&&!(y instanceof URLSearchParams)&&X(y)&&(y=JSON.stringify(y)),w(m({},a),{credentials:N,body:y,method:u,url:j+q,headers:m(m({Accept:E+", text/plain, */*","Accept-Encoding":"gzip, deflate, br",[Q]:E+";charset=utf-8"},e.headers||{}),a.headers||{})})},R=(s,c)=>{var a;r(s)||((a=e.logger)!=null&&a.warn&&e.logger.warn("API ERROR",s),c.onError&&c.onError(s),e.onError&&e.onError(s))},p=async(s,c,a)=>{let u=r(s),P=a.strategy||e.strategy,i=typeof a.rejectCancelled!="undefined"?a.rejectCancelled:e.rejectCancelled;if(!(u&&!i)){if(P==="silent")await new Promise(()=>null);else if(P==="reject")return Promise.reject(s)}return g(c,a,s)},r=s=>s.name==="AbortError"||s.name==="CanceledError",d=async(s,c=null,a=null)=>{var z,G;let u=null,i=b(s,c,a||{}),A=typeof i.timeout!="undefined"?i.timeout:e.timeout,O=typeof i.cancellable!="undefined"?i.cancellable:e.cancellable,H=typeof i.dedupeTime!="undefined"?i.dedupeTime:e.dedupeTime,{retries:y,delay:_,backoff:N,retryOn:q,shouldRetry:J,maxDelay:j,resetTimeout:se}=m(m({},e.retry),(i==null?void 0:i.retry)||{}),x=0,I=_;for(;x<=y;)try{let h=(await te(i,A,H,O,A>0&&(!y||se))).signal,f=m({signal:h},i);if(f=await L(f,f==null?void 0:f.onRequest),f=await L(f,e==null?void 0:e.onRequest),n())u=await o.request(f);else if(u=await fetch(f.url,f),u.config=f,u.data=await ne(u),!u.ok)throw new U(`${f.url} failed! Status: ${u.status||null}`,f,u);return u=await $(u,f==null?void 0:f.onResponse),u=await $(u,e==null?void 0:e.onResponse),S(i),g(u,f)}catch(Y){let h=Y,f=((z=h==null?void 0:h.response)==null?void 0:z.status)||(h==null?void 0:h.status)||0;if(x===y||!await J(h,x)||!(q!=null&&q.includes(f)))return R(h,i),S(i),p(h,u,i);(G=e.logger)!=null&&G.warn&&e.logger.warn(`Attempt ${x+1} failed. Retrying in ${I}ms...`),await v(I),I*=N,I=Math.min(I,j),x++}return g(u,i)},g=(s,c,a=null)=>{let u=typeof c.defaultResponse!="undefined"?c.defaultResponse:e.defaultResponse,P=typeof c.flattenResponse!="undefined"?c.flattenResponse:e.flattenResponse;if(!s)return P?u:{error:a,headers:null,data:u,config:c};D(a,"response"),D(a,"request"),D(a,"config");let i=s==null?void 0:s.data;return(i==null||typeof i=="object"&&Object.keys(i).length===0)&&(i=u),P?B(i):n()?s:{body:s.body,blob:s.blob,json:s.json,text:s.text,clone:s.clone,bodyUsed:s.bodyUsed,arrayBuffer:s.arrayBuffer,formData:s.formData,ok:s.ok,redirected:s.redirected,type:s.type,url:s.url,status:s.status,statusText:s.statusText,error:a,data:i,headers:ee(s.headers),config:c}};return{getInstance:l,buildConfig:b,config:t,request:d}}function fe(t){let e=t.endpoints,n=M(t);function o(){return n.getInstance()}function l(r){return console.error(`${r} endpoint must be added to 'endpoints'.`),Promise.resolve(null)}async function b(r,d={},g={},s={}){let c=e[r];return await n.request(c.url,d,w(m(m({},c||{}),s),{urlPathParams:g}))}function R(r){return r in p?p[r]:e[r]?p.request.bind(null,r):l.bind(null,r)}let p={config:t,endpoints:e,requestHandler:n,getInstance:o,request:b};return new Proxy(p,{get:(r,d)=>R(d)})}async function Re(t,e={}){return M(e).request(t,null,e)}0&&(module.exports={createApiFetcher,fetchf}); +"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}); //# 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 9ad8ea0..00dc167 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/utils.ts","../../src/queue-manager.ts","../../src/const.ts","../../src/response-parser.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","import type { RequestHandlerConfig, FetchResponse } from './types';\nimport type {\n RequestInterceptor,\n ResponseInterceptor,\n} from './types/interceptor-manager';\n\n/**\n * Applies a series of request interceptors to the provided configuration.\n * @param {RequestHandlerConfig} config - The initial request configuration.\n * @param {RequestInterceptor | RequestInterceptor[]} interceptors - The request interceptor function(s) to apply.\n * @returns {Promise} - The modified request configuration.\n */\nexport async function interceptRequest(\n config: RequestHandlerConfig,\n interceptors?: RequestInterceptor | RequestInterceptor[],\n): Promise {\n if (!interceptors) {\n return config;\n }\n\n const interceptorList = Array.isArray(interceptors)\n ? interceptors\n : [interceptors];\n\n let interceptedConfig = { ...config };\n\n for (const interceptor of interceptorList) {\n interceptedConfig = await interceptor(interceptedConfig);\n }\n\n return interceptedConfig;\n}\n\n/**\n * Applies a series of response interceptors to the provided response.\n * @param {FetchResponse} response - The initial response object.\n * @param {ResponseInterceptor | ResponseInterceptor[]} interceptors - The response interceptor function(s) to apply.\n * @returns {Promise>} - The modified response object.\n */\nexport async function interceptResponse(\n response: FetchResponse,\n interceptors?: ResponseInterceptor | ResponseInterceptor[],\n): Promise> {\n if (!interceptors) {\n return response;\n }\n\n const interceptorList = Array.isArray(interceptors)\n ? interceptors\n : [interceptors];\n\n let interceptedResponse = response;\n\n for (const interceptor of interceptorList) {\n interceptedResponse = await interceptor(interceptedResponse);\n }\n\n return interceptedResponse;\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","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { HeadersObject, QueryParams, UrlPathParams } from './types';\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 (params instanceof URLSearchParams) {\n const encodedQueryString = params.toString();\n\n return url.includes('?')\n ? `${url}&${encodedQueryString}`\n : encodedQueryString\n ? `${url}?${encodedQueryString}`\n : url;\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 add = function (k: string, v: any) {\n v = typeof v === 'function' ? v() : v;\n v = v === null ? '' : v === undefined ? '' : v;\n s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(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 +\n '[' +\n (typeof obj[i] === 'object' && obj[i] ? i : '') +\n ']',\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 url.includes('?')\n ? `${url}&${encodedQueryString}`\n : encodedQueryString\n ? `${url}?${encodedQueryString}`\n : url;\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(/:[a-zA-Z]+/gi, (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 if (value === undefined || value === null) {\n return false;\n }\n\n const t = typeof value;\n if (t === 'string' || t === 'number' || t === 'boolean') {\n return true;\n }\n\n if (t !== 'object') {\n return false; // bigint, function, symbol, undefined\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 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 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 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,\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 // If the request is already in the queue and within the dedupeTime, reuse the existing controller\n if (!item.isCancellable && now - item.timestamp < dedupeTime) {\n return item.controller;\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 (item.isCancellable) {\n item.controller.abort(\n new DOMException('Aborted due to new request', 'AbortError'),\n );\n }\n\n if (item.timeoutId !== null) {\n clearTimeout(item.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 'TimeoutError',\n );\n\n removeRequest(config, error);\n }, timeout)\n : null;\n\n queue.set(config, { controller, timeoutId, timestamp: 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 // If the request is not yet aborted, abort it with the provided error\n if (error && !item.controller.signal.aborted) {\n item.controller.abort(error);\n }\n\n if (item.timeoutId !== null) {\n clearTimeout(item.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?.controller;\n}\n","export const APPLICATION_JSON = 'application/json';\nexport const CONTENT_TYPE = 'Content-Type';\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","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {\n RequestHandlerConfig,\n RequestConfig,\n FetcherInstance,\n Method,\n RetryOptions,\n FetchResponse,\n ResponseError,\n RequestHandlerReturnType,\n} from './types/request-handler';\nimport type {\n APIResponse,\n BodyPayload,\n QueryParams,\n QueryParamsOrBody,\n} from './types/api-handler';\nimport { interceptRequest, interceptResponse } from './interceptor-manager';\nimport { ResponseErr } from './response-error';\nimport {\n appendQueryParams,\n isJSONSerializable,\n replaceUrlPathParams,\n delayInvocation,\n flattenData,\n processHeaders,\n deleteProperty,\n} from './utils';\nimport { addRequest, removeRequest } from './queue-manager';\nimport { APPLICATION_JSON, CONTENT_TYPE } from './const';\nimport { parseResponseData } from './response-parser';\n\nconst defaultConfig: RequestHandlerConfig = {\n method: 'GET',\n strategy: 'reject',\n timeout: 30000,\n rejectCancelled: false,\n dedupeTime: 1000,\n withCredentials: false,\n flattenResponse: false,\n defaultResponse: null,\n logger: null,\n fetcher: null,\n baseURL: '',\n retry: {\n retries: 0,\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 shouldRetry: async () => true,\n },\n};\n\n/**\n * Create a Request Handler\n *\n * @param {RequestHandlerConfig} config - Configuration object for the request handler\n * @returns {Object} An object with methods for handling requests\n */\nfunction createRequestHandler(\n config: RequestHandlerConfig,\n): RequestHandlerReturnType {\n const handlerConfig: RequestHandlerConfig = {\n ...defaultConfig,\n baseURL: config.apiUrl || '',\n ...config,\n };\n\n /**\n * Detects if a custom fetcher is utilized\n *\n * @returns {boolean} True if it's a custom fetcher\n */\n const isCustomFetcher = (): boolean => {\n return handlerConfig.fetcher !== null;\n };\n\n const requestInstance = isCustomFetcher()\n ? (handlerConfig.fetcher as any).create({\n ...config,\n baseURL: handlerConfig.baseURL,\n timeout: handlerConfig.timeout,\n })\n : null;\n\n /**\n * Get Provider Instance\n *\n * @returns {FetcherInstance} Provider's instance\n */\n const getInstance = (): FetcherInstance => {\n return requestInstance;\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 ): RequestConfig => {\n const method = (\n reqConfig.method || (handlerConfig.method as string)\n ).toUpperCase() as Method;\n const isGetAlikeMethod = method === 'GET' || method === 'HEAD';\n\n const dynamicUrl = replaceUrlPathParams(\n url,\n reqConfig.urlPathParams || handlerConfig.urlPathParams || null,\n );\n\n // The explicitly passed \"params\"\n const explicitParams = reqConfig.params || handlerConfig.params;\n\n // The explicitly passed \"body\" or \"data\"\n const explicitBodyData =\n reqConfig.body ||\n reqConfig.data ||\n handlerConfig.body ||\n handlerConfig.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 if (isCustomFetcher()) {\n return {\n ...reqConfig,\n method,\n url: dynamicUrl,\n params: shouldTreatDataAsParams\n ? (data as QueryParams)\n : explicitParams,\n data: body,\n };\n }\n\n // Native fetch\n const isWithCredentials =\n typeof reqConfig.withCredentials !== 'undefined'\n ? reqConfig.withCredentials\n : handlerConfig.withCredentials;\n\n const credentials = isWithCredentials\n ? 'include'\n : reqConfig.credentials || handlerConfig.credentials || undefined;\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 ? '' : reqConfig.baseURL || handlerConfig.baseURL;\n\n // Automatically stringify request body, if possible and when not dealing with strings\n if (\n body &&\n typeof body !== 'string' &&\n !(body instanceof URLSearchParams) &&\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 // Add sensible defaults\n headers: {\n Accept: APPLICATION_JSON + ', text/plain, */*',\n 'Accept-Encoding': 'gzip, deflate, br',\n [CONTENT_TYPE]: APPLICATION_JSON + ';charset=utf-8',\n ...(handlerConfig.headers || {}),\n ...(reqConfig.headers || {}),\n },\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 {void}\n */\n const processError = (\n error: ResponseError,\n requestConfig: RequestConfig,\n ): void => {\n if (isRequestCancelled(error)) {\n return;\n }\n\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn('API ERROR', error);\n }\n\n // Invoke per request \"onError\" interceptor\n if (requestConfig.onError) {\n requestConfig.onError(error);\n }\n\n // Invoke global \"onError\" interceptor\n if (handlerConfig.onError) {\n handlerConfig.onError(error);\n }\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 =\n requestConfig.strategy || handlerConfig.strategy;\n const rejectCancelled =\n typeof requestConfig.rejectCancelled !== 'undefined'\n ? requestConfig.rejectCancelled\n : handlerConfig.rejectCancelled;\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 === 'AbortError' || error.name === 'CanceledError';\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 let response: FetchResponse | null = null;\n const _reqConfig = reqConfig || {};\n const fetcherConfig = buildConfig(url, data, _reqConfig);\n\n const timeout =\n typeof fetcherConfig.timeout !== 'undefined'\n ? fetcherConfig.timeout\n : (handlerConfig.timeout as number);\n const isCancellable =\n typeof fetcherConfig.cancellable !== 'undefined'\n ? fetcherConfig.cancellable\n : handlerConfig.cancellable;\n const dedupeTime =\n typeof fetcherConfig.dedupeTime !== 'undefined'\n ? fetcherConfig.dedupeTime\n : handlerConfig.dedupeTime;\n\n const {\n retries,\n delay,\n backoff,\n retryOn,\n shouldRetry,\n maxDelay,\n resetTimeout,\n } = {\n ...handlerConfig.retry,\n ...(fetcherConfig?.retry || {}),\n } as Required;\n\n let attempt = 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 isCancellable,\n // Reset timeouts by default or when retries are ON\n timeout > 0 && (!retries || resetTimeout),\n );\n const signal = controller.signal;\n\n let requestConfig: RequestConfig = {\n signal,\n ...fetcherConfig,\n };\n\n // Local interceptors\n requestConfig = await interceptRequest(\n requestConfig,\n requestConfig?.onRequest,\n );\n\n // Global interceptors\n requestConfig = await interceptRequest(\n requestConfig,\n handlerConfig?.onRequest,\n );\n\n if (isCustomFetcher()) {\n response = (await (requestInstance as any).request(\n requestConfig,\n )) as FetchResponse;\n } else {\n response = (await fetch(\n requestConfig.url as string,\n requestConfig as RequestInit,\n )) as FetchResponse;\n\n // Add more information to response object\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 response = await interceptResponse(response, requestConfig?.onResponse);\n\n // Global interceptors\n response = await interceptResponse(response, handlerConfig?.onResponse);\n\n removeRequest(fetcherConfig);\n\n return outputResponse(response, requestConfig) as ResponseData &\n FetchResponse;\n } catch (err) {\n const error = err as ResponseError;\n const status = error?.response?.status || (error as any)?.status || 0;\n\n if (\n attempt === retries ||\n !(await shouldRetry(error, attempt)) ||\n !retryOn?.includes(status)\n ) {\n processError(error, fetcherConfig);\n\n removeRequest(fetcherConfig);\n\n return outputErrorResponse(error, response, fetcherConfig);\n }\n\n if (handlerConfig.logger?.warn) {\n handlerConfig.logger.warn(\n `Attempt ${attempt + 1} failed. Retrying in ${waitTime}ms...`,\n );\n }\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 =\n typeof requestConfig.defaultResponse !== 'undefined'\n ? requestConfig.defaultResponse\n : handlerConfig.defaultResponse;\n const flattenResponse =\n typeof requestConfig.flattenResponse !== 'undefined'\n ? requestConfig.flattenResponse\n : handlerConfig.flattenResponse;\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 (isCustomFetcher()) {\n return response;\n }\n\n return {\n // Native fetch()\n body: response.body,\n blob: response.blob,\n json: response.json,\n text: response.text,\n clone: response.clone,\n bodyUsed: response.bodyUsed,\n arrayBuffer: response.arrayBuffer,\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 // 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\nexport { createRequestHandler };\n","import type {\n FetcherInstance,\n RequestConfig,\n FetchResponse,\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 *\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 {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 Fetcher Provider Instance\n *\n * @returns {FetcherInstance} Request Handler's Fetcher instance\n */\n function getInstance(): FetcherInstance {\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(`${endpointName} endpoint must be added 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","interceptRequest","config","interceptors","interceptorList","interceptedConfig","__spreadValues","interceptor","interceptResponse","response","interceptedResponse","ResponseErr","message","requestInfo","response","__publicField","appendQueryParams","url","params","encodedQueryString","s","add","k","v","buildParams","prefix","obj","i","len","key","replaceUrlPathParams","urlPathParams","str","word","isJSONSerializable","value","t","proto","delayInvocation","ms","resolve","flattenData","data","processHeaders","headers","headersObject","deleteProperty","property","queue","addRequest","config","timeout","dedupeTime","isCancellable","isTimeoutEnabled","now","item","controller","timeoutId","error","removeRequest","APPLICATION_JSON","CONTENT_TYPE","parseResponseData","response","_a","contentType","CONTENT_TYPE","data","APPLICATION_JSON","_e","_error","defaultConfig","createRequestHandler","config","handlerConfig","__spreadValues","__spreadProps","isCustomFetcher","requestInstance","getInstance","buildConfig","url","data","reqConfig","method","isGetAlikeMethod","dynamicUrl","replaceUrlPathParams","explicitParams","explicitBodyData","shouldTreatDataAsParams","body","credentials","deleteProperty","urlPath","appendQueryParams","baseURL","isJSONSerializable","APPLICATION_JSON","CONTENT_TYPE","processError","error","requestConfig","_a","isRequestCancelled","outputErrorResponse","response","_isRequestCancelled","errorHandlingStrategy","rejectCancelled","outputResponse","request","_b","fetcherConfig","timeout","isCancellable","dedupeTime","retries","delay","backoff","retryOn","shouldRetry","maxDelay","resetTimeout","attempt","waitTime","signal","addRequest","interceptRequest","parseResponseData","ResponseErr","interceptResponse","removeRequest","err","status","delayInvocation","defaultResponse","flattenResponse","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/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