@zayne-labs/callapi 0.8.0 → 1.0.0-rc-2

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
@@ -1,47 +1,47 @@
1
- # CallApi
2
-
3
- [![Build Size](https://img.shields.io/bundlephobia/minzip/@zayne-labs/callapi?label=bundle%20size&style=flat&colorA=000000&colorB=000000)](https://bundlephobia.com/result?p=@zayne-labs/callapi)[![Version](https://img.shields.io/npm/v/@zayne-labs/callapi?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/@zayne-labs/callapi)
4
-
5
- CallApi Fetch is an extra-lightweight wrapper over fetch that provides quality of life improvements beyond the bare fetch api, while keeping the API familiar.
6
-
7
- It takes in a url and a request options object, just like fetch, but with some additional options to make your life easier. Check out the [API Reference](https://zayne-callapi.netlify.app/api-reference) for a quick look at each option.
8
-
9
- # Docs
10
-
11
- [View Documentation website](https://zayne-callapi.netlify.app)
12
-
13
- ## Installing `CallApi`
14
-
15
- ### Through npm (recommended)
16
-
17
- ```bash
18
- # npm
19
- npm install @zayne-labs/callapi
20
-
21
- # pnpm
22
- pnpm add @zayne-labs/callapi
23
- ```
24
-
25
- Then you can use it by importing it in your JavaScript file.
26
-
27
- ```js
28
- import { callApi } from "@zayne-labs/callapi";
29
- ```
30
-
31
- ### Using `CallApi` without `npm`
32
-
33
- You can import callApi directly into JavaScript through a CDN.
34
-
35
- To do this, you first need to set your `script`'s type to `module`, then import `callApi`.
36
-
37
- ```html
38
- <script type="module">
39
- import { callApi } from "https://esm.run/@zayne-labs/callapi";
40
- </script>
41
-
42
- <!-- Locked to a specific version -->
43
- <script type="module">
44
- import { callApi } from "https://esm.run/@zayne-labs/callapi@0.3.2";
45
- </script>
46
- ```
47
-
1
+ # CallApi
2
+
3
+ [![Build Size](https://img.shields.io/bundlephobia/minzip/@zayne-labs/callapi?label=bundle%20size&style=flat&colorA=000000&colorB=000000)](https://bundlephobia.com/result?p=@zayne-labs/callapi)[![Version](https://img.shields.io/npm/v/@zayne-labs/callapi?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/@zayne-labs/callapi)
4
+
5
+ CallApi Fetch is an extra-lightweight wrapper over fetch that provides quality of life improvements beyond the bare fetch api, while keeping the API familiar.
6
+
7
+ It takes in a url and a request options object, just like fetch, but with some additional options to make your life easier. Check out the [API Reference](https://zayne-callapi.netlify.app/api-reference) for a quick look at each option.
8
+
9
+ # Docs
10
+
11
+ [View Documentation website](https://zayne-callapi.netlify.app)
12
+
13
+ ## Installing `CallApi`
14
+
15
+ ### Through npm (recommended)
16
+
17
+ ```bash
18
+ # npm
19
+ npm install @zayne-labs/callapi
20
+
21
+ # pnpm
22
+ pnpm add @zayne-labs/callapi
23
+ ```
24
+
25
+ Then you can use it by importing it in your JavaScript file.
26
+
27
+ ```js
28
+ import { callApi } from "@zayne-labs/callapi";
29
+ ```
30
+
31
+ ### Using `CallApi` without `npm`
32
+
33
+ You can import callApi directly into JavaScript through a CDN.
34
+
35
+ To do this, you first need to set your `script`'s type to `module`, then import `callApi`.
36
+
37
+ ```html
38
+ <script type="module">
39
+ import { callApi } from "https://esm.run/@zayne-labs/callapi";
40
+ </script>
41
+
42
+ <!-- Locked to a specific version -->
43
+ <script type="module">
44
+ import { callApi } from "https://esm.run/@zayne-labs/callapi@0.3.2";
45
+ </script>
46
+ ```
47
+
@@ -7,8 +7,8 @@ type AnyNumber = number & {
7
7
  type Prettify<TObject> = {
8
8
  [Key in keyof TObject]: TObject[Key];
9
9
  } & NonNullable<unknown>;
10
- type ResponseHeader = "Access-Control-Allow-Credentials" | "Access-Control-Allow-Headers" | "Access-Control-Allow-Methods" | "Access-Control-Allow-Origin" | "Access-Control-Expose-Headers" | "Access-Control-Max-Age" | "Age" | "Allow" | "Cache-Control" | "Clear-Site-Data" | "Content-Disposition" | "Content-Encoding" | "Content-Language" | "Content-Length" | "Content-Location" | "Content-Range" | "Content-Security-Policy-Report-Only" | "Content-Security-Policy" | "Cookie" | "Cross-Origin-Embedder-Policy" | "Cross-Origin-Opener-Policy" | "Cross-Origin-Resource-Policy" | "Date" | "ETag" | "Expires" | "Last-Modified" | "Location" | "Permissions-Policy" | "Pragma" | "Retry-After" | "Save-Data" | "Sec-CH-Prefers-Color-Scheme" | "Sec-CH-Prefers-Reduced-Motion" | "Sec-CH-UA-Arch" | "Sec-CH-UA-Bitness" | "Sec-CH-UA-Form-Factor" | "Sec-CH-UA-Full-Version-List" | "Sec-CH-UA-Full-Version" | "Sec-CH-UA-Mobile" | "Sec-CH-UA-Model" | "Sec-CH-UA-Platform-Version" | "Sec-CH-UA-Platform" | "Sec-CH-UA-WoW64" | "Sec-CH-UA" | "Sec-Fetch-Dest" | "Sec-Fetch-Mode" | "Sec-Fetch-Site" | "Sec-Fetch-User" | "Sec-GPC" | "Server-Timing" | "Server" | "Service-Worker-Navigation-Preload" | "Set-Cookie" | "Strict-Transport-Security" | "Timing-Allow-Origin" | "Trailer" | "Transfer-Encoding" | "Upgrade" | "Vary" | "Warning" | "WWW-Authenticate" | "X-Content-Type-Options" | "X-DNS-Prefetch-Control" | "X-Frame-Options" | "X-Permitted-Cross-Domain-Policies" | "X-Powered-By" | "X-Robots-Tag" | "X-XSS-Protection";
11
- type BaseMime = "application/epub+zip" | "application/gzip" | "application/json" | "application/ld+json" | "application/octet-stream" | "application/ogg" | "application/pdf" | "application/rtf" | "application/vnd.ms-fontobject" | "application/wasm" | "application/xhtml+xml" | "application/xml" | "application/zip" | "audio/aac" | "audio/mpeg" | "audio/ogg" | "audio/opus" | "audio/webm" | "audio/x-midi" | "font/otf" | "font/ttf" | "font/woff" | "font/woff2" | "image/avif" | "image/bmp" | "image/gif" | "image/jpeg" | "image/png" | "image/svg+xml" | "image/tiff" | "image/webp" | "image/x-icon" | "model/gltf-binary" | "model/gltf+json" | "text/calendar" | "text/css" | "text/csv" | "text/html" | "text/javascript" | "text/plain" | "video/3gpp" | "video/3gpp2" | "video/av1" | "video/mp2t" | "video/mp4" | "video/mpeg" | "video/ogg" | "video/webm" | "video/x-msvideo";
10
+ type CommonRequestHeaders = "Access-Control-Allow-Credentials" | "Access-Control-Allow-Headers" | "Access-Control-Allow-Methods" | "Access-Control-Allow-Origin" | "Access-Control-Expose-Headers" | "Access-Control-Max-Age" | "Age" | "Allow" | "Cache-Control" | "Clear-Site-Data" | "Content-Disposition" | "Content-Encoding" | "Content-Language" | "Content-Length" | "Content-Location" | "Content-Range" | "Content-Security-Policy-Report-Only" | "Content-Security-Policy" | "Cookie" | "Cross-Origin-Embedder-Policy" | "Cross-Origin-Opener-Policy" | "Cross-Origin-Resource-Policy" | "Date" | "ETag" | "Expires" | "Last-Modified" | "Location" | "Permissions-Policy" | "Pragma" | "Retry-After" | "Save-Data" | "Sec-CH-Prefers-Color-Scheme" | "Sec-CH-Prefers-Reduced-Motion" | "Sec-CH-UA-Arch" | "Sec-CH-UA-Bitness" | "Sec-CH-UA-Form-Factor" | "Sec-CH-UA-Full-Version-List" | "Sec-CH-UA-Full-Version" | "Sec-CH-UA-Mobile" | "Sec-CH-UA-Model" | "Sec-CH-UA-Platform-Version" | "Sec-CH-UA-Platform" | "Sec-CH-UA-WoW64" | "Sec-CH-UA" | "Sec-Fetch-Dest" | "Sec-Fetch-Mode" | "Sec-Fetch-Site" | "Sec-Fetch-User" | "Sec-GPC" | "Server-Timing" | "Server" | "Service-Worker-Navigation-Preload" | "Set-Cookie" | "Strict-Transport-Security" | "Timing-Allow-Origin" | "Trailer" | "Transfer-Encoding" | "Upgrade" | "Vary" | "Warning" | "WWW-Authenticate" | "X-Content-Type-Options" | "X-DNS-Prefetch-Control" | "X-Frame-Options" | "X-Permitted-Cross-Domain-Policies" | "X-Powered-By" | "X-Robots-Tag" | "X-XSS-Protection";
11
+ type CommonContentTypes = "application/epub+zip" | "application/gzip" | "application/json" | "application/ld+json" | "application/octet-stream" | "application/ogg" | "application/pdf" | "application/rtf" | "application/vnd.ms-fontobject" | "application/wasm" | "application/xhtml+xml" | "application/xml" | "application/zip" | "audio/aac" | "audio/mpeg" | "audio/ogg" | "audio/opus" | "audio/webm" | "audio/x-midi" | "font/otf" | "font/ttf" | "font/woff" | "font/woff2" | "image/avif" | "image/bmp" | "image/gif" | "image/jpeg" | "image/png" | "image/svg+xml" | "image/tiff" | "image/webp" | "image/x-icon" | "model/gltf-binary" | "model/gltf+json" | "text/calendar" | "text/css" | "text/csv" | "text/html" | "text/javascript" | "text/plain" | "video/3gpp" | "video/3gpp2" | "video/av1" | "video/mp2t" | "video/mp4" | "video/mpeg" | "video/ogg" | "video/webm" | "video/x-msvideo";
12
12
 
13
13
  type ToQueryStringFn = {
14
14
  (params: ExtraOptions["query"]): string | null;
@@ -45,10 +45,10 @@ declare class HTTPError<TErrorResponse = Record<string, unknown>> extends Error
45
45
  }
46
46
  declare const isHTTPErrorInstance: <TErrorResponse>(error: unknown) => error is HTTPError<TErrorResponse>;
47
47
 
48
- interface FetchConfig<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = "all"> extends Omit<RequestInit, "body" | "headers" | "method">, ExtraOptions<TData, TErrorData, TResultMode> {
48
+ interface CallApiConfig<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = "all"> extends Omit<RequestInit, "body" | "headers" | "method">, ExtraOptions<TData, TErrorData, TResultMode> {
49
49
  }
50
- type BaseConfig<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = "all"> = FetchConfig<TBaseData, TBaseErrorData, TBaseResultMode>;
51
- interface $RequestOptions extends Pick<FetchConfig, (typeof fetchSpecificKeys)[number]> {
50
+ type BaseCallApiConfig<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = "all"> = CallApiConfig<TBaseData, TBaseErrorData, TBaseResultMode>;
51
+ interface RequestOptions extends Pick<CallApiConfig, (typeof fetchSpecificKeys)[number]> {
52
52
  }
53
53
  interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = ResultModeUnion> {
54
54
  /**
@@ -74,6 +74,7 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
74
74
  /**
75
75
  * @description If true, cancels previous unfinished requests to the same URL.
76
76
  * @default true
77
+ * @deprecated use dedupeStrategy option instead
77
78
  */
78
79
  cancelRedundantRequests?: boolean;
79
80
  /**
@@ -81,6 +82,16 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
81
82
  * @default false
82
83
  */
83
84
  cloneResponse?: boolean;
85
+ /**
86
+ * @description Defines the deduplication strategy for the request, can be set to "none" | "defer" | "cancel".
87
+ * - If set to "none", deduplication is disabled.
88
+ *
89
+ * - If set to "cancel"(default), the previous pending request to the same URL will be cancelled and lets the new request through.
90
+ *
91
+ * - If set to "defer", no new requests to the same URL will be allowed through, until the previous one is completed.
92
+ * @default "cancel"
93
+ */
94
+ dedupeStrategy?: "cancel" | "defer" | "none";
84
95
  /**
85
96
  * @description Default error message to use if none is provided from a response.
86
97
  * @default "Failed to fetch data from server!"
@@ -89,7 +100,7 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
89
100
  /**
90
101
  * @description Headers to be used in the request.
91
102
  */
92
- headers?: Record<"Content-Type", BaseMime> | Record<ResponseHeader, string> | RequestInit["headers"];
103
+ headers?: Record<"Content-Type", CommonContentTypes> | Record<CommonRequestHeaders, string> | RequestInit["headers"];
93
104
  /**
94
105
  * @description an optional field you can fill with additional information,
95
106
  * to associate with the request, typically used for logging or tracing.
@@ -126,22 +137,31 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
126
137
  /** @description Interceptor to be called just before the request is made, allowing for modifications or additional operations. */
127
138
  onRequest?: (requestContext: {
128
139
  options: ExtraOptions;
129
- request: $RequestOptions;
140
+ request: RequestOptions;
130
141
  }) => Promise<void> | void;
131
142
  /** @description Interceptor to be called when an error occurs during the fetch request. */
132
143
  onRequestError?: (requestErrorContext: {
133
144
  error: Error;
134
145
  options: ExtraOptions;
135
- request: $RequestOptions;
146
+ request: RequestOptions;
136
147
  }) => Promise<void> | void;
137
148
  /** @description Interceptor to be called when a successful response is received from the api. */
138
149
  onResponse?: (responseContext: ResponseContext<TData>) => Promise<void> | void;
139
150
  /** @description Interceptor to be called when an error response is received from the api. */
140
151
  onResponseError?: (responseErrorContext: ResponseErrorContext<TErrorData>) => Promise<void> | void;
152
+ /**
153
+ * @description Params to be appended to the URL (i.e: /:id)
154
+ */
155
+ params?: Record<string, boolean | number | string> | Array<boolean | number | string>;
141
156
  /**
142
157
  * @description Query parameters to append to the URL.
143
158
  */
144
159
  query?: Record<string, boolean | number | string>;
160
+ /**
161
+ * @description Custom request key to be used to identify a request in the fetch deduplication strategy.
162
+ * @default request url + string formed from the request options
163
+ */
164
+ requestKey?: string;
145
165
  /**
146
166
  * @description Custom function to parse the response string into a object.
147
167
  */
@@ -186,7 +206,7 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
186
206
  * The function is passed the error object and can be used to conditionally throw the error
187
207
  * @default false
188
208
  */
189
- throwOnError?: boolean | ((error?: Error | HTTPError<TErrorData>) => boolean);
209
+ throwOnError?: boolean | ((error: ErrorContext<TErrorData>["error"], optionsAndRequest: ExtraOptions & RequestOptions) => boolean);
190
210
  /**
191
211
  * @description Request timeout in milliseconds
192
212
  */
@@ -195,13 +215,13 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
195
215
  type ResponseContext<TData> = Prettify<{
196
216
  data: TData;
197
217
  options: ExtraOptions;
198
- request: $RequestOptions;
218
+ request: RequestOptions;
199
219
  response: Response;
200
220
  }>;
201
221
  type ResponseErrorContext<TErrorData> = Prettify<{
202
222
  errorData: TErrorData;
203
223
  options: ExtraOptions;
204
- request: $RequestOptions;
224
+ request: RequestOptions;
205
225
  response: Response;
206
226
  }>;
207
227
  type ErrorContext<TErrorData> = {
@@ -209,14 +229,14 @@ type ErrorContext<TErrorData> = {
209
229
  name: PossibleErrorNames;
210
230
  }>;
211
231
  options: ExtraOptions;
212
- request: $RequestOptions;
232
+ request: RequestOptions;
213
233
  response: null;
214
234
  } | {
215
235
  error: Extract<ErrorObjectUnion<TErrorData>, {
216
236
  name: "HTTPError";
217
237
  }>;
218
238
  options: ExtraOptions;
219
- request: $RequestOptions;
239
+ request: RequestOptions;
220
240
  response: Response;
221
241
  };
222
242
  type ApiSuccessVariant<TData> = {
@@ -225,7 +245,7 @@ type ApiSuccessVariant<TData> = {
225
245
  response: Response;
226
246
  };
227
247
  type PossibleErrorNames = {
228
- _: "AbortError" | "Error" | "SyntaxError" | "TimeoutError" | "TypeError" | "UnknownError";
248
+ _: "AbortError" | "Error" | "SyntaxError" | "TimeoutError" | "TypeError";
229
249
  }["_"];
230
250
  type ErrorObjectUnion<TErrorData = unknown> = {
231
251
  errorData: Error;
@@ -262,4 +282,4 @@ type ResultModeUnion = {
262
282
  }["_"];
263
283
  type GetCallApiResult<TData, TErrorData, TResultMode> = TResultMode extends NonNullable<ResultModeUnion> ? ResultModeMap<TData, TErrorData>[TResultMode] : ResultModeMap<TData, TErrorData>["all"];
264
284
 
265
- export { type $RequestOptions as $, type BaseConfig as B, type ExtraOptions as E, type FetchConfig as F, type GetCallApiResult as G, HTTPError as H, type ResultModeUnion as R, type ResponseContext as a, type ResponseErrorContext as b, type ErrorContext as c, isHTTPErrorInstance as d, isHTTPError as i, toQueryString as t };
285
+ export { type BaseCallApiConfig as B, type CallApiConfig as C, type ExtraOptions as E, type GetCallApiResult as G, HTTPError as H, type ResultModeUnion as R, type RequestOptions as a, type ResponseContext as b, type ResponseErrorContext as c, type ErrorContext as d, isHTTPErrorInstance as e, isHTTPError as i, toQueryString as t };
@@ -1 +1 @@
1
- "use strict";var e,r=Object.defineProperty,t=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.prototype.hasOwnProperty,n={};((e,t)=>{for(var o in t)r(e,o,{get:t[o],enumerable:!0})})(n,{callApi:()=>E,createFetchClient:()=>w}),module.exports=(e=n,((e,n,a,i)=>{if(n&&"object"==typeof n||"function"==typeof n)for(let l of o(n))s.call(e,l)||l===a||r(e,l,{get:()=>n[l],enumerable:!(i=t(n,l))||i.enumerable});return e})(r({},"__esModule",{value:!0}),e));var a=e=>!("object"!=typeof e||null===e||e instanceof FormData||Array.isArray(e)),i=e=>"string"==typeof e,l=(e,r)=>{if(!r)return e;const t=(o=r)?new URLSearchParams(o).toString():(console.error("toQueryString:","No query params provided!"),null);var o;return""===t?e:e.endsWith("?")?`${e}${t}`:e.includes("?")?`${e}&${t}`:`${e}?${t}`},c=e=>{return!e||a(e)?e:Object.fromEntries((r=e,Array.isArray(r)?e:e.entries()));var r},u=Object.keys({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"}).map(Number),p=["GET"],d=["body","integrity","method","headers","signal","cache","redirect","window","credentials","keepalive","referrer","priority","mode","referrerPolicy"],y=(e,r)=>{const t=Object.entries(e).filter((([e])=>!r.includes(e)));return Object.fromEntries(t)},f=(e,r)=>{const t=new Set(r),o=Object.entries(e).filter((([e])=>t.has(e)));return Object.fromEntries(o)},m=e=>[f(e,d),y(e,d)],h=(e,r,t)=>{const o=((e,r)=>({arrayBuffer:()=>e.arrayBuffer(),blob:()=>e.blob(),formData:()=>e.formData(),json:async()=>r?r(await e.text()):e.json(),text:()=>e.text()}))(e,t);if(!Object.hasOwn(o,r))throw new Error(`Invalid response type: ${r}`);return o[r]()},b=class extends Error{errorData;isHTTPError=!0;name="HTTPError";response;constructor(e,r){const{defaultErrorMessage:t,errorData:o,response:s}=e;super(o.message??t,r),this.errorData=o,this.response=s}},w=e=>{const r=new Map,[t,o]=m(e??{}),{body:s,headers:n,signal:d,...f}=t,E=async(e,t)=>{const[w,g]=m(t??{}),{body:R=s,headers:O,signal:T=d,...q}=w,D={baseURL:"",bodySerializer:JSON.stringify,cancelRedundantRequests:!0,defaultErrorMessage:"Failed to fetch data from server!",responseType:"json",retries:0,retryCodes:u,retryDelay:0,retryMethods:p,...o,...g},j={body:a(R)?D.bodySerializer(R):R,headers:n||O||D.auth||a(R)?{...a(R)&&{Accept:"application/json","Content-Type":"application/json"},...($=R,"string"==typeof $&&$.includes("=")&&{"Content-Type":"application/x-www-form-urlencoded"}),...(i(D.auth)||null==D.auth)&&{Authorization:`Bearer ${D.auth}`},...a(D.auth)&&{Authorization:"bearer"in D.auth?`Bearer ${D.auth.bearer}`:`Token ${D.auth.token}`},...c(n),...c(O)}:void 0,method:"GET",...f,...q};var $;const v=((e,r)=>`${e} | ${JSON.stringify(r??{})}`)(e,y({...j,...D},["onRequest","onResponse","onResponseError","onError","onRequestError"])),M=r.get(v);if(M&&D.cancelRedundantRequests){const r=new DOMException(`Request aborted as another request to this same endpoint: ${e}, with the same request options was initiated.`,"AbortError");M.abort(r)}const P=new AbortController;r.set(v,P);const A=D.timeout?AbortSignal.timeout(D.timeout):null,S=AbortSignal.any([P.signal,...A?[A]:[],...T?[T]:[]]),x={signal:S,...j};try{await(D.onRequest?.({options:D,request:x}));const r=await fetch(`${D.baseURL}${l(e,D.query)}`,x);if(!r.ok&&!S.aborted&&D.retries>0&&D.retryCodes.includes(r.status)&&D.retryMethods.includes(x.method))return await(e=>{if(0===e)return;const{promise:r,resolve:t}=Promise.withResolvers();return setTimeout(t,e),r})(D.retryDelay),await E(e,{...t,retries:D.retries-1});if(!r.ok){const e=await h(D.cloneResponse?r.clone():r,D.responseType,D.responseParser);throw new b({defaultErrorMessage:D.defaultErrorMessage,errorData:e,response:r})}const o=await h(D.cloneResponse?r.clone():r,D.responseType,D.responseParser),s=D.responseValidator?D.responseValidator(o):o;return await(D.onResponse?.({data:s,options:D,request:x,response:D.cloneResponse?r.clone():r})),(e=>{const{options:r,response:t,successData:o}=e,s={data:o,error:null,response:t};return r.resultMode&&"all"!==r.resultMode?{onlyError:s.error,onlyResponse:s.response,onlySuccess:s.data}[r.resultMode]:s})({options:D,response:r,successData:s})}catch(e){const r=(e=>{const{error:r,options:t}=e;return e=>{const{errorData:o,message:s,response:n}=e??{};if("function"==typeof t.throwOnError?t.throwOnError(r):t.throwOnError)throw r;return{data:null,error:{errorData:o??r,message:s??r?.message??t.defaultErrorMessage,name:r?.name??"UnknownError"},response:n??null}}})({error:e,options:D});if(e instanceof DOMException&&"TimeoutError"===e.name){const t=`Request timed out after ${D.timeout}ms`;return console.error(`${e.name}:`,t),r({message:t})}if(e instanceof DOMException&&"AbortError"===e.name){const{message:t,name:o}=e;return console.error(`${o}:`,t),r({message:t})}if((e=>e instanceof b||a(e)&&"HTTPError"===e.name&&!0===e.isHTTPError)(e)){const{errorData:t,response:o}=e;return await Promise.all([D.onResponseError?.({errorData:t,options:D,request:x,response:D.cloneResponse?o.clone():o}),D.onError?.({error:e,options:D,request:x,response:o})]),r(e)}const t=r();return await Promise.all([D.onRequestError?.({error:e,options:D,request:x}),D.onError?.({error:t.error,options:D,request:x,response:null})]),t}finally{r.delete(v)}};return E.create=w,E},E=w();//# sourceMappingURL=index.cjs.map
1
+ "use strict";var e,r=Object.defineProperty,t=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.prototype.hasOwnProperty,n={};((e,t)=>{for(var o in t)r(e,o,{get:t[o],enumerable:!0})})(n,{callApi:()=>D,createFetchClient:()=>$}),module.exports=(e=n,((e,n,a,i)=>{if(n&&"object"==typeof n||"function"==typeof n)for(let l of o(n))s.call(e,l)||l===a||r(e,l,{get:()=>n[l],enumerable:!(i=t(n,l))||i.enumerable});return e})(r({},"__esModule",{value:!0}),e));var a=e=>Array.isArray(e),i=e=>!("object"!=typeof e||null===e||e instanceof FormData||a(e)),l=e=>"string"==typeof e,c="&",u=(e,r)=>{if(!r)return e;const t=(o=r)?new URLSearchParams(o).toString():(console.error("toQueryString:","No query params provided!"),null);var o;return 0===t?.length?e:e.endsWith("?")?`${e}${t}`:e.includes("?")?`${e}${c}${t}`:`${e}?${t}`},p=(e,r,t)=>{const o=((e,r)=>{if(!r)return e;let t=e;if(a(r)){const e=t.split("/").filter((e=>e.startsWith(":")));for(const[o,s]of e.entries()){const e=r[o];t=t.replace(s,e)}return t}for(const[e,o]of Object.entries(r))t=t.replace(`:${e}`,String(o));return t})(e,r);return u(o,t)},d=e=>!e||i(e)?e:Object.fromEntries(a(e)?e:e.entries()),f=Object.keys({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"}).map(Number),y=["GET"],m=["body","integrity","method","headers","signal","cache","redirect","window","credentials","keepalive","referrer","priority","mode","referrerPolicy"],h=(e,r)=>{const t=Object.entries(e).filter((([e])=>!r.includes(e)));return Object.fromEntries(t)},b=(e,r)=>{const t=new Set(r),o=Object.entries(e).filter((([e])=>t.has(e)));return Object.fromEntries(o)},g=e=>[b(e,m),h(e,m)],w=(e,r,t)=>{const o=((e,r)=>({arrayBuffer:()=>e.arrayBuffer(),blob:()=>e.blob(),formData:()=>e.formData(),json:async()=>r?r(await e.text()):e.json(),text:()=>e.text()}))(e,t);if(!Object.hasOwn(o,r))throw new Error(`Invalid response type: ${r}`);return o[r]()},E=class extends Error{errorData;isHTTPError=!0;name="HTTPError";response;constructor(e,r){const{defaultErrorMessage:t,errorData:o,response:s}=e;super(o.message??t,r),this.errorData=o,this.response=s}},O=e=>e instanceof E||i(e)&&"HTTPError"===e.name&&!0===e.isHTTPError,T=e=>{if(0===e)return;const{promise:r,resolve:t}=Promise.withResolvers();return setTimeout(t,e),r},$=(e={})=>{const r=new Map,[t,o]=g(e),{body:s,headers:n,signal:a,...u}=t,m=async(e,t={})=>{const[h,b]=g(t),{body:$=s,headers:D,signal:S=a,...j}=h,M={baseURL:"",bodySerializer:JSON.stringify,dedupeStrategy:"cancel",defaultErrorMessage:"Failed to fetch data from server!",responseType:"json",retries:0,retryCodes:f,retryDelay:0,retryMethods:y,...o,...b},q={method:"GET",body:i($)?M.bodySerializer($):$,headers:n||D||M.auth||i($)?{...i($)&&{Accept:"application/json","Content-Type":"application/json"},...(P=$,l(P)&&P.includes("=")&&{"Content-Type":"application/x-www-form-urlencoded"}),...(l(M.auth)||null==M.auth)&&{Authorization:`Bearer ${M.auth}`},...i(M.auth)&&{Authorization:"bearer"in M.auth?`Bearer ${M.auth.bearer}`:`Token ${M.auth.token}`},...d(n),...d(D)}:void 0,...u,...j};var P;const v="cancel"===M.dedupeStrategy||"defer"===M.dedupeStrategy,R=M.requestKey??(v&&((e,r)=>`${e} ${c} ${JSON.stringify(r)}`)(e,{...q,...M}));R&&await T(.1);const A=R?r:null,x=A?.get(R);if(x&&"cancel"===M.dedupeStrategy){const r=new DOMException(`Request aborted as another request to this same endpoint: ${e}, with the same request options was initiated.`,"AbortError");x.controller.abort(r)}const C=new AbortController,k=M.timeout?AbortSignal.timeout(M.timeout):null,B=AbortSignal.any([C.signal,...k?[k]:[],...S?[S]:[]]),N={signal:B,...q};try{await(M.onRequest?.({options:M,request:N}));const r=x&&"defer"===M.dedupeStrategy?x.responsePromise:fetch(`${M.baseURL}${p(e,M.params,M.query)}`,N);A?.set(R,{controller:C,responsePromise:r});const o=await r;if(!o.ok&&!B.aborted&&M.retries>0&&M.retryCodes.includes(o.status)&&M.retryMethods.includes(N.method))return await T(M.retryDelay),await m(e,{...t,retries:M.retries-1});const s=M.cloneResponse||"defer"===M.dedupeStrategy;if(!o.ok){const e=await w(s?o.clone():o,M.responseType,M.responseParser);throw new E({defaultErrorMessage:M.defaultErrorMessage,errorData:e,response:o})}const n=await w(s?o.clone():o,M.responseType,M.responseParser),a=M.responseValidator?M.responseValidator(n):n;return await(M.onResponse?.({data:a,options:M,request:N,response:M.cloneResponse?o.clone():o})),(e=>{const{options:r,response:t,successData:o}=e,s={data:o,error:null,response:t};return r.resultMode&&"all"!==r.resultMode?{onlyError:s.error,onlyResponse:s.response,onlySuccess:s.data}[r.resultMode]:s})({options:M,response:o,successData:a})}catch(e){const r=(e=>{const{defaultErrorMessage:r,error:t}=e;if(O(t)){const{errorData:e,message:o=r,name:s,response:n}=t;return{data:null,error:{errorData:e,message:o,name:s},response:n}}return{data:null,error:{errorData:t,message:t.message,name:t.name},response:null}})({defaultErrorMessage:M.defaultErrorMessage,error:e}),t=(e=>"function"==typeof e)(M.throwOnError)?M.throwOnError(r.error,M):M.throwOnError;if(t)throw e;if(e instanceof DOMException&&"TimeoutError"===e.name){const t=`Request timed out after ${M.timeout}ms`;return console.error(`${e.name}:`,t),{...r,message:t}}if(e instanceof DOMException&&"AbortError"===e.name){const{message:t,name:o}=e;return console.error(`${o}:`,t),r}if(O(e)){const{errorData:t,response:o}=e;return await Promise.all([M.onResponseError?.({errorData:t,options:M,request:N,response:M.cloneResponse?o.clone():o}),M.onError?.({error:e,options:M,request:N,response:o})]),r}return await Promise.all([M.onRequestError?.({error:e,options:M,request:N}),M.onError?.({error:r.error,options:M,request:N,response:null})]),r}finally{A?.delete(R)}};return m.create=$,m},D=$();//# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts","../../src/utils/typeof.ts","../../src/utils/utils.ts","../../src/createFetchClient.ts"],"sourcesContent":["export { callApi, createFetchClient } from \"./createFetchClient\";\n\nexport type {\n\t$RequestOptions,\n\tExtraOptions,\n\tFetchConfig,\n\tResponseContext,\n\tResponseErrorContext,\n\tErrorContext,\n} from \"./types\";\n","import type { AnyFunction } from \"./type-helpers\";\n\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\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 isQueryString = (value: unknown): value is string =>\n\ttypeof value === \"string\" && value.includes(\"=\");\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n","import type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"../types\";\nimport { isArray, isFunction, isObject } from \"./typeof\";\n\n// prettier-ignore\nexport const getRequestKey = <TConfig extends Record<string, unknown>>(url: string, config?: TConfig) => `${url} | ${JSON.stringify(config ?? {})}`;\n\ntype ToQueryStringFn = {\n\t(params: ExtraOptions[\"query\"]): string | null;\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"toQueryString:\", \"No query params provided!\");\n\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, query: ExtraOptions[\"query\"]): string => {\n\tif (!query) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(query);\n\n\tif (paramsString === \"\") {\n\t\treturn url;\n\t}\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\tif (url.includes(\"?\")) {\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\nexport const omitKeys = <\n\tTObject extends Record<string, unknown>,\n\tconst TOmitArray extends Array<keyof TObject>,\n>(\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\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\n\tblob: () => response.blob() as Promise<TResponse>,\n\tformData: () => response.formData() as Promise<TResponse>,\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\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 SuccessInfo = {\n\toptions: ExtraOptions;\n\tresponse: Response;\n\tsuccessData: unknown;\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: SuccessInfo): CallApiResult => {\n\tconst { options, response, successData } = info;\n\n\tconst apiDetails = {\n\t\tdata: successData,\n\t\terror: null,\n\t\tresponse,\n\t};\n\n\tif (!options.resultMode || options.resultMode === \"all\") {\n\t\treturn apiDetails as CallApiResult;\n\t}\n\n\treturn {\n\t\tonlyError: apiDetails.error,\n\t\tonlyResponse: apiDetails.response,\n\t\tonlySuccess: apiDetails.data,\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 getResolveErrorResultFn = <CallApiResult>(initInfo: {\n\terror?: unknown;\n\toptions: ExtraOptions;\n}) => {\n\tconst { error, options } = initInfo;\n\n\tconst resolveErrorResult = <TErrorData>(errorInfo?: Partial<HTTPError<TErrorData>>): CallApiResult => {\n\t\tconst { errorData, message, response } = errorInfo ?? {};\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\terrorData: errorData ?? error,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t\tname: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\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.name === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\tdefaultErrorMessage: string;\n\terrorData: TErrorResponse;\n\tresponse: Response;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\n\tisHTTPError = true;\n\n\toverride name = \"HTTPError\" as const;\n\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, errorData, response } = 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 type {\n\t$RequestOptions,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tResultModeUnion,\n} from \"./types\";\nimport { isObject, isQueryString, isString } from \"./utils/typeof\";\nimport {\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetRequestKey,\n\tgetResolveErrorResultFn,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tomitKeys,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils/utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData = unknown,\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\tbody: baseBody,\n\t\theaders: baseHeaders,\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 { body = baseBody, headers, signal = baseSignal, ...restOfFetchConfig } = fetchConfig;\n\n\t\t// == Default Extra Options\n\t\tconst options = {\n\t\t\tbaseURL: \"\",\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tcancelRedundantRequests: true,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tresponseType: \"json\",\n\t\t\tretries: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryDelay: 0,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\t// == Default Fetch Config\n\t\tconst defaultFetchOptions = {\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\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\tAccept: \"application/json\",\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isQueryString(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) || options.auth == null) && {\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// == 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\tmethod: \"GET\",\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestOptions;\n\n\t\tconst requestKey = getRequestKey(\n\t\t\turl,\n\t\t\tomitKeys({ ...defaultFetchOptions, ...options }, [\n\t\t\t\t\"onRequest\",\n\t\t\t\t\"onResponse\",\n\t\t\t\t\"onResponseError\",\n\t\t\t\t\"onError\",\n\t\t\t\t\"onRequestError\",\n\t\t\t])\n\t\t);\n\n\t\tconst prevFetchController = abortControllerStore.get(requestKey);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\n\t\t\t\t`Request aborted as another request to this same endpoint: ${url}, with the same request options was initiated.`,\n\t\t\t\t\"AbortError\"\n\t\t\t);\n\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(requestKey, 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\t...(timeoutSignal ? [timeoutSignal] : []),\n\t\t\t...(signal ? [signal] : []),\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\t\t\t...defaultFetchOptions,\n\t\t} satisfies $RequestOptions;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ options, request: requestInit });\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\toptions.cloneResponse ? response.clone() : response,\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\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\toptions.cloneResponse ? response.clone() : response,\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\tdata: validSuccessData,\n\t\t\t\toptions,\n\t\t\t\trequest: requestInit,\n\t\t\t\tresponse: options.cloneResponse ? response.clone() : response,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ options, response, successData: validSuccessData });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = getResolveErrorResultFn<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.error(`${error.name}:`, message);\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, name } = error;\n\n\t\t\t\tconsole.error(`${name}:`, message);\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\tvoid (await Promise.all([\n\t\t\t\t\toptions.onResponseError?.({\n\t\t\t\t\t\terrorData,\n\t\t\t\t\t\toptions,\n\t\t\t\t\t\trequest: requestInit,\n\t\t\t\t\t\tresponse: options.cloneResponse ? response.clone() : response,\n\t\t\t\t\t}),\n\n\t\t\t\t\t// == Also call the onError interceptor\n\t\t\t\t\toptions.onError?.({\n\t\t\t\t\t\terror,\n\t\t\t\t\t\toptions,\n\t\t\t\t\t\trequest: requestInit,\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t}),\n\t\t\t\t]));\n\n\t\t\t\treturn resolveErrorResult(error);\n\t\t\t}\n\n\t\t\tconst errorResult = resolveErrorResult();\n\n\t\t\tvoid (await Promise.all([\n\t\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\t\toptions.onRequestError?.({ error: error as Error, options, request: requestInit }),\n\n\t\t\t\t// == Also call the onError interceptor\n\t\t\t\toptions.onError?.({\n\t\t\t\t\terror: (errorResult as { error: never }).error,\n\t\t\t\t\toptions,\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\tresponse: null,\n\t\t\t\t}),\n\t\t\t]));\n\n\t\t\treturn errorResult;\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(requestKey);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"],"mappings":"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","names":["callApi"]}
1
+ {"version":3,"sources":["../../src/index.ts","../../src/utils/typeof.ts","../../src/utils/utils.ts","../../src/createFetchClient.ts"],"sourcesContent":["export { callApi, createFetchClient } from \"./createFetchClient\";\r\n\r\nexport type {\r\n\tRequestOptions,\r\n\tExtraOptions,\r\n\tCallApiConfig,\r\n\tResponseContext,\r\n\tResponseErrorContext,\r\n\tErrorContext,\r\n} from \"./types\";\r\n","import type { AnyFunction } from \"./type-helpers\";\r\n\r\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\r\n\r\nexport const isObject = <TObject extends Record<string, unknown>>(value: unknown): value is TObject => {\r\n\treturn typeof value === \"object\" && value !== null && !(value instanceof FormData) && !isArray(value);\r\n};\r\n\r\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\r\n\ttypeof value === \"function\";\r\n\r\nexport const isQueryString = (value: unknown): value is string => isString(value) && value.includes(\"=\");\r\n\r\nexport const isString = (value: unknown) => typeof value === \"string\";\r\n","import type {\r\n\tApiErrorVariant,\r\n\tBaseCallApiConfig,\r\n\tBaseRequestOptions,\r\n\tCallApiConfig,\r\n\tExtraOptions,\r\n\tRequestOptions,\r\n} from \"../types\";\r\nimport { isArray, isObject } from \"./typeof\";\r\n\r\n// prettier-ignore\r\nexport const generateRequestKey = (url: string, config: Record<string, unknown>) => `${url} ${ampersand} ${JSON.stringify(config)}`;\r\n\r\ntype ToQueryStringFn = {\r\n\t(params: ExtraOptions[\"query\"]): string | null;\r\n\t(params: Required<ExtraOptions>[\"query\"]): string;\r\n};\r\n\r\nexport const toQueryString: ToQueryStringFn = (params) => {\r\n\tif (!params) {\r\n\t\tconsole.error(\"toQueryString:\", \"No query params provided!\");\r\n\r\n\t\treturn null as never;\r\n\t}\r\n\r\n\treturn new URLSearchParams(params as Record<string, string>).toString();\r\n};\r\n\r\nconst slash = \"/\";\r\nconst column = \":\";\r\nconst mergeUrlWithParams = (url: string, params: ExtraOptions[\"params\"]) => {\r\n\tif (!params) {\r\n\t\treturn url;\r\n\t}\r\n\r\n\tlet newUrl = url;\r\n\r\n\tif (isArray(params)) {\r\n\t\tconst matchedParamArray = newUrl\r\n\t\t\t.split(slash)\r\n\t\t\t.filter((matchedParam) => matchedParam.startsWith(column));\r\n\r\n\t\tfor (const [index, matchedParam] of matchedParamArray.entries()) {\r\n\t\t\tconst param = params[index] as string;\r\n\t\t\tnewUrl = newUrl.replace(matchedParam, param);\r\n\t\t}\r\n\r\n\t\treturn newUrl;\r\n\t}\r\n\r\n\tfor (const [key, value] of Object.entries(params)) {\r\n\t\tnewUrl = newUrl.replace(`:${key}`, String(value));\r\n\t}\r\n\r\n\treturn newUrl;\r\n};\r\n\r\nconst questionMark = \"?\";\r\nconst ampersand = \"&\";\r\nconst mergeUrlWithQuery = (url: string, query: ExtraOptions[\"query\"]): string => {\r\n\tif (!query) {\r\n\t\treturn url;\r\n\t}\r\n\r\n\tconst queryString = toQueryString(query);\r\n\r\n\tif (queryString?.length === 0) {\r\n\t\treturn url;\r\n\t}\r\n\r\n\tif (url.endsWith(questionMark)) {\r\n\t\treturn `${url}${queryString}`;\r\n\t}\r\n\r\n\tif (url.includes(questionMark)) {\r\n\t\treturn `${url}${ampersand}${queryString}`;\r\n\t}\r\n\r\n\treturn `${url}${questionMark}${queryString}`;\r\n};\r\n\r\nexport const mergeUrlWithParamsAndQuery = (\r\n\turl: string,\r\n\tparams: ExtraOptions[\"params\"],\r\n\tquery: ExtraOptions[\"query\"]\r\n) => {\r\n\tconst urlWithMergedParams = mergeUrlWithParams(url, params);\r\n\r\n\treturn mergeUrlWithQuery(urlWithMergedParams, query);\r\n};\r\n\r\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\r\n\tif (!headers || isObject(headers)) {\r\n\t\treturn headers;\r\n\t}\r\n\r\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\r\n};\r\n\r\nconst retryCodesLookup = {\r\n\t408: \"Request Timeout\",\r\n\t409: \"Conflict\",\r\n\t425: \"Too Early\",\r\n\t429: \"Too Many Requests\",\r\n\t500: \"Internal Server Error\",\r\n\t502: \"Bad Gateway\",\r\n\t503: \"Service Unavailable\",\r\n\t504: \"Gateway Timeout\",\r\n};\r\n\r\nexport const defaultRetryCodes: Required<BaseCallApiConfig>[\"retryCodes\"] =\r\n\tObject.keys(retryCodesLookup).map(Number);\r\n\r\nexport const defaultRetryMethods: Required<BaseCallApiConfig>[\"retryMethods\"] = [\"GET\"];\r\n\r\nexport const fetchSpecificKeys = [\r\n\t\"body\",\r\n\t\"integrity\",\r\n\t\"method\",\r\n\t\"headers\",\r\n\t\"signal\",\r\n\t\"cache\",\r\n\t\"redirect\",\r\n\t\"window\",\r\n\t\"credentials\",\r\n\t\"keepalive\",\r\n\t\"referrer\",\r\n\t\"priority\",\r\n\t\"mode\",\r\n\t\"referrerPolicy\",\r\n] satisfies Array<keyof CallApiConfig>;\r\n\r\nexport const omitKeys = <\r\n\tTObject extends Record<string, unknown>,\r\n\tconst TOmitArray extends Array<keyof TObject>,\r\n>(\r\n\tinitialObject: TObject,\r\n\tkeysToOmit: TOmitArray\r\n) => {\r\n\tconst arrayFromFilteredObject = Object.entries(initialObject).filter(\r\n\t\t([key]) => !keysToOmit.includes(key)\r\n\t);\r\n\r\n\tconst updatedObject = Object.fromEntries(arrayFromFilteredObject);\r\n\r\n\treturn updatedObject as Omit<TObject, keyof TOmitArray>;\r\n};\r\n\r\nconst pickKeys = <TObject extends Record<string, unknown>, const TPickArray extends Array<keyof TObject>>(\r\n\tinitialObject: TObject,\r\n\tkeysToPick: TPickArray\r\n) => {\r\n\tconst keysToPickSet = new Set(keysToPick);\r\n\r\n\tconst arrayFromInitObject = Object.entries(initialObject);\r\n\r\n\tconst filteredArray = arrayFromInitObject.filter(([objectKey]) => keysToPickSet.has(objectKey));\r\n\r\n\tconst updatedObject = Object.fromEntries(filteredArray);\r\n\r\n\treturn updatedObject as Pick<TObject, TPickArray[number]>;\r\n};\r\n\r\nexport const splitConfig = <TObject extends object>(\r\n\tconfig: TObject\r\n): [\"body\" extends keyof TObject ? RequestOptions : BaseRequestOptions, ExtraOptions] => [\r\n\tpickKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\r\n\tomitKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\r\n];\r\n\r\nexport const handleResponseType = <TResponse>(\r\n\tresponse: Response,\r\n\tparser?: Required<ExtraOptions>[\"responseParser\"]\r\n) => ({\r\n\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\r\n\tblob: () => response.blob() as Promise<TResponse>,\r\n\tformData: () => response.formData() as Promise<TResponse>,\r\n\tjson: async () => {\r\n\t\tif (parser) {\r\n\t\t\treturn parser(await response.text());\r\n\t\t}\r\n\r\n\t\treturn response.json() as Promise<TResponse>;\r\n\t},\r\n\ttext: () => response.text() as Promise<TResponse>,\r\n});\r\n\r\nexport const getResponseData = <TResponse>(\r\n\tresponse: Response,\r\n\tresponseType: keyof ReturnType<typeof handleResponseType>,\r\n\tparser: ExtraOptions[\"responseParser\"]\r\n) => {\r\n\tconst RESPONSE_TYPE_LOOKUP = handleResponseType<TResponse>(response, parser);\r\n\r\n\tif (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {\r\n\t\tthrow new Error(`Invalid response type: ${responseType}`);\r\n\t}\r\n\r\n\treturn RESPONSE_TYPE_LOOKUP[responseType]();\r\n};\r\n\r\ntype SuccessInfo = {\r\n\toptions: ExtraOptions;\r\n\tresponse: Response;\r\n\tsuccessData: unknown;\r\n};\r\n\r\n// == The CallApiResult type is used to cast all return statements due to a design limitation in ts.\r\n// LINK - See https://www.zhenghao.io/posts/type-functions for more info\r\nexport const resolveSuccessResult = <CallApiResult>(info: SuccessInfo): CallApiResult => {\r\n\tconst { options, response, successData } = info;\r\n\r\n\tconst apiDetails = {\r\n\t\tdata: successData,\r\n\t\terror: null,\r\n\t\tresponse,\r\n\t};\r\n\r\n\tif (!options.resultMode || options.resultMode === \"all\") {\r\n\t\treturn apiDetails as CallApiResult;\r\n\t}\r\n\r\n\treturn {\r\n\t\tonlyError: apiDetails.error,\r\n\t\tonlyResponse: apiDetails.response,\r\n\t\tonlySuccess: apiDetails.data,\r\n\t}[options.resultMode] as CallApiResult;\r\n};\r\n\r\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\r\nexport const resolveErrorResult = <CallApiResult>(errorInfo: {\r\n\tdefaultErrorMessage: ExtraOptions[\"defaultErrorMessage\"];\r\n\terror?: unknown;\r\n}): CallApiResult => {\r\n\tconst { defaultErrorMessage, error } = errorInfo;\r\n\r\n\tif (isHTTPErrorInstance(error)) {\r\n\t\tconst { errorData, message = defaultErrorMessage, name, response } = error;\r\n\r\n\t\treturn {\r\n\t\t\tdata: null,\r\n\t\t\terror: { errorData, message, name },\r\n\t\t\tresponse,\r\n\t\t} as CallApiResult;\r\n\t}\r\n\r\n\treturn {\r\n\t\tdata: null,\r\n\t\terror: {\r\n\t\t\terrorData: error,\r\n\t\t\tmessage: (error as Error).message,\r\n\t\t\tname: (error as Error).name,\r\n\t\t},\r\n\t\tresponse: null,\r\n\t} as CallApiResult;\r\n};\r\n\r\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\r\n\treturn isObject(error) && error.name === \"HTTPError\";\r\n};\r\n\r\ntype ErrorDetails<TErrorResponse> = {\r\n\tdefaultErrorMessage: string;\r\n\terrorData: TErrorResponse;\r\n\tresponse: Response;\r\n};\r\n\r\ntype ErrorOptions = {\r\n\tcause?: unknown;\r\n};\r\n\r\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\r\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\r\n\tisHTTPError = true;\r\n\r\n\toverride name = \"HTTPError\" as const;\r\n\r\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\r\n\r\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\r\n\t\tconst { defaultErrorMessage, errorData, response } = errorDetails;\r\n\r\n\t\tsuper((errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\r\n\r\n\t\tthis.errorData = errorData;\r\n\t\tthis.response = response;\r\n\t}\r\n}\r\n\r\n// prettier-ignore\r\nexport const isHTTPErrorInstance = <TErrorResponse>(\r\n\terror: unknown\r\n): error is HTTPError<TErrorResponse> => {\r\n\treturn (\r\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\r\n\t);\r\n};\r\n\r\nexport const waitUntil = (delay: number) => {\r\n\tif (delay === 0) return;\r\n\r\n\tconst { promise, resolve } = Promise.withResolvers();\r\n\r\n\tsetTimeout(resolve, delay);\r\n\r\n\treturn promise;\r\n};\r\n","import type {\r\n\tBaseCallApiConfig,\r\n\tCallApiConfig,\r\n\tExtraOptions,\r\n\tGetCallApiResult,\r\n\tRequestOptions,\r\n\tResultModeUnion,\r\n} from \"./types\";\r\nimport { isFunction, isObject, isQueryString, isString } from \"./utils/typeof\";\r\nimport {\r\n\tHTTPError,\r\n\tdefaultRetryCodes,\r\n\tdefaultRetryMethods,\r\n\tgenerateRequestKey,\r\n\tgetResponseData,\r\n\tisHTTPErrorInstance,\r\n\tmergeUrlWithParamsAndQuery,\r\n\tobjectifyHeaders,\r\n\tresolveErrorResult,\r\n\tresolveSuccessResult,\r\n\tsplitConfig,\r\n\twaitUntil,\r\n} from \"./utils/utils\";\r\n\r\nexport const createFetchClient = <\r\n\tTBaseData,\r\n\tTBaseErrorData = unknown,\r\n\tTBaseResultMode extends ResultModeUnion = undefined,\r\n>(\r\n\tbaseConfig: BaseCallApiConfig<TBaseData, TBaseErrorData, TBaseResultMode> = {}\r\n) => {\r\n\tconst requestInfoCache = new Map<\r\n\t\tfalse | string,\r\n\t\t{ controller: AbortController; responsePromise: Promise<Response> }\r\n\t>();\r\n\r\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig);\r\n\r\n\tconst {\r\n\t\tbody: baseBody,\r\n\t\theaders: baseHeaders,\r\n\t\tsignal: baseSignal,\r\n\t\t...restOfBaseFetchConfig\r\n\t} = baseFetchConfig;\r\n\r\n\t// eslint-disable-next-line complexity\r\n\tconst callApi = async <\r\n\t\tTData = TBaseData,\r\n\t\tTErrorData = TBaseErrorData,\r\n\t\tTResultMode extends ResultModeUnion = TBaseResultMode,\r\n\t>(\r\n\t\turl: string,\r\n\t\tconfig: CallApiConfig<TData, TErrorData, TResultMode> = {}\r\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\r\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\r\n\r\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config);\r\n\r\n\t\tconst { body = baseBody, headers, signal = baseSignal, ...restOfFetchConfig } = fetchConfig;\r\n\r\n\t\t// == Default Extra Options\r\n\t\tconst options = {\r\n\t\t\tbaseURL: \"\",\r\n\t\t\tbodySerializer: JSON.stringify,\r\n\t\t\tdedupeStrategy: \"cancel\",\r\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\r\n\t\t\tresponseType: \"json\",\r\n\t\t\tretries: 0,\r\n\t\t\tretryCodes: defaultRetryCodes,\r\n\t\t\tretryDelay: 0,\r\n\t\t\tretryMethods: defaultRetryMethods,\r\n\t\t\t...baseExtraOptions,\r\n\t\t\t...extraOptions,\r\n\t\t} satisfies ExtraOptions;\r\n\r\n\t\t// == Default Request Init\r\n\t\tconst defaultRequestOptions = {\r\n\t\t\tmethod: \"GET\",\r\n\r\n\t\t\t// eslint-disable-next-line perfectionist/sort-objects\r\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\r\n\r\n\t\t\t// == Return undefined if the following conditions are not met (so that native fetch would auto set the correct headers):\r\n\t\t\t// - headers are provided\r\n\t\t\t// - The body is an object\r\n\t\t\t// - The auth option is provided\r\n\t\t\theaders:\r\n\t\t\t\tbaseHeaders || headers || options.auth || isObject(body)\r\n\t\t\t\t\t? {\r\n\t\t\t\t\t\t\t...(isObject(body) && {\r\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\r\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\r\n\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\t...(isQueryString(body) && {\r\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\r\n\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\t...((isString(options.auth) || options.auth == null) && {\r\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\r\n\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\r\n\t\t\t\t\t\t\t\tAuthorization:\r\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\r\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\r\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\r\n\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\r\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t: undefined,\r\n\r\n\t\t\t...restOfBaseFetchConfig,\r\n\t\t\t...restOfFetchConfig,\r\n\t\t} satisfies RequestOptions;\r\n\r\n\t\t// prettier-ignore\r\n\t\tconst shouldHaveRequestKey = options.dedupeStrategy === \"cancel\" || options.dedupeStrategy === \"defer\";\r\n\r\n\t\t// prettier-ignore\r\n\t\tconst requestKey = options.requestKey ?? (shouldHaveRequestKey && generateRequestKey(url, { ...defaultRequestOptions, ...options }));\r\n\r\n\t\t// == This is required to leave the smallest window of time for the cache to be updated with the last request info, if all requests with the same key start at the same time\r\n\t\tif (requestKey) {\r\n\t\t\tawait waitUntil(0.1);\r\n\t\t}\r\n\r\n\t\t// == This ensures cache operations only occur when key is available\r\n\t\tconst requestInfoCacheOrNull = requestKey ? requestInfoCache : null;\r\n\r\n\t\tconst prevRequestInfo = requestInfoCacheOrNull?.get(requestKey);\r\n\r\n\t\tif (prevRequestInfo && options.dedupeStrategy === \"cancel\") {\r\n\t\t\tconst reason = new DOMException(\r\n\t\t\t\t`Request aborted as another request to this same endpoint: ${url}, with the same request options was initiated.`,\r\n\t\t\t\t\"AbortError\"\r\n\t\t\t);\r\n\r\n\t\t\tprevRequestInfo.controller.abort(reason);\r\n\t\t}\r\n\r\n\t\tconst newFetchController = new AbortController();\r\n\r\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\r\n\r\n\t\tconst combinedSignal = AbortSignal.any([\r\n\t\t\tnewFetchController.signal,\r\n\t\t\t...(timeoutSignal ? [timeoutSignal] : []),\r\n\t\t\t...(signal ? [signal] : []),\r\n\t\t]);\r\n\r\n\t\tconst requestInit = {\r\n\t\t\tsignal: combinedSignal,\r\n\t\t\t...defaultRequestOptions,\r\n\t\t} satisfies RequestOptions;\r\n\r\n\t\ttry {\r\n\t\t\tawait options.onRequest?.({ options, request: requestInit });\r\n\r\n\t\t\tconst responsePromise =\r\n\t\t\t\tprevRequestInfo && options.dedupeStrategy === \"defer\"\r\n\t\t\t\t\t? prevRequestInfo.responsePromise\r\n\t\t\t\t\t: fetch(\r\n\t\t\t\t\t\t\t`${options.baseURL}${mergeUrlWithParamsAndQuery(url, options.params, options.query)}`,\r\n\t\t\t\t\t\t\trequestInit\r\n\t\t\t\t\t\t);\r\n\r\n\t\t\trequestInfoCacheOrNull?.set(requestKey, { controller: newFetchController, responsePromise });\r\n\r\n\t\t\tconst response = await responsePromise;\r\n\r\n\t\t\tconst shouldRetry =\r\n\t\t\t\t!response.ok &&\r\n\t\t\t\t!combinedSignal.aborted &&\r\n\t\t\t\toptions.retries > 0 &&\r\n\t\t\t\toptions.retryCodes.includes(response.status) &&\r\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\r\n\r\n\t\t\tif (shouldRetry) {\r\n\t\t\t\tawait waitUntil(options.retryDelay);\r\n\r\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\r\n\t\t\t}\r\n\r\n\t\t\t// == Also clone response when dedupeStrategy is set to \"defer\", to avoid error thrown from reading response.json more than once\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\r\n\t\t\tconst shouldCloneResponse = options.cloneResponse || options.dedupeStrategy === \"defer\";\r\n\r\n\t\t\tif (!response.ok) {\r\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\r\n\t\t\t\t\tshouldCloneResponse ? response.clone() : response,\r\n\t\t\t\t\toptions.responseType,\r\n\t\t\t\t\toptions.responseParser\r\n\t\t\t\t);\r\n\r\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\r\n\t\t\t\tthrow new HTTPError({\r\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\r\n\t\t\t\t\terrorData,\r\n\t\t\t\t\tresponse,\r\n\t\t\t\t});\r\n\t\t\t}\r\n\r\n\t\t\tconst successData = await getResponseData<TData>(\r\n\t\t\t\tshouldCloneResponse ? response.clone() : response,\r\n\t\t\t\toptions.responseType,\r\n\t\t\t\toptions.responseParser\r\n\t\t\t);\r\n\r\n\t\t\tconst validSuccessData = options.responseValidator\r\n\t\t\t\t? options.responseValidator(successData)\r\n\t\t\t\t: successData;\r\n\r\n\t\t\tawait options.onResponse?.({\r\n\t\t\t\tdata: validSuccessData,\r\n\t\t\t\toptions,\r\n\t\t\t\trequest: requestInit,\r\n\t\t\t\tresponse: options.cloneResponse ? response.clone() : response,\r\n\t\t\t});\r\n\r\n\t\t\treturn resolveSuccessResult<CallApiResult>({ options, response, successData: validSuccessData });\r\n\r\n\t\t\t// == Exhaustive Error handling\r\n\t\t} catch (error) {\r\n\t\t\tconst generalErrorResult = resolveErrorResult<CallApiResult>({\r\n\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\r\n\t\t\t\terror,\r\n\t\t\t});\r\n\r\n\t\t\tconst shouldThrowOnError = isFunction(options.throwOnError)\r\n\t\t\t\t? options.throwOnError((generalErrorResult as { error: never }).error, options)\r\n\t\t\t\t: options.throwOnError;\r\n\r\n\t\t\tif (shouldThrowOnError) {\r\n\t\t\t\tthrow error;\r\n\t\t\t}\r\n\r\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\r\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\r\n\r\n\t\t\t\tconsole.error(`${error.name}:`, message);\r\n\r\n\t\t\t\treturn { ...generalErrorResult, message };\r\n\t\t\t}\r\n\r\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\r\n\t\t\t\tconst { message, name } = error;\r\n\r\n\t\t\t\tconsole.error(`${name}:`, message);\r\n\r\n\t\t\t\treturn generalErrorResult;\r\n\t\t\t}\r\n\r\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\r\n\t\t\t\tconst { errorData, response } = error;\r\n\r\n\t\t\t\tvoid (await Promise.all([\r\n\t\t\t\t\toptions.onResponseError?.({\r\n\t\t\t\t\t\terrorData,\r\n\t\t\t\t\t\toptions,\r\n\t\t\t\t\t\trequest: requestInit,\r\n\t\t\t\t\t\tresponse: options.cloneResponse ? response.clone() : response,\r\n\t\t\t\t\t}),\r\n\r\n\t\t\t\t\t// == Also call the onError interceptor\r\n\t\t\t\t\toptions.onError?.({\r\n\t\t\t\t\t\terror,\r\n\t\t\t\t\t\toptions,\r\n\t\t\t\t\t\trequest: requestInit,\r\n\t\t\t\t\t\tresponse,\r\n\t\t\t\t\t}),\r\n\t\t\t\t]));\r\n\r\n\t\t\t\treturn generalErrorResult;\r\n\t\t\t}\r\n\r\n\t\t\tvoid (await Promise.all([\r\n\t\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\r\n\t\t\t\toptions.onRequestError?.({ error: error as Error, options, request: requestInit }),\r\n\r\n\t\t\t\t// == Also call the onError interceptor\r\n\t\t\t\toptions.onError?.({\r\n\t\t\t\t\terror: (generalErrorResult as { error: never }).error,\r\n\t\t\t\t\toptions,\r\n\t\t\t\t\trequest: requestInit,\r\n\t\t\t\t\tresponse: null,\r\n\t\t\t\t}),\r\n\t\t\t]));\r\n\r\n\t\t\treturn generalErrorResult;\r\n\r\n\t\t\t// == Removing the now unneeded AbortController from store\r\n\t\t} finally {\r\n\t\t\trequestInfoCacheOrNull?.delete(requestKey);\r\n\t\t}\r\n\t};\r\n\r\n\tcallApi.create = createFetchClient;\r\n\r\n\treturn callApi;\r\n};\r\n\r\nexport const callApi = createFetchClient();\r\n"],"mappings":"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","names":["callApi"]}
@@ -1,16 +1,16 @@
1
- import { R as ResultModeUnion, B as BaseConfig, F as FetchConfig, G as GetCallApiResult } from './index-D5Oy7DgF.cjs';
2
- export { $ as $RequestOptions, c as ErrorContext, E as ExtraOptions, a as ResponseContext, b as ResponseErrorContext } from './index-D5Oy7DgF.cjs';
1
+ import { R as ResultModeUnion, B as BaseCallApiConfig, C as CallApiConfig, G as GetCallApiResult } from './index-B9T5fw7z.cjs';
2
+ export { d as ErrorContext, E as ExtraOptions, a as RequestOptions, b as ResponseContext, c as ResponseErrorContext } from './index-B9T5fw7z.cjs';
3
3
 
4
- declare const createFetchClient: <TBaseData, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
5
- <TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
4
+ declare const createFetchClient: <TBaseData, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseCallApiConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
5
+ <TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: CallApiConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
6
6
  create: any;
7
7
  };
8
8
  declare const callApi: {
9
- <TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = undefined>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode> | undefined): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
10
- create: <TBaseData, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
11
- <TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
9
+ <TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = undefined>(url: string, config?: CallApiConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
10
+ create: <TBaseData, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseCallApiConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
11
+ <TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: CallApiConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
12
12
  create: any;
13
13
  };
14
14
  };
15
15
 
16
- export { FetchConfig, callApi, createFetchClient };
16
+ export { CallApiConfig, callApi, createFetchClient };
@@ -1 +1 @@
1
- "use strict";var r,e=Object.defineProperty,o=Object.getOwnPropertyDescriptor,t=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,s={};((r,o)=>{for(var t in o)e(r,t,{get:o[t],enumerable:!0})})(s,{HTTPError:()=>u,isHTTPError:()=>c,isHTTPErrorInstance:()=>l,toQueryString:()=>i}),module.exports=(r=s,((r,s,n,i)=>{if(s&&"object"==typeof s||"function"==typeof s)for(let c of t(s))a.call(r,c)||c===n||e(r,c,{get:()=>s[c],enumerable:!(i=o(s,c))||i.enumerable});return r})(e({},"__esModule",{value:!0}),r));var n=r=>!("object"!=typeof r||null===r||r instanceof FormData||Array.isArray(r)),i=r=>r?new URLSearchParams(r).toString():(console.error("toQueryString:","No query params provided!"),null),c=(Object.keys({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"}).map(Number),r=>n(r)&&"HTTPError"===r.name),u=class extends Error{errorData;isHTTPError=!0;name="HTTPError";response;constructor(r,e){const{defaultErrorMessage:o,errorData:t,response:a}=r;super(t.message??o,e),this.errorData=t,this.response=a}},l=r=>r instanceof u||n(r)&&"HTTPError"===r.name&&!0===r.isHTTPError;//# sourceMappingURL=index.cjs.map
1
+ "use strict";var r,e=Object.defineProperty,o=Object.getOwnPropertyDescriptor,t=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,s={};((r,o)=>{for(var t in o)e(r,t,{get:o[t],enumerable:!0})})(s,{HTTPError:()=>u,isHTTPError:()=>c,isHTTPErrorInstance:()=>l,toQueryString:()=>i}),module.exports=(r=s,((r,s,n,i)=>{if(s&&"object"==typeof s||"function"==typeof s)for(let c of t(s))a.call(r,c)||c===n||e(r,c,{get:()=>s[c],enumerable:!(i=o(s,c))||i.enumerable});return r})(e({},"__esModule",{value:!0}),r));var n=r=>!("object"!=typeof r||null===r||r instanceof FormData||(r=>Array.isArray(r))(r)),i=r=>r?new URLSearchParams(r).toString():(console.error("toQueryString:","No query params provided!"),null),c=(Object.keys({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"}).map(Number),r=>n(r)&&"HTTPError"===r.name),u=class extends Error{errorData;isHTTPError=!0;name="HTTPError";response;constructor(r,e){const{defaultErrorMessage:o,errorData:t,response:a}=r;super(t.message??o,e),this.errorData=t,this.response=a}},l=r=>r instanceof u||n(r)&&"HTTPError"===r.name&&!0===r.isHTTPError;//# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/index.ts","../../../src/utils/typeof.ts","../../../src/utils/utils.ts"],"sourcesContent":["export { 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 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 isQueryString = (value: unknown): value is string =>\n\ttypeof value === \"string\" && value.includes(\"=\");\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n","import type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"../types\";\nimport { isArray, isFunction, isObject } from \"./typeof\";\n\n// prettier-ignore\nexport const getRequestKey = <TConfig extends Record<string, unknown>>(url: string, config?: TConfig) => `${url} | ${JSON.stringify(config ?? {})}`;\n\ntype ToQueryStringFn = {\n\t(params: ExtraOptions[\"query\"]): string | null;\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"toQueryString:\", \"No query params provided!\");\n\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, query: ExtraOptions[\"query\"]): string => {\n\tif (!query) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(query);\n\n\tif (paramsString === \"\") {\n\t\treturn url;\n\t}\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\tif (url.includes(\"?\")) {\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\nexport const omitKeys = <\n\tTObject extends Record<string, unknown>,\n\tconst TOmitArray extends Array<keyof TObject>,\n>(\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\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\n\tblob: () => response.blob() as Promise<TResponse>,\n\tformData: () => response.formData() as Promise<TResponse>,\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\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 SuccessInfo = {\n\toptions: ExtraOptions;\n\tresponse: Response;\n\tsuccessData: unknown;\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: SuccessInfo): CallApiResult => {\n\tconst { options, response, successData } = info;\n\n\tconst apiDetails = {\n\t\tdata: successData,\n\t\terror: null,\n\t\tresponse,\n\t};\n\n\tif (!options.resultMode || options.resultMode === \"all\") {\n\t\treturn apiDetails as CallApiResult;\n\t}\n\n\treturn {\n\t\tonlyError: apiDetails.error,\n\t\tonlyResponse: apiDetails.response,\n\t\tonlySuccess: apiDetails.data,\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 getResolveErrorResultFn = <CallApiResult>(initInfo: {\n\terror?: unknown;\n\toptions: ExtraOptions;\n}) => {\n\tconst { error, options } = initInfo;\n\n\tconst resolveErrorResult = <TErrorData>(errorInfo?: Partial<HTTPError<TErrorData>>): CallApiResult => {\n\t\tconst { errorData, message, response } = errorInfo ?? {};\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\terrorData: errorData ?? error,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t\tname: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\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.name === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\tdefaultErrorMessage: string;\n\terrorData: TErrorResponse;\n\tresponse: Response;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\n\tisHTTPError = true;\n\n\toverride name = \"HTTPError\" as const;\n\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, errorData, response } = 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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACIO,IAAM,WAAW,CAA0C,UAAqC;AACtG,SACC,OAAO,UAAU,YAAY,UAAU,QAAQ,EAAE,iBAAiB,aAAa,CAAC,MAAM,QAAQ,KAAK;AAErG;;;ACWO,IAAM,gBAAiC,CAAC,WAAW;AACzD,MAAI,CAAC,QAAQ;AACZ,YAAQ,MAAM,kBAAkB,2BAA2B;AAE3D,WAAO;AAAA,EACR;AAEA,SAAO,IAAI,gBAAgB,MAAgC,EAAE,SAAS;AACvE;AAgCA,IAAM,mBAAmB;AAAA,EACxB,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACN;AAEO,IAAM,oBACZ,OAAO,KAAK,gBAAgB,EAAE,IAAI,MAAM;AAsJlC,IAAM,cAAc,CAAa,UAAuD;AAC9F,SAAO,SAAS,KAAK,KAAK,MAAM,SAAS;AAC1C;AAYO,IAAM,YAAN,cAAkE,MAAM;AAAA,EAC9E;AAAA,EACA,cAAc;AAAA,EAEL,OAAO;AAAA,EAEhB;AAAA,EAEA,YAAY,cAA4C,cAA6B;AACpF,UAAM,EAAE,qBAAqB,WAAW,SAAS,IAAI;AAErD,UAAO,UAAmC,WAAW,qBAAqB,YAAY;AAEtF,SAAK,YAAY;AACjB,SAAK,WAAW;AAAA,EACjB;AACD;AAGO,IAAM,sBAAsB,CAClC,UACwC;AACxC,SACC,iBAAiB,aAAc,SAAS,KAAK,KAAK,MAAM,SAAS,eAAe,MAAM,gBAAgB;AAExG;","names":[]}
1
+ {"version":3,"sources":["../../../src/utils/index.ts","../../../src/utils/typeof.ts","../../../src/utils/utils.ts"],"sourcesContent":["export { HTTPError, isHTTPError, isHTTPErrorInstance, toQueryString } from \"./utils\";\r\n","import type { AnyFunction } from \"./type-helpers\";\r\n\r\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\r\n\r\nexport const isObject = <TObject extends Record<string, unknown>>(value: unknown): value is TObject => {\r\n\treturn typeof value === \"object\" && value !== null && !(value instanceof FormData) && !isArray(value);\r\n};\r\n\r\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\r\n\ttypeof value === \"function\";\r\n\r\nexport const isQueryString = (value: unknown): value is string => isString(value) && value.includes(\"=\");\r\n\r\nexport const isString = (value: unknown) => typeof value === \"string\";\r\n","import type {\r\n\tApiErrorVariant,\r\n\tBaseCallApiConfig,\r\n\tBaseRequestOptions,\r\n\tCallApiConfig,\r\n\tExtraOptions,\r\n\tRequestOptions,\r\n} from \"../types\";\r\nimport { isArray, isObject } from \"./typeof\";\r\n\r\n// prettier-ignore\r\nexport const generateRequestKey = (url: string, config: Record<string, unknown>) => `${url} ${ampersand} ${JSON.stringify(config)}`;\r\n\r\ntype ToQueryStringFn = {\r\n\t(params: ExtraOptions[\"query\"]): string | null;\r\n\t(params: Required<ExtraOptions>[\"query\"]): string;\r\n};\r\n\r\nexport const toQueryString: ToQueryStringFn = (params) => {\r\n\tif (!params) {\r\n\t\tconsole.error(\"toQueryString:\", \"No query params provided!\");\r\n\r\n\t\treturn null as never;\r\n\t}\r\n\r\n\treturn new URLSearchParams(params as Record<string, string>).toString();\r\n};\r\n\r\nconst slash = \"/\";\r\nconst column = \":\";\r\nconst mergeUrlWithParams = (url: string, params: ExtraOptions[\"params\"]) => {\r\n\tif (!params) {\r\n\t\treturn url;\r\n\t}\r\n\r\n\tlet newUrl = url;\r\n\r\n\tif (isArray(params)) {\r\n\t\tconst matchedParamArray = newUrl\r\n\t\t\t.split(slash)\r\n\t\t\t.filter((matchedParam) => matchedParam.startsWith(column));\r\n\r\n\t\tfor (const [index, matchedParam] of matchedParamArray.entries()) {\r\n\t\t\tconst param = params[index] as string;\r\n\t\t\tnewUrl = newUrl.replace(matchedParam, param);\r\n\t\t}\r\n\r\n\t\treturn newUrl;\r\n\t}\r\n\r\n\tfor (const [key, value] of Object.entries(params)) {\r\n\t\tnewUrl = newUrl.replace(`:${key}`, String(value));\r\n\t}\r\n\r\n\treturn newUrl;\r\n};\r\n\r\nconst questionMark = \"?\";\r\nconst ampersand = \"&\";\r\nconst mergeUrlWithQuery = (url: string, query: ExtraOptions[\"query\"]): string => {\r\n\tif (!query) {\r\n\t\treturn url;\r\n\t}\r\n\r\n\tconst queryString = toQueryString(query);\r\n\r\n\tif (queryString?.length === 0) {\r\n\t\treturn url;\r\n\t}\r\n\r\n\tif (url.endsWith(questionMark)) {\r\n\t\treturn `${url}${queryString}`;\r\n\t}\r\n\r\n\tif (url.includes(questionMark)) {\r\n\t\treturn `${url}${ampersand}${queryString}`;\r\n\t}\r\n\r\n\treturn `${url}${questionMark}${queryString}`;\r\n};\r\n\r\nexport const mergeUrlWithParamsAndQuery = (\r\n\turl: string,\r\n\tparams: ExtraOptions[\"params\"],\r\n\tquery: ExtraOptions[\"query\"]\r\n) => {\r\n\tconst urlWithMergedParams = mergeUrlWithParams(url, params);\r\n\r\n\treturn mergeUrlWithQuery(urlWithMergedParams, query);\r\n};\r\n\r\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\r\n\tif (!headers || isObject(headers)) {\r\n\t\treturn headers;\r\n\t}\r\n\r\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\r\n};\r\n\r\nconst retryCodesLookup = {\r\n\t408: \"Request Timeout\",\r\n\t409: \"Conflict\",\r\n\t425: \"Too Early\",\r\n\t429: \"Too Many Requests\",\r\n\t500: \"Internal Server Error\",\r\n\t502: \"Bad Gateway\",\r\n\t503: \"Service Unavailable\",\r\n\t504: \"Gateway Timeout\",\r\n};\r\n\r\nexport const defaultRetryCodes: Required<BaseCallApiConfig>[\"retryCodes\"] =\r\n\tObject.keys(retryCodesLookup).map(Number);\r\n\r\nexport const defaultRetryMethods: Required<BaseCallApiConfig>[\"retryMethods\"] = [\"GET\"];\r\n\r\nexport const fetchSpecificKeys = [\r\n\t\"body\",\r\n\t\"integrity\",\r\n\t\"method\",\r\n\t\"headers\",\r\n\t\"signal\",\r\n\t\"cache\",\r\n\t\"redirect\",\r\n\t\"window\",\r\n\t\"credentials\",\r\n\t\"keepalive\",\r\n\t\"referrer\",\r\n\t\"priority\",\r\n\t\"mode\",\r\n\t\"referrerPolicy\",\r\n] satisfies Array<keyof CallApiConfig>;\r\n\r\nexport const omitKeys = <\r\n\tTObject extends Record<string, unknown>,\r\n\tconst TOmitArray extends Array<keyof TObject>,\r\n>(\r\n\tinitialObject: TObject,\r\n\tkeysToOmit: TOmitArray\r\n) => {\r\n\tconst arrayFromFilteredObject = Object.entries(initialObject).filter(\r\n\t\t([key]) => !keysToOmit.includes(key)\r\n\t);\r\n\r\n\tconst updatedObject = Object.fromEntries(arrayFromFilteredObject);\r\n\r\n\treturn updatedObject as Omit<TObject, keyof TOmitArray>;\r\n};\r\n\r\nconst pickKeys = <TObject extends Record<string, unknown>, const TPickArray extends Array<keyof TObject>>(\r\n\tinitialObject: TObject,\r\n\tkeysToPick: TPickArray\r\n) => {\r\n\tconst keysToPickSet = new Set(keysToPick);\r\n\r\n\tconst arrayFromInitObject = Object.entries(initialObject);\r\n\r\n\tconst filteredArray = arrayFromInitObject.filter(([objectKey]) => keysToPickSet.has(objectKey));\r\n\r\n\tconst updatedObject = Object.fromEntries(filteredArray);\r\n\r\n\treturn updatedObject as Pick<TObject, TPickArray[number]>;\r\n};\r\n\r\nexport const splitConfig = <TObject extends object>(\r\n\tconfig: TObject\r\n): [\"body\" extends keyof TObject ? RequestOptions : BaseRequestOptions, ExtraOptions] => [\r\n\tpickKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\r\n\tomitKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\r\n];\r\n\r\nexport const handleResponseType = <TResponse>(\r\n\tresponse: Response,\r\n\tparser?: Required<ExtraOptions>[\"responseParser\"]\r\n) => ({\r\n\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\r\n\tblob: () => response.blob() as Promise<TResponse>,\r\n\tformData: () => response.formData() as Promise<TResponse>,\r\n\tjson: async () => {\r\n\t\tif (parser) {\r\n\t\t\treturn parser(await response.text());\r\n\t\t}\r\n\r\n\t\treturn response.json() as Promise<TResponse>;\r\n\t},\r\n\ttext: () => response.text() as Promise<TResponse>,\r\n});\r\n\r\nexport const getResponseData = <TResponse>(\r\n\tresponse: Response,\r\n\tresponseType: keyof ReturnType<typeof handleResponseType>,\r\n\tparser: ExtraOptions[\"responseParser\"]\r\n) => {\r\n\tconst RESPONSE_TYPE_LOOKUP = handleResponseType<TResponse>(response, parser);\r\n\r\n\tif (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {\r\n\t\tthrow new Error(`Invalid response type: ${responseType}`);\r\n\t}\r\n\r\n\treturn RESPONSE_TYPE_LOOKUP[responseType]();\r\n};\r\n\r\ntype SuccessInfo = {\r\n\toptions: ExtraOptions;\r\n\tresponse: Response;\r\n\tsuccessData: unknown;\r\n};\r\n\r\n// == The CallApiResult type is used to cast all return statements due to a design limitation in ts.\r\n// LINK - See https://www.zhenghao.io/posts/type-functions for more info\r\nexport const resolveSuccessResult = <CallApiResult>(info: SuccessInfo): CallApiResult => {\r\n\tconst { options, response, successData } = info;\r\n\r\n\tconst apiDetails = {\r\n\t\tdata: successData,\r\n\t\terror: null,\r\n\t\tresponse,\r\n\t};\r\n\r\n\tif (!options.resultMode || options.resultMode === \"all\") {\r\n\t\treturn apiDetails as CallApiResult;\r\n\t}\r\n\r\n\treturn {\r\n\t\tonlyError: apiDetails.error,\r\n\t\tonlyResponse: apiDetails.response,\r\n\t\tonlySuccess: apiDetails.data,\r\n\t}[options.resultMode] as CallApiResult;\r\n};\r\n\r\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\r\nexport const resolveErrorResult = <CallApiResult>(errorInfo: {\r\n\tdefaultErrorMessage: ExtraOptions[\"defaultErrorMessage\"];\r\n\terror?: unknown;\r\n}): CallApiResult => {\r\n\tconst { defaultErrorMessage, error } = errorInfo;\r\n\r\n\tif (isHTTPErrorInstance(error)) {\r\n\t\tconst { errorData, message = defaultErrorMessage, name, response } = error;\r\n\r\n\t\treturn {\r\n\t\t\tdata: null,\r\n\t\t\terror: { errorData, message, name },\r\n\t\t\tresponse,\r\n\t\t} as CallApiResult;\r\n\t}\r\n\r\n\treturn {\r\n\t\tdata: null,\r\n\t\terror: {\r\n\t\t\terrorData: error,\r\n\t\t\tmessage: (error as Error).message,\r\n\t\t\tname: (error as Error).name,\r\n\t\t},\r\n\t\tresponse: null,\r\n\t} as CallApiResult;\r\n};\r\n\r\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\r\n\treturn isObject(error) && error.name === \"HTTPError\";\r\n};\r\n\r\ntype ErrorDetails<TErrorResponse> = {\r\n\tdefaultErrorMessage: string;\r\n\terrorData: TErrorResponse;\r\n\tresponse: Response;\r\n};\r\n\r\ntype ErrorOptions = {\r\n\tcause?: unknown;\r\n};\r\n\r\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\r\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\r\n\tisHTTPError = true;\r\n\r\n\toverride name = \"HTTPError\" as const;\r\n\r\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\r\n\r\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\r\n\t\tconst { defaultErrorMessage, errorData, response } = errorDetails;\r\n\r\n\t\tsuper((errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\r\n\r\n\t\tthis.errorData = errorData;\r\n\t\tthis.response = response;\r\n\t}\r\n}\r\n\r\n// prettier-ignore\r\nexport const isHTTPErrorInstance = <TErrorResponse>(\r\n\terror: unknown\r\n): error is HTTPError<TErrorResponse> => {\r\n\treturn (\r\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\r\n\t);\r\n};\r\n\r\nexport const waitUntil = (delay: number) => {\r\n\tif (delay === 0) return;\r\n\r\n\tconst { promise, resolve } = Promise.withResolvers();\r\n\r\n\tsetTimeout(resolve, delay);\r\n\r\n\treturn promise;\r\n};\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEO,IAAM,UAAU,CAAS,UAAsC,MAAM,QAAQ,KAAK;AAElF,IAAM,WAAW,CAA0C,UAAqC;AACtG,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,EAAE,iBAAiB,aAAa,CAAC,QAAQ,KAAK;AACrG;;;ACYO,IAAM,gBAAiC,CAAC,WAAW;AACzD,MAAI,CAAC,QAAQ;AACZ,YAAQ,MAAM,kBAAkB,2BAA2B;AAE3D,WAAO;AAAA,EACR;AAEA,SAAO,IAAI,gBAAgB,MAAgC,EAAE,SAAS;AACvE;AAyEA,IAAM,mBAAmB;AAAA,EACxB,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACN;AAEO,IAAM,oBACZ,OAAO,KAAK,gBAAgB,EAAE,IAAI,MAAM;AAkJlC,IAAM,cAAc,CAAa,UAAuD;AAC9F,SAAO,SAAS,KAAK,KAAK,MAAM,SAAS;AAC1C;AAYO,IAAM,YAAN,cAAkE,MAAM;AAAA,EAC9E;AAAA,EACA,cAAc;AAAA,EAEL,OAAO;AAAA,EAEhB;AAAA,EAEA,YAAY,cAA4C,cAA6B;AACpF,UAAM,EAAE,qBAAqB,WAAW,SAAS,IAAI;AAErD,UAAO,UAAmC,WAAW,qBAAqB,YAAY;AAEtF,SAAK,YAAY;AACjB,SAAK,WAAW;AAAA,EACjB;AACD;AAGO,IAAM,sBAAsB,CAClC,UACwC;AACxC,SACC,iBAAiB,aAAc,SAAS,KAAK,KAAK,MAAM,SAAS,eAAe,MAAM,gBAAgB;AAExG;","names":[]}
@@ -1 +1 @@
1
- export { H as HTTPError, i as isHTTPError, d as isHTTPErrorInstance, t as toQueryString } from '../index-D5Oy7DgF.cjs';
1
+ export { H as HTTPError, i as isHTTPError, e as isHTTPErrorInstance, t as toQueryString } from '../index-B9T5fw7z.cjs';
@@ -0,0 +1 @@
1
+ var r=r=>Array.isArray(r),e=e=>!("object"!=typeof e||null===e||e instanceof FormData||r(e)),t=r=>"function"==typeof r,s=r=>o(r)&&r.includes("="),o=r=>"string"==typeof r,n=(r,e)=>`${r} ${i} ${JSON.stringify(e)}`,a=r=>r?new URLSearchParams(r).toString():(console.error("toQueryString:","No query params provided!"),null),i="&",l=(e,t,s)=>{const o=((e,t)=>{if(!t)return e;let s=e;if(r(t)){const r=s.split("/").filter((r=>r.startsWith(":")));for(const[e,o]of r.entries()){const r=t[e];s=s.replace(o,r)}return s}for(const[r,e]of Object.entries(t))s=s.replace(`:${r}`,String(e));return s})(e,t);return((r,e)=>{if(!e)return r;const t=a(e);return 0===t?.length?r:r.endsWith("?")?`${r}${t}`:r.includes("?")?`${r}${i}${t}`:`${r}?${t}`})(o,s)},c=t=>!t||e(t)?t:Object.fromEntries(r(t)?t:t.entries()),u=Object.keys({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"}).map(Number),f=["GET"],m=["body","integrity","method","headers","signal","cache","redirect","window","credentials","keepalive","referrer","priority","mode","referrerPolicy"],p=(r,e)=>{const t=Object.entries(r).filter((([r])=>!e.includes(r)));return Object.fromEntries(t)},d=(r,e)=>{const t=new Set(e),s=Object.entries(r).filter((([r])=>t.has(r)));return Object.fromEntries(s)},y=r=>[d(r,m),p(r,m)],b=(r,e,t)=>{const s=((r,e)=>({arrayBuffer:()=>r.arrayBuffer(),blob:()=>r.blob(),formData:()=>r.formData(),json:async()=>e?e(await r.text()):r.json(),text:()=>r.text()}))(r,t);if(!Object.hasOwn(s,e))throw new Error(`Invalid response type: ${e}`);return s[e]()},E=r=>{const{options:e,response:t,successData:s}=r,o={data:s,error:null,response:t};return e.resultMode&&"all"!==e.resultMode?{onlyError:o.error,onlyResponse:o.response,onlySuccess:o.data}[e.resultMode]:o},T=r=>{const{defaultErrorMessage:e,error:t}=r;if($(t)){const{errorData:r,message:s=e,name:o,response:n}=t;return{data:null,error:{errorData:r,message:s,name:o},response:n}}return{data:null,error:{errorData:t,message:t.message,name:t.name},response:null}},g=r=>e(r)&&"HTTPError"===r.name,h=class extends Error{errorData;isHTTPError=!0;name="HTTPError";response;constructor(r,e){const{defaultErrorMessage:t,errorData:s,response:o}=r;super(s.message??t,e),this.errorData=s,this.response=o}},$=r=>r instanceof h||e(r)&&"HTTPError"===r.name&&!0===r.isHTTPError,j=r=>{if(0===r)return;const{promise:e,resolve:t}=Promise.withResolvers();return setTimeout(t,r),e};export{h as HTTPError,u as defaultRetryCodes,f as defaultRetryMethods,n as generateRequestKey,b as getResponseData,t as isFunction,g as isHTTPError,$ as isHTTPErrorInstance,e as isObject,s as isQueryString,o as isString,l as mergeUrlWithParamsAndQuery,c as objectifyHeaders,T as resolveErrorResult,E as resolveSuccessResult,y as splitConfig,a as toQueryString,j as waitUntil};//# sourceMappingURL=chunk-AVPAVK2E.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/typeof.ts","../../src/utils/utils.ts"],"names":[],"mappings":"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","file":"chunk-AVPAVK2E.js","sourcesContent":["import type { AnyFunction } from \"./type-helpers\";\r\n\r\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\r\n\r\nexport const isObject = <TObject extends Record<string, unknown>>(value: unknown): value is TObject => {\r\n\treturn typeof value === \"object\" && value !== null && !(value instanceof FormData) && !isArray(value);\r\n};\r\n\r\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\r\n\ttypeof value === \"function\";\r\n\r\nexport const isQueryString = (value: unknown): value is string => isString(value) && value.includes(\"=\");\r\n\r\nexport const isString = (value: unknown) => typeof value === \"string\";\r\n","import type {\r\n\tApiErrorVariant,\r\n\tBaseCallApiConfig,\r\n\tBaseRequestOptions,\r\n\tCallApiConfig,\r\n\tExtraOptions,\r\n\tRequestOptions,\r\n} from \"../types\";\r\nimport { isArray, isObject } from \"./typeof\";\r\n\r\n// prettier-ignore\r\nexport const generateRequestKey = (url: string, config: Record<string, unknown>) => `${url} ${ampersand} ${JSON.stringify(config)}`;\r\n\r\ntype ToQueryStringFn = {\r\n\t(params: ExtraOptions[\"query\"]): string | null;\r\n\t(params: Required<ExtraOptions>[\"query\"]): string;\r\n};\r\n\r\nexport const toQueryString: ToQueryStringFn = (params) => {\r\n\tif (!params) {\r\n\t\tconsole.error(\"toQueryString:\", \"No query params provided!\");\r\n\r\n\t\treturn null as never;\r\n\t}\r\n\r\n\treturn new URLSearchParams(params as Record<string, string>).toString();\r\n};\r\n\r\nconst slash = \"/\";\r\nconst column = \":\";\r\nconst mergeUrlWithParams = (url: string, params: ExtraOptions[\"params\"]) => {\r\n\tif (!params) {\r\n\t\treturn url;\r\n\t}\r\n\r\n\tlet newUrl = url;\r\n\r\n\tif (isArray(params)) {\r\n\t\tconst matchedParamArray = newUrl\r\n\t\t\t.split(slash)\r\n\t\t\t.filter((matchedParam) => matchedParam.startsWith(column));\r\n\r\n\t\tfor (const [index, matchedParam] of matchedParamArray.entries()) {\r\n\t\t\tconst param = params[index] as string;\r\n\t\t\tnewUrl = newUrl.replace(matchedParam, param);\r\n\t\t}\r\n\r\n\t\treturn newUrl;\r\n\t}\r\n\r\n\tfor (const [key, value] of Object.entries(params)) {\r\n\t\tnewUrl = newUrl.replace(`:${key}`, String(value));\r\n\t}\r\n\r\n\treturn newUrl;\r\n};\r\n\r\nconst questionMark = \"?\";\r\nconst ampersand = \"&\";\r\nconst mergeUrlWithQuery = (url: string, query: ExtraOptions[\"query\"]): string => {\r\n\tif (!query) {\r\n\t\treturn url;\r\n\t}\r\n\r\n\tconst queryString = toQueryString(query);\r\n\r\n\tif (queryString?.length === 0) {\r\n\t\treturn url;\r\n\t}\r\n\r\n\tif (url.endsWith(questionMark)) {\r\n\t\treturn `${url}${queryString}`;\r\n\t}\r\n\r\n\tif (url.includes(questionMark)) {\r\n\t\treturn `${url}${ampersand}${queryString}`;\r\n\t}\r\n\r\n\treturn `${url}${questionMark}${queryString}`;\r\n};\r\n\r\nexport const mergeUrlWithParamsAndQuery = (\r\n\turl: string,\r\n\tparams: ExtraOptions[\"params\"],\r\n\tquery: ExtraOptions[\"query\"]\r\n) => {\r\n\tconst urlWithMergedParams = mergeUrlWithParams(url, params);\r\n\r\n\treturn mergeUrlWithQuery(urlWithMergedParams, query);\r\n};\r\n\r\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\r\n\tif (!headers || isObject(headers)) {\r\n\t\treturn headers;\r\n\t}\r\n\r\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\r\n};\r\n\r\nconst retryCodesLookup = {\r\n\t408: \"Request Timeout\",\r\n\t409: \"Conflict\",\r\n\t425: \"Too Early\",\r\n\t429: \"Too Many Requests\",\r\n\t500: \"Internal Server Error\",\r\n\t502: \"Bad Gateway\",\r\n\t503: \"Service Unavailable\",\r\n\t504: \"Gateway Timeout\",\r\n};\r\n\r\nexport const defaultRetryCodes: Required<BaseCallApiConfig>[\"retryCodes\"] =\r\n\tObject.keys(retryCodesLookup).map(Number);\r\n\r\nexport const defaultRetryMethods: Required<BaseCallApiConfig>[\"retryMethods\"] = [\"GET\"];\r\n\r\nexport const fetchSpecificKeys = [\r\n\t\"body\",\r\n\t\"integrity\",\r\n\t\"method\",\r\n\t\"headers\",\r\n\t\"signal\",\r\n\t\"cache\",\r\n\t\"redirect\",\r\n\t\"window\",\r\n\t\"credentials\",\r\n\t\"keepalive\",\r\n\t\"referrer\",\r\n\t\"priority\",\r\n\t\"mode\",\r\n\t\"referrerPolicy\",\r\n] satisfies Array<keyof CallApiConfig>;\r\n\r\nexport const omitKeys = <\r\n\tTObject extends Record<string, unknown>,\r\n\tconst TOmitArray extends Array<keyof TObject>,\r\n>(\r\n\tinitialObject: TObject,\r\n\tkeysToOmit: TOmitArray\r\n) => {\r\n\tconst arrayFromFilteredObject = Object.entries(initialObject).filter(\r\n\t\t([key]) => !keysToOmit.includes(key)\r\n\t);\r\n\r\n\tconst updatedObject = Object.fromEntries(arrayFromFilteredObject);\r\n\r\n\treturn updatedObject as Omit<TObject, keyof TOmitArray>;\r\n};\r\n\r\nconst pickKeys = <TObject extends Record<string, unknown>, const TPickArray extends Array<keyof TObject>>(\r\n\tinitialObject: TObject,\r\n\tkeysToPick: TPickArray\r\n) => {\r\n\tconst keysToPickSet = new Set(keysToPick);\r\n\r\n\tconst arrayFromInitObject = Object.entries(initialObject);\r\n\r\n\tconst filteredArray = arrayFromInitObject.filter(([objectKey]) => keysToPickSet.has(objectKey));\r\n\r\n\tconst updatedObject = Object.fromEntries(filteredArray);\r\n\r\n\treturn updatedObject as Pick<TObject, TPickArray[number]>;\r\n};\r\n\r\nexport const splitConfig = <TObject extends object>(\r\n\tconfig: TObject\r\n): [\"body\" extends keyof TObject ? RequestOptions : BaseRequestOptions, ExtraOptions] => [\r\n\tpickKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\r\n\tomitKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\r\n];\r\n\r\nexport const handleResponseType = <TResponse>(\r\n\tresponse: Response,\r\n\tparser?: Required<ExtraOptions>[\"responseParser\"]\r\n) => ({\r\n\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\r\n\tblob: () => response.blob() as Promise<TResponse>,\r\n\tformData: () => response.formData() as Promise<TResponse>,\r\n\tjson: async () => {\r\n\t\tif (parser) {\r\n\t\t\treturn parser(await response.text());\r\n\t\t}\r\n\r\n\t\treturn response.json() as Promise<TResponse>;\r\n\t},\r\n\ttext: () => response.text() as Promise<TResponse>,\r\n});\r\n\r\nexport const getResponseData = <TResponse>(\r\n\tresponse: Response,\r\n\tresponseType: keyof ReturnType<typeof handleResponseType>,\r\n\tparser: ExtraOptions[\"responseParser\"]\r\n) => {\r\n\tconst RESPONSE_TYPE_LOOKUP = handleResponseType<TResponse>(response, parser);\r\n\r\n\tif (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {\r\n\t\tthrow new Error(`Invalid response type: ${responseType}`);\r\n\t}\r\n\r\n\treturn RESPONSE_TYPE_LOOKUP[responseType]();\r\n};\r\n\r\ntype SuccessInfo = {\r\n\toptions: ExtraOptions;\r\n\tresponse: Response;\r\n\tsuccessData: unknown;\r\n};\r\n\r\n// == The CallApiResult type is used to cast all return statements due to a design limitation in ts.\r\n// LINK - See https://www.zhenghao.io/posts/type-functions for more info\r\nexport const resolveSuccessResult = <CallApiResult>(info: SuccessInfo): CallApiResult => {\r\n\tconst { options, response, successData } = info;\r\n\r\n\tconst apiDetails = {\r\n\t\tdata: successData,\r\n\t\terror: null,\r\n\t\tresponse,\r\n\t};\r\n\r\n\tif (!options.resultMode || options.resultMode === \"all\") {\r\n\t\treturn apiDetails as CallApiResult;\r\n\t}\r\n\r\n\treturn {\r\n\t\tonlyError: apiDetails.error,\r\n\t\tonlyResponse: apiDetails.response,\r\n\t\tonlySuccess: apiDetails.data,\r\n\t}[options.resultMode] as CallApiResult;\r\n};\r\n\r\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\r\nexport const resolveErrorResult = <CallApiResult>(errorInfo: {\r\n\tdefaultErrorMessage: ExtraOptions[\"defaultErrorMessage\"];\r\n\terror?: unknown;\r\n}): CallApiResult => {\r\n\tconst { defaultErrorMessage, error } = errorInfo;\r\n\r\n\tif (isHTTPErrorInstance(error)) {\r\n\t\tconst { errorData, message = defaultErrorMessage, name, response } = error;\r\n\r\n\t\treturn {\r\n\t\t\tdata: null,\r\n\t\t\terror: { errorData, message, name },\r\n\t\t\tresponse,\r\n\t\t} as CallApiResult;\r\n\t}\r\n\r\n\treturn {\r\n\t\tdata: null,\r\n\t\terror: {\r\n\t\t\terrorData: error,\r\n\t\t\tmessage: (error as Error).message,\r\n\t\t\tname: (error as Error).name,\r\n\t\t},\r\n\t\tresponse: null,\r\n\t} as CallApiResult;\r\n};\r\n\r\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\r\n\treturn isObject(error) && error.name === \"HTTPError\";\r\n};\r\n\r\ntype ErrorDetails<TErrorResponse> = {\r\n\tdefaultErrorMessage: string;\r\n\terrorData: TErrorResponse;\r\n\tresponse: Response;\r\n};\r\n\r\ntype ErrorOptions = {\r\n\tcause?: unknown;\r\n};\r\n\r\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\r\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\r\n\tisHTTPError = true;\r\n\r\n\toverride name = \"HTTPError\" as const;\r\n\r\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\r\n\r\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\r\n\t\tconst { defaultErrorMessage, errorData, response } = errorDetails;\r\n\r\n\t\tsuper((errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\r\n\r\n\t\tthis.errorData = errorData;\r\n\t\tthis.response = response;\r\n\t}\r\n}\r\n\r\n// prettier-ignore\r\nexport const isHTTPErrorInstance = <TErrorResponse>(\r\n\terror: unknown\r\n): error is HTTPError<TErrorResponse> => {\r\n\treturn (\r\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\r\n\t);\r\n};\r\n\r\nexport const waitUntil = (delay: number) => {\r\n\tif (delay === 0) return;\r\n\r\n\tconst { promise, resolve } = Promise.withResolvers();\r\n\r\n\tsetTimeout(resolve, delay);\r\n\r\n\treturn promise;\r\n};\r\n"]}
@@ -7,8 +7,8 @@ type AnyNumber = number & {
7
7
  type Prettify<TObject> = {
8
8
  [Key in keyof TObject]: TObject[Key];
9
9
  } & NonNullable<unknown>;
10
- type ResponseHeader = "Access-Control-Allow-Credentials" | "Access-Control-Allow-Headers" | "Access-Control-Allow-Methods" | "Access-Control-Allow-Origin" | "Access-Control-Expose-Headers" | "Access-Control-Max-Age" | "Age" | "Allow" | "Cache-Control" | "Clear-Site-Data" | "Content-Disposition" | "Content-Encoding" | "Content-Language" | "Content-Length" | "Content-Location" | "Content-Range" | "Content-Security-Policy-Report-Only" | "Content-Security-Policy" | "Cookie" | "Cross-Origin-Embedder-Policy" | "Cross-Origin-Opener-Policy" | "Cross-Origin-Resource-Policy" | "Date" | "ETag" | "Expires" | "Last-Modified" | "Location" | "Permissions-Policy" | "Pragma" | "Retry-After" | "Save-Data" | "Sec-CH-Prefers-Color-Scheme" | "Sec-CH-Prefers-Reduced-Motion" | "Sec-CH-UA-Arch" | "Sec-CH-UA-Bitness" | "Sec-CH-UA-Form-Factor" | "Sec-CH-UA-Full-Version-List" | "Sec-CH-UA-Full-Version" | "Sec-CH-UA-Mobile" | "Sec-CH-UA-Model" | "Sec-CH-UA-Platform-Version" | "Sec-CH-UA-Platform" | "Sec-CH-UA-WoW64" | "Sec-CH-UA" | "Sec-Fetch-Dest" | "Sec-Fetch-Mode" | "Sec-Fetch-Site" | "Sec-Fetch-User" | "Sec-GPC" | "Server-Timing" | "Server" | "Service-Worker-Navigation-Preload" | "Set-Cookie" | "Strict-Transport-Security" | "Timing-Allow-Origin" | "Trailer" | "Transfer-Encoding" | "Upgrade" | "Vary" | "Warning" | "WWW-Authenticate" | "X-Content-Type-Options" | "X-DNS-Prefetch-Control" | "X-Frame-Options" | "X-Permitted-Cross-Domain-Policies" | "X-Powered-By" | "X-Robots-Tag" | "X-XSS-Protection";
11
- type BaseMime = "application/epub+zip" | "application/gzip" | "application/json" | "application/ld+json" | "application/octet-stream" | "application/ogg" | "application/pdf" | "application/rtf" | "application/vnd.ms-fontobject" | "application/wasm" | "application/xhtml+xml" | "application/xml" | "application/zip" | "audio/aac" | "audio/mpeg" | "audio/ogg" | "audio/opus" | "audio/webm" | "audio/x-midi" | "font/otf" | "font/ttf" | "font/woff" | "font/woff2" | "image/avif" | "image/bmp" | "image/gif" | "image/jpeg" | "image/png" | "image/svg+xml" | "image/tiff" | "image/webp" | "image/x-icon" | "model/gltf-binary" | "model/gltf+json" | "text/calendar" | "text/css" | "text/csv" | "text/html" | "text/javascript" | "text/plain" | "video/3gpp" | "video/3gpp2" | "video/av1" | "video/mp2t" | "video/mp4" | "video/mpeg" | "video/ogg" | "video/webm" | "video/x-msvideo";
10
+ type CommonRequestHeaders = "Access-Control-Allow-Credentials" | "Access-Control-Allow-Headers" | "Access-Control-Allow-Methods" | "Access-Control-Allow-Origin" | "Access-Control-Expose-Headers" | "Access-Control-Max-Age" | "Age" | "Allow" | "Cache-Control" | "Clear-Site-Data" | "Content-Disposition" | "Content-Encoding" | "Content-Language" | "Content-Length" | "Content-Location" | "Content-Range" | "Content-Security-Policy-Report-Only" | "Content-Security-Policy" | "Cookie" | "Cross-Origin-Embedder-Policy" | "Cross-Origin-Opener-Policy" | "Cross-Origin-Resource-Policy" | "Date" | "ETag" | "Expires" | "Last-Modified" | "Location" | "Permissions-Policy" | "Pragma" | "Retry-After" | "Save-Data" | "Sec-CH-Prefers-Color-Scheme" | "Sec-CH-Prefers-Reduced-Motion" | "Sec-CH-UA-Arch" | "Sec-CH-UA-Bitness" | "Sec-CH-UA-Form-Factor" | "Sec-CH-UA-Full-Version-List" | "Sec-CH-UA-Full-Version" | "Sec-CH-UA-Mobile" | "Sec-CH-UA-Model" | "Sec-CH-UA-Platform-Version" | "Sec-CH-UA-Platform" | "Sec-CH-UA-WoW64" | "Sec-CH-UA" | "Sec-Fetch-Dest" | "Sec-Fetch-Mode" | "Sec-Fetch-Site" | "Sec-Fetch-User" | "Sec-GPC" | "Server-Timing" | "Server" | "Service-Worker-Navigation-Preload" | "Set-Cookie" | "Strict-Transport-Security" | "Timing-Allow-Origin" | "Trailer" | "Transfer-Encoding" | "Upgrade" | "Vary" | "Warning" | "WWW-Authenticate" | "X-Content-Type-Options" | "X-DNS-Prefetch-Control" | "X-Frame-Options" | "X-Permitted-Cross-Domain-Policies" | "X-Powered-By" | "X-Robots-Tag" | "X-XSS-Protection";
11
+ type CommonContentTypes = "application/epub+zip" | "application/gzip" | "application/json" | "application/ld+json" | "application/octet-stream" | "application/ogg" | "application/pdf" | "application/rtf" | "application/vnd.ms-fontobject" | "application/wasm" | "application/xhtml+xml" | "application/xml" | "application/zip" | "audio/aac" | "audio/mpeg" | "audio/ogg" | "audio/opus" | "audio/webm" | "audio/x-midi" | "font/otf" | "font/ttf" | "font/woff" | "font/woff2" | "image/avif" | "image/bmp" | "image/gif" | "image/jpeg" | "image/png" | "image/svg+xml" | "image/tiff" | "image/webp" | "image/x-icon" | "model/gltf-binary" | "model/gltf+json" | "text/calendar" | "text/css" | "text/csv" | "text/html" | "text/javascript" | "text/plain" | "video/3gpp" | "video/3gpp2" | "video/av1" | "video/mp2t" | "video/mp4" | "video/mpeg" | "video/ogg" | "video/webm" | "video/x-msvideo";
12
12
 
13
13
  type ToQueryStringFn = {
14
14
  (params: ExtraOptions["query"]): string | null;
@@ -45,10 +45,10 @@ declare class HTTPError<TErrorResponse = Record<string, unknown>> extends Error
45
45
  }
46
46
  declare const isHTTPErrorInstance: <TErrorResponse>(error: unknown) => error is HTTPError<TErrorResponse>;
47
47
 
48
- interface FetchConfig<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = "all"> extends Omit<RequestInit, "body" | "headers" | "method">, ExtraOptions<TData, TErrorData, TResultMode> {
48
+ interface CallApiConfig<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = "all"> extends Omit<RequestInit, "body" | "headers" | "method">, ExtraOptions<TData, TErrorData, TResultMode> {
49
49
  }
50
- type BaseConfig<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = "all"> = FetchConfig<TBaseData, TBaseErrorData, TBaseResultMode>;
51
- interface $RequestOptions extends Pick<FetchConfig, (typeof fetchSpecificKeys)[number]> {
50
+ type BaseCallApiConfig<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = "all"> = CallApiConfig<TBaseData, TBaseErrorData, TBaseResultMode>;
51
+ interface RequestOptions extends Pick<CallApiConfig, (typeof fetchSpecificKeys)[number]> {
52
52
  }
53
53
  interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = ResultModeUnion> {
54
54
  /**
@@ -74,6 +74,7 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
74
74
  /**
75
75
  * @description If true, cancels previous unfinished requests to the same URL.
76
76
  * @default true
77
+ * @deprecated use dedupeStrategy option instead
77
78
  */
78
79
  cancelRedundantRequests?: boolean;
79
80
  /**
@@ -81,6 +82,16 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
81
82
  * @default false
82
83
  */
83
84
  cloneResponse?: boolean;
85
+ /**
86
+ * @description Defines the deduplication strategy for the request, can be set to "none" | "defer" | "cancel".
87
+ * - If set to "none", deduplication is disabled.
88
+ *
89
+ * - If set to "cancel"(default), the previous pending request to the same URL will be cancelled and lets the new request through.
90
+ *
91
+ * - If set to "defer", no new requests to the same URL will be allowed through, until the previous one is completed.
92
+ * @default "cancel"
93
+ */
94
+ dedupeStrategy?: "cancel" | "defer" | "none";
84
95
  /**
85
96
  * @description Default error message to use if none is provided from a response.
86
97
  * @default "Failed to fetch data from server!"
@@ -89,7 +100,7 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
89
100
  /**
90
101
  * @description Headers to be used in the request.
91
102
  */
92
- headers?: Record<"Content-Type", BaseMime> | Record<ResponseHeader, string> | RequestInit["headers"];
103
+ headers?: Record<"Content-Type", CommonContentTypes> | Record<CommonRequestHeaders, string> | RequestInit["headers"];
93
104
  /**
94
105
  * @description an optional field you can fill with additional information,
95
106
  * to associate with the request, typically used for logging or tracing.
@@ -126,22 +137,31 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
126
137
  /** @description Interceptor to be called just before the request is made, allowing for modifications or additional operations. */
127
138
  onRequest?: (requestContext: {
128
139
  options: ExtraOptions;
129
- request: $RequestOptions;
140
+ request: RequestOptions;
130
141
  }) => Promise<void> | void;
131
142
  /** @description Interceptor to be called when an error occurs during the fetch request. */
132
143
  onRequestError?: (requestErrorContext: {
133
144
  error: Error;
134
145
  options: ExtraOptions;
135
- request: $RequestOptions;
146
+ request: RequestOptions;
136
147
  }) => Promise<void> | void;
137
148
  /** @description Interceptor to be called when a successful response is received from the api. */
138
149
  onResponse?: (responseContext: ResponseContext<TData>) => Promise<void> | void;
139
150
  /** @description Interceptor to be called when an error response is received from the api. */
140
151
  onResponseError?: (responseErrorContext: ResponseErrorContext<TErrorData>) => Promise<void> | void;
152
+ /**
153
+ * @description Params to be appended to the URL (i.e: /:id)
154
+ */
155
+ params?: Record<string, boolean | number | string> | Array<boolean | number | string>;
141
156
  /**
142
157
  * @description Query parameters to append to the URL.
143
158
  */
144
159
  query?: Record<string, boolean | number | string>;
160
+ /**
161
+ * @description Custom request key to be used to identify a request in the fetch deduplication strategy.
162
+ * @default request url + string formed from the request options
163
+ */
164
+ requestKey?: string;
145
165
  /**
146
166
  * @description Custom function to parse the response string into a object.
147
167
  */
@@ -186,7 +206,7 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
186
206
  * The function is passed the error object and can be used to conditionally throw the error
187
207
  * @default false
188
208
  */
189
- throwOnError?: boolean | ((error?: Error | HTTPError<TErrorData>) => boolean);
209
+ throwOnError?: boolean | ((error: ErrorContext<TErrorData>["error"], optionsAndRequest: ExtraOptions & RequestOptions) => boolean);
190
210
  /**
191
211
  * @description Request timeout in milliseconds
192
212
  */
@@ -195,13 +215,13 @@ interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extend
195
215
  type ResponseContext<TData> = Prettify<{
196
216
  data: TData;
197
217
  options: ExtraOptions;
198
- request: $RequestOptions;
218
+ request: RequestOptions;
199
219
  response: Response;
200
220
  }>;
201
221
  type ResponseErrorContext<TErrorData> = Prettify<{
202
222
  errorData: TErrorData;
203
223
  options: ExtraOptions;
204
- request: $RequestOptions;
224
+ request: RequestOptions;
205
225
  response: Response;
206
226
  }>;
207
227
  type ErrorContext<TErrorData> = {
@@ -209,14 +229,14 @@ type ErrorContext<TErrorData> = {
209
229
  name: PossibleErrorNames;
210
230
  }>;
211
231
  options: ExtraOptions;
212
- request: $RequestOptions;
232
+ request: RequestOptions;
213
233
  response: null;
214
234
  } | {
215
235
  error: Extract<ErrorObjectUnion<TErrorData>, {
216
236
  name: "HTTPError";
217
237
  }>;
218
238
  options: ExtraOptions;
219
- request: $RequestOptions;
239
+ request: RequestOptions;
220
240
  response: Response;
221
241
  };
222
242
  type ApiSuccessVariant<TData> = {
@@ -225,7 +245,7 @@ type ApiSuccessVariant<TData> = {
225
245
  response: Response;
226
246
  };
227
247
  type PossibleErrorNames = {
228
- _: "AbortError" | "Error" | "SyntaxError" | "TimeoutError" | "TypeError" | "UnknownError";
248
+ _: "AbortError" | "Error" | "SyntaxError" | "TimeoutError" | "TypeError";
229
249
  }["_"];
230
250
  type ErrorObjectUnion<TErrorData = unknown> = {
231
251
  errorData: Error;
@@ -262,4 +282,4 @@ type ResultModeUnion = {
262
282
  }["_"];
263
283
  type GetCallApiResult<TData, TErrorData, TResultMode> = TResultMode extends NonNullable<ResultModeUnion> ? ResultModeMap<TData, TErrorData>[TResultMode] : ResultModeMap<TData, TErrorData>["all"];
264
284
 
265
- export { type $RequestOptions as $, type BaseConfig as B, type ExtraOptions as E, type FetchConfig as F, type GetCallApiResult as G, HTTPError as H, type ResultModeUnion as R, type ResponseContext as a, type ResponseErrorContext as b, type ErrorContext as c, isHTTPErrorInstance as d, isHTTPError as i, toQueryString as t };
285
+ export { type BaseCallApiConfig as B, type CallApiConfig as C, type ExtraOptions as E, type GetCallApiResult as G, HTTPError as H, type ResultModeUnion as R, type RequestOptions as a, type ResponseContext as b, type ResponseErrorContext as c, type ErrorContext as d, isHTTPErrorInstance as e, isHTTPError as i, toQueryString as t };
@@ -1,16 +1,16 @@
1
- import { R as ResultModeUnion, B as BaseConfig, F as FetchConfig, G as GetCallApiResult } from './index-D5Oy7DgF.js';
2
- export { $ as $RequestOptions, c as ErrorContext, E as ExtraOptions, a as ResponseContext, b as ResponseErrorContext } from './index-D5Oy7DgF.js';
1
+ import { R as ResultModeUnion, B as BaseCallApiConfig, C as CallApiConfig, G as GetCallApiResult } from './index-B9T5fw7z.js';
2
+ export { d as ErrorContext, E as ExtraOptions, a as RequestOptions, b as ResponseContext, c as ResponseErrorContext } from './index-B9T5fw7z.js';
3
3
 
4
- declare const createFetchClient: <TBaseData, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
5
- <TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
4
+ declare const createFetchClient: <TBaseData, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseCallApiConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
5
+ <TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: CallApiConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
6
6
  create: any;
7
7
  };
8
8
  declare const callApi: {
9
- <TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = undefined>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode> | undefined): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
10
- create: <TBaseData, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
11
- <TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
9
+ <TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = undefined>(url: string, config?: CallApiConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
10
+ create: <TBaseData, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseCallApiConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
11
+ <TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: CallApiConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
12
12
  create: any;
13
13
  };
14
14
  };
15
15
 
16
- export { FetchConfig, callApi, createFetchClient };
16
+ export { CallApiConfig, callApi, createFetchClient };
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- import{splitConfig as e,defaultRetryCodes as r,defaultRetryMethods as o,isObject as t,isQueryString as s,isString as n,objectifyHeaders as a,getRequestKey as i,omitKeys as u,mergeUrlWithParams as p,waitUntil as c,getResponseData as l,HTTPError as d,resolveSuccessResult as h,getResolveErrorResultFn as m,isHTTPErrorInstance as y}from"./chunk-I6HJ6GR4.js";var R=f=>{const w=new Map,[b,q]=e(f??{}),{body:E,headers:g,signal:D,...M}=b,$=async(R,f)=>{const[b,A]=e(f??{}),{body:T=E,headers:k,signal:x=D,...C}=b,S={baseURL:"",bodySerializer:JSON.stringify,cancelRedundantRequests:!0,defaultErrorMessage:"Failed to fetch data from server!",responseType:"json",retries:0,retryCodes:r,retryDelay:0,retryMethods:o,...q,...A},j={body:t(T)?S.bodySerializer(T):T,headers:g||k||S.auth||t(T)?{...t(T)&&{Accept:"application/json","Content-Type":"application/json"},...s(T)&&{"Content-Type":"application/x-www-form-urlencoded"},...(n(S.auth)||null==S.auth)&&{Authorization:`Bearer ${S.auth}`},...t(S.auth)&&{Authorization:"bearer"in S.auth?`Bearer ${S.auth.bearer}`:`Token ${S.auth.token}`},...a(g),...a(k)}:void 0,method:"GET",...M,...C},z=i(R,u({...j,...S},["onRequest","onResponse","onResponseError","onError","onRequestError"])),O=w.get(z);if(O&&S.cancelRedundantRequests){const e=new DOMException(`Request aborted as another request to this same endpoint: ${R}, with the same request options was initiated.`,"AbortError");O.abort(e)}const P=new AbortController;w.set(z,P);const v=S.timeout?AbortSignal.timeout(S.timeout):null,B=AbortSignal.any([P.signal,...v?[v]:[],...x?[x]:[]]),G={signal:B,...j};try{await(S.onRequest?.({options:S,request:G}));const e=await fetch(`${S.baseURL}${p(R,S.query)}`,G);if(!e.ok&&!B.aborted&&S.retries>0&&S.retryCodes.includes(e.status)&&S.retryMethods.includes(G.method))return await c(S.retryDelay),await $(R,{...f,retries:S.retries-1});if(!e.ok){const r=await l(S.cloneResponse?e.clone():e,S.responseType,S.responseParser);throw new d({defaultErrorMessage:S.defaultErrorMessage,errorData:r,response:e})}const r=await l(S.cloneResponse?e.clone():e,S.responseType,S.responseParser),o=S.responseValidator?S.responseValidator(r):r;return await(S.onResponse?.({data:o,options:S,request:G,response:S.cloneResponse?e.clone():e})),h({options:S,response:e,successData:o})}catch(e){const r=m({error:e,options:S});if(e instanceof DOMException&&"TimeoutError"===e.name){const o=`Request timed out after ${S.timeout}ms`;return console.error(`${e.name}:`,o),r({message:o})}if(e instanceof DOMException&&"AbortError"===e.name){const{message:o,name:t}=e;return console.error(`${t}:`,o),r({message:o})}if(y(e)){const{errorData:o,response:t}=e;return await Promise.all([S.onResponseError?.({errorData:o,options:S,request:G,response:S.cloneResponse?t.clone():t}),S.onError?.({error:e,options:S,request:G,response:t})]),r(e)}const o=r();return await Promise.all([S.onRequestError?.({error:e,options:S,request:G}),S.onError?.({error:o.error,options:S,request:G,response:null})]),o}finally{w.delete(z)}};return $.create=R,$},f=R();export{f as callApi,R as createFetchClient};//# sourceMappingURL=index.js.map
1
+ import{splitConfig as e,defaultRetryCodes as r,defaultRetryMethods as o,isObject as t,isQueryString as s,isString as n,objectifyHeaders as a,generateRequestKey as i,waitUntil as u,mergeUrlWithParamsAndQuery as l,getResponseData as p,HTTPError as c,resolveSuccessResult as d,resolveErrorResult as y,isFunction as h,isHTTPErrorInstance as m}from"./chunk-AVPAVK2E.js";var f=(w={})=>{const g=new Map,[E,b]=e(w),{body:q,headers:M,signal:R,...S}=E,A=async(f,w={})=>{const[E,D]=e(w),{body:$=q,headers:T,signal:O=R,...P}=E,k={baseURL:"",bodySerializer:JSON.stringify,dedupeStrategy:"cancel",defaultErrorMessage:"Failed to fetch data from server!",responseType:"json",retries:0,retryCodes:r,retryDelay:0,retryMethods:o,...b,...D},x={method:"GET",body:t($)?k.bodySerializer($):$,headers:M||T||k.auth||t($)?{...t($)&&{Accept:"application/json","Content-Type":"application/json"},...s($)&&{"Content-Type":"application/x-www-form-urlencoded"},...(n(k.auth)||null==k.auth)&&{Authorization:`Bearer ${k.auth}`},...t(k.auth)&&{Authorization:"bearer"in k.auth?`Bearer ${k.auth.bearer}`:`Token ${k.auth.token}`},...a(M),...a(T)}:void 0,...S,...P},C="cancel"===k.dedupeStrategy||"defer"===k.dedupeStrategy,j=k.requestKey??(C&&i(f,{...x,...k}));j&&await u(.1);const z=j?g:null,V=z?.get(j);if(V&&"cancel"===k.dedupeStrategy){const e=new DOMException(`Request aborted as another request to this same endpoint: ${f}, with the same request options was initiated.`,"AbortError");V.controller.abort(e)}const v=new AbortController,B=k.timeout?AbortSignal.timeout(k.timeout):null,K=AbortSignal.any([v.signal,...B?[B]:[],...O?[O]:[]]),L={signal:K,...x};try{await(k.onRequest?.({options:k,request:L}));const e=V&&"defer"===k.dedupeStrategy?V.responsePromise:fetch(`${k.baseURL}${l(f,k.params,k.query)}`,L);z?.set(j,{controller:v,responsePromise:e});const r=await e;if(!r.ok&&!K.aborted&&k.retries>0&&k.retryCodes.includes(r.status)&&k.retryMethods.includes(L.method))return await u(k.retryDelay),await A(f,{...w,retries:k.retries-1});const o=k.cloneResponse||"defer"===k.dedupeStrategy;if(!r.ok){const e=await p(o?r.clone():r,k.responseType,k.responseParser);throw new c({defaultErrorMessage:k.defaultErrorMessage,errorData:e,response:r})}const t=await p(o?r.clone():r,k.responseType,k.responseParser),s=k.responseValidator?k.responseValidator(t):t;return await(k.onResponse?.({data:s,options:k,request:L,response:k.cloneResponse?r.clone():r})),d({options:k,response:r,successData:s})}catch(e){const r=y({defaultErrorMessage:k.defaultErrorMessage,error:e});if(h(k.throwOnError)?k.throwOnError(r.error,k):k.throwOnError)throw e;if(e instanceof DOMException&&"TimeoutError"===e.name){const o=`Request timed out after ${k.timeout}ms`;return console.error(`${e.name}:`,o),{...r,message:o}}if(e instanceof DOMException&&"AbortError"===e.name){const{message:o,name:t}=e;return console.error(`${t}:`,o),r}if(m(e)){const{errorData:o,response:t}=e;return await Promise.all([k.onResponseError?.({errorData:o,options:k,request:L,response:k.cloneResponse?t.clone():t}),k.onError?.({error:e,options:k,request:L,response:t})]),r}return await Promise.all([k.onRequestError?.({error:e,options:k,request:L}),k.onError?.({error:r.error,options:k,request:L,response:null})]),r}finally{z?.delete(j)}};return A.create=f,A},w=f();export{w as callApi,f as createFetchClient};//# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/createFetchClient.ts"],"names":["callApi"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n\t$RequestOptions,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tResultModeUnion,\n} from \"./types\";\nimport { isObject, isQueryString, isString } from \"./utils/typeof\";\nimport {\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetRequestKey,\n\tgetResolveErrorResultFn,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tomitKeys,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils/utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData = unknown,\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\tbody: baseBody,\n\t\theaders: baseHeaders,\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 { body = baseBody, headers, signal = baseSignal, ...restOfFetchConfig } = fetchConfig;\n\n\t\t// == Default Extra Options\n\t\tconst options = {\n\t\t\tbaseURL: \"\",\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tcancelRedundantRequests: true,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tresponseType: \"json\",\n\t\t\tretries: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryDelay: 0,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\t// == Default Fetch Config\n\t\tconst defaultFetchOptions = {\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\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\tAccept: \"application/json\",\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isQueryString(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) || options.auth == null) && {\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// == 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\tmethod: \"GET\",\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestOptions;\n\n\t\tconst requestKey = getRequestKey(\n\t\t\turl,\n\t\t\tomitKeys({ ...defaultFetchOptions, ...options }, [\n\t\t\t\t\"onRequest\",\n\t\t\t\t\"onResponse\",\n\t\t\t\t\"onResponseError\",\n\t\t\t\t\"onError\",\n\t\t\t\t\"onRequestError\",\n\t\t\t])\n\t\t);\n\n\t\tconst prevFetchController = abortControllerStore.get(requestKey);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\n\t\t\t\t`Request aborted as another request to this same endpoint: ${url}, with the same request options was initiated.`,\n\t\t\t\t\"AbortError\"\n\t\t\t);\n\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(requestKey, 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\t...(timeoutSignal ? [timeoutSignal] : []),\n\t\t\t...(signal ? [signal] : []),\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\t\t\t...defaultFetchOptions,\n\t\t} satisfies $RequestOptions;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ options, request: requestInit });\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\toptions.cloneResponse ? response.clone() : response,\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\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\toptions.cloneResponse ? response.clone() : response,\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\tdata: validSuccessData,\n\t\t\t\toptions,\n\t\t\t\trequest: requestInit,\n\t\t\t\tresponse: options.cloneResponse ? response.clone() : response,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ options, response, successData: validSuccessData });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = getResolveErrorResultFn<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.error(`${error.name}:`, message);\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, name } = error;\n\n\t\t\t\tconsole.error(`${name}:`, message);\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\tvoid (await Promise.all([\n\t\t\t\t\toptions.onResponseError?.({\n\t\t\t\t\t\terrorData,\n\t\t\t\t\t\toptions,\n\t\t\t\t\t\trequest: requestInit,\n\t\t\t\t\t\tresponse: options.cloneResponse ? response.clone() : response,\n\t\t\t\t\t}),\n\n\t\t\t\t\t// == Also call the onError interceptor\n\t\t\t\t\toptions.onError?.({\n\t\t\t\t\t\terror,\n\t\t\t\t\t\toptions,\n\t\t\t\t\t\trequest: requestInit,\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t}),\n\t\t\t\t]));\n\n\t\t\t\treturn resolveErrorResult(error);\n\t\t\t}\n\n\t\t\tconst errorResult = resolveErrorResult();\n\n\t\t\tvoid (await Promise.all([\n\t\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\t\toptions.onRequestError?.({ error: error as Error, options, request: requestInit }),\n\n\t\t\t\t// == Also call the onError interceptor\n\t\t\t\toptions.onError?.({\n\t\t\t\t\terror: (errorResult as { error: never }).error,\n\t\t\t\t\toptions,\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\tresponse: null,\n\t\t\t\t}),\n\t\t\t]));\n\n\t\t\treturn errorResult;\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(requestKey);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"]}
1
+ {"version":3,"sources":["../../src/createFetchClient.ts"],"names":["callApi"],"mappings":"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","file":"index.js","sourcesContent":["import type {\r\n\tBaseCallApiConfig,\r\n\tCallApiConfig,\r\n\tExtraOptions,\r\n\tGetCallApiResult,\r\n\tRequestOptions,\r\n\tResultModeUnion,\r\n} from \"./types\";\r\nimport { isFunction, isObject, isQueryString, isString } from \"./utils/typeof\";\r\nimport {\r\n\tHTTPError,\r\n\tdefaultRetryCodes,\r\n\tdefaultRetryMethods,\r\n\tgenerateRequestKey,\r\n\tgetResponseData,\r\n\tisHTTPErrorInstance,\r\n\tmergeUrlWithParamsAndQuery,\r\n\tobjectifyHeaders,\r\n\tresolveErrorResult,\r\n\tresolveSuccessResult,\r\n\tsplitConfig,\r\n\twaitUntil,\r\n} from \"./utils/utils\";\r\n\r\nexport const createFetchClient = <\r\n\tTBaseData,\r\n\tTBaseErrorData = unknown,\r\n\tTBaseResultMode extends ResultModeUnion = undefined,\r\n>(\r\n\tbaseConfig: BaseCallApiConfig<TBaseData, TBaseErrorData, TBaseResultMode> = {}\r\n) => {\r\n\tconst requestInfoCache = new Map<\r\n\t\tfalse | string,\r\n\t\t{ controller: AbortController; responsePromise: Promise<Response> }\r\n\t>();\r\n\r\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig);\r\n\r\n\tconst {\r\n\t\tbody: baseBody,\r\n\t\theaders: baseHeaders,\r\n\t\tsignal: baseSignal,\r\n\t\t...restOfBaseFetchConfig\r\n\t} = baseFetchConfig;\r\n\r\n\t// eslint-disable-next-line complexity\r\n\tconst callApi = async <\r\n\t\tTData = TBaseData,\r\n\t\tTErrorData = TBaseErrorData,\r\n\t\tTResultMode extends ResultModeUnion = TBaseResultMode,\r\n\t>(\r\n\t\turl: string,\r\n\t\tconfig: CallApiConfig<TData, TErrorData, TResultMode> = {}\r\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\r\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\r\n\r\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config);\r\n\r\n\t\tconst { body = baseBody, headers, signal = baseSignal, ...restOfFetchConfig } = fetchConfig;\r\n\r\n\t\t// == Default Extra Options\r\n\t\tconst options = {\r\n\t\t\tbaseURL: \"\",\r\n\t\t\tbodySerializer: JSON.stringify,\r\n\t\t\tdedupeStrategy: \"cancel\",\r\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\r\n\t\t\tresponseType: \"json\",\r\n\t\t\tretries: 0,\r\n\t\t\tretryCodes: defaultRetryCodes,\r\n\t\t\tretryDelay: 0,\r\n\t\t\tretryMethods: defaultRetryMethods,\r\n\t\t\t...baseExtraOptions,\r\n\t\t\t...extraOptions,\r\n\t\t} satisfies ExtraOptions;\r\n\r\n\t\t// == Default Request Init\r\n\t\tconst defaultRequestOptions = {\r\n\t\t\tmethod: \"GET\",\r\n\r\n\t\t\t// eslint-disable-next-line perfectionist/sort-objects\r\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\r\n\r\n\t\t\t// == Return undefined if the following conditions are not met (so that native fetch would auto set the correct headers):\r\n\t\t\t// - headers are provided\r\n\t\t\t// - The body is an object\r\n\t\t\t// - The auth option is provided\r\n\t\t\theaders:\r\n\t\t\t\tbaseHeaders || headers || options.auth || isObject(body)\r\n\t\t\t\t\t? {\r\n\t\t\t\t\t\t\t...(isObject(body) && {\r\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\r\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\r\n\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\t...(isQueryString(body) && {\r\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\r\n\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\t...((isString(options.auth) || options.auth == null) && {\r\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\r\n\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\r\n\t\t\t\t\t\t\t\tAuthorization:\r\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\r\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\r\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\r\n\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\r\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t: undefined,\r\n\r\n\t\t\t...restOfBaseFetchConfig,\r\n\t\t\t...restOfFetchConfig,\r\n\t\t} satisfies RequestOptions;\r\n\r\n\t\t// prettier-ignore\r\n\t\tconst shouldHaveRequestKey = options.dedupeStrategy === \"cancel\" || options.dedupeStrategy === \"defer\";\r\n\r\n\t\t// prettier-ignore\r\n\t\tconst requestKey = options.requestKey ?? (shouldHaveRequestKey && generateRequestKey(url, { ...defaultRequestOptions, ...options }));\r\n\r\n\t\t// == This is required to leave the smallest window of time for the cache to be updated with the last request info, if all requests with the same key start at the same time\r\n\t\tif (requestKey) {\r\n\t\t\tawait waitUntil(0.1);\r\n\t\t}\r\n\r\n\t\t// == This ensures cache operations only occur when key is available\r\n\t\tconst requestInfoCacheOrNull = requestKey ? requestInfoCache : null;\r\n\r\n\t\tconst prevRequestInfo = requestInfoCacheOrNull?.get(requestKey);\r\n\r\n\t\tif (prevRequestInfo && options.dedupeStrategy === \"cancel\") {\r\n\t\t\tconst reason = new DOMException(\r\n\t\t\t\t`Request aborted as another request to this same endpoint: ${url}, with the same request options was initiated.`,\r\n\t\t\t\t\"AbortError\"\r\n\t\t\t);\r\n\r\n\t\t\tprevRequestInfo.controller.abort(reason);\r\n\t\t}\r\n\r\n\t\tconst newFetchController = new AbortController();\r\n\r\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\r\n\r\n\t\tconst combinedSignal = AbortSignal.any([\r\n\t\t\tnewFetchController.signal,\r\n\t\t\t...(timeoutSignal ? [timeoutSignal] : []),\r\n\t\t\t...(signal ? [signal] : []),\r\n\t\t]);\r\n\r\n\t\tconst requestInit = {\r\n\t\t\tsignal: combinedSignal,\r\n\t\t\t...defaultRequestOptions,\r\n\t\t} satisfies RequestOptions;\r\n\r\n\t\ttry {\r\n\t\t\tawait options.onRequest?.({ options, request: requestInit });\r\n\r\n\t\t\tconst responsePromise =\r\n\t\t\t\tprevRequestInfo && options.dedupeStrategy === \"defer\"\r\n\t\t\t\t\t? prevRequestInfo.responsePromise\r\n\t\t\t\t\t: fetch(\r\n\t\t\t\t\t\t\t`${options.baseURL}${mergeUrlWithParamsAndQuery(url, options.params, options.query)}`,\r\n\t\t\t\t\t\t\trequestInit\r\n\t\t\t\t\t\t);\r\n\r\n\t\t\trequestInfoCacheOrNull?.set(requestKey, { controller: newFetchController, responsePromise });\r\n\r\n\t\t\tconst response = await responsePromise;\r\n\r\n\t\t\tconst shouldRetry =\r\n\t\t\t\t!response.ok &&\r\n\t\t\t\t!combinedSignal.aborted &&\r\n\t\t\t\toptions.retries > 0 &&\r\n\t\t\t\toptions.retryCodes.includes(response.status) &&\r\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\r\n\r\n\t\t\tif (shouldRetry) {\r\n\t\t\t\tawait waitUntil(options.retryDelay);\r\n\r\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\r\n\t\t\t}\r\n\r\n\t\t\t// == Also clone response when dedupeStrategy is set to \"defer\", to avoid error thrown from reading response.json more than once\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\r\n\t\t\tconst shouldCloneResponse = options.cloneResponse || options.dedupeStrategy === \"defer\";\r\n\r\n\t\t\tif (!response.ok) {\r\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\r\n\t\t\t\t\tshouldCloneResponse ? response.clone() : response,\r\n\t\t\t\t\toptions.responseType,\r\n\t\t\t\t\toptions.responseParser\r\n\t\t\t\t);\r\n\r\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\r\n\t\t\t\tthrow new HTTPError({\r\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\r\n\t\t\t\t\terrorData,\r\n\t\t\t\t\tresponse,\r\n\t\t\t\t});\r\n\t\t\t}\r\n\r\n\t\t\tconst successData = await getResponseData<TData>(\r\n\t\t\t\tshouldCloneResponse ? response.clone() : response,\r\n\t\t\t\toptions.responseType,\r\n\t\t\t\toptions.responseParser\r\n\t\t\t);\r\n\r\n\t\t\tconst validSuccessData = options.responseValidator\r\n\t\t\t\t? options.responseValidator(successData)\r\n\t\t\t\t: successData;\r\n\r\n\t\t\tawait options.onResponse?.({\r\n\t\t\t\tdata: validSuccessData,\r\n\t\t\t\toptions,\r\n\t\t\t\trequest: requestInit,\r\n\t\t\t\tresponse: options.cloneResponse ? response.clone() : response,\r\n\t\t\t});\r\n\r\n\t\t\treturn resolveSuccessResult<CallApiResult>({ options, response, successData: validSuccessData });\r\n\r\n\t\t\t// == Exhaustive Error handling\r\n\t\t} catch (error) {\r\n\t\t\tconst generalErrorResult = resolveErrorResult<CallApiResult>({\r\n\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\r\n\t\t\t\terror,\r\n\t\t\t});\r\n\r\n\t\t\tconst shouldThrowOnError = isFunction(options.throwOnError)\r\n\t\t\t\t? options.throwOnError((generalErrorResult as { error: never }).error, options)\r\n\t\t\t\t: options.throwOnError;\r\n\r\n\t\t\tif (shouldThrowOnError) {\r\n\t\t\t\tthrow error;\r\n\t\t\t}\r\n\r\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\r\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\r\n\r\n\t\t\t\tconsole.error(`${error.name}:`, message);\r\n\r\n\t\t\t\treturn { ...generalErrorResult, message };\r\n\t\t\t}\r\n\r\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\r\n\t\t\t\tconst { message, name } = error;\r\n\r\n\t\t\t\tconsole.error(`${name}:`, message);\r\n\r\n\t\t\t\treturn generalErrorResult;\r\n\t\t\t}\r\n\r\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\r\n\t\t\t\tconst { errorData, response } = error;\r\n\r\n\t\t\t\tvoid (await Promise.all([\r\n\t\t\t\t\toptions.onResponseError?.({\r\n\t\t\t\t\t\terrorData,\r\n\t\t\t\t\t\toptions,\r\n\t\t\t\t\t\trequest: requestInit,\r\n\t\t\t\t\t\tresponse: options.cloneResponse ? response.clone() : response,\r\n\t\t\t\t\t}),\r\n\r\n\t\t\t\t\t// == Also call the onError interceptor\r\n\t\t\t\t\toptions.onError?.({\r\n\t\t\t\t\t\terror,\r\n\t\t\t\t\t\toptions,\r\n\t\t\t\t\t\trequest: requestInit,\r\n\t\t\t\t\t\tresponse,\r\n\t\t\t\t\t}),\r\n\t\t\t\t]));\r\n\r\n\t\t\t\treturn generalErrorResult;\r\n\t\t\t}\r\n\r\n\t\t\tvoid (await Promise.all([\r\n\t\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\r\n\t\t\t\toptions.onRequestError?.({ error: error as Error, options, request: requestInit }),\r\n\r\n\t\t\t\t// == Also call the onError interceptor\r\n\t\t\t\toptions.onError?.({\r\n\t\t\t\t\terror: (generalErrorResult as { error: never }).error,\r\n\t\t\t\t\toptions,\r\n\t\t\t\t\trequest: requestInit,\r\n\t\t\t\t\tresponse: null,\r\n\t\t\t\t}),\r\n\t\t\t]));\r\n\r\n\t\t\treturn generalErrorResult;\r\n\r\n\t\t\t// == Removing the now unneeded AbortController from store\r\n\t\t} finally {\r\n\t\t\trequestInfoCacheOrNull?.delete(requestKey);\r\n\t\t}\r\n\t};\r\n\r\n\tcallApi.create = createFetchClient;\r\n\r\n\treturn callApi;\r\n};\r\n\r\nexport const callApi = createFetchClient();\r\n"]}
@@ -1 +1 @@
1
- export { H as HTTPError, i as isHTTPError, d as isHTTPErrorInstance, t as toQueryString } from '../index-D5Oy7DgF.js';
1
+ export { H as HTTPError, i as isHTTPError, e as isHTTPErrorInstance, t as toQueryString } from '../index-B9T5fw7z.js';
@@ -1 +1 @@
1
- export{HTTPError,isHTTPError,isHTTPErrorInstance,toQueryString}from"../chunk-I6HJ6GR4.js";//# sourceMappingURL=index.js.map
1
+ export{HTTPError,isHTTPError,isHTTPErrorInstance,toQueryString}from"../chunk-AVPAVK2E.js";//# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zayne-labs/callapi",
3
- "version": "0.8.0",
3
+ "version": "1.0.0-rc-2",
4
4
  "type": "module",
5
5
  "description": "A lightweight wrapper over fetch with quality of life improvements like built-in request cancellation, retries, interceptors and more",
6
6
  "repository": {
@@ -17,49 +17,46 @@
17
17
  "module": "./dist/esm/index.js",
18
18
  "types": "./dist/esm/index.d.ts",
19
19
  "exports": {
20
- ".": {
21
- "import": "./dist/esm/index.js",
22
- "require": "./dist/cjs/index.cjs"
23
- },
24
- "./utils": {
25
- "import": "./dist/esm/utils/index.js",
26
- "require": "./dist/cjs/utils/index.cjs"
27
- }
20
+ ".": "./dist/esm/index.js",
21
+ "./utils": "./dist/esm/utils/index.js",
22
+ "./legacy": "./dist/cjs/index.cjs",
23
+ "./legacy/utils": "./dist/cjs/utils/index.cjs"
28
24
  },
29
25
  "devDependencies": {
30
- "@arethetypeswrong/cli": "0.16.1",
26
+ "@arethetypeswrong/cli": "0.16.2",
31
27
  "@changesets/cli": "2.27.8",
32
28
  "@eslint/js": "9.10.0",
33
- "@size-limit/esbuild-why": "11.1.4",
34
- "@size-limit/preset-small-lib": "11.1.4",
35
- "@stylistic/eslint-plugin": "2.7.2",
29
+ "@size-limit/esbuild-why": "11.1.5",
30
+ "@size-limit/preset-small-lib": "11.1.5",
31
+ "@stylistic/eslint-plugin": "2.8.0",
36
32
  "@total-typescript/ts-reset": "0.6.1",
37
33
  "@types/eslint": "9.6.1",
34
+ "@types/node": "^22.5.5",
38
35
  "@zayne-labs/tsconfig": "0.1.1",
39
- "concurrently": "8.2.2",
36
+ "concurrently": "9.0.1",
37
+ "cross-env": "^7.0.3",
40
38
  "eslint": "9.10.0",
41
39
  "eslint-import-resolver-typescript": "3.6.3",
42
40
  "eslint-plugin-import-x": "4.2.1",
43
- "eslint-plugin-jsdoc": "50.2.2",
44
- "eslint-plugin-perfectionist": "3.5.0",
41
+ "eslint-plugin-jsdoc": "50.2.3",
42
+ "eslint-plugin-perfectionist": "3.6.0",
45
43
  "eslint-plugin-unicorn": "55.0.0",
46
- "eslint-typegen": "0.3.1",
44
+ "eslint-typegen": "0.3.2",
47
45
  "globals": "15.9.0",
48
- "husky": "9.1.5",
46
+ "husky": "9.1.6",
49
47
  "lint-staged": "15.2.10",
50
48
  "pkg-pr-new": "0.0.24",
51
49
  "prettier": "3.3.3",
52
- "publint": "0.2.10",
53
- "size-limit": "11.1.4",
54
- "terser": "5.31.6",
50
+ "publint": "0.2.11",
51
+ "size-limit": "11.1.5",
52
+ "terser": "5.32.0",
55
53
  "tsup": "8.2.4",
56
- "typescript": "5.5.4",
57
- "typescript-eslint": "8.4.0"
54
+ "typescript": "5.6.2",
55
+ "typescript-eslint": "8.6.0"
58
56
  },
59
57
  "publishConfig": {
60
58
  "access": "public",
61
- "registry": "https://registry.npmjs.org/",
62
- "provenance": true
59
+ "registry": "https://registry.npmjs.org/"
63
60
  },
64
61
  "files": [
65
62
  "dist"
@@ -67,7 +64,11 @@
67
64
  "size-limit": [
68
65
  {
69
66
  "path": "./src/index.ts",
70
- "limit": "2 kb"
67
+ "limit": "2.2 kb"
68
+ },
69
+ {
70
+ "path": "./src/utils/index.ts",
71
+ "limit": "400 b"
71
72
  }
72
73
  ],
73
74
  "sideEffects": false,
@@ -84,16 +85,20 @@
84
85
  "node": ">=18.17.x"
85
86
  },
86
87
  "scripts": {
87
- "test:check-types": "tsc --pretty --incremental -p tsconfig.json",
88
- "test:format": "prettier --cache --write .",
89
- "test:lint": "eslint src/**/*.ts --cache --max-warnings 0 --report-unused-disable-directives",
90
- "test:size": "size-limit",
91
- "test:publint": "publint --strict .",
92
- "test:attw": "attw --pack . --exclude-entrypoints utils",
93
- "test:release": "pkg-pr-new publish",
88
+ "build:dev": "cross-env NODE_ENV=development tsup",
89
+ "build:test": "concurrently --prefix-colors \"yellow.bold,#7da4f8.bold,magenta\" --names PUBLINT,TSUP,ATTW 'pnpm:lint:publint' 'pnpm:build:dev' 'pnpm:lint:attw'",
94
90
  "build": "tsup",
95
- "build:dev": "concurrently --prefix-colors \"yellow.bold,#7da4f8.bold,magenta\" --names PUBLINT,TSUP,ATTW 'pnpm:test:publint' \"pnpm build --watch\" 'pnpm:test:attw'",
96
- "version-package": "changeset version",
97
- "release": "changeset publish"
91
+ "inspect:eslint-config": "pnpx @eslint/config-inspector@latest --config eslint.config.js",
92
+ "lint:attw": "attw --pack . --ignore-rules=cjs-resolves-to-esm",
93
+ "lint:check-types": "tsc --pretty --incremental -p tsconfig.json",
94
+ "lint:commitlint": "commitlint --edit",
95
+ "lint:eslint": "eslint src/**/*.ts --cache --max-warnings 0 --report-unused-disable-directives",
96
+ "lint:format": "prettier --cache --write .",
97
+ "lint:packages": "pnpm dedupe --check",
98
+ "lint:publint": "publint --strict .",
99
+ "lint:size": "size-limit",
100
+ "release": "changeset publish",
101
+ "test:release": "pkg-pr-new publish",
102
+ "version-package": "changeset version"
98
103
  }
99
104
  }
@@ -1 +0,0 @@
1
- var r=r=>!("object"!=typeof r||null===r||r instanceof FormData||Array.isArray(r)),e=r=>"string"==typeof r&&r.includes("="),t=r=>"string"==typeof r,o=(r,e)=>`${r} | ${JSON.stringify(e??{})}`,s=r=>r?new URLSearchParams(r).toString():(console.error("toQueryString:","No query params provided!"),null),n=(r,e)=>{if(!e)return r;const t=s(e);return""===t?r:r.endsWith("?")?`${r}${t}`:r.includes("?")?`${r}&${t}`:`${r}?${t}`},a=e=>{return!e||r(e)?e:Object.fromEntries((t=e,Array.isArray(t)?e:e.entries()));var t},i=Object.keys({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"}).map(Number),l=["GET"],c=["body","integrity","method","headers","signal","cache","redirect","window","credentials","keepalive","referrer","priority","mode","referrerPolicy"],u=(r,e)=>{const t=Object.entries(r).filter((([r])=>!e.includes(r)));return Object.fromEntries(t)},f=(r,e)=>{const t=new Set(e),o=Object.entries(r).filter((([r])=>t.has(r)));return Object.fromEntries(o)},y=r=>[f(r,c),u(r,c)],m=(r,e,t)=>{const o=((r,e)=>({arrayBuffer:()=>r.arrayBuffer(),blob:()=>r.blob(),formData:()=>r.formData(),json:async()=>e?e(await r.text()):r.json(),text:()=>r.text()}))(r,t);if(!Object.hasOwn(o,e))throw new Error(`Invalid response type: ${e}`);return o[e]()},p=r=>{const{options:e,response:t,successData:o}=r,s={data:o,error:null,response:t};return e.resultMode&&"all"!==e.resultMode?{onlyError:s.error,onlyResponse:s.response,onlySuccess:s.data}[e.resultMode]:s},d=r=>{const{error:e,options:t}=r;return r=>{const{errorData:o,message:s,response:n}=r??{};if("function"==typeof t.throwOnError?t.throwOnError(e):t.throwOnError)throw e;return{data:null,error:{errorData:o??e,message:s??e?.message??t.defaultErrorMessage,name:e?.name??"UnknownError"},response:n??null}}},E=e=>r(e)&&"HTTPError"===e.name,w=class extends Error{errorData;isHTTPError=!0;name="HTTPError";response;constructor(r,e){const{defaultErrorMessage:t,errorData:o,response:s}=r;super(o.message??t,e),this.errorData=o,this.response=s}},T=e=>e instanceof w||r(e)&&"HTTPError"===e.name&&!0===e.isHTTPError,b=r=>{if(0===r)return;const{promise:e,resolve:t}=Promise.withResolvers();return setTimeout(t,r),e};export{w as HTTPError,i as defaultRetryCodes,l as defaultRetryMethods,o as getRequestKey,d as getResolveErrorResultFn,m as getResponseData,E as isHTTPError,T as isHTTPErrorInstance,r as isObject,e as isQueryString,t as isString,n as mergeUrlWithParams,a as objectifyHeaders,u as omitKeys,p as resolveSuccessResult,y as splitConfig,s as toQueryString,b as waitUntil};//# sourceMappingURL=chunk-I6HJ6GR4.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utils/typeof.ts","../../src/utils/utils.ts"],"names":[],"mappings":";AAEO,IAAM,OAAU,GAAA,CAAS,KAAsC,KAAA,KAAA,CAAM,QAAQ,KAAK,CAAA,CAAA;AAE5E,IAAA,QAAA,GAAW,CAA0C,KAAqC,KAAA;AACtG,EACC,OAAA,OAAO,KAAU,KAAA,QAAA,IAAY,KAAU,KAAA,IAAA,IAAQ,EAAE,KAAA,YAAiB,QAAa,CAAA,IAAA,CAAC,KAAM,CAAA,OAAA,CAAQ,KAAK,CAAA,CAAA;AAErG,EAAA;AAEO,IAAM,UAAa,GAAA,CAAgC,KACzD,KAAA,OAAO,KAAU,KAAA,UAAA,CAAA;AAEL,IAAA,aAAA,GAAgB,CAAC,KAC7B,KAAA,OAAO,UAAU,QAAY,IAAA,KAAA,CAAM,SAAS,GAAG,EAAA;AAEzC,IAAM,QAAW,GAAA,CAAC,KAAmB,KAAA,OAAO,KAAU,KAAA,SAAA;;;ACJtD,IAAM,aAAgB,GAAA,CAA0C,GAAa,EAAA,MAAA,KAAqB,CAAG,EAAA,GAAG,CAAM,GAAA,EAAA,IAAA,CAAK,SAAU,CAAA,MAAA,IAAU,EAAE,CAAC,CAAA,EAAA;AAOpI,IAAA,aAAA,GAAiC,CAAC,MAAW,KAAA;AACzD,EAAA,IAAI,CAAC,MAAQ,EAAA;AACZ,IAAQ,OAAA,CAAA,KAAA,CAAM,kBAAkB,2BAA2B,CAAA,CAAA;AAE3D,IAAO,OAAA,IAAA,CAAA;AAAA,GACR;AAEA,EAAA,OAAO,IAAI,eAAA,CAAgB,MAAgC,CAAA,CAAE,QAAS,EAAA,CAAA;AACvE,EAAA;AAEa,IAAA,kBAAA,GAAqB,CAAC,GAAA,EAAa,KAAyC,KAAA;AACxF,EAAA,IAAI,CAAC,KAAO,EAAA;AACX,IAAO,OAAA,GAAA,CAAA;AAAA,GACR;AAEA,EAAM,MAAA,YAAA,GAAe,cAAc,KAAK,CAAA,CAAA;AAExC,EAAA,IAAI,iBAAiB,EAAI,EAAA;AACxB,IAAO,OAAA,GAAA,CAAA;AAAA,GACR;AAEA,EAAI,IAAA,GAAA,CAAI,QAAS,CAAA,GAAG,CAAG,EAAA;AACtB,IAAO,OAAA,CAAA,EAAG,GAAG,CAAA,EAAG,YAAY,CAAA,CAAA,CAAA;AAAA,GAC7B;AAEA,EAAI,IAAA,GAAA,CAAI,QAAS,CAAA,GAAG,CAAG,EAAA;AACtB,IAAO,OAAA,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA,CAAA;AAAA,GAC9B;AAEA,EAAO,OAAA,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA,CAAA;AAC9B,EAAA;AAEa,IAAA,gBAAA,GAAmB,CAAC,OAAwE,KAAA;AACxG,EAAA,IAAI,CAAC,OAAA,IAAW,QAAS,CAAA,OAAO,CAAG,EAAA;AAClC,IAAO,OAAA,OAAA,CAAA;AAAA,GACR;AAEA,EAAO,OAAA,MAAA,CAAO,YAAY,OAAQ,CAAA,OAAO,IAAI,OAAU,GAAA,OAAA,CAAQ,SAAS,CAAA,CAAA;AACzE,EAAA;AAEA,IAAM,gBAAmB,GAAA;AAAA,EACxB,GAAK,EAAA,iBAAA;AAAA,EACL,GAAK,EAAA,UAAA;AAAA,EACL,GAAK,EAAA,WAAA;AAAA,EACL,GAAK,EAAA,mBAAA;AAAA,EACL,GAAK,EAAA,uBAAA;AAAA,EACL,GAAK,EAAA,aAAA;AAAA,EACL,GAAK,EAAA,qBAAA;AAAA,EACL,GAAK,EAAA,iBAAA;AACN,CAAA,CAAA;AAEO,IAAM,oBACZ,MAAO,CAAA,IAAA,CAAK,gBAAgB,CAAA,CAAE,IAAI,MAAM,EAAA;AAE5B,IAAA,mBAAA,GAA4D,CAAC,KAAK,EAAA;AAExE,IAAM,iBAAoB,GAAA;AAAA,EAChC,MAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA,aAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA,gBAAA;AACD,CAAA,CAAA;AAEa,IAAA,QAAA,GAAW,CAIvB,aAAA,EACA,UACI,KAAA;AACJ,EAAA,MAAM,uBAA0B,GAAA,MAAA,CAAO,OAAQ,CAAA,aAAa,CAAE,CAAA,MAAA;AAAA,IAC7D,CAAC,CAAC,GAAG,MAAM,CAAC,UAAA,CAAW,SAAS,GAAG,CAAA;AAAA,GACpC,CAAA;AAEA,EAAM,MAAA,aAAA,GAAgB,MAAO,CAAA,WAAA,CAAY,uBAAuB,CAAA,CAAA;AAEhE,EAAO,OAAA,aAAA,CAAA;AACR,EAAA;AAEA,IAAM,QAAA,GAAW,CAChB,aAAA,EACA,UACI,KAAA;AACJ,EAAM,MAAA,aAAA,GAAgB,IAAI,GAAA,CAAI,UAAU,CAAA,CAAA;AAExC,EAAM,MAAA,mBAAA,GAAsB,MAAO,CAAA,OAAA,CAAQ,aAAa,CAAA,CAAA;AAExD,EAAM,MAAA,aAAA,GAAgB,mBAAoB,CAAA,MAAA,CAAO,CAAC,CAAC,SAAS,CAAM,KAAA,aAAA,CAAc,GAAI,CAAA,SAAS,CAAC,CAAA,CAAA;AAE9F,EAAM,MAAA,aAAA,GAAgB,MAAO,CAAA,WAAA,CAAY,aAAa,CAAA,CAAA;AAEtD,EAAO,OAAA,aAAA,CAAA;AACR,CAAA,CAAA;AAEa,IAAA,WAAA,GAAc,CAC1B,MAC0F,KAAA;AAAA,EAC1F,QAAA,CAAS,QAAmC,iBAAiB,CAAA;AAAA,EAC7D,QAAA,CAAS,QAAmC,iBAAiB,CAAA;AAC9D,EAAA;AAEO,IAAM,kBAAA,GAAqB,CACjC,QAAA,EACA,MACK,MAAA;AAAA,EACL,WAAA,EAAa,MAAM,QAAA,CAAS,WAAY,EAAA;AAAA,EACxC,IAAA,EAAM,MAAM,QAAA,CAAS,IAAK,EAAA;AAAA,EAC1B,QAAA,EAAU,MAAM,QAAA,CAAS,QAAS,EAAA;AAAA,EAClC,MAAM,YAAY;AACjB,IAAA,IAAI,MAAQ,EAAA;AACX,MAAA,OAAO,MAAO,CAAA,MAAM,QAAS,CAAA,IAAA,EAAM,CAAA,CAAA;AAAA,KACpC;AAEA,IAAA,OAAO,SAAS,IAAK,EAAA,CAAA;AAAA,GACtB;AAAA,EACA,IAAA,EAAM,MAAM,QAAA,CAAS,IAAK,EAAA;AAC3B,CAAA,CAAA,CAAA;AAEO,IAAM,eAAkB,GAAA,CAC9B,QACA,EAAA,YAAA,EACA,MACI,KAAA;AACJ,EAAM,MAAA,oBAAA,GAAuB,kBAA8B,CAAA,QAAA,EAAU,MAAM,CAAA,CAAA;AAE3E,EAAA,IAAI,CAAC,MAAA,CAAO,MAAO,CAAA,oBAAA,EAAsB,YAAY,CAAG,EAAA;AACvD,IAAA,MAAM,IAAI,KAAA,CAAM,CAA0B,uBAAA,EAAA,YAAY,CAAE,CAAA,CAAA,CAAA;AAAA,GACzD;AAEA,EAAO,OAAA,oBAAA,CAAqB,YAAY,CAAE,EAAA,CAAA;AAC3C,EAAA;AAUa,IAAA,oBAAA,GAAuB,CAAgB,IAAqC,KAAA;AACxF,EAAA,MAAM,EAAE,OAAA,EAAS,QAAU,EAAA,WAAA,EAAgB,GAAA,IAAA,CAAA;AAE3C,EAAA,MAAM,UAAa,GAAA;AAAA,IAClB,IAAM,EAAA,WAAA;AAAA,IACN,KAAO,EAAA,IAAA;AAAA,IACP,QAAA;AAAA,GACD,CAAA;AAEA,EAAA,IAAI,CAAC,OAAA,CAAQ,UAAc,IAAA,OAAA,CAAQ,eAAe,KAAO,EAAA;AACxD,IAAO,OAAA,UAAA,CAAA;AAAA,GACR;AAEA,EAAO,OAAA;AAAA,IACN,WAAW,UAAW,CAAA,KAAA;AAAA,IACtB,cAAc,UAAW,CAAA,QAAA;AAAA,IACzB,aAAa,UAAW,CAAA,IAAA;AAAA,GACzB,CAAE,QAAQ,UAAU,CAAA,CAAA;AACrB,EAAA;AAGa,IAAA,uBAAA,GAA0B,CAAgB,QAGjD,KAAA;AACL,EAAM,MAAA,EAAE,KAAO,EAAA,OAAA,EAAY,GAAA,QAAA,CAAA;AAE3B,EAAM,MAAA,kBAAA,GAAqB,CAAa,SAA8D,KAAA;AACrG,IAAA,MAAM,EAAE,SAAW,EAAA,OAAA,EAAS,QAAS,EAAA,GAAI,aAAa,EAAC,CAAA;AAEvD,IAAM,MAAA,kBAAA,GAAqB,WAAW,OAAQ,CAAA,YAAY,IACvD,OAAQ,CAAA,YAAA,CAAa,KAAc,CAAA,GACnC,OAAQ,CAAA,YAAA,CAAA;AAEX,IAAA,IAAI,kBAAoB,EAAA;AACvB,MAAM,MAAA,KAAA,CAAA;AAAA,KACP;AAEA,IAAO,OAAA;AAAA,MACN,IAAM,EAAA,IAAA;AAAA,MACN,KAAO,EAAA;AAAA,QACN,WAAW,SAAa,IAAA,KAAA;AAAA,QACxB,OAAS,EAAA,OAAA,IAAY,KAA+B,EAAA,OAAA,IAAW,OAAQ,CAAA,mBAAA;AAAA,QACvE,IAAA,EAAO,OAA+B,IAAQ,IAAA,cAAA;AAAA,OAC/C;AAAA,MACA,UAAU,QAAY,IAAA,IAAA;AAAA,KACvB,CAAA;AAAA,GACD,CAAA;AAEA,EAAO,OAAA,kBAAA,CAAA;AACR,EAAA;AAEa,IAAA,WAAA,GAAc,CAAa,KAAuD,KAAA;AAC9F,EAAA,OAAO,QAAS,CAAA,KAAK,CAAK,IAAA,KAAA,CAAM,IAAS,KAAA,WAAA,CAAA;AAC1C,EAAA;AAYa,IAAA,SAAA,GAAN,cAAkE,KAAM,CAAA;AAAA,EAC9E,SAAA,CAAA;AAAA,EACA,WAAc,GAAA,IAAA,CAAA;AAAA,EAEL,IAAO,GAAA,WAAA,CAAA;AAAA,EAEhB,QAAA,CAAA;AAAA,EAEA,WAAA,CAAY,cAA4C,YAA6B,EAAA;AACpF,IAAA,MAAM,EAAE,mBAAA,EAAqB,SAAW,EAAA,QAAA,EAAa,GAAA,YAAA,CAAA;AAErD,IAAO,KAAA,CAAA,SAAA,CAAmC,OAAW,IAAA,mBAAA,EAAqB,YAAY,CAAA,CAAA;AAEtF,IAAA,IAAA,CAAK,SAAY,GAAA,SAAA,CAAA;AACjB,IAAA,IAAA,CAAK,QAAW,GAAA,QAAA,CAAA;AAAA,GACjB;AACD,EAAA;AAGa,IAAA,mBAAA,GAAsB,CAClC,KACwC,KAAA;AACxC,EACC,OAAA,KAAA,YAAiB,aAAc,QAAS,CAAA,KAAK,KAAK,KAAM,CAAA,IAAA,KAAS,WAAe,IAAA,KAAA,CAAM,WAAgB,KAAA,IAAA,CAAA;AAExG,EAAA;AAEa,IAAA,SAAA,GAAY,CAAC,KAAkB,KAAA;AAC3C,EAAA,IAAI,UAAU,CAAG,EAAA,OAAA;AAEjB,EAAA,MAAM,EAAE,OAAA,EAAS,OAAQ,EAAA,GAAI,QAAQ,aAAc,EAAA,CAAA;AAEnD,EAAA,UAAA,CAAW,SAAS,KAAK,CAAA,CAAA;AAEzB,EAAO,OAAA,OAAA,CAAA;AACR","file":"chunk-I6HJ6GR4.js","sourcesContent":["import type { AnyFunction } from \"./type-helpers\";\n\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\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 isQueryString = (value: unknown): value is string =>\n\ttypeof value === \"string\" && value.includes(\"=\");\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n","import type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"../types\";\nimport { isArray, isFunction, isObject } from \"./typeof\";\n\n// prettier-ignore\nexport const getRequestKey = <TConfig extends Record<string, unknown>>(url: string, config?: TConfig) => `${url} | ${JSON.stringify(config ?? {})}`;\n\ntype ToQueryStringFn = {\n\t(params: ExtraOptions[\"query\"]): string | null;\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"toQueryString:\", \"No query params provided!\");\n\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, query: ExtraOptions[\"query\"]): string => {\n\tif (!query) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(query);\n\n\tif (paramsString === \"\") {\n\t\treturn url;\n\t}\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\tif (url.includes(\"?\")) {\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\nexport const omitKeys = <\n\tTObject extends Record<string, unknown>,\n\tconst TOmitArray extends Array<keyof TObject>,\n>(\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\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\n\tblob: () => response.blob() as Promise<TResponse>,\n\tformData: () => response.formData() as Promise<TResponse>,\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\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 SuccessInfo = {\n\toptions: ExtraOptions;\n\tresponse: Response;\n\tsuccessData: unknown;\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: SuccessInfo): CallApiResult => {\n\tconst { options, response, successData } = info;\n\n\tconst apiDetails = {\n\t\tdata: successData,\n\t\terror: null,\n\t\tresponse,\n\t};\n\n\tif (!options.resultMode || options.resultMode === \"all\") {\n\t\treturn apiDetails as CallApiResult;\n\t}\n\n\treturn {\n\t\tonlyError: apiDetails.error,\n\t\tonlyResponse: apiDetails.response,\n\t\tonlySuccess: apiDetails.data,\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 getResolveErrorResultFn = <CallApiResult>(initInfo: {\n\terror?: unknown;\n\toptions: ExtraOptions;\n}) => {\n\tconst { error, options } = initInfo;\n\n\tconst resolveErrorResult = <TErrorData>(errorInfo?: Partial<HTTPError<TErrorData>>): CallApiResult => {\n\t\tconst { errorData, message, response } = errorInfo ?? {};\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\terrorData: errorData ?? error,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t\tname: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\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.name === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\tdefaultErrorMessage: string;\n\terrorData: TErrorResponse;\n\tresponse: Response;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\n\tisHTTPError = true;\n\n\toverride name = \"HTTPError\" as const;\n\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, errorData, response } = 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"]}