@zayne-labs/callapi 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
 
3
3
  var utils = require('./utils');
4
4
 
5
- const m=C=>{const l=new Map,[D,A]=utils.splitConfig(C??{}),{headers:p,signal:w,...M}=D,o=async(s,f)=>{const[q,S]=utils.splitConfig(f??{}),e={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:utils.defaultRetryCodes,retryMethods:utils.defaultRetryMethods,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...A,...S},{signal:O=w,body:a,headers:T,...x}=q,g=l.get(s);if(g&&e.cancelRedundantRequests){const t=new DOMException("Cancelled the previous unfinished request","AbortError");g.abort(t);}const c=new AbortController;l.set(s,c);const B=e.timeout?AbortSignal.timeout(e.timeout):null,E=AbortSignal.any([c.signal,B??c.signal,O??c.signal]),n={signal:E,method:"GET",body:utils.isObject(a)?e.bodySerializer(a):a,headers:p||T||utils.isObject(a)?{...utils.isObject(a)&&{"Content-Type":"application/json",Accept:"application/json"},...utils.isFormData(a)&&{"Content-Type":"multipart/form-data"},...utils.objectifyHeaders(p),...utils.objectifyHeaders(T)}:void 0,...M,...x};try{await e.onRequest?.({request:n,options:e});const t=await fetch(`${e.baseURL}${utils.mergeUrlWithParams(s,e.query)}`,n);if(!E.aborted&&e.retries!==0&&!t.ok&&e.retryCodes.includes(t.status)&&e.retryMethods.includes(n.method))return await utils.wait(e.retryDelay),await o(s,{...f,retries:e.retries-1});if(!t.ok){const u=await utils.getResponseData(t,e.responseType,e.responseParser);throw new utils.HTTPError({response:{...t,errorData:u},defaultErrorMessage:e.defaultErrorMessage})}const r=await utils.getResponseData(t,e.responseType,e.responseParser);return await e.onResponse?.({response:{...t.clone(),data:r},request:n,options:e}),utils.resolveSuccessResult({successData:r,response:t.clone(),options:e})}catch(t){const i=utils.$resolveErrorResult({error:t,options:e});if(t instanceof DOMException&&t.name==="TimeoutError"){const r=`Request timed out after ${e.timeout}ms`;return console.info(`%cTimeoutError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),i({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){const r="Request was cancelled";return console.info(`%AbortError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),i({message:r})}if(utils.isHTTPErrorInstance(t)){const{errorData:r,...u}=t.response;return await e.onResponseError?.({response:{...u.clone(),errorData:r},request:n,options:e}),i({errorData:r,response:u.clone(),message:r?.message})}return await e.onRequestError?.({request:n,error:t,options:e}),i()}finally{l.delete(s);}};return o.create=m,o.isHTTPErrorInfo=utils.isHTTPErrorInfo,o.isHTTPErrorInstance=utils.isHTTPErrorInstance,o.cancel=s=>l.get(s)?.abort(),o},G=m();var k=G;
5
+ const m=C=>{const l=new Map,[D,A]=utils.splitConfig(C??{}),{headers:p,signal:w,...M}=D,o=async(s,f)=>{const[q,S]=utils.splitConfig(f??{}),e={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:utils.defaultRetryCodes,retryMethods:utils.defaultRetryMethods,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...A,...S},{signal:O=w,body:a,headers:T,...x}=q,g=l.get(s);if(g&&e.cancelRedundantRequests){const t=new DOMException("Cancelled the previous unfinished request","AbortError");g.abort(t);}const c=new AbortController;l.set(s,c);const B=e.timeout?AbortSignal.timeout(e.timeout):null,E=AbortSignal.any([c.signal,B??c.signal,O??c.signal]),n={signal:E,method:"GET",body:utils.isObject(a)?e.bodySerializer(a):a,headers:p||T||utils.isObject(a)?{...utils.isObject(a)&&{"Content-Type":"application/json",Accept:"application/json"},...utils.isFormData(a)&&{"Content-Type":"multipart/form-data"},...utils.objectifyHeaders(p),...utils.objectifyHeaders(T)}:void 0,...M,...x};try{await e.onRequest?.({request:n,options:e});const t=await fetch(`${e.baseURL}${utils.mergeUrlWithParams(s,e.query)}`,n);if(!E.aborted&&e.retries!==0&&!t.ok&&e.retryCodes.includes(t.status)&&e.retryMethods.includes(n.method))return await utils.wait(e.retryDelay),await o(s,{...f,retries:e.retries-1});if(!t.ok){const u=await utils.getResponseData(t,e.responseType,e.responseParser);throw new utils.HTTPError({response:{...t,errorData:u},defaultErrorMessage:e.defaultErrorMessage})}const r=await utils.getResponseData(t,e.responseType,e.responseParser);return await e.onResponse?.({response:{...t,data:r},request:n,options:e}),utils.resolveSuccessResult({successData:r,response:t,options:e})}catch(t){const i=utils.$resolveErrorResult({error:t,options:e});if(t instanceof DOMException&&t.name==="TimeoutError"){const r=`Request timed out after ${e.timeout}ms`;return console.info(`%cTimeoutError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),i({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){const r="Request was cancelled";return console.info(`%AbortError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),i({message:r})}if(utils.isHTTPErrorInstance(t)){const{errorData:r,...u}=t.response;return await e.onResponseError?.({response:{...u,errorData:r},request:n,options:e}),i({errorData:r,response:u,message:r?.message})}return await e.onRequestError?.({request:n,error:t,options:e}),i()}finally{l.delete(s);}};return o.create=m,o.isHTTPErrorInfo=utils.isHTTPErrorInfo,o.isHTTPErrorInstance=utils.isHTTPErrorInstance,o.cancel=s=>l.get(s)?.abort(),o},G=m();var k=G;
6
6
 
7
7
  module.exports = k;
8
8
  //# sourceMappingURL=out.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/createFetchClient.ts"],"names":["$resolveErrorResult","HTTPError","defaultRetryCodes","defaultRetryMethods","getResponseData","isFormData","isHTTPErrorInfo","isHTTPErrorInstance","isObject","mergeUrlWithParams","objectifyHeaders","resolveSuccessResult","splitConfig","wait","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","baseHeaders","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","options","signal","body","headers","restOfFetchConfig","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","response","errorData","successData","error","resolveErrorResult","message","createFetchClient_default"],"mappings":"AAUA,OACC,uBAAAA,EACA,aAAAC,EACA,qBAAAC,EACA,uBAAAC,EACA,mBAAAC,EACA,cAAAC,EACA,mBAAAC,EACA,uBAAAC,EACA,YAAAC,EACA,sBAAAC,EACA,oBAAAC,EACA,wBAAAC,EACA,eAAAC,EACA,QAAAC,MACM,UAEP,MAAMC,EAKLC,GACI,CACJ,MAAMC,EAAuB,IAAI,IAE3B,CAACC,EAAiBC,CAAgB,EAAIN,EAAYG,GAAc,CAAC,CAAC,EAElE,CAAE,QAASI,EAAa,OAAQC,EAAY,GAAGC,CAAsB,EAAIJ,EAEzEK,EAAU,MAKfC,EACAC,IAC+D,CAG/D,KAAM,CAACC,EAAaC,CAAY,EAAId,EAAYY,GAAU,CAAC,CAAC,EAEtDG,EAAU,CACf,eAAgB,KAAK,UACrB,aAAc,OACd,QAAS,GACT,QAAS,EACT,WAAY,EACZ,WAAYzB,EACZ,aAAcC,EACd,oBAAqB,oCACrB,wBAAyB,GACzB,GAAGe,EACH,GAAGQ,CACJ,EAEM,CAAE,OAAAE,EAASR,EAAY,KAAAS,EAAM,QAAAC,EAAS,GAAGC,CAAkB,EAAIN,EAE/DO,EAAsBhB,EAAqB,IAAIO,CAAG,EAExD,GAAIS,GAAuBL,EAAQ,wBAAyB,CAC3D,MAAMM,EAAS,IAAI,aAAa,4CAA6C,YAAY,EACzFD,EAAoB,MAAMC,CAAM,CACjC,CAEA,MAAMC,EAAqB,IAAI,gBAE/BlB,EAAqB,IAAIO,EAAKW,CAAkB,EAEhD,MAAMC,EAAgBR,EAAQ,QAAU,YAAY,QAAQA,EAAQ,OAAO,EAAI,KAGzES,EAAkB,YAAmC,IAAI,CAC9DF,EAAmB,OACnBC,GAAiBD,EAAmB,OACpCN,GAAUM,EAAmB,MAC9B,CAAC,EAEKG,EAAc,CACnB,OAAQD,EAER,OAAQ,MAER,KAAM5B,EAASqB,CAAI,EAAIF,EAAQ,eAAeE,CAAI,EAAIA,EAGtD,QACCV,GAAeW,GAAWtB,EAASqB,CAAI,EACpC,CACA,GAAIrB,EAASqB,CAAI,GAAK,CACrB,eAAgB,mBAChB,OAAQ,kBACT,EACA,GAAIxB,EAAWwB,CAAI,GAAK,CACvB,eAAgB,qBACjB,EACA,GAAGnB,EAAiBS,CAAW,EAC/B,GAAGT,EAAiBoB,CAAO,CAC5B,EACC,OAEJ,GAAGT,EACH,GAAGU,CACJ,EAEA,GAAI,CACH,MAAMJ,EAAQ,YAAY,CAAE,QAASU,EAAa,QAAAV,CAAQ,CAAC,EAE3D,MAAMW,EAAW,MAAM,MACtB,GAAGX,EAAQ,OAAO,GAAGlB,EAAmBc,EAAKI,EAAQ,KAAK,CAAC,GAC3DU,CACD,EASA,GANC,CAACD,EAAe,SAChBT,EAAQ,UAAY,GACpB,CAACW,EAAS,IACVX,EAAQ,WAAW,SAASW,EAAS,MAAM,GAC3CX,EAAQ,aAAa,SAASU,EAAY,MAAM,EAGhD,aAAMxB,EAAKc,EAAQ,UAAU,EAEtB,MAAML,EAAQC,EAAK,CAAE,GAAGC,EAAQ,QAASG,EAAQ,QAAU,CAAE,CAAC,EAGtE,GAAI,CAACW,EAAS,GAAI,CACjB,MAAMC,EAAY,MAAMnC,EACvBkC,EACAX,EAAQ,aACRA,EAAQ,cACT,EAGA,MAAM,IAAI1B,EAAU,CACnB,SAAU,CAAE,GAAGqC,EAAU,UAAAC,CAAU,EACnC,oBAAqBZ,EAAQ,mBAC9B,CAAC,CACF,CAEA,MAAMa,EAAc,MAAMpC,EACzBkC,EACAX,EAAQ,aACRA,EAAQ,cACT,EAEA,aAAMA,EAAQ,aAAa,CAC1B,SAAU,CAAE,GAAGW,EAAS,MAAM,EAAG,KAAME,CAAY,EACnD,QAASH,EACT,QAAAV,CACD,CAAC,EAEMhB,EAAoC,CAAE,YAAA6B,EAAa,SAAUF,EAAS,MAAM,EAAG,QAAAX,CAAQ,CAAC,CAGhG,OAASc,EAAO,CACf,MAAMC,EAAqB1C,EAAmC,CAAE,MAAAyC,EAAO,QAAAd,CAAQ,CAAC,EAEhF,GAAIc,aAAiB,cAAgBA,EAAM,OAAS,eAAgB,CACnE,MAAME,EAAU,2BAA2BhB,EAAQ,OAAO,KAE1D,eAAQ,KAAK,mBAAmBgB,CAAO,GAAI,gDAAgD,EAC3F,QAAQ,MAAM,cAAc,EAErBD,EAAmB,CAAE,QAAAC,CAAQ,CAAC,CACtC,CAEA,GAAIF,aAAiB,cAAgBA,EAAM,OAAS,aAAc,CACjE,MAAME,EAAU,wBAEhB,eAAQ,KAAK,gBAAgBA,CAAO,GAAI,gDAAgD,EACxF,QAAQ,MAAM,YAAY,EAEnBD,EAAmB,CAAE,QAAAC,CAAQ,CAAC,CACtC,CAEA,GAAIpC,EAAgCkC,CAAK,EAAG,CAC3C,KAAM,CAAE,UAAAF,EAAW,GAAGD,CAAS,EAAIG,EAAM,SAEzC,aAAMd,EAAQ,kBAAkB,CAC/B,SAAU,CAAE,GAAGW,EAAS,MAAM,EAAG,UAAAC,CAAU,EAC3C,QAASF,EACT,QAAAV,CACD,CAAC,EAEMe,EAAmB,CACzB,UAAAH,EACA,SAAUD,EAAS,MAAM,EACzB,QAAUC,GAAmC,OAC9C,CAAC,CACF,CAGA,aAAMZ,EAAQ,iBAAiB,CAAE,QAASU,EAAa,MAAOI,EAAgB,QAAAd,CAAQ,CAAC,EAEhFe,EAAmB,CAG3B,QAAE,CACD1B,EAAqB,OAAOO,CAAG,CAChC,CACD,EAEA,OAAAD,EAAQ,OAASR,EACjBQ,EAAQ,gBAAkBhB,EAC1BgB,EAAQ,oBAAsBf,EAC9Be,EAAQ,OAAUC,GAAgBP,EAAqB,IAAIO,CAAG,GAAG,MAAM,EAEhED,CACR,EAEMA,EAAUR,EAAkB,EAElC,IAAO8B,EAAQtB","sourcesContent":["import type {\n\t$RequestConfig,\n\tAbortSignalWithAny,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultStyleUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisFormData,\n\tisHTTPErrorInfo,\n\tisHTTPErrorInstance,\n\tisObject,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twait,\n} from \"./utils\";\n\nconst createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultStyleUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst { headers: baseHeaders, signal: baseSignal, ...restOfBaseFetchConfig } = baseFetchConfig;\n\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultStyleUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst { signal = baseSignal, body, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\"Cancelled the previous unfinished request\", \"AbortError\");\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\t// FIXME - Remove this type cast once TS updates its lib-dom types for AbortSignal to include the any() method\n\t\tconst combinedSignal = (AbortSignal as AbortSignalWithAny).any([\n\t\t\tnewFetchController.signal,\n\t\t\ttimeoutSignal ?? newFetchController.signal,\n\t\t\tsignal ?? newFetchController.signal,\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if there are no headers provided or if the body is not an object\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestConfig;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries !== 0 &&\n\t\t\t\t!response.ok &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait wait(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse,\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse,\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\tresponse: { ...response.clone(), data: successData },\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData, response: response.clone(), options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.info(`%cTimeoutError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"TimeoutError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request was cancelled`;\n\n\t\t\t\tconsole.info(`%AbortError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"AbortError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, ...response } = error.response;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: { ...response.clone(), errorData },\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse: response.clone(),\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\tcallApi.isHTTPErrorInfo = isHTTPErrorInfo;\n\tcallApi.isHTTPErrorInstance = isHTTPErrorInstance;\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nconst callApi = createFetchClient();\n\nexport default callApi;\n"]}
1
+ {"version":3,"sources":["../src/createFetchClient.ts"],"names":["$resolveErrorResult","HTTPError","defaultRetryCodes","defaultRetryMethods","getResponseData","isFormData","isHTTPErrorInfo","isHTTPErrorInstance","isObject","mergeUrlWithParams","objectifyHeaders","resolveSuccessResult","splitConfig","wait","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","baseHeaders","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","options","signal","body","headers","restOfFetchConfig","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","response","errorData","successData","error","resolveErrorResult","message","createFetchClient_default"],"mappings":"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","sourcesContent":["import type {\n\t$RequestConfig,\n\tAbortSignalWithAny,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultStyleUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisFormData,\n\tisHTTPErrorInfo,\n\tisHTTPErrorInstance,\n\tisObject,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twait,\n} from \"./utils\";\n\nconst createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultStyleUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst { headers: baseHeaders, signal: baseSignal, ...restOfBaseFetchConfig } = baseFetchConfig;\n\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultStyleUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst { signal = baseSignal, body, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\"Cancelled the previous unfinished request\", \"AbortError\");\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\t// FIXME - Remove this type cast once TS updates its lib-dom types for AbortSignal to include the any() method\n\t\tconst combinedSignal = (AbortSignal as AbortSignalWithAny).any([\n\t\t\tnewFetchController.signal,\n\t\t\ttimeoutSignal ?? newFetchController.signal,\n\t\t\tsignal ?? newFetchController.signal,\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if there are no headers provided or if the body is not an object\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestConfig;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries !== 0 &&\n\t\t\t\t!response.ok &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait wait(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse,\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse,\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\tresponse: { ...response, data: successData },\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData, response, options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.info(`%cTimeoutError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"TimeoutError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request was cancelled`;\n\n\t\t\t\tconsole.info(`%AbortError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"AbortError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, ...response } = error.response;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\tcallApi.isHTTPErrorInfo = isHTTPErrorInfo;\n\tcallApi.isHTTPErrorInstance = isHTTPErrorInstance;\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nconst callApi = createFetchClient();\n\nexport default callApi;\n"]}
@@ -1,6 +1,6 @@
1
1
  import { splitConfig, isHTTPErrorInfo, isHTTPErrorInstance, defaultRetryCodes, defaultRetryMethods, isObject, isFormData, objectifyHeaders, mergeUrlWithParams, wait, getResponseData, HTTPError, resolveSuccessResult, $resolveErrorResult } from './utils';
2
2
 
3
- const m=C=>{const l=new Map,[D,A]=splitConfig(C??{}),{headers:p,signal:w,...M}=D,o=async(s,f)=>{const[q,S]=splitConfig(f??{}),e={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:defaultRetryCodes,retryMethods:defaultRetryMethods,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...A,...S},{signal:O=w,body:a,headers:T,...x}=q,g=l.get(s);if(g&&e.cancelRedundantRequests){const t=new DOMException("Cancelled the previous unfinished request","AbortError");g.abort(t);}const c=new AbortController;l.set(s,c);const B=e.timeout?AbortSignal.timeout(e.timeout):null,E=AbortSignal.any([c.signal,B??c.signal,O??c.signal]),n={signal:E,method:"GET",body:isObject(a)?e.bodySerializer(a):a,headers:p||T||isObject(a)?{...isObject(a)&&{"Content-Type":"application/json",Accept:"application/json"},...isFormData(a)&&{"Content-Type":"multipart/form-data"},...objectifyHeaders(p),...objectifyHeaders(T)}:void 0,...M,...x};try{await e.onRequest?.({request:n,options:e});const t=await fetch(`${e.baseURL}${mergeUrlWithParams(s,e.query)}`,n);if(!E.aborted&&e.retries!==0&&!t.ok&&e.retryCodes.includes(t.status)&&e.retryMethods.includes(n.method))return await wait(e.retryDelay),await o(s,{...f,retries:e.retries-1});if(!t.ok){const u=await getResponseData(t,e.responseType,e.responseParser);throw new HTTPError({response:{...t,errorData:u},defaultErrorMessage:e.defaultErrorMessage})}const r=await getResponseData(t,e.responseType,e.responseParser);return await e.onResponse?.({response:{...t.clone(),data:r},request:n,options:e}),resolveSuccessResult({successData:r,response:t.clone(),options:e})}catch(t){const i=$resolveErrorResult({error:t,options:e});if(t instanceof DOMException&&t.name==="TimeoutError"){const r=`Request timed out after ${e.timeout}ms`;return console.info(`%cTimeoutError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),i({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){const r="Request was cancelled";return console.info(`%AbortError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),i({message:r})}if(isHTTPErrorInstance(t)){const{errorData:r,...u}=t.response;return await e.onResponseError?.({response:{...u.clone(),errorData:r},request:n,options:e}),i({errorData:r,response:u.clone(),message:r?.message})}return await e.onRequestError?.({request:n,error:t,options:e}),i()}finally{l.delete(s);}};return o.create=m,o.isHTTPErrorInfo=isHTTPErrorInfo,o.isHTTPErrorInstance=isHTTPErrorInstance,o.cancel=s=>l.get(s)?.abort(),o},G=m();var k=G;
3
+ const m=C=>{const l=new Map,[D,A]=splitConfig(C??{}),{headers:p,signal:w,...M}=D,o=async(s,f)=>{const[q,S]=splitConfig(f??{}),e={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:defaultRetryCodes,retryMethods:defaultRetryMethods,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...A,...S},{signal:O=w,body:a,headers:T,...x}=q,g=l.get(s);if(g&&e.cancelRedundantRequests){const t=new DOMException("Cancelled the previous unfinished request","AbortError");g.abort(t);}const c=new AbortController;l.set(s,c);const B=e.timeout?AbortSignal.timeout(e.timeout):null,E=AbortSignal.any([c.signal,B??c.signal,O??c.signal]),n={signal:E,method:"GET",body:isObject(a)?e.bodySerializer(a):a,headers:p||T||isObject(a)?{...isObject(a)&&{"Content-Type":"application/json",Accept:"application/json"},...isFormData(a)&&{"Content-Type":"multipart/form-data"},...objectifyHeaders(p),...objectifyHeaders(T)}:void 0,...M,...x};try{await e.onRequest?.({request:n,options:e});const t=await fetch(`${e.baseURL}${mergeUrlWithParams(s,e.query)}`,n);if(!E.aborted&&e.retries!==0&&!t.ok&&e.retryCodes.includes(t.status)&&e.retryMethods.includes(n.method))return await wait(e.retryDelay),await o(s,{...f,retries:e.retries-1});if(!t.ok){const u=await getResponseData(t,e.responseType,e.responseParser);throw new HTTPError({response:{...t,errorData:u},defaultErrorMessage:e.defaultErrorMessage})}const r=await getResponseData(t,e.responseType,e.responseParser);return await e.onResponse?.({response:{...t,data:r},request:n,options:e}),resolveSuccessResult({successData:r,response:t,options:e})}catch(t){const i=$resolveErrorResult({error:t,options:e});if(t instanceof DOMException&&t.name==="TimeoutError"){const r=`Request timed out after ${e.timeout}ms`;return console.info(`%cTimeoutError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),i({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){const r="Request was cancelled";return console.info(`%AbortError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),i({message:r})}if(isHTTPErrorInstance(t)){const{errorData:r,...u}=t.response;return await e.onResponseError?.({response:{...u,errorData:r},request:n,options:e}),i({errorData:r,response:u,message:r?.message})}return await e.onRequestError?.({request:n,error:t,options:e}),i()}finally{l.delete(s);}};return o.create=m,o.isHTTPErrorInfo=isHTTPErrorInfo,o.isHTTPErrorInstance=isHTTPErrorInstance,o.cancel=s=>l.get(s)?.abort(),o},G=m();var k=G;
4
4
 
5
5
  export { k as default };
6
6
  //# sourceMappingURL=out.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/createFetchClient.ts"],"names":["$resolveErrorResult","HTTPError","defaultRetryCodes","defaultRetryMethods","getResponseData","isFormData","isHTTPErrorInfo","isHTTPErrorInstance","isObject","mergeUrlWithParams","objectifyHeaders","resolveSuccessResult","splitConfig","wait","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","baseHeaders","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","options","signal","body","headers","restOfFetchConfig","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","response","errorData","successData","error","resolveErrorResult","message","createFetchClient_default"],"mappings":"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","sourcesContent":["import type {\n\t$RequestConfig,\n\tAbortSignalWithAny,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultStyleUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisFormData,\n\tisHTTPErrorInfo,\n\tisHTTPErrorInstance,\n\tisObject,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twait,\n} from \"./utils\";\n\nconst createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultStyleUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst { headers: baseHeaders, signal: baseSignal, ...restOfBaseFetchConfig } = baseFetchConfig;\n\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultStyleUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst { signal = baseSignal, body, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\"Cancelled the previous unfinished request\", \"AbortError\");\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\t// FIXME - Remove this type cast once TS updates its lib-dom types for AbortSignal to include the any() method\n\t\tconst combinedSignal = (AbortSignal as AbortSignalWithAny).any([\n\t\t\tnewFetchController.signal,\n\t\t\ttimeoutSignal ?? newFetchController.signal,\n\t\t\tsignal ?? newFetchController.signal,\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if there are no headers provided or if the body is not an object\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestConfig;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries !== 0 &&\n\t\t\t\t!response.ok &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait wait(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse,\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse,\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\tresponse: { ...response.clone(), data: successData },\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData, response: response.clone(), options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.info(`%cTimeoutError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"TimeoutError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request was cancelled`;\n\n\t\t\t\tconsole.info(`%AbortError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"AbortError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, ...response } = error.response;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: { ...response.clone(), errorData },\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse: response.clone(),\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\tcallApi.isHTTPErrorInfo = isHTTPErrorInfo;\n\tcallApi.isHTTPErrorInstance = isHTTPErrorInstance;\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nconst callApi = createFetchClient();\n\nexport default callApi;\n"]}
1
+ {"version":3,"sources":["../src/createFetchClient.ts"],"names":["$resolveErrorResult","HTTPError","defaultRetryCodes","defaultRetryMethods","getResponseData","isFormData","isHTTPErrorInfo","isHTTPErrorInstance","isObject","mergeUrlWithParams","objectifyHeaders","resolveSuccessResult","splitConfig","wait","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","baseHeaders","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","options","signal","body","headers","restOfFetchConfig","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","response","errorData","successData","error","resolveErrorResult","message","createFetchClient_default"],"mappings":"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","sourcesContent":["import type {\n\t$RequestConfig,\n\tAbortSignalWithAny,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultStyleUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisFormData,\n\tisHTTPErrorInfo,\n\tisHTTPErrorInstance,\n\tisObject,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twait,\n} from \"./utils\";\n\nconst createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultStyleUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst { headers: baseHeaders, signal: baseSignal, ...restOfBaseFetchConfig } = baseFetchConfig;\n\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultStyleUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst { signal = baseSignal, body, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\"Cancelled the previous unfinished request\", \"AbortError\");\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\t// FIXME - Remove this type cast once TS updates its lib-dom types for AbortSignal to include the any() method\n\t\tconst combinedSignal = (AbortSignal as AbortSignalWithAny).any([\n\t\t\tnewFetchController.signal,\n\t\t\ttimeoutSignal ?? newFetchController.signal,\n\t\t\tsignal ?? newFetchController.signal,\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if there are no headers provided or if the body is not an object\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestConfig;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries !== 0 &&\n\t\t\t\t!response.ok &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait wait(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse,\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse,\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\tresponse: { ...response, data: successData },\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData, response, options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.info(`%cTimeoutError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"TimeoutError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request was cancelled`;\n\n\t\t\t\tconsole.info(`%AbortError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"AbortError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, ...response } = error.response;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\tcallApi.isHTTPErrorInfo = isHTTPErrorInfo;\n\tcallApi.isHTTPErrorInstance = isHTTPErrorInstance;\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nconst callApi = createFetchClient();\n\nexport default callApi;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zayne-labs/callapi",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "description": "A lightweight wrapper over fetch with quality of life improvements like built-in request cancellation, retries, interceptors and more",
6
6
  "repository": "ryan-zayne/callApi",