@zayne-labs/callapi 0.3.2 → 0.3.4

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.
package/README.md CHANGED
@@ -34,6 +34,11 @@ To do this, you first need to set your `script`'s type to `module`, then import
34
34
  <script type="module">
35
35
  import { callApi } from "https://esm.run/@zayne-labs/callapi";
36
36
  </script>
37
+
38
+ <!-- Locked to a specific version -->
39
+ <script type="module">
40
+ import { callApi } from "https://esm.run/@zayne-labs/callapi@0.3.2";
41
+ </script>
37
42
  ```
38
43
 
39
44
  ## Quick Start
@@ -54,7 +59,11 @@ const { data, error, response } = await callApi("url", fetchOptions);
54
59
 
55
60
  To see how to use callApi with typescript for extra autocomplete convenience, visit the [Typescript section](#usage-with-typescript)
56
61
 
57
- ## Supported response types
62
+ <br>
63
+
64
+ # Helpful Features
65
+
66
+ ## ✔️ Supported response types
58
67
 
59
68
  CallApi supports all response types offered by the fetch api like `json`, `text`,`blob`,`formData` etc, so you don't have to write `response.json()`, `response.text()`, `response.formData()` etc.
60
69
 
@@ -75,7 +84,7 @@ const response = await fetch("some-url");
75
84
  const data = await response.json(); // Or response.text() or response.blob() etc
76
85
  ```
77
86
 
78
- ## Easy error handling via using `async`/`await`
87
+ ## ✔️ Easy error handling via `async`/`await`
79
88
 
80
89
  CallApi provides a unified error handling through an `error` object, which captures both HTTP errors (errors coming as a response from the api) and standard JavaScript errors.
81
90
 
@@ -102,23 +111,24 @@ const { data, error } = await callApi("some-url");
102
111
 
103
112
  console.log(error.errorName);
104
113
  console.log(error.message);
105
- // Will reference the Error object if the error not an http error, else would contain the parsed error response data
114
+ // Will reference the would contain the parsed error response data if it is an HTTPError, else it would just reference the corresponding js Error object
106
115
  console.log(error.errorData);
107
116
  ```
108
117
 
109
118
  For extra convenience with typescript, visit the [Typescript section](#usage-with-typescript)
110
119
 
111
- ## Helpful Features
112
-
113
120
  ## ✔️ Automatic Cancellation of Redundant Requests (No more race conditions🤩)
114
121
 
115
- `CallApi` implements an internal request management system to prevent race conditions and ensure that only the most recent request to a given URL is processed.
122
+ `CallApi` uses an internal request management system to ensure that only the most recent request to a given URL is processed, hence preventing the dreaded race conditions.
116
123
 
117
124
  **How this feature Works in detail**:
118
125
 
119
- - When a new request is made, `callApi` internals first check if there's an ongoing request to the same URL.
126
+ - When a request is made, `callApi` internals check if there's an ongoing request to the same URL.
120
127
  - If a pending request exists, it's automatically cancelled.
121
- - The new request is then processed, ensuring that only the most recent data is retrieved and handled.
128
+ - Then the new request is sent.
129
+ - This process is repeated until it is certain that only the latest request to that same URL is allowed to proceed.
130
+
131
+ ![Example of automatic cancellation](./assets/image.avif)
122
132
 
123
133
  **Key takeaways**:
124
134
 
@@ -128,12 +138,16 @@ For extra convenience with typescript, visit the [Typescript section](#usage-wit
128
138
  - Configurable: If you prefer to handle request management differently, you can disable this feature by setting { cancelRedundantRequests: false } in the fetch options. No pressure 👌.
129
139
  - Manual Cancellation: You can manually cancel requests to a specific URL using the cancel method attached to `callApi`. You can also pass an abort controller signal to `callApi` (just like with fetch) as an option and abort the request when you want to.
130
140
 
141
+ Using `callApi.cancel`:
142
+
131
143
  ```js
132
144
  callApi("some-url");
133
145
 
134
146
  callApi.cancel("some-url");
135
147
  ```
136
148
 
149
+ Using `AbortController`:
150
+
137
151
  ```js
138
152
  const controller = new AbortController();
139
153
 
@@ -220,29 +234,42 @@ But if you pass in an `object`, you would have two options to chose from:
220
234
  - Use `bearer` option if you want to generate a Bearer Auth Header.
221
235
  - Use `token` if you want to generate a Token Auth Header.
222
236
 
237
+ **Passing a string:**
238
+
223
239
  ```js
224
- // Passing a string
225
240
  callApi("some-url", { auth: "token12345" });
241
+ ```
226
242
 
227
- // The above request can be written in Fetch like this:
243
+ The above request can be written in Fetch like this:
244
+
245
+ ```js
228
246
  fetch("some-url", {
229
247
  headers: { Authorization: `Bearer token12345` },
230
248
  });
249
+ ```
231
250
 
232
- // Passing an object:
251
+ **Passing an object:**
233
252
 
253
+ ```js
234
254
  // For Bearer Auth
235
255
  callApi("some-url", { auth: { bearer: "token12345" } });
256
+
257
+ // Or
258
+
236
259
  // For Token Auth
237
260
  callApi("some-url", { auth: { token: "token12345" } });
261
+ ```
238
262
 
239
- // The above requests can be written in Fetch like this:
263
+ The above requests can be written in Fetch like this:
240
264
 
265
+ ```js
241
266
  // For Bearer Auth
242
267
  fetch("some-url", {
243
268
  headers: { Authorization: `Bearer token12345` },
244
269
  });
245
270
 
271
+ // Or
272
+
246
273
  // For Token Auth
247
274
  fetch("some-url", {
248
275
  headers: { Authorization: `Token token12345` },
@@ -282,7 +309,7 @@ You could also use the `createFetchClient` function to create an instance, if yo
282
309
  ```js
283
310
  import { createFetchClient } from "@zayne-labs/callapi";
284
311
 
285
- const callAnotherApi = createFetchClient({
312
+ const callApi = createFetchClient({
286
313
  timeout: 5000,
287
314
  baseURL: "https://api.example.com"
288
315
  });
@@ -1,2 +1,2 @@
1
- "use strict";var h=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Z=(e,r)=>{for(var o in r)h(e,o,{get:r[o],enumerable:!0})},ee=(e,r,o,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of Y(r))!X.call(e,s)&&s!==o&&h(e,s,{get:()=>r[s],enumerable:!(a=J(r,s))||a.enumerable});return e};var re=e=>ee(h({},"__esModule",{value:!0}),e);var ae={};Z(ae,{HTTPError:()=>p,callApi:()=>N,createFetchClient:()=>g,isHTTPError:()=>G,isHTTPErrorInstance:()=>b,toQueryString:()=>w});module.exports=re(ae);var S=e=>Array.isArray(e),F=e=>e instanceof FormData,c=e=>typeof e=="object"&&e!==null&&!(e instanceof FormData)&&!Array.isArray(e),M=e=>typeof e=="function",x=e=>typeof e=="string";var w=e=>e?new URLSearchParams(e).toString():(console.error("No query params provided"),null),v=(e,r)=>{if(!r)return e;let o=w(r);return e.includes("?")&&!e.endsWith("?")?`${e}&${o}`:e.endsWith("?")?`${e}${o}`:`${e}?${o}`},A=e=>!e||c(e)?e:Object.fromEntries(S(e)?e:e.entries()),te={408:"Request Timeout",409:"Conflict",425:"Too Early",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},B=Object.keys(te).map(Number),H=["GET"],$=["body","integrity","method","headers","signal","cache","redirect","window","credentials","keepalive","referrer","priority","mode","referrerPolicy"],oe=(e,r)=>{let o=Object.entries(e).filter(([s])=>!r.includes(s));return Object.fromEntries(o)},se=(e,r)=>{let o=new Set(r),s=Object.entries(e).filter(([y])=>o.has(y));return Object.fromEntries(s)},C=e=>[se(e,$),oe(e,$)],ne=(e,r)=>({json:async()=>r?r(await e.text()):e.json(),arrayBuffer:()=>e.arrayBuffer(),blob:()=>e.blob(),formData:()=>e.formData(),text:()=>e.text()}),D=(e,r,o)=>{let a=ne(e,o);if(!Object.hasOwn(a,r))throw new Error(`Invalid response type: ${r}`);return a[r]()},I=e=>{let{options:r,response:o,successData:a}=e,s={data:a,errorInfo:null,response:o};return r.resultMode===void 0||r.resultMode==="all"?s:{onlySuccess:s.data,onlyError:s.errorInfo,onlyResponse:s.response}[r.resultMode]},U=e=>{let{error:r,options:o}=e;return(s={})=>{let{errorData:R,message:y,response:O}=s;if(M(o.throwOnError)?o.throwOnError(r):o.throwOnError)throw r;return{data:null,error:{errorName:r?.name??"UnknownError",errorData:R??r,message:y??r?.message??o.defaultErrorMessage},response:O??null}}},G=e=>c(e)&&e.errorName==="HTTPError",p=class extends Error{response;name="HTTPError";isHTTPError=!0;constructor(r,o){let{defaultErrorMessage:a,response:s}=r;super(s.errorData.message??a,o),this.response=s}},b=e=>e instanceof p||c(e)&&e.name==="HTTPError"&&e.isHTTPError===!0,z=e=>{if(e===0)return;let{promise:r,resolve:o}=Promise.withResolvers();return setTimeout(o,e),r};var g=e=>{let r=new Map,[o,a]=C(e??{}),{headers:s,body:R,signal:y,...O}=o,f=async(l,j)=>{let[W,L]=C(j??{}),t={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:B,retryMethods:H,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...a,...L},{signal:K=y,body:u=R,headers:P,...Q}=W,k=r.get(l);if(k&&t.cancelRedundantRequests){let n=new DOMException("Cancelled the previous unfinished request","AbortError");k.abort(n)}let m=new AbortController;r.set(l,m);let V=t.timeout?AbortSignal.timeout(t.timeout):null,q=AbortSignal.any([m.signal,V??m.signal,K??m.signal]),d={signal:q,method:"GET",body:c(u)?t.bodySerializer(u):u,headers:s||P||t.auth||c(u)?{...c(u)&&{"Content-Type":"application/json",Accept:"application/json"},...F(u)&&{"Content-Type":"multipart/form-data"},...x(u)&&{"Content-Type":"application/x-www-form-urlencoded"},...x(t.auth)&&{Authorization:`Bearer ${t.auth}`},...c(t.auth)&&{Authorization:"bearer"in t.auth?`Bearer ${t.auth.bearer}`:`Token ${t.auth.token}`},...A(s),...A(P)}:void 0,...O,...Q};try{await t.onRequest?.({request:d,options:t});let n=await fetch(`${t.baseURL}${v(l,t.query)}`,d);if(!n.ok&&!q.aborted&&t.retries>0&&t.retryCodes.includes(n.status)&&t.retryMethods.includes(d.method))return await z(t.retryDelay),await f(l,{...j,retries:t.retries-1});if(!n.ok){let _=await D(n.clone(),t.responseType,t.responseParser);throw new p({response:{...n,errorData:_},defaultErrorMessage:t.defaultErrorMessage})}let i=await D(n.clone(),t.responseType,t.responseParser),E=t.responseValidator?t.responseValidator(i):i;return await t.onResponse?.({response:{...n,data:E},request:d,options:t}),I({successData:E,response:n,options:t})}catch(n){let T=U({error:n,options:t});if(n instanceof DOMException&&n.name==="TimeoutError"){let i=`Request timed out after ${t.timeout}ms`;return console.info(`%cTimeoutError: ${i}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),T({message:i})}if(n instanceof DOMException&&n.name==="AbortError"){let i="Request was cancelled";return console.info(`%AbortError: ${i}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),T({message:i})}if(b(n)){let{errorData:i,...E}=n.response;return await t.onResponseError?.({response:{...E,errorData:i},request:d,options:t}),T({errorData:i,message:i?.message,response:E})}return await t.onRequestError?.({request:d,error:n,options:t}),T()}finally{r.delete(l)}};return f.create=g,f.cancel=l=>r.get(l)?.abort(),f},N=g();0&&(module.exports={HTTPError,callApi,createFetchClient,isHTTPError,isHTTPErrorInstance,toQueryString});
1
+ "use strict";var h=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Z=(e,r)=>{for(var o in r)h(e,o,{get:r[o],enumerable:!0})},ee=(e,r,o,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of Y(r))!X.call(e,s)&&s!==o&&h(e,s,{get:()=>r[s],enumerable:!(a=J(r,s))||a.enumerable});return e};var re=e=>ee(h({},"__esModule",{value:!0}),e);var ae={};Z(ae,{HTTPError:()=>d,callApi:()=>N,createFetchClient:()=>g,isHTTPError:()=>G,isHTTPErrorInstance:()=>b,toQueryString:()=>w});module.exports=re(ae);var F=e=>Array.isArray(e),M=e=>e instanceof FormData,c=e=>typeof e=="object"&&e!==null&&!(e instanceof FormData)&&!Array.isArray(e),S=e=>typeof e=="function",x=e=>typeof e=="string";var w=e=>e?new URLSearchParams(e).toString():(console.error("No query params provided"),null),v=(e,r)=>{if(!r)return e;let o=w(r);return e.includes("?")&&!e.endsWith("?")?`${e}&${o}`:e.endsWith("?")?`${e}${o}`:`${e}?${o}`},D=e=>!e||c(e)?e:Object.fromEntries(F(e)?e:e.entries()),te={408:"Request Timeout",409:"Conflict",425:"Too Early",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},B=Object.keys(te).map(Number),H=["GET"],$=["body","integrity","method","headers","signal","cache","redirect","window","credentials","keepalive","referrer","priority","mode","referrerPolicy"],oe=(e,r)=>{let o=Object.entries(e).filter(([s])=>!r.includes(s));return Object.fromEntries(o)},se=(e,r)=>{let o=new Set(r),s=Object.entries(e).filter(([y])=>o.has(y));return Object.fromEntries(s)},A=e=>[se(e,$),oe(e,$)],ne=(e,r)=>({json:async()=>r?r(await e.text()):e.json(),arrayBuffer:()=>e.arrayBuffer(),blob:()=>e.blob(),formData:()=>e.formData(),text:()=>e.text()}),C=(e,r,o)=>{let a=ne(e,o);if(!Object.hasOwn(a,r))throw new Error(`Invalid response type: ${r}`);return a[r]()},I=e=>{let{options:r,response:o,successData:a}=e,s={data:a,errorInfo:null,response:o};return r.resultMode===void 0||r.resultMode==="all"?s:{onlySuccess:s.data,onlyError:s.errorInfo,onlyResponse:s.response}[r.resultMode]},U=e=>{let{error:r,options:o}=e;return(s={})=>{let{errorData:l,message:y,response:O}=s;if(S(o.throwOnError)?o.throwOnError(r):o.throwOnError)throw r;return{data:null,error:{errorName:r?.name??"UnknownError",errorData:l??r,message:y??r?.message??o.defaultErrorMessage},response:O??null}}},G=e=>c(e)&&e.errorName==="HTTPError",d=class extends Error{response;errorData;name="HTTPError";isHTTPError=!0;constructor(r,o){let{defaultErrorMessage:a,response:s,errorData:l}=r;super(l.message??a,o),this.errorData=l,this.response=s}},b=e=>e instanceof d||c(e)&&e.name==="HTTPError"&&e.isHTTPError===!0,z=e=>{if(e===0)return;let{promise:r,resolve:o}=Promise.withResolvers();return setTimeout(o,e),r};var g=e=>{let r=new Map,[o,a]=A(e??{}),{headers:s,body:l,signal:y,...O}=o,T=async(u,j)=>{let[L,K]=A(j??{}),{signal:Q=y,body:p=l,headers:P,...V}=L,t={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:B,retryMethods:H,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...a,...K},k=r.get(u);if(k&&t.cancelRedundantRequests){let n=new DOMException("Cancelled the previous unfinished request","AbortError");k.abort(n)}let m=new AbortController;r.set(u,m);let W=t.timeout?AbortSignal.timeout(t.timeout):null,q=AbortSignal.any([m.signal,W??m.signal,Q??m.signal]),f={signal:q,method:"GET",body:c(p)?t.bodySerializer(p):p,headers:s||P||t.auth||c(p)?{...c(p)&&{"Content-Type":"application/json",Accept:"application/json"},...M(p)&&{"Content-Type":"multipart/form-data"},...x(p)&&{"Content-Type":"application/x-www-form-urlencoded"},...x(t.auth)&&{Authorization:`Bearer ${t.auth}`},...c(t.auth)&&{Authorization:"bearer"in t.auth?`Bearer ${t.auth.bearer}`:`Token ${t.auth.token}`},...D(s),...D(P)}:void 0,...O,...V};try{await t.onRequest?.({request:f,options:t});let n=await fetch(`${t.baseURL}${v(u,t.query)}`,f);if(!n.ok&&!q.aborted&&t.retries>0&&t.retryCodes.includes(n.status)&&t.retryMethods.includes(f.method))return await z(t.retryDelay),await T(u,{...j,retries:t.retries-1});if(!n.ok){let _=await C(n.clone(),t.responseType,t.responseParser);throw new d({errorData:_,response:n,defaultErrorMessage:t.defaultErrorMessage})}let i=await C(n.clone(),t.responseType,t.responseParser),R=t.responseValidator?t.responseValidator(i):i;return await t.onResponse?.({response:Object.assign(n.clone(),{data:R}),request:f,options:t}),I({successData:R,response:n,options:t})}catch(n){let E=U({error:n,options:t});if(n instanceof DOMException&&n.name==="TimeoutError"){let i=`Request timed out after ${t.timeout}ms`;return console.info(`%cTimeoutError: ${i}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),E({message:i})}if(n instanceof DOMException&&n.name==="AbortError"){let i="Request was cancelled";return console.info(`%AbortError: ${i}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),E({message:i})}if(b(n)){let{errorData:i,response:R}=n;return await t.onResponseError?.({response:Object.assign(R.clone(),{errorData:i}),request:f,options:t}),E({errorData:i,message:i?.message,response:R})}return await t.onRequestError?.({request:f,error:n,options:t}),E()}finally{r.delete(u)}};return T.create=g,T.cancel=u=>r.get(u)?.abort(),T},N=g();0&&(module.exports={HTTPError,callApi,createFetchClient,isHTTPError,isHTTPErrorInstance,toQueryString});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts","../../src/typeof.ts","../../src/utils.ts","../../src/createFetchClient.ts"],"sourcesContent":["export { callApi, createFetchClient } from \"./createFetchClient\";\n\nexport type {\n\tFetchConfig,\n\t$RequestOptions,\n\tExtraOptions,\n\tResponseContext,\n\tResponseErrorContext,\n} from \"./types\";\n\nexport { HTTPError, isHTTPError, isHTTPErrorInstance, toQueryString } from \"./utils\";\n","import type { AnyFunction } from \"./type-helpers\";\n\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\n\nexport const isFormData = (value: unknown) => value instanceof FormData;\n\nexport const isObject = <TObject extends Record<string, unknown>>(value: unknown): value is TObject => {\n\treturn (\n\t\ttypeof value === \"object\" && value !== null && !(value instanceof FormData) && !Array.isArray(value)\n\t);\n};\n\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\n\ttypeof value === \"function\";\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n","import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n\t(params: ExtraOptions[\"query\"]): string | null;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"No query params provided\");\n\t\treturn null as never;\n\t}\n\n\treturn new URLSearchParams(params as Record<string, string>).toString();\n};\n\nexport const mergeUrlWithParams = (url: string, params: ExtraOptions[\"query\"]): string => {\n\tif (!params) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(params);\n\n\tif (url.includes(\"?\") && !url.endsWith(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\nconst omitKeys = <TObject extends Record<string, unknown>, const TOmitArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToOmit: TOmitArray\n) => {\n\tconst arrayFromFilteredObject = Object.entries(initialObject).filter(\n\t\t([key]) => !keysToOmit.includes(key)\n\t);\n\n\tconst updatedObject = Object.fromEntries(arrayFromFilteredObject);\n\n\treturn updatedObject as Omit<TObject, keyof TOmitArray>;\n};\n\nconst pickKeys = <TObject extends Record<string, unknown>, const TPickArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToPick: TPickArray\n) => {\n\tconst keysToPickSet = new Set(keysToPick);\n\n\tconst arrayFromInitObject = Object.entries(initialObject);\n\n\tconst filteredArray = arrayFromInitObject.filter(([objectKey]) => keysToPickSet.has(objectKey));\n\n\tconst updatedObject = Object.fromEntries(filteredArray);\n\n\treturn updatedObject as Pick<TObject, TPickArray[number]>;\n};\n\nexport const splitConfig = <TObject extends object>(\n\tconfig: TObject\n): [\"body\" extends keyof TObject ? $RequestOptions : $BaseRequestOptions, ExtraOptions] => [\n\tpickKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n\tomitKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n];\n\nexport const handleResponseType = <TResponse>(\n\tresponse: Response,\n\tparser?: Required<ExtraOptions>[\"responseParser\"]\n) => ({\n\tjson: async () => {\n\t\tif (parser) {\n\t\t\treturn parser(await response.text());\n\t\t}\n\n\t\treturn response.json() as Promise<TResponse>;\n\t},\n\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\n\tblob: () => response.blob() as Promise<TResponse>,\n\tformData: () => response.formData() as Promise<TResponse>,\n\ttext: () => response.text() as Promise<TResponse>,\n});\n\nexport const getResponseData = <TResponse>(\n\tresponse: Response,\n\tresponseType: keyof ReturnType<typeof handleResponseType>,\n\tparser: ExtraOptions[\"responseParser\"]\n) => {\n\tconst RESPONSE_TYPE_LOOKUP = handleResponseType<TResponse>(response, parser);\n\n\tif (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {\n\t\tthrow new Error(`Invalid response type: ${responseType}`);\n\t}\n\n\treturn RESPONSE_TYPE_LOOKUP[responseType]();\n};\n\ntype data = {\n\tsuccessData: unknown;\n\toptions: ExtraOptions;\n\tresponse: Response;\n};\n\n// == The CallApiResult type is used to cast all return statements due to a design limitation in ts.\n// LINK - See https://www.zhenghao.io/posts/type-functions for more info\nexport const resolveSuccessResult = <CallApiResult>(info: data): CallApiResult => {\n\tconst { options, response, successData } = info;\n\n\tconst apiDetails = {\n\t\tdata: successData,\n\t\terrorInfo: null,\n\t\tresponse,\n\t};\n\n\tif (options.resultMode === undefined || options.resultMode === \"all\") {\n\t\treturn apiDetails as CallApiResult;\n\t}\n\n\treturn {\n\t\tonlySuccess: apiDetails.data,\n\t\tonlyError: apiDetails.errorInfo,\n\t\tonlyResponse: apiDetails.response,\n\t}[options.resultMode] as CallApiResult;\n};\n\n// == Using curring here so error and options are not required to be passed every time, instead to be captured once by way of closure\nexport const $resolveErrorResult = <CallApiResult>($info: { error?: unknown; options: ExtraOptions }) => {\n\tconst { error, options } = $info;\n\n\ttype ErrorInfo = {\n\t\tresponse?: Response;\n\t\terrorData?: unknown;\n\t\tmessage?: string;\n\t};\n\n\tconst resolveErrorResult = (info: ErrorInfo = {}): CallApiResult => {\n\t\tconst { errorData, message, response } = info;\n\n\t\tconst shouldThrowOnError = isFunction(options.throwOnError)\n\t\t\t? options.throwOnError(error as Error)\n\t\t\t: options.throwOnError;\n\n\t\tif (shouldThrowOnError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn {\n\t\t\tdata: null,\n\t\t\terror: {\n\t\t\t\terrorName: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? error,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.errorName === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\tresponse: Response & { errorData: TErrorResponse };\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response } = errorDetails;\n\n\t\tsuper((response.errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n","import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tAbortSignalWithAny,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = 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 {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = 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\t/** Default Options */\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 = baseBody, 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 the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || 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...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization:\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\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 $RequestOptions;\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!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\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 waitUntil(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.clone(),\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.clone(),\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\tresponse: { ...response, data: validSuccessData },\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, 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\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t\tresponse,\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\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"],"mappings":"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","names":["src_exports","__export","HTTPError","callApi","createFetchClient","isHTTPError","isHTTPErrorInstance","toQueryString","__toCommonJS","isArray","value","isFormData","isObject","isFunction","isString","toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","isObject","isArray","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isFunction","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","splitConfig","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","options","defaultRetryCodes","defaultRetryMethods","signal","body","headers","restOfFetchConfig","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","isObject","isFormData","isString","objectifyHeaders","response","mergeUrlWithParams","waitUntil","errorData","getResponseData","HTTPError","successData","validSuccessData","resolveSuccessResult","error","resolveErrorResult","$resolveErrorResult","message","isHTTPErrorInstance"]}
1
+ {"version":3,"sources":["../../src/index.ts","../../src/typeof.ts","../../src/utils.ts","../../src/createFetchClient.ts"],"sourcesContent":["export { callApi, createFetchClient } from \"./createFetchClient\";\n\nexport type {\n\tFetchConfig,\n\t$RequestOptions,\n\tExtraOptions,\n\tResponseContext,\n\tResponseErrorContext,\n} from \"./types\";\n\nexport { HTTPError, isHTTPError, isHTTPErrorInstance, toQueryString } from \"./utils\";\n","import type { AnyFunction } from \"./type-helpers\";\n\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\n\nexport const isFormData = (value: unknown) => value instanceof FormData;\n\nexport const isObject = <TObject extends Record<string, unknown>>(value: unknown): value is TObject => {\n\treturn (\n\t\ttypeof value === \"object\" && value !== null && !(value instanceof FormData) && !Array.isArray(value)\n\t);\n};\n\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\n\ttypeof value === \"function\";\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n","import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n\t(params: ExtraOptions[\"query\"]): string | null;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"No query params provided\");\n\t\treturn null as never;\n\t}\n\n\treturn new URLSearchParams(params as Record<string, string>).toString();\n};\n\nexport const mergeUrlWithParams = (url: string, params: ExtraOptions[\"query\"]): string => {\n\tif (!params) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(params);\n\n\tif (url.includes(\"?\") && !url.endsWith(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\nconst omitKeys = <TObject extends Record<string, unknown>, const TOmitArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToOmit: TOmitArray\n) => {\n\tconst arrayFromFilteredObject = Object.entries(initialObject).filter(\n\t\t([key]) => !keysToOmit.includes(key)\n\t);\n\n\tconst updatedObject = Object.fromEntries(arrayFromFilteredObject);\n\n\treturn updatedObject as Omit<TObject, keyof TOmitArray>;\n};\n\nconst pickKeys = <TObject extends Record<string, unknown>, const TPickArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToPick: TPickArray\n) => {\n\tconst keysToPickSet = new Set(keysToPick);\n\n\tconst arrayFromInitObject = Object.entries(initialObject);\n\n\tconst filteredArray = arrayFromInitObject.filter(([objectKey]) => keysToPickSet.has(objectKey));\n\n\tconst updatedObject = Object.fromEntries(filteredArray);\n\n\treturn updatedObject as Pick<TObject, TPickArray[number]>;\n};\n\nexport const splitConfig = <TObject extends object>(\n\tconfig: TObject\n): [\"body\" extends keyof TObject ? $RequestOptions : $BaseRequestOptions, ExtraOptions] => [\n\tpickKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n\tomitKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n];\n\nexport const handleResponseType = <TResponse>(\n\tresponse: Response,\n\tparser?: Required<ExtraOptions>[\"responseParser\"]\n) => ({\n\tjson: async () => {\n\t\tif (parser) {\n\t\t\treturn parser(await response.text());\n\t\t}\n\n\t\treturn response.json() as Promise<TResponse>;\n\t},\n\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\n\tblob: () => response.blob() as Promise<TResponse>,\n\tformData: () => response.formData() as Promise<TResponse>,\n\ttext: () => response.text() as Promise<TResponse>,\n});\n\nexport const getResponseData = <TResponse>(\n\tresponse: Response,\n\tresponseType: keyof ReturnType<typeof handleResponseType>,\n\tparser: ExtraOptions[\"responseParser\"]\n) => {\n\tconst RESPONSE_TYPE_LOOKUP = handleResponseType<TResponse>(response, parser);\n\n\tif (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {\n\t\tthrow new Error(`Invalid response type: ${responseType}`);\n\t}\n\n\treturn RESPONSE_TYPE_LOOKUP[responseType]();\n};\n\ntype data = {\n\tsuccessData: unknown;\n\toptions: ExtraOptions;\n\tresponse: Response;\n};\n\n// == The CallApiResult type is used to cast all return statements due to a design limitation in ts.\n// LINK - See https://www.zhenghao.io/posts/type-functions for more info\nexport const resolveSuccessResult = <CallApiResult>(info: data): CallApiResult => {\n\tconst { options, response, successData } = info;\n\n\tconst apiDetails = {\n\t\tdata: successData,\n\t\terrorInfo: null,\n\t\tresponse,\n\t};\n\n\tif (options.resultMode === undefined || options.resultMode === \"all\") {\n\t\treturn apiDetails as CallApiResult;\n\t}\n\n\treturn {\n\t\tonlySuccess: apiDetails.data,\n\t\tonlyError: apiDetails.errorInfo,\n\t\tonlyResponse: apiDetails.response,\n\t}[options.resultMode] as CallApiResult;\n};\n\n// == Using curring here so error and options are not required to be passed every time, instead to be captured once by way of closure\nexport const $resolveErrorResult = <CallApiResult>($info: { error?: unknown; options: ExtraOptions }) => {\n\tconst { error, options } = $info;\n\n\ttype ErrorInfo = {\n\t\tresponse?: Response;\n\t\terrorData?: unknown;\n\t\tmessage?: string;\n\t};\n\n\tconst resolveErrorResult = (info: ErrorInfo = {}): CallApiResult => {\n\t\tconst { errorData, message, response } = info;\n\n\t\tconst shouldThrowOnError = isFunction(options.throwOnError)\n\t\t\t? options.throwOnError(error as Error)\n\t\t\t: options.throwOnError;\n\n\t\tif (shouldThrowOnError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn {\n\t\t\tdata: null,\n\t\t\terror: {\n\t\t\t\terrorName: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? error,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.errorName === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\terrorData: TErrorResponse;\n\tresponse: Response;\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response, errorData } = errorDetails;\n\n\t\tsuper((errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.errorData = errorData;\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n","import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = 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 {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = 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 { signal = baseSignal, body = baseBody, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\t// == Default Options\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 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\tconst combinedSignal = AbortSignal.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 the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || 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...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization:\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\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 $RequestOptions;\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!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\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 waitUntil(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.clone(),\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\terrorData,\n\t\t\t\t\tresponse,\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.clone(),\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\t// == Workaround as opposed to using the spread operator, as it doesn't work on the response object. So using Object.assign instead on a clone of the response object.\n\t\t\t\tresponse: Object.assign(response.clone(), { data: validSuccessData }),\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, 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;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: Object.assign(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\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t\tresponse,\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\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"],"mappings":"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","names":["src_exports","__export","HTTPError","callApi","createFetchClient","isHTTPError","isHTTPErrorInstance","toQueryString","__toCommonJS","isArray","value","isFormData","isObject","isFunction","isString","toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","isObject","isArray","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isFunction","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","splitConfig","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","signal","body","headers","restOfFetchConfig","options","defaultRetryCodes","defaultRetryMethods","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","isObject","isFormData","isString","objectifyHeaders","response","mergeUrlWithParams","waitUntil","errorData","getResponseData","HTTPError","successData","validSuccessData","resolveSuccessResult","error","resolveErrorResult","$resolveErrorResult","message","isHTTPErrorInstance"]}
@@ -24,9 +24,8 @@ declare const isHTTPError: <TErrorData>(error: ApiErrorVariant<TErrorData>["erro
24
24
  message: string;
25
25
  };
26
26
  type ErrorDetails<TErrorResponse> = {
27
- response: Response & {
28
- errorData: TErrorResponse;
29
- };
27
+ errorData: TErrorResponse;
28
+ response: Response;
30
29
  defaultErrorMessage: string;
31
30
  };
32
31
  type ErrorOptions = {
@@ -34,6 +33,7 @@ type ErrorOptions = {
34
33
  };
35
34
  declare class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {
36
35
  response: ErrorDetails<TErrorResponse>["response"];
36
+ errorData: ErrorDetails<TErrorResponse>["errorData"];
37
37
  name: "HTTPError";
38
38
  isHTTPError: boolean;
39
39
  constructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions);
@@ -0,0 +1,8 @@
1
+ import { g, d, e, c as c$1, b as b$1, o, i, m, j, k, n } from './chunk-W6T7K2ZN.js';
2
+ import { c, b, e as e$1 } from './chunk-3E33QJL5.js';
3
+
4
+ var x=S=>{let c$2=new Map,[$,F]=g(S??{}),{headers:R,body:j$1,signal:P,...v}=$,u=async(o$1,h)=>{let[U,z]=g(h??{}),{signal:k$1=P,body:s=j$1,headers:b$2,...G}=U,e$2={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:d,retryMethods:e,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...F,...z},E=c$2.get(o$1);if(E&&e$2.cancelRedundantRequests){let t=new DOMException("Cancelled the previous unfinished request","AbortError");E.abort(t);}let d$1=new AbortController;c$2.set(o$1,d$1);let H=e$2.timeout?AbortSignal.timeout(e$2.timeout):null,y=AbortSignal.any([d$1.signal,H??d$1.signal,k$1??d$1.signal]),a={signal:y,method:"GET",body:c(s)?e$2.bodySerializer(s):s,headers:R||b$2||e$2.auth||c(s)?{...c(s)&&{"Content-Type":"application/json",Accept:"application/json"},...b(s)&&{"Content-Type":"multipart/form-data"},...e$1(s)&&{"Content-Type":"application/x-www-form-urlencoded"},...e$1(e$2.auth)&&{Authorization:`Bearer ${e$2.auth}`},...c(e$2.auth)&&{Authorization:"bearer"in e$2.auth?`Bearer ${e$2.auth.bearer}`:`Token ${e$2.auth.token}`},...c$1(R),...c$1(b$2)}:void 0,...v,...G};try{await e$2.onRequest?.({request:a,options:e$2});let t=await fetch(`${e$2.baseURL}${b$1(o$1,e$2.query)}`,a);if(!t.ok&&!y.aborted&&e$2.retries>0&&e$2.retryCodes.includes(t.status)&&e$2.retryMethods.includes(a.method))return await o(e$2.retryDelay),await u(o$1,{...h,retries:e$2.retries-1});if(!t.ok){let I=await i(t.clone(),e$2.responseType,e$2.responseParser);throw new m({errorData:I,response:t,defaultErrorMessage:e$2.defaultErrorMessage})}let r=await i(t.clone(),e$2.responseType,e$2.responseParser),i$1=e$2.responseValidator?e$2.responseValidator(r):r;return await e$2.onResponse?.({response:Object.assign(t.clone(),{data:i$1}),request:a,options:e$2}),j({successData:i$1,response:t,options:e$2})}catch(t){let n$1=k({error:t,options:e$2});if(t instanceof DOMException&&t.name==="TimeoutError"){let r=`Request timed out after ${e$2.timeout}ms`;return console.info(`%cTimeoutError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),n$1({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){let r="Request was cancelled";return console.info(`%AbortError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),n$1({message:r})}if(n(t)){let{errorData:r,response:i}=t;return await e$2.onResponseError?.({response:Object.assign(i.clone(),{errorData:r}),request:a,options:e$2}),n$1({errorData:r,message:r?.message,response:i})}return await e$2.onRequestError?.({request:a,error:t,options:e$2}),n$1()}finally{c$2.delete(o$1);}};return u.create=x,u.cancel=o=>c$2.get(o)?.abort(),u},J=x();
5
+
6
+ export { x as a, J as b };
7
+ //# sourceMappingURL=out.js.map
8
+ //# sourceMappingURL=chunk-LO64E2L2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/createFetchClient.ts"],"names":["createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","splitConfig","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","signal","body","headers","restOfFetchConfig","options","defaultRetryCodes","defaultRetryMethods","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","isObject","isFormData","isString","objectifyHeaders","response","mergeUrlWithParams","waitUntil","errorData","getResponseData","HTTPError","successData","validSuccessData","resolveSuccessResult","error","resolveErrorResult","$resolveErrorResult","message","isHTTPErrorInstance"],"mappings":"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","sourcesContent":["import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = 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 {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = 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 { signal = baseSignal, body = baseBody, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\t// == Default Options\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 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\tconst combinedSignal = AbortSignal.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 the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || 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...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization:\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\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 $RequestOptions;\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!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\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 waitUntil(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.clone(),\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\terrorData,\n\t\t\t\t\tresponse,\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.clone(),\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\t// == Workaround as opposed to using the spread operator, as it doesn't work on the response object. So using Object.assign instead on a clone of the response object.\n\t\t\t\tresponse: Object.assign(response.clone(), { data: validSuccessData }),\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, 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;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: Object.assign(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\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t\tresponse,\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\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"]}
@@ -0,0 +1,7 @@
1
+ import { c as c$1, a, d } from './chunk-3E33QJL5.js';
2
+
3
+ var E=r=>r?new URLSearchParams(r).toString():(console.error("No query params provided"),null),b=(r,e)=>{if(!e)return r;let t=E(e);return r.includes("?")&&!r.endsWith("?")?`${r}&${t}`:r.endsWith("?")?`${r}${t}`:`${r}?${t}`},g=r=>!r||c$1(r)?r:Object.fromEntries(a(r)?r:r.entries()),y={408:"Request Timeout",409:"Conflict",425:"Too Early",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},x=Object.keys(y).map(Number),j=["GET"],l=["body","integrity","method","headers","signal","cache","redirect","window","credentials","keepalive","referrer","priority","mode","referrerPolicy"],T=(r,e)=>{let t=Object.entries(r).filter(([s])=>!e.includes(s));return Object.fromEntries(t)},R=(r,e)=>{let t=new Set(e),s=Object.entries(r).filter(([i])=>t.has(i));return Object.fromEntries(s)},w=r=>[R(r,l),T(r,l)],O=(r,e)=>({json:async()=>e?e(await r.text()):r.json(),arrayBuffer:()=>r.arrayBuffer(),blob:()=>r.blob(),formData:()=>r.formData(),text:()=>r.text()}),P=(r,e,t)=>{let o=O(r,t);if(!Object.hasOwn(o,e))throw new Error(`Invalid response type: ${e}`);return o[e]()},k=r=>{let{options:e,response:t,successData:o}=r,s={data:o,errorInfo:null,response:t};return e.resultMode===void 0||e.resultMode==="all"?s:{onlySuccess:s.data,onlyError:s.errorInfo,onlyResponse:s.response}[e.resultMode]},h=r=>{let{error:e,options:t}=r;return (s={})=>{let{errorData:n,message:i,response:d$1}=s;if(d(t.throwOnError)?t.throwOnError(e):t.throwOnError)throw e;return {data:null,error:{errorName:e?.name??"UnknownError",errorData:n??e,message:i??e?.message??t.defaultErrorMessage},response:d$1??null}}},A=r=>c$1(r)&&r.errorName==="HTTPError",c=class extends Error{response;errorData;name="HTTPError";isHTTPError=!0;constructor(e,t){let{defaultErrorMessage:o,response:s,errorData:n}=e;super(n.message??o,t),this.errorData=n,this.response=s;}},D=r=>r instanceof c||c$1(r)&&r.name==="HTTPError"&&r.isHTTPError===!0,C=r=>{if(r===0)return;let{promise:e,resolve:t}=Promise.withResolvers();return setTimeout(t,r),e};
4
+
5
+ export { E as a, b, g as c, x as d, j as e, l as f, w as g, O as h, P as i, k as j, h as k, A as l, c as m, D as n, C as o };
6
+ //# sourceMappingURL=out.js.map
7
+ //# sourceMappingURL=chunk-W6T7K2ZN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils.ts"],"names":["toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","isObject","isArray","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isFunction","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve"],"mappings":"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","sourcesContent":["import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n\t(params: ExtraOptions[\"query\"]): string | null;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"No query params provided\");\n\t\treturn null as never;\n\t}\n\n\treturn new URLSearchParams(params as Record<string, string>).toString();\n};\n\nexport const mergeUrlWithParams = (url: string, params: ExtraOptions[\"query\"]): string => {\n\tif (!params) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(params);\n\n\tif (url.includes(\"?\") && !url.endsWith(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\nconst omitKeys = <TObject extends Record<string, unknown>, const TOmitArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToOmit: TOmitArray\n) => {\n\tconst arrayFromFilteredObject = Object.entries(initialObject).filter(\n\t\t([key]) => !keysToOmit.includes(key)\n\t);\n\n\tconst updatedObject = Object.fromEntries(arrayFromFilteredObject);\n\n\treturn updatedObject as Omit<TObject, keyof TOmitArray>;\n};\n\nconst pickKeys = <TObject extends Record<string, unknown>, const TPickArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToPick: TPickArray\n) => {\n\tconst keysToPickSet = new Set(keysToPick);\n\n\tconst arrayFromInitObject = Object.entries(initialObject);\n\n\tconst filteredArray = arrayFromInitObject.filter(([objectKey]) => keysToPickSet.has(objectKey));\n\n\tconst updatedObject = Object.fromEntries(filteredArray);\n\n\treturn updatedObject as Pick<TObject, TPickArray[number]>;\n};\n\nexport const splitConfig = <TObject extends object>(\n\tconfig: TObject\n): [\"body\" extends keyof TObject ? $RequestOptions : $BaseRequestOptions, ExtraOptions] => [\n\tpickKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n\tomitKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n];\n\nexport const handleResponseType = <TResponse>(\n\tresponse: Response,\n\tparser?: Required<ExtraOptions>[\"responseParser\"]\n) => ({\n\tjson: async () => {\n\t\tif (parser) {\n\t\t\treturn parser(await response.text());\n\t\t}\n\n\t\treturn response.json() as Promise<TResponse>;\n\t},\n\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\n\tblob: () => response.blob() as Promise<TResponse>,\n\tformData: () => response.formData() as Promise<TResponse>,\n\ttext: () => response.text() as Promise<TResponse>,\n});\n\nexport const getResponseData = <TResponse>(\n\tresponse: Response,\n\tresponseType: keyof ReturnType<typeof handleResponseType>,\n\tparser: ExtraOptions[\"responseParser\"]\n) => {\n\tconst RESPONSE_TYPE_LOOKUP = handleResponseType<TResponse>(response, parser);\n\n\tif (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {\n\t\tthrow new Error(`Invalid response type: ${responseType}`);\n\t}\n\n\treturn RESPONSE_TYPE_LOOKUP[responseType]();\n};\n\ntype data = {\n\tsuccessData: unknown;\n\toptions: ExtraOptions;\n\tresponse: Response;\n};\n\n// == The CallApiResult type is used to cast all return statements due to a design limitation in ts.\n// LINK - See https://www.zhenghao.io/posts/type-functions for more info\nexport const resolveSuccessResult = <CallApiResult>(info: data): CallApiResult => {\n\tconst { options, response, successData } = info;\n\n\tconst apiDetails = {\n\t\tdata: successData,\n\t\terrorInfo: null,\n\t\tresponse,\n\t};\n\n\tif (options.resultMode === undefined || options.resultMode === \"all\") {\n\t\treturn apiDetails as CallApiResult;\n\t}\n\n\treturn {\n\t\tonlySuccess: apiDetails.data,\n\t\tonlyError: apiDetails.errorInfo,\n\t\tonlyResponse: apiDetails.response,\n\t}[options.resultMode] as CallApiResult;\n};\n\n// == Using curring here so error and options are not required to be passed every time, instead to be captured once by way of closure\nexport const $resolveErrorResult = <CallApiResult>($info: { error?: unknown; options: ExtraOptions }) => {\n\tconst { error, options } = $info;\n\n\ttype ErrorInfo = {\n\t\tresponse?: Response;\n\t\terrorData?: unknown;\n\t\tmessage?: string;\n\t};\n\n\tconst resolveErrorResult = (info: ErrorInfo = {}): CallApiResult => {\n\t\tconst { errorData, message, response } = info;\n\n\t\tconst shouldThrowOnError = isFunction(options.throwOnError)\n\t\t\t? options.throwOnError(error as Error)\n\t\t\t: options.throwOnError;\n\n\t\tif (shouldThrowOnError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn {\n\t\t\tdata: null,\n\t\t\terror: {\n\t\t\t\terrorName: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? error,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.errorName === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\terrorData: TErrorResponse;\n\tresponse: Response;\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response, errorData } = errorDetails;\n\n\t\tsuper((errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.errorData = errorData;\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n"]}
@@ -1,4 +1,4 @@
1
- import { R as ResultModeUnion, B as BaseConfig, F as FetchConfig, G as GetCallApiResult } from './types-Cq2rXhid.js';
1
+ import { R as ResultModeUnion, B as BaseConfig, F as FetchConfig, G as GetCallApiResult } from './types-BbqJOcbJ.js';
2
2
  import './type-helpers-Dibitydy.js';
3
3
 
4
4
  declare const createFetchClient: <TBaseData, TBaseErrorData, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
@@ -1,5 +1,5 @@
1
- export { b as callApi, a as createFetchClient } from './chunk-L2G3D2QS.js';
2
- import './chunk-SZIMUYPI.js';
1
+ export { b as callApi, a as createFetchClient } from './chunk-LO64E2L2.js';
2
+ import './chunk-W6T7K2ZN.js';
3
3
  import './chunk-3E33QJL5.js';
4
4
  //# sourceMappingURL=out.js.map
5
5
  //# sourceMappingURL=createFetchClient.js.map
@@ -1,3 +1,3 @@
1
1
  export { callApi, createFetchClient } from './createFetchClient.js';
2
- export { $ as $RequestOptions, E as ExtraOptions, F as FetchConfig, H as HTTPError, a as ResponseContext, b as ResponseErrorContext, i as isHTTPError, c as isHTTPErrorInstance, t as toQueryString } from './types-Cq2rXhid.js';
2
+ export { $ as $RequestOptions, E as ExtraOptions, F as FetchConfig, H as HTTPError, a as ResponseContext, b as ResponseErrorContext, i as isHTTPError, c as isHTTPErrorInstance, t as toQueryString } from './types-BbqJOcbJ.js';
3
3
  import './type-helpers-Dibitydy.js';
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
- export { b as callApi, a as createFetchClient } from './chunk-L2G3D2QS.js';
2
- export { m as HTTPError, l as isHTTPError, n as isHTTPErrorInstance, a as toQueryString } from './chunk-SZIMUYPI.js';
1
+ export { b as callApi, a as createFetchClient } from './chunk-LO64E2L2.js';
2
+ export { m as HTTPError, l as isHTTPError, n as isHTTPErrorInstance, a as toQueryString } from './chunk-W6T7K2ZN.js';
3
3
  import './chunk-3E33QJL5.js';
4
4
  //# sourceMappingURL=out.js.map
5
5
  //# sourceMappingURL=index.js.map
@@ -39,9 +39,8 @@ declare const isHTTPError: <TErrorData>(error: ApiErrorVariant<TErrorData>["erro
39
39
  message: string;
40
40
  };
41
41
  type ErrorDetails<TErrorResponse> = {
42
- response: Response & {
43
- errorData: TErrorResponse;
44
- };
42
+ errorData: TErrorResponse;
43
+ response: Response;
45
44
  defaultErrorMessage: string;
46
45
  };
47
46
  type ErrorOptions = {
@@ -49,6 +48,7 @@ type ErrorOptions = {
49
48
  };
50
49
  declare class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {
51
50
  response: ErrorDetails<TErrorResponse>["response"];
51
+ errorData: ErrorDetails<TErrorResponse>["errorData"];
52
52
  name: "HTTPError";
53
53
  isHTTPError: boolean;
54
54
  constructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions);
@@ -1,2 +1,2 @@
1
- export { j as $resolveErrorResult, H as HTTPError, d as defaultRetryCodes, e as defaultRetryMethods, f as fetchSpecificKeys, g as getResponseData, h as handleResponseType, i as isHTTPError, c as isHTTPErrorInstance, m as mergeUrlWithParams, o as objectifyHeaders, r as resolveSuccessResult, s as splitConfig, t as toQueryString, w as waitUntil } from './types-Cq2rXhid.js';
1
+ export { j as $resolveErrorResult, H as HTTPError, d as defaultRetryCodes, e as defaultRetryMethods, f as fetchSpecificKeys, g as getResponseData, h as handleResponseType, i as isHTTPError, c as isHTTPErrorInstance, m as mergeUrlWithParams, o as objectifyHeaders, r as resolveSuccessResult, s as splitConfig, t as toQueryString, w as waitUntil } from './types-BbqJOcbJ.js';
2
2
  import './type-helpers-Dibitydy.js';
package/dist/esm/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- export { k as $resolveErrorResult, m as HTTPError, d as defaultRetryCodes, e as defaultRetryMethods, f as fetchSpecificKeys, i as getResponseData, h as handleResponseType, l as isHTTPError, n as isHTTPErrorInstance, b as mergeUrlWithParams, c as objectifyHeaders, j as resolveSuccessResult, g as splitConfig, a as toQueryString, o as waitUntil } from './chunk-SZIMUYPI.js';
1
+ export { k as $resolveErrorResult, m as HTTPError, d as defaultRetryCodes, e as defaultRetryMethods, f as fetchSpecificKeys, i as getResponseData, h as handleResponseType, l as isHTTPError, n as isHTTPErrorInstance, b as mergeUrlWithParams, c as objectifyHeaders, j as resolveSuccessResult, g as splitConfig, a as toQueryString, o as waitUntil } from './chunk-W6T7K2ZN.js';
2
2
  import './chunk-3E33QJL5.js';
3
3
  //# sourceMappingURL=out.js.map
4
4
  //# sourceMappingURL=utils.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zayne-labs/callapi",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
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
  "main": "./dist/cjs/index.cjs",
@@ -1,8 +0,0 @@
1
- import { g, d, e, c as c$1, b as b$1, o, i, m, j, k, n } from './chunk-SZIMUYPI.js';
2
- import { c, b, e as e$1 } from './chunk-3E33QJL5.js';
3
-
4
- var x=S=>{let c$2=new Map,[$,F]=g(S??{}),{headers:T,body:P,signal:j$1,...v}=$,u=async(o$1,R)=>{let[U,z]=g(R??{}),e$2={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:d,retryMethods:e,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...F,...z},{signal:k$1=j$1,body:s=P,headers:y,...G}=U,b$2=c$2.get(o$1);if(b$2&&e$2.cancelRedundantRequests){let t=new DOMException("Cancelled the previous unfinished request","AbortError");b$2.abort(t);}let d$1=new AbortController;c$2.set(o$1,d$1);let H=e$2.timeout?AbortSignal.timeout(e$2.timeout):null,E=AbortSignal.any([d$1.signal,H??d$1.signal,k$1??d$1.signal]),a={signal:E,method:"GET",body:c(s)?e$2.bodySerializer(s):s,headers:T||y||e$2.auth||c(s)?{...c(s)&&{"Content-Type":"application/json",Accept:"application/json"},...b(s)&&{"Content-Type":"multipart/form-data"},...e$1(s)&&{"Content-Type":"application/x-www-form-urlencoded"},...e$1(e$2.auth)&&{Authorization:`Bearer ${e$2.auth}`},...c(e$2.auth)&&{Authorization:"bearer"in e$2.auth?`Bearer ${e$2.auth.bearer}`:`Token ${e$2.auth.token}`},...c$1(T),...c$1(y)}:void 0,...v,...G};try{await e$2.onRequest?.({request:a,options:e$2});let t=await fetch(`${e$2.baseURL}${b$1(o$1,e$2.query)}`,a);if(!t.ok&&!E.aborted&&e$2.retries>0&&e$2.retryCodes.includes(t.status)&&e$2.retryMethods.includes(a.method))return await o(e$2.retryDelay),await u(o$1,{...R,retries:e$2.retries-1});if(!t.ok){let W=await i(t.clone(),e$2.responseType,e$2.responseParser);throw new m({response:{...t,errorData:W},defaultErrorMessage:e$2.defaultErrorMessage})}let r=await i(t.clone(),e$2.responseType,e$2.responseParser),i$1=e$2.responseValidator?e$2.responseValidator(r):r;return await e$2.onResponse?.({response:{...t,data:i$1},request:a,options:e$2}),j({successData:i$1,response:t,options:e$2})}catch(t){let n$1=k({error:t,options:e$2});if(t instanceof DOMException&&t.name==="TimeoutError"){let r=`Request timed out after ${e$2.timeout}ms`;return console.info(`%cTimeoutError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),n$1({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){let r="Request was cancelled";return console.info(`%AbortError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),n$1({message:r})}if(n(t)){let{errorData:r,...i}=t.response;return await e$2.onResponseError?.({response:{...i,errorData:r},request:a,options:e$2}),n$1({errorData:r,message:r?.message,response:i})}return await e$2.onRequestError?.({request:a,error:t,options:e$2}),n$1()}finally{c$2.delete(o$1);}};return u.create=x,u.cancel=o=>c$2.get(o)?.abort(),u},V=x();
5
-
6
- export { x as a, V as b };
7
- //# sourceMappingURL=out.js.map
8
- //# sourceMappingURL=chunk-L2G3D2QS.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/createFetchClient.ts"],"names":["createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","splitConfig","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","options","defaultRetryCodes","defaultRetryMethods","signal","body","headers","restOfFetchConfig","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","isObject","isFormData","isString","objectifyHeaders","response","mergeUrlWithParams","waitUntil","errorData","getResponseData","HTTPError","successData","validSuccessData","resolveSuccessResult","error","resolveErrorResult","$resolveErrorResult","message","isHTTPErrorInstance"],"mappings":"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","sourcesContent":["import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tAbortSignalWithAny,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = 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 {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = 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\t/** Default Options */\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 = baseBody, 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 the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || 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...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization:\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\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 $RequestOptions;\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!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\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 waitUntil(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.clone(),\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.clone(),\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\tresponse: { ...response, data: validSuccessData },\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, 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\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t\tresponse,\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\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"]}
@@ -1,7 +0,0 @@
1
- import { c, a, d } from './chunk-3E33QJL5.js';
2
-
3
- var y=r=>r?new URLSearchParams(r).toString():(console.error("No query params provided"),null),b=(r,e)=>{if(!e)return r;let t=y(e);return r.includes("?")&&!r.endsWith("?")?`${r}&${t}`:r.endsWith("?")?`${r}${t}`:`${r}?${t}`},g=r=>!r||c(r)?r:Object.fromEntries(a(r)?r:r.entries()),E={408:"Request Timeout",409:"Conflict",425:"Too Early",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},x=Object.keys(E).map(Number),j=["GET"],l=["body","integrity","method","headers","signal","cache","redirect","window","credentials","keepalive","referrer","priority","mode","referrerPolicy"],T=(r,e)=>{let t=Object.entries(r).filter(([s])=>!e.includes(s));return Object.fromEntries(t)},R=(r,e)=>{let t=new Set(e),s=Object.entries(r).filter(([a])=>t.has(a));return Object.fromEntries(s)},w=r=>[R(r,l),T(r,l)],O=(r,e)=>({json:async()=>e?e(await r.text()):r.json(),arrayBuffer:()=>r.arrayBuffer(),blob:()=>r.blob(),formData:()=>r.formData(),text:()=>r.text()}),P=(r,e,t)=>{let o=O(r,t);if(!Object.hasOwn(o,e))throw new Error(`Invalid response type: ${e}`);return o[e]()},k=r=>{let{options:e,response:t,successData:o}=r,s={data:o,errorInfo:null,response:t};return e.resultMode===void 0||e.resultMode==="all"?s:{onlySuccess:s.data,onlyError:s.errorInfo,onlyResponse:s.response}[e.resultMode]},h=r=>{let{error:e,options:t}=r;return (s={})=>{let{errorData:c,message:a,response:d$1}=s;if(d(t.throwOnError)?t.throwOnError(e):t.throwOnError)throw e;return {data:null,error:{errorName:e?.name??"UnknownError",errorData:c??e,message:a??e?.message??t.defaultErrorMessage},response:d$1??null}}},A=r=>c(r)&&r.errorName==="HTTPError",i=class extends Error{response;name="HTTPError";isHTTPError=!0;constructor(e,t){let{defaultErrorMessage:o,response:s}=e;super(s.errorData.message??o,t),this.response=s;}},C=r=>r instanceof i||c(r)&&r.name==="HTTPError"&&r.isHTTPError===!0,v=r=>{if(r===0)return;let{promise:e,resolve:t}=Promise.withResolvers();return setTimeout(t,r),e};
4
-
5
- export { y as a, b, g as c, x as d, j as e, l as f, w as g, O as h, P as i, k as j, h as k, A as l, i as m, C as n, v as o };
6
- //# sourceMappingURL=out.js.map
7
- //# sourceMappingURL=chunk-SZIMUYPI.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utils.ts"],"names":["toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","isObject","isArray","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isFunction","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve"],"mappings":"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","sourcesContent":["import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n\t(params: ExtraOptions[\"query\"]): string | null;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"No query params provided\");\n\t\treturn null as never;\n\t}\n\n\treturn new URLSearchParams(params as Record<string, string>).toString();\n};\n\nexport const mergeUrlWithParams = (url: string, params: ExtraOptions[\"query\"]): string => {\n\tif (!params) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(params);\n\n\tif (url.includes(\"?\") && !url.endsWith(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\nconst omitKeys = <TObject extends Record<string, unknown>, const TOmitArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToOmit: TOmitArray\n) => {\n\tconst arrayFromFilteredObject = Object.entries(initialObject).filter(\n\t\t([key]) => !keysToOmit.includes(key)\n\t);\n\n\tconst updatedObject = Object.fromEntries(arrayFromFilteredObject);\n\n\treturn updatedObject as Omit<TObject, keyof TOmitArray>;\n};\n\nconst pickKeys = <TObject extends Record<string, unknown>, const TPickArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToPick: TPickArray\n) => {\n\tconst keysToPickSet = new Set(keysToPick);\n\n\tconst arrayFromInitObject = Object.entries(initialObject);\n\n\tconst filteredArray = arrayFromInitObject.filter(([objectKey]) => keysToPickSet.has(objectKey));\n\n\tconst updatedObject = Object.fromEntries(filteredArray);\n\n\treturn updatedObject as Pick<TObject, TPickArray[number]>;\n};\n\nexport const splitConfig = <TObject extends object>(\n\tconfig: TObject\n): [\"body\" extends keyof TObject ? $RequestOptions : $BaseRequestOptions, ExtraOptions] => [\n\tpickKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n\tomitKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n];\n\nexport const handleResponseType = <TResponse>(\n\tresponse: Response,\n\tparser?: Required<ExtraOptions>[\"responseParser\"]\n) => ({\n\tjson: async () => {\n\t\tif (parser) {\n\t\t\treturn parser(await response.text());\n\t\t}\n\n\t\treturn response.json() as Promise<TResponse>;\n\t},\n\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\n\tblob: () => response.blob() as Promise<TResponse>,\n\tformData: () => response.formData() as Promise<TResponse>,\n\ttext: () => response.text() as Promise<TResponse>,\n});\n\nexport const getResponseData = <TResponse>(\n\tresponse: Response,\n\tresponseType: keyof ReturnType<typeof handleResponseType>,\n\tparser: ExtraOptions[\"responseParser\"]\n) => {\n\tconst RESPONSE_TYPE_LOOKUP = handleResponseType<TResponse>(response, parser);\n\n\tif (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {\n\t\tthrow new Error(`Invalid response type: ${responseType}`);\n\t}\n\n\treturn RESPONSE_TYPE_LOOKUP[responseType]();\n};\n\ntype data = {\n\tsuccessData: unknown;\n\toptions: ExtraOptions;\n\tresponse: Response;\n};\n\n// == The CallApiResult type is used to cast all return statements due to a design limitation in ts.\n// LINK - See https://www.zhenghao.io/posts/type-functions for more info\nexport const resolveSuccessResult = <CallApiResult>(info: data): CallApiResult => {\n\tconst { options, response, successData } = info;\n\n\tconst apiDetails = {\n\t\tdata: successData,\n\t\terrorInfo: null,\n\t\tresponse,\n\t};\n\n\tif (options.resultMode === undefined || options.resultMode === \"all\") {\n\t\treturn apiDetails as CallApiResult;\n\t}\n\n\treturn {\n\t\tonlySuccess: apiDetails.data,\n\t\tonlyError: apiDetails.errorInfo,\n\t\tonlyResponse: apiDetails.response,\n\t}[options.resultMode] as CallApiResult;\n};\n\n// == Using curring here so error and options are not required to be passed every time, instead to be captured once by way of closure\nexport const $resolveErrorResult = <CallApiResult>($info: { error?: unknown; options: ExtraOptions }) => {\n\tconst { error, options } = $info;\n\n\ttype ErrorInfo = {\n\t\tresponse?: Response;\n\t\terrorData?: unknown;\n\t\tmessage?: string;\n\t};\n\n\tconst resolveErrorResult = (info: ErrorInfo = {}): CallApiResult => {\n\t\tconst { errorData, message, response } = info;\n\n\t\tconst shouldThrowOnError = isFunction(options.throwOnError)\n\t\t\t? options.throwOnError(error as Error)\n\t\t\t: options.throwOnError;\n\n\t\tif (shouldThrowOnError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn {\n\t\t\tdata: null,\n\t\t\terror: {\n\t\t\t\terrorName: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? error,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.errorName === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\tresponse: Response & { errorData: TErrorResponse };\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response } = errorDetails;\n\n\t\tsuper((response.errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n"]}