@zayne-labs/callapi 0.2.4 → 0.2.6

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
@@ -32,15 +32,15 @@ To do this, you first need to set your `script`'s type to `module`, then import
32
32
 
33
33
  ```html
34
34
  <script type="module">
35
- import { callApi } from "https://cdn.jsdelivr.net/npm/@zayne-labs/callapi/dist/index.js";
35
+ import { callApi } from "https://cdn.jsdelivr.net/npm/@zayne-labs/callapi/dist/src/index.min.js";
36
36
  </script>
37
37
  ```
38
38
 
39
39
  ## Quick Start
40
40
 
41
- You can use callApi just like a normal `fetch` function. The only difference is you don't have to write a `response.json` or `response.text`, you could just destructure the data and data directly.
41
+ You can use callApi just like a normal `fetch` function. The only difference is you don't have to write a `response.json` or `response.text`, you could just destructure the data and error directly.
42
42
 
43
- This also means that all options for the native fetch function are supported, and you can use the same syntax to send requests.
43
+ This also means that all options for the native fetch options are supported, and you can use the same syntax to send requests.
44
44
 
45
45
  ```js
46
46
  const { data, error } = await callApi("url", fetchOptions);
@@ -56,27 +56,39 @@ To see how to use callApi with typescript for extra autocomplete convenience, vi
56
56
 
57
57
  ## Supported response types
58
58
 
59
- CallApi supports all response types offered by the fetch api like `json`, `text`,`blob` etc, so you don't have to write `response.json()`, `response.text()` or `response.blob()`.
59
+ CallApi supports all response types offered by the fetch api like `json`, `text`,`blob`,`formData` etc, so you don't have to write `response.json()`, `response.text()`, `response.formData()` etc.
60
60
 
61
- It can configure the response type by passing in the `responseType` option and setting it to the appropriate type. By default it's set to `json`.
61
+ You can configure the response type you prefer by passing in the `responseType` option and setting it to the form you want the data from the response to be in. By default it's set to `json`.
62
62
 
63
63
  ```js
64
- const { data, error } = await callApi("url", { responseType: "json" });
64
+ // Json (default)
65
+ const { data } = await callApi("url", { responseType: "json" });
66
+ // Text
67
+ const { data } = await callApi("url", {responseType: "text"});
68
+ // Blob, etc
69
+ const { data } = await callApi("url", {responseType: "blob"});
70
+
71
+
72
+ // Doing this in fetch would imply:
73
+ const response = await fetch("some-url");
74
+
75
+ const data = await response.json(); // Or response.text() or response.blob() etc
65
76
  ```
66
77
 
67
78
  ## Easy error handling when using `async`/`await`
68
79
 
69
- CallApi lets you access all errors, both http errors and javascript errors, in an `error` object. This object contains the `errorName` (eg: TypeError, SyntaxError etx) and the error message as well.
80
+ CallApi lets you access all errors, both http errors and other regular javascript errors, in an `error` object. This object contains the `errorName` (eg: 'TypeError', 'SyntaxError' etc) and the error message as well.
70
81
 
71
- If the error is an http error, the `errorName` property will be set to "HTTPError" and the `error` object will also contain a property `errorData`.
82
+ If the error is an http error, the `errorName` property will be set to "HTTPError" and the `error` object will also have a property `errorData`.
72
83
 
73
- This property contains the error response data coming from the api. If the error is not an http error but some other error, the `errorData` property will be set to `null`.
84
+ This property would contains the error response data coming from the api. If the error is not an http error but some other error, the `errorData` property will be set to `null`.
74
85
 
75
86
  ```js
76
87
  const { data, error } = await callApi("some-url");
77
88
 
78
89
  console.log(error.errorName);
79
90
  console.log(error.message);
91
+ // Will be null if not an http error, else would contain the parsed error response data
80
92
  console.log(error.errorData);
81
93
  ```
82
94
 
@@ -84,15 +96,23 @@ For extra convenience with typescript, visit the [Typescript section](#usage-wit
84
96
 
85
97
  ## Helpful Features
86
98
 
87
- ## Auto cancellation of redundant requests (No more race conditions🤩)
99
+ ## ✔️ Automatic Cancellation of Redundant Requests (No more race conditions🤩)
88
100
 
89
- `CallApi` automatically cancels the previous requests if the same url is called again before the previous request is resolved. This essentially only lets the last request through, hence preventing dreaded race conditions.
101
+ `CallApi` implements an internal request management system to prevent race conditions and ensure that only the most recent request to a given URL is processed.
90
102
 
91
- What this implies is that you can use `callApi` in a `useEffect` hook for instance and it will automatically cancel the previous request if the url is called again before the previous request is resolved 🤩.
103
+ **How this feature Works in detail**:
92
104
 
93
- This behavior can be disabled if you don't like it, by passing in `{ cancelPreviousRequest: false }` to the fetch options.
105
+ - When a new request is made, `callApi` internals first check if there's an ongoing request to the same URL.
106
+ - If a pending request exists, it's automatically cancelled.
107
+ - The new request is then processed, ensuring that only the most recent data is retrieved and handled.
94
108
 
95
- You can also cancel a request to a particular url by passing the url as a parameter to the cancel property attached to callApi.
109
+ **Key takeaways**:
110
+
111
+ - Automatic Cancellation: When multiple requests are made to the same URL in quick succession, `callApi` automatically cancels any pending previous requests, allowing only the latest request to proceed.
112
+ - Race Condition Prevention: This mechanism eliminates race conditions that can occur when rapid, successive API calls are made, such as during fast typing in a search input, button clicks, etc.
113
+ - Ideal for React Hooks: This feature is particularly useful when `callApi` is used within React's useEffect hook or similar scenarios where component updates might trigger multiple API calls.
114
+ - Configurable: If you prefer to handle request management differently, you can disable this feature by setting { cancelRedundantRequests: false } in the fetch options. No pressure 👌.
115
+ - Manual Cancellation: You can manually cancel requests to a specific URL using the cancel method attached to `callApi`. You can also pass an abort controller signal to `callApi` (just like with fetch) as an option and abort the request when you want to.
96
116
 
97
117
  ```js
98
118
  callApi("some-url");
@@ -100,8 +120,6 @@ callApi("some-url");
100
120
  callApi.cancel("some-url");
101
121
  ```
102
122
 
103
- You can also pass a signal to callApi as an option and cancel it when you want to.
104
-
105
123
  ```js
106
124
  const controller = new AbortController();
107
125
 
@@ -179,17 +197,42 @@ callApi("some-url", { body: data });
179
197
 
180
198
  ## ✔️ Authorization header helpers
181
199
 
182
- If you provide callApi with an `auth` property, it will generate an Authorization Header for you.
200
+ If you provide callApi with an `auth` property, it will conveniently generate an Authorization Header for you.
183
201
 
184
202
  If you pass in a `string` (commonly for tokens) , it will generate a Bearer Auth.
185
203
 
204
+ But if you pass in an `object`, you would have two options to chose from:
205
+
206
+ - Use `bearer` option if you want to generate a Bearer Auth Header.
207
+ - Use `token` if you want to generate a Token Auth Header.
208
+
186
209
  ```js
210
+ // Passing a string
187
211
  callApi("some-url", { auth: "token12345" });
188
212
 
189
213
  // The above request can be written in Fetch like this:
190
214
  fetch("some-url", {
191
215
  headers: { Authorization: `Bearer token12345` },
192
216
  });
217
+
218
+ // Passing an object:
219
+
220
+ // For Bearer Auth
221
+ callApi("some-url", { auth: { bearer: "token12345" } });
222
+ // For Token Auth
223
+ callApi("some-url", { auth: { token: "token12345" } });
224
+
225
+ // The above requests can be written in Fetch like this:
226
+
227
+ // For Bearer Auth
228
+ fetch("some-url", {
229
+ headers: { Authorization: `Bearer token12345` },
230
+ });
231
+
232
+ // For Token Auth
233
+ fetch("some-url", {
234
+ headers: { Authorization: `Token token12345` },
235
+ });
193
236
  ```
194
237
 
195
238
  ## ✔️ Creating a callApi Instance
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ var _typeof = require('./typeof');
4
+ var utils = require('./utils');
5
+
6
+ const m=D=>{const l=new Map,[C,A]=utils.splitConfig(D??{}),{headers:p,body:w,signal:M,...q}=C,c=async(o,f)=>{const[B,O]=utils.splitConfig(f??{}),e={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:utils.defaultRetryCodes,retryMethods:utils.defaultRetryMethods,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...A,...O},{signal:x=M,body:s=w,headers:g,...S}=B,h=l.get(o);if(h&&e.cancelRedundantRequests){const t=new DOMException("Cancelled the previous unfinished request","AbortError");h.abort(t);}const u=new AbortController;l.set(o,u);const $=e.timeout?AbortSignal.timeout(e.timeout):null,T=AbortSignal.any([u.signal,$??u.signal,x??u.signal]),a={signal:T,method:"GET",body:_typeof.isObject(s)?e.bodySerializer(s):s,headers:p||g||e.auth||_typeof.isObject(s)?{..._typeof.isObject(s)&&{"Content-Type":"application/json",Accept:"application/json"},..._typeof.isFormData(s)&&{"Content-Type":"multipart/form-data"},..._typeof.isString(s)&&{"Content-Type":"application/x-www-form-urlencoded"},..._typeof.isString(e.auth)&&{Authorization:`Bearer ${e.auth}`},..._typeof.isObject(e.auth)&&{Authorization:"bearer"in e.auth?`Bearer ${e.auth.bearer}`:`Token ${e.auth.token}`},...utils.objectifyHeaders(p),...utils.objectifyHeaders(g)}:void 0,...q,...S};try{await e.onRequest?.({request:a,options:e});const t=await fetch(`${e.baseURL}${utils.mergeUrlWithParams(o,e.query)}`,a);if(!t.ok&&!T.aborted&&e.retries>0&&e.retryCodes.includes(t.status)&&e.retryMethods.includes(a.method))return await utils.waitUntil(e.retryDelay),await c(o,{...f,retries:e.retries-1});if(!t.ok){const F=await utils.getResponseData(t,e.responseType,e.responseParser);throw new utils.HTTPError({response:{...t,errorData:F},defaultErrorMessage:e.defaultErrorMessage})}const r=await utils.getResponseData(t,e.responseType,e.responseParser),i=e.responseValidator?e.responseValidator(r):r;return await e.onResponse?.({response:{...t,data:i},request:a,options:e}),utils.resolveSuccessResult({successData:i,response:t,options:e})}catch(t){const n=utils.$resolveErrorResult({error:t,options:e});if(t instanceof DOMException&&t.name==="TimeoutError"){const r=`Request timed out after ${e.timeout}ms`;return console.info(`%cTimeoutError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),n({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){const r="Request was cancelled";return console.info(`%AbortError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),n({message:r})}if(utils.isHTTPErrorInstance(t)){const{errorData:r,...i}=t.response;return await e.onResponseError?.({response:{...i,errorData:r},request:a,options:e}),n({errorData:r,response:i,message:r?.message})}return await e.onRequestError?.({request:a,error:t,options:e}),n()}finally{l.delete(o);}};return c.create=m,c.cancel=o=>l.get(o)?.abort(),c},V=m();
7
+
8
+ exports.callApi = V;
9
+ exports.createFetchClient = m;
10
+ //# sourceMappingURL=out.js.map
11
+ //# sourceMappingURL=createFetchClient.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/createFetchClient.ts"],"names":["isFormData","isObject","isString","$resolveErrorResult","HTTPError","defaultRetryCodes","defaultRetryMethods","getResponseData","isHTTPErrorInstance","mergeUrlWithParams","objectifyHeaders","resolveSuccessResult","splitConfig","waitUntil","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","options","signal","body","headers","restOfFetchConfig","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","response","errorData","successData","validSuccessData","error","resolveErrorResult","message"],"mappings":"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","sourcesContent":["import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tAbortSignalWithAny,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\t/** Default Options */\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst { signal = baseSignal, body = baseBody, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\"Cancelled the previous unfinished request\", \"AbortError\");\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\t// FIXME - Remove this type cast once TS updates its lib-dom types for AbortSignal to include the any() method\n\t\tconst combinedSignal = (AbortSignal as AbortSignalWithAny).any([\n\t\t\tnewFetchController.signal,\n\t\t\ttimeoutSignal ?? newFetchController.signal,\n\t\t\tsignal ?? newFetchController.signal,\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization:\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestOptions;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait waitUntil(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse,\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse,\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\tresponse: { ...response, data: validSuccessData },\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, response, options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.info(`%cTimeoutError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"TimeoutError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request was cancelled`;\n\n\t\t\t\tconsole.info(`%AbortError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"AbortError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, ...response } = error.response;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"]}
@@ -1,4 +1,4 @@
1
- import { R as ResultModeUnion, B as BaseConfig, F as FetchConfig, G as GetCallApiResult } from './types-DfUaOAyg.cjs';
1
+ import { R as ResultModeUnion, B as BaseConfig, F as FetchConfig, G as GetCallApiResult } from './types-CAxhtQRR.cjs';
2
2
  import './type-helpers-Dibitydy.cjs';
3
3
 
4
4
  declare const createFetchClient: <TBaseData, TBaseErrorData, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
@@ -1,4 +1,4 @@
1
- import { R as ResultModeUnion, B as BaseConfig, F as FetchConfig, G as GetCallApiResult } from './types-D17lSUr9.js';
1
+ import { R as ResultModeUnion, B as BaseConfig, F as FetchConfig, G as GetCallApiResult } from './types-BFhLAZSM.js';
2
2
  import './type-helpers-Dibitydy.js';
3
3
 
4
4
  declare const createFetchClient: <TBaseData, TBaseErrorData, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
@@ -0,0 +1,8 @@
1
+ import { isObject, isFormData, isString } from './typeof';
2
+ import { splitConfig, defaultRetryCodes, defaultRetryMethods, objectifyHeaders, mergeUrlWithParams, waitUntil, getResponseData, HTTPError, resolveSuccessResult, $resolveErrorResult, isHTTPErrorInstance } from './utils';
3
+
4
+ const m=D=>{const l=new Map,[C,A]=splitConfig(D??{}),{headers:p,body:w,signal:M,...q}=C,c=async(o,f)=>{const[B,O]=splitConfig(f??{}),e={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:defaultRetryCodes,retryMethods:defaultRetryMethods,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...A,...O},{signal:x=M,body:s=w,headers:g,...S}=B,h=l.get(o);if(h&&e.cancelRedundantRequests){const t=new DOMException("Cancelled the previous unfinished request","AbortError");h.abort(t);}const u=new AbortController;l.set(o,u);const $=e.timeout?AbortSignal.timeout(e.timeout):null,T=AbortSignal.any([u.signal,$??u.signal,x??u.signal]),a={signal:T,method:"GET",body:isObject(s)?e.bodySerializer(s):s,headers:p||g||e.auth||isObject(s)?{...isObject(s)&&{"Content-Type":"application/json",Accept:"application/json"},...isFormData(s)&&{"Content-Type":"multipart/form-data"},...isString(s)&&{"Content-Type":"application/x-www-form-urlencoded"},...isString(e.auth)&&{Authorization:`Bearer ${e.auth}`},...isObject(e.auth)&&{Authorization:"bearer"in e.auth?`Bearer ${e.auth.bearer}`:`Token ${e.auth.token}`},...objectifyHeaders(p),...objectifyHeaders(g)}:void 0,...q,...S};try{await e.onRequest?.({request:a,options:e});const t=await fetch(`${e.baseURL}${mergeUrlWithParams(o,e.query)}`,a);if(!t.ok&&!T.aborted&&e.retries>0&&e.retryCodes.includes(t.status)&&e.retryMethods.includes(a.method))return await waitUntil(e.retryDelay),await c(o,{...f,retries:e.retries-1});if(!t.ok){const F=await getResponseData(t,e.responseType,e.responseParser);throw new HTTPError({response:{...t,errorData:F},defaultErrorMessage:e.defaultErrorMessage})}const r=await getResponseData(t,e.responseType,e.responseParser),i=e.responseValidator?e.responseValidator(r):r;return await e.onResponse?.({response:{...t,data:i},request:a,options:e}),resolveSuccessResult({successData:i,response:t,options:e})}catch(t){const n=$resolveErrorResult({error:t,options:e});if(t instanceof DOMException&&t.name==="TimeoutError"){const r=`Request timed out after ${e.timeout}ms`;return console.info(`%cTimeoutError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),n({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){const r="Request was cancelled";return console.info(`%AbortError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),n({message:r})}if(isHTTPErrorInstance(t)){const{errorData:r,...i}=t.response;return await e.onResponseError?.({response:{...i,errorData:r},request:a,options:e}),n({errorData:r,response:i,message:r?.message})}return await e.onRequestError?.({request:a,error:t,options:e}),n()}finally{l.delete(o);}};return c.create=m,c.cancel=o=>l.get(o)?.abort(),c},V=m();
5
+
6
+ export { V as callApi, m as createFetchClient };
7
+ //# sourceMappingURL=out.js.map
8
+ //# sourceMappingURL=createFetchClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/createFetchClient.ts"],"names":["isFormData","isObject","isString","$resolveErrorResult","HTTPError","defaultRetryCodes","defaultRetryMethods","getResponseData","isHTTPErrorInstance","mergeUrlWithParams","objectifyHeaders","resolveSuccessResult","splitConfig","waitUntil","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","options","signal","body","headers","restOfFetchConfig","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","response","errorData","successData","validSuccessData","error","resolveErrorResult","message"],"mappings":"AAAA,OAAS,cAAAA,EAAY,YAAAC,EAAU,YAAAC,MAAgB,WAW/C,OACC,uBAAAC,EACA,aAAAC,EACA,qBAAAC,EACA,uBAAAC,EACA,mBAAAC,EACA,uBAAAC,EACA,sBAAAC,EACA,oBAAAC,EACA,wBAAAC,EACA,eAAAC,EACA,aAAAC,MACM,UAEA,MAAMC,EAKZC,GACI,CACJ,MAAMC,EAAuB,IAAI,IAE3B,CAACC,EAAiBC,CAAgB,EAAIN,EAAYG,GAAc,CAAC,CAAC,EAElE,CACL,QAASI,EACT,KAAMC,EACN,OAAQC,EACR,GAAGC,CACJ,EAAIL,EAGEM,EAAU,MAKfC,EACAC,IAC+D,CAG/D,KAAM,CAACC,EAAaC,CAAY,EAAIf,EAAYa,GAAU,CAAC,CAAC,EAGtDG,EAAU,CACf,eAAgB,KAAK,UACrB,aAAc,OACd,QAAS,GACT,QAAS,EACT,WAAY,EACZ,WAAYvB,EACZ,aAAcC,EACd,oBAAqB,oCACrB,wBAAyB,GACzB,GAAGY,EACH,GAAGS,CACJ,EAEM,CAAE,OAAAE,EAASR,EAAY,KAAAS,EAAOV,EAAU,QAAAW,EAAS,GAAGC,CAAkB,EAAIN,EAE1EO,EAAsBjB,EAAqB,IAAIQ,CAAG,EAExD,GAAIS,GAAuBL,EAAQ,wBAAyB,CAC3D,MAAMM,EAAS,IAAI,aAAa,4CAA6C,YAAY,EACzFD,EAAoB,MAAMC,CAAM,CACjC,CAEA,MAAMC,EAAqB,IAAI,gBAE/BnB,EAAqB,IAAIQ,EAAKW,CAAkB,EAEhD,MAAMC,EAAgBR,EAAQ,QAAU,YAAY,QAAQA,EAAQ,OAAO,EAAI,KAGzES,EAAkB,YAAmC,IAAI,CAC9DF,EAAmB,OACnBC,GAAiBD,EAAmB,OACpCN,GAAUM,EAAmB,MAC9B,CAAC,EAEKG,EAAc,CACnB,OAAQD,EAER,OAAQ,MAER,KAAMpC,EAAS6B,CAAI,EAAIF,EAAQ,eAAeE,CAAI,EAAIA,EAMtD,QACCX,GAAeY,GAAWH,EAAQ,MAAQ3B,EAAS6B,CAAI,EACpD,CACA,GAAI7B,EAAS6B,CAAI,GAAK,CACrB,eAAgB,mBAChB,OAAQ,kBACT,EACA,GAAI9B,EAAW8B,CAAI,GAAK,CACvB,eAAgB,qBACjB,EACA,GAAI5B,EAAS4B,CAAI,GAAK,CACrB,eAAgB,mCACjB,EACA,GAAI5B,EAAS0B,EAAQ,IAAI,GAAK,CAC7B,cAAe,UAAUA,EAAQ,IAAI,EACtC,EACA,GAAI3B,EAAS2B,EAAQ,IAAI,GAAK,CAC7B,cACC,WAAYA,EAAQ,KACjB,UAAUA,EAAQ,KAAK,MAAM,GAC7B,SAASA,EAAQ,KAAK,KAAK,EAChC,EACA,GAAGlB,EAAiBS,CAAW,EAC/B,GAAGT,EAAiBqB,CAAO,CAC5B,EACC,OAEJ,GAAGT,EACH,GAAGU,CACJ,EAEA,GAAI,CACH,MAAMJ,EAAQ,YAAY,CAAE,QAASU,EAAa,QAAAV,CAAQ,CAAC,EAE3D,MAAMW,EAAW,MAAM,MACtB,GAAGX,EAAQ,OAAO,GAAGnB,EAAmBe,EAAKI,EAAQ,KAAK,CAAC,GAC3DU,CACD,EASA,GANC,CAACC,EAAS,IACV,CAACF,EAAe,SAChBT,EAAQ,QAAU,GAClBA,EAAQ,WAAW,SAASW,EAAS,MAAM,GAC3CX,EAAQ,aAAa,SAASU,EAAY,MAAM,EAGhD,aAAMzB,EAAUe,EAAQ,UAAU,EAE3B,MAAML,EAAQC,EAAK,CAAE,GAAGC,EAAQ,QAASG,EAAQ,QAAU,CAAE,CAAC,EAGtE,GAAI,CAACW,EAAS,GAAI,CACjB,MAAMC,EAAY,MAAMjC,EACvBgC,EACAX,EAAQ,aACRA,EAAQ,cACT,EAGA,MAAM,IAAIxB,EAAU,CACnB,SAAU,CAAE,GAAGmC,EAAU,UAAAC,CAAU,EACnC,oBAAqBZ,EAAQ,mBAC9B,CAAC,CACF,CAEA,MAAMa,EAAc,MAAMlC,EACzBgC,EACAX,EAAQ,aACRA,EAAQ,cACT,EAEMc,EAAmBd,EAAQ,kBAC9BA,EAAQ,kBAAkBa,CAAW,EACrCA,EAEH,aAAMb,EAAQ,aAAa,CAC1B,SAAU,CAAE,GAAGW,EAAU,KAAMG,CAAiB,EAChD,QAASJ,EACT,QAAAV,CACD,CAAC,EAEMjB,EAAoC,CAAE,YAAa+B,EAAkB,SAAAH,EAAU,QAAAX,CAAQ,CAAC,CAGhG,OAASe,EAAO,CACf,MAAMC,EAAqBzC,EAAmC,CAAE,MAAAwC,EAAO,QAAAf,CAAQ,CAAC,EAEhF,GAAIe,aAAiB,cAAgBA,EAAM,OAAS,eAAgB,CACnE,MAAME,EAAU,2BAA2BjB,EAAQ,OAAO,KAE1D,eAAQ,KAAK,mBAAmBiB,CAAO,GAAI,gDAAgD,EAC3F,QAAQ,MAAM,cAAc,EAErBD,EAAmB,CAAE,QAAAC,CAAQ,CAAC,CACtC,CAEA,GAAIF,aAAiB,cAAgBA,EAAM,OAAS,aAAc,CACjE,MAAME,EAAU,wBAEhB,eAAQ,KAAK,gBAAgBA,CAAO,GAAI,gDAAgD,EACxF,QAAQ,MAAM,YAAY,EAEnBD,EAAmB,CAAE,QAAAC,CAAQ,CAAC,CACtC,CAEA,GAAIrC,EAAgCmC,CAAK,EAAG,CAC3C,KAAM,CAAE,UAAAH,EAAW,GAAGD,CAAS,EAAII,EAAM,SAEzC,aAAMf,EAAQ,kBAAkB,CAC/B,SAAU,CAAE,GAAGW,EAAU,UAAAC,CAAU,EACnC,QAASF,EACT,QAAAV,CACD,CAAC,EAEMgB,EAAmB,CACzB,UAAAJ,EACA,SAAAD,EACA,QAAUC,GAAmC,OAC9C,CAAC,CACF,CAGA,aAAMZ,EAAQ,iBAAiB,CAAE,QAASU,EAAa,MAAOK,EAAgB,QAAAf,CAAQ,CAAC,EAEhFgB,EAAmB,CAG3B,QAAE,CACD5B,EAAqB,OAAOQ,CAAG,CAChC,CACD,EAEA,OAAAD,EAAQ,OAAST,EAEjBS,EAAQ,OAAUC,GAAgBR,EAAqB,IAAIQ,CAAG,GAAG,MAAM,EAEhED,CACR,EAEaA,EAAUT,EAAkB","sourcesContent":["import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tAbortSignalWithAny,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\t/** Default Options */\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst { signal = baseSignal, body = baseBody, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\"Cancelled the previous unfinished request\", \"AbortError\");\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\t// FIXME - Remove this type cast once TS updates its lib-dom types for AbortSignal to include the any() method\n\t\tconst combinedSignal = (AbortSignal as AbortSignalWithAny).any([\n\t\t\tnewFetchController.signal,\n\t\t\ttimeoutSignal ?? newFetchController.signal,\n\t\t\tsignal ?? newFetchController.signal,\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization:\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestOptions;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait waitUntil(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse,\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse,\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\tresponse: { ...response, data: validSuccessData },\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, response, options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.info(`%cTimeoutError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"TimeoutError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request was cancelled`;\n\n\t\t\t\tconsole.info(`%AbortError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"AbortError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, ...response } = error.response;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["callApi","createFetchClient","HTTPError","isHTTPError","isHTTPErrorInstance","toQueryString"],"mappings":"AAAA,OAAS,WAAAA,EAAS,qBAAAC,MAAyB,sBAU3C,OAAS,aAAAC,EAAW,eAAAC,EAAa,uBAAAC,EAAqB,iBAAAC,MAAqB","sourcesContent":["export { callApi, createFetchClient } from \"./createFetchClient\";\n\nexport type {\n\tFetchConfig,\n\t$RequestOptions,\n\tExtraOptions,\n\tResponseContext,\n\tResponseErrorContext,\n} from \"./types\";\n\nexport { HTTPError, isHTTPError, isHTTPErrorInstance, toQueryString } from \"./utils\";\n"]}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { callApi, createFetchClient } from './createFetchClient.cjs';
2
- export { $ as $RequestOptions, E as ExtraOptions, F as FetchConfig, H as HTTPError, a as ResponseContext, b as ResponseErrorContext, i as isHTTPError, c as isHTTPErrorInstance, t as toQueryString } from './types-DfUaOAyg.cjs';
2
+ export { $ as $RequestOptions, E as ExtraOptions, F as FetchConfig, H as HTTPError, a as ResponseContext, b as ResponseErrorContext, i as isHTTPError, c as isHTTPErrorInstance, t as toQueryString } from './types-CAxhtQRR.cjs';
3
3
  import './type-helpers-Dibitydy.cjs';
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { callApi, createFetchClient } from './createFetchClient.js';
2
- export { $ as $RequestOptions, E as ExtraOptions, F as FetchConfig, H as HTTPError, a as ResponseContext, b as ResponseErrorContext, i as isHTTPError, c as isHTTPErrorInstance, t as toQueryString } from './types-D17lSUr9.js';
2
+ export { $ as $RequestOptions, E as ExtraOptions, F as FetchConfig, H as HTTPError, a as ResponseContext, b as ResponseErrorContext, i as isHTTPError, c as isHTTPErrorInstance, t as toQueryString } from './types-BFhLAZSM.js';
3
3
  import './type-helpers-Dibitydy.js';
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["callApi","createFetchClient","HTTPError","isHTTPError","isHTTPErrorInstance","toQueryString"],"mappings":"AAAA,OAAS,WAAAA,EAAS,qBAAAC,MAAyB,sBAU3C,OAAS,aAAAC,EAAW,eAAAC,EAAa,uBAAAC,EAAqB,iBAAAC,MAAqB","sourcesContent":["export { callApi, createFetchClient } from \"./createFetchClient\";\n\nexport type {\n\tFetchConfig,\n\t$RequestOptions,\n\tExtraOptions,\n\tResponseContext,\n\tResponseErrorContext,\n} from \"./types\";\n\nexport { HTTPError, isHTTPError, isHTTPErrorInstance, toQueryString } from \"./utils\";\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/typeof.ts"],"names":["isArray","value","isFormData","isObject","isFunction","isString"],"mappings":"AAEO,MAAMA,EAAmBC,GAAsC,MAAM,QAAQA,CAAK,EAE5EC,EAAcD,GAAmBA,aAAiB,SAElDE,EAAqDF,GAC1D,OAAOA,GAAU,UAAYA,IAAU,MAAQ,CAACC,EAAWD,CAAK,GAAK,CAAC,MAAM,QAAQA,CAAK,EAGpFG,EAA6CH,GACzD,OAAOA,GAAU,WAELI,EAAYJ,GAAmB,OAAOA,GAAU","sourcesContent":["import type { AnyFunction } from \"./type-helpers\";\n\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\n\nexport const isFormData = (value: unknown) => value instanceof FormData;\n\nexport const isObject = <TObject extends Record<string, unknown>>(value: unknown): value is TObject => {\n\treturn typeof value === \"object\" && value !== null && !isFormData(value) && !Array.isArray(value);\n};\n\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\n\ttypeof value === \"function\";\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/typeof.ts"],"names":["isArray","value","isFormData","isObject","isFunction","isString"],"mappings":"AAEO,MAAMA,EAAmBC,GAAsC,MAAM,QAAQA,CAAK,EAE5EC,EAAcD,GAAmBA,aAAiB,SAElDE,EAAqDF,GAC1D,OAAOA,GAAU,UAAYA,IAAU,MAAQ,CAACC,EAAWD,CAAK,GAAK,CAAC,MAAM,QAAQA,CAAK,EAGpFG,EAA6CH,GACzD,OAAOA,GAAU,WAELI,EAAYJ,GAAmB,OAAOA,GAAU","sourcesContent":["import type { AnyFunction } from \"./type-helpers\";\n\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\n\nexport const isFormData = (value: unknown) => value instanceof FormData;\n\nexport const isObject = <TObject extends Record<string, unknown>>(value: unknown): value is TObject => {\n\treturn typeof value === \"object\" && value !== null && !isFormData(value) && !Array.isArray(value);\n};\n\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\n\ttypeof value === \"function\";\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n"]}
@@ -73,7 +73,11 @@ type ExtraOptions<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode
73
73
  /**
74
74
  * @description Authorization header value.
75
75
  */
76
- auth?: string;
76
+ auth?: string | {
77
+ bearer: string;
78
+ } | {
79
+ token: string;
80
+ };
77
81
  /**
78
82
  * @description Custom function to validate the response data.
79
83
  */
@@ -73,7 +73,11 @@ type ExtraOptions<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode
73
73
  /**
74
74
  * @description Authorization header value.
75
75
  */
76
- auth?: string;
76
+ auth?: string | {
77
+ bearer: string;
78
+ } | {
79
+ token: string;
80
+ };
77
81
  /**
78
82
  * @description Custom function to validate the response data.
79
83
  */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts"],"names":["isArray","isFunction","isObject","toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve"],"mappings":"AAAA,OAAS,WAAAA,EAAS,cAAAC,EAAY,YAAAC,MAAgB,WAgBvC,MAAMC,EAAkCC,GACzCA,EAKE,IAAI,gBAAgBA,CAAgC,EAAE,SAAS,GAJrE,QAAQ,MAAM,0BAA0B,EACjC,MAMIC,EAAqB,CAACC,EAAaF,IAA0C,CACzF,GAAI,CAACA,EACJ,OAAOE,EAGR,MAAMC,EAAeJ,EAAcC,CAAM,EAEzC,OAAIE,EAAI,SAAS,GAAG,GAAK,CAACA,EAAI,SAAS,GAAG,EAClC,GAAGA,CAAG,IAAIC,CAAY,GAG1BD,EAAI,SAAS,GAAG,EACZ,GAAGA,CAAG,GAAGC,CAAY,GAGtB,GAAGD,CAAG,IAAIC,CAAY,EAC9B,EAEaC,EAAoBC,GAC5B,CAACA,GAAWP,EAASO,CAAO,EACxBA,EAGD,OAAO,YAAYT,EAAQS,CAAO,EAAIA,EAAUA,EAAQ,QAAQ,CAAC,EAGnEC,EAAmB,CACxB,IAAK,kBACL,IAAK,WACL,IAAK,YACL,IAAK,oBACL,IAAK,wBACL,IAAK,cACL,IAAK,sBACL,IAAK,iBACN,EAEaC,EACZ,OAAO,KAAKD,CAAgB,EAAE,IAAI,MAAM,EAE5BE,EAA4D,CAAC,KAAK,EAElEC,EAAoB,CAChC,OACA,YACA,SACA,UACA,SACA,QACA,WACA,SACA,cACA,YACA,WACA,WACA,OACA,gBACD,EAEMC,EAAW,CAChBC,EACAC,IACI,CACJ,MAAMC,EAA0B,OAAO,QAAQF,CAAa,EAAE,OAC7D,CAAC,CAACG,CAAG,IAAM,CAACF,EAAW,SAASE,CAAG,CACpC,EAIA,OAFsB,OAAO,YAAYD,CAAuB,CAGjE,EAEME,EAAW,CAChBJ,EACAK,IACI,CACJ,MAAMC,EAAgB,IAAI,IAAID,CAAU,EAIlCE,EAFsB,OAAO,QAAQP,CAAa,EAEd,OAAO,CAAC,CAACQ,CAAS,IAAMF,EAAc,IAAIE,CAAS,CAAC,EAI9F,OAFsB,OAAO,YAAYD,CAAa,CAGvD,EAEaE,EACZC,GAC0F,CAC1FN,EAASM,EAAmCZ,CAAiB,EAC7DC,EAASW,EAAmCZ,CAAiB,CAC9D,EAEaa,EAAqB,CACjCC,EACAC,KACK,CACL,KAAM,SACDA,EACIA,EAAO,MAAMD,EAAS,KAAK,CAAC,EAG7BA,EAAS,KAAK,EAEtB,YAAa,IAAMA,EAAS,YAAY,EACxC,KAAM,IAAMA,EAAS,KAAK,EAC1B,SAAU,IAAMA,EAAS,SAAS,EAClC,KAAM,IAAMA,EAAS,KAAK,CAC3B,GAEaE,EAAkB,CAC9BF,EACAG,EACAF,IACI,CACJ,MAAMG,EAAuBL,EAA8BC,EAAUC,CAAM,EAE3E,GAAI,CAAC,OAAO,OAAOG,EAAsBD,CAAY,EACpD,MAAM,IAAI,MAAM,0BAA0BA,CAAY,EAAE,EAGzD,OAAOC,EAAqBD,CAAY,EAAE,CAC3C,EAUaE,EAAuCC,GAA8B,CACjF,KAAM,CAAE,QAAAC,EAAS,SAAAP,EAAU,YAAAQ,CAAY,EAAIF,EAErCG,EAAa,CAClB,KAAMD,EACN,UAAW,KACX,SAAAR,CACD,EAEA,OAAIO,EAAQ,aAAe,QAAaA,EAAQ,aAAe,MACvDE,EAGD,CACN,YAAaA,EAAW,KACxB,UAAWA,EAAW,UACtB,aAAcA,EAAW,QAC1B,EAAEF,EAAQ,UAAU,CACrB,EAGaG,EAAsCC,GAAsD,CACxG,KAAM,CAAE,MAAAC,EAAO,QAAAL,CAAQ,EAAII,EA8B3B,MAtB2B,CAACL,EAAkB,CAAC,IAAqB,CACnE,KAAM,CAAE,UAAAO,EAAW,QAAAC,EAAS,SAAAd,CAAS,EAAIM,EAMzC,GAJ2BhC,EAAWiC,EAAQ,YAAY,EACvDA,EAAQ,aAAaK,CAAc,EACnCL,EAAQ,aAGV,MAAMK,EAGP,MAAO,CACN,KAAM,KACN,MAAO,CACN,UAAYA,GAA+B,MAAQ,eACnD,UAAWC,GAAa,KACxB,QAASC,GAAYF,GAA+B,SAAWL,EAAQ,mBACxE,EACA,SAAUP,GAAY,IACvB,CACD,CAGD,EAEae,EAA2BH,GAChCrC,EAASqC,CAAK,GAAKA,EAAM,YAAc,YAYxC,MAAMI,UAA4D,KAAM,CAC9E,SAES,KAAO,YAEhB,YAAc,GAEd,YAAYC,EAA4CC,EAA6B,CACpF,KAAM,CAAE,oBAAAC,EAAqB,SAAAnB,CAAS,EAAIiB,EAE1C,MAAOjB,EAAS,UAAmC,SAAWmB,EAAqBD,CAAY,EAE/F,KAAK,SAAWlB,CACjB,CACD,CAGO,MAAMoB,EACZR,GAGCA,aAAiBI,GAAczC,EAASqC,CAAK,GAAKA,EAAM,OAAS,aAAeA,EAAM,cAAgB,GAI3FS,EAAaC,GAAkB,CAC3C,GAAIA,IAAU,EAAG,OAEjB,KAAM,CAAE,QAAAC,EAAS,QAAAC,CAAQ,EAAI,QAAQ,cAAc,EAEnD,kBAAWA,EAASF,CAAK,EAElBC,CACR","sourcesContent":["import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n\t(params: ExtraOptions[\"query\"]): string | null;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"No query params provided\");\n\t\treturn null as never;\n\t}\n\n\treturn new URLSearchParams(params as Record<string, string>).toString();\n};\n\nexport const mergeUrlWithParams = (url: string, params: ExtraOptions[\"query\"]): string => {\n\tif (!params) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(params);\n\n\tif (url.includes(\"?\") && !url.endsWith(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\nconst omitKeys = <TObject extends Record<string, unknown>, const TOmitArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToOmit: TOmitArray\n) => {\n\tconst arrayFromFilteredObject = Object.entries(initialObject).filter(\n\t\t([key]) => !keysToOmit.includes(key)\n\t);\n\n\tconst updatedObject = Object.fromEntries(arrayFromFilteredObject);\n\n\treturn updatedObject as Omit<TObject, keyof TOmitArray>;\n};\n\nconst pickKeys = <TObject extends Record<string, unknown>, const TPickArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToPick: TPickArray\n) => {\n\tconst keysToPickSet = new Set(keysToPick);\n\n\tconst arrayFromInitObject = Object.entries(initialObject);\n\n\tconst filteredArray = arrayFromInitObject.filter(([objectKey]) => keysToPickSet.has(objectKey));\n\n\tconst updatedObject = Object.fromEntries(filteredArray);\n\n\treturn updatedObject as Pick<TObject, TPickArray[number]>;\n};\n\nexport const splitConfig = <TObject extends object>(\n\tconfig: TObject\n): [\"body\" extends keyof TObject ? $RequestOptions : $BaseRequestOptions, ExtraOptions] => [\n\tpickKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n\tomitKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n];\n\nexport const handleResponseType = <TResponse>(\n\tresponse: Response,\n\tparser?: Required<ExtraOptions>[\"responseParser\"]\n) => ({\n\tjson: async () => {\n\t\tif (parser) {\n\t\t\treturn parser(await response.text());\n\t\t}\n\n\t\treturn response.json() as Promise<TResponse>;\n\t},\n\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\n\tblob: () => response.blob() as Promise<TResponse>,\n\tformData: () => response.formData() as Promise<TResponse>,\n\ttext: () => response.text() as Promise<TResponse>,\n});\n\nexport const getResponseData = <TResponse>(\n\tresponse: Response,\n\tresponseType: keyof ReturnType<typeof handleResponseType>,\n\tparser: ExtraOptions[\"responseParser\"]\n) => {\n\tconst RESPONSE_TYPE_LOOKUP = handleResponseType<TResponse>(response, parser);\n\n\tif (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {\n\t\tthrow new Error(`Invalid response type: ${responseType}`);\n\t}\n\n\treturn RESPONSE_TYPE_LOOKUP[responseType]();\n};\n\ntype data = {\n\tsuccessData: unknown;\n\toptions: ExtraOptions;\n\tresponse: Response;\n};\n\n// == The CallApiResult type is used to cast all return statements due to a design limitation in ts.\n// LINK - See https://www.zhenghao.io/posts/type-functions for more info\nexport const resolveSuccessResult = <CallApiResult>(info: data): CallApiResult => {\n\tconst { options, response, successData } = info;\n\n\tconst apiDetails = {\n\t\tdata: successData,\n\t\terrorInfo: null,\n\t\tresponse,\n\t};\n\n\tif (options.resultMode === undefined || options.resultMode === \"all\") {\n\t\treturn apiDetails as CallApiResult;\n\t}\n\n\treturn {\n\t\tonlySuccess: apiDetails.data,\n\t\tonlyError: apiDetails.errorInfo,\n\t\tonlyResponse: apiDetails.response,\n\t}[options.resultMode] as CallApiResult;\n};\n\n// == Using curring here so error and options are not required to be passed every time, instead to be captured once by way of closure\nexport const $resolveErrorResult = <CallApiResult>($info: { error?: unknown; options: ExtraOptions }) => {\n\tconst { error, options } = $info;\n\n\ttype ErrorInfo = {\n\t\tresponse?: Response;\n\t\terrorData?: unknown;\n\t\tmessage?: string;\n\t};\n\n\tconst resolveErrorResult = (info: ErrorInfo = {}): CallApiResult => {\n\t\tconst { errorData, message, response } = info;\n\n\t\tconst shouldThrowOnError = isFunction(options.throwOnError)\n\t\t\t? options.throwOnError(error as Error)\n\t\t\t: options.throwOnError;\n\n\t\tif (shouldThrowOnError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn {\n\t\t\tdata: null,\n\t\t\terror: {\n\t\t\t\terrorName: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? null,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.errorName === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\tresponse: Response & { errorData: TErrorResponse };\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response } = errorDetails;\n\n\t\tsuper((response.errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n"]}
package/dist/utils.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- export { j as $resolveErrorResult, H as HTTPError, d as defaultRetryCodes, e as defaultRetryMethods, f as fetchSpecificKeys, g as getResponseData, h as handleResponseType, i as isHTTPError, c as isHTTPErrorInstance, m as mergeUrlWithParams, o as objectifyHeaders, r as resolveSuccessResult, s as splitConfig, t as toQueryString, w as waitUntil } from './types-DfUaOAyg.cjs';
1
+ export { j as $resolveErrorResult, H as HTTPError, d as defaultRetryCodes, e as defaultRetryMethods, f as fetchSpecificKeys, g as getResponseData, h as handleResponseType, i as isHTTPError, c as isHTTPErrorInstance, m as mergeUrlWithParams, o as objectifyHeaders, r as resolveSuccessResult, s as splitConfig, t as toQueryString, w as waitUntil } from './types-CAxhtQRR.cjs';
2
2
  import './type-helpers-Dibitydy.cjs';
package/dist/utils.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { j as $resolveErrorResult, H as HTTPError, d as defaultRetryCodes, e as defaultRetryMethods, f as fetchSpecificKeys, g as getResponseData, h as handleResponseType, i as isHTTPError, c as isHTTPErrorInstance, m as mergeUrlWithParams, o as objectifyHeaders, r as resolveSuccessResult, s as splitConfig, t as toQueryString, w as waitUntil } from './types-D17lSUr9.js';
1
+ export { j as $resolveErrorResult, H as HTTPError, d as defaultRetryCodes, e as defaultRetryMethods, f as fetchSpecificKeys, g as getResponseData, h as handleResponseType, i as isHTTPError, c as isHTTPErrorInstance, m as mergeUrlWithParams, o as objectifyHeaders, r as resolveSuccessResult, s as splitConfig, t as toQueryString, w as waitUntil } from './types-BFhLAZSM.js';
2
2
  import './type-helpers-Dibitydy.js';
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts"],"names":["isArray","isFunction","isObject","toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve"],"mappings":"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","sourcesContent":["import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n\t(params: ExtraOptions[\"query\"]): string | null;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"No query params provided\");\n\t\treturn null as never;\n\t}\n\n\treturn new URLSearchParams(params as Record<string, string>).toString();\n};\n\nexport const mergeUrlWithParams = (url: string, params: ExtraOptions[\"query\"]): string => {\n\tif (!params) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(params);\n\n\tif (url.includes(\"?\") && !url.endsWith(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\nconst omitKeys = <TObject extends Record<string, unknown>, const TOmitArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToOmit: TOmitArray\n) => {\n\tconst arrayFromFilteredObject = Object.entries(initialObject).filter(\n\t\t([key]) => !keysToOmit.includes(key)\n\t);\n\n\tconst updatedObject = Object.fromEntries(arrayFromFilteredObject);\n\n\treturn updatedObject as Omit<TObject, keyof TOmitArray>;\n};\n\nconst pickKeys = <TObject extends Record<string, unknown>, const TPickArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToPick: TPickArray\n) => {\n\tconst keysToPickSet = new Set(keysToPick);\n\n\tconst arrayFromInitObject = Object.entries(initialObject);\n\n\tconst filteredArray = arrayFromInitObject.filter(([objectKey]) => keysToPickSet.has(objectKey));\n\n\tconst updatedObject = Object.fromEntries(filteredArray);\n\n\treturn updatedObject as Pick<TObject, TPickArray[number]>;\n};\n\nexport const splitConfig = <TObject extends object>(\n\tconfig: TObject\n): [\"body\" extends keyof TObject ? $RequestOptions : $BaseRequestOptions, ExtraOptions] => [\n\tpickKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n\tomitKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n];\n\nexport const handleResponseType = <TResponse>(\n\tresponse: Response,\n\tparser?: Required<ExtraOptions>[\"responseParser\"]\n) => ({\n\tjson: async () => {\n\t\tif (parser) {\n\t\t\treturn parser(await response.text());\n\t\t}\n\n\t\treturn response.json() as Promise<TResponse>;\n\t},\n\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\n\tblob: () => response.blob() as Promise<TResponse>,\n\tformData: () => response.formData() as Promise<TResponse>,\n\ttext: () => response.text() as Promise<TResponse>,\n});\n\nexport const getResponseData = <TResponse>(\n\tresponse: Response,\n\tresponseType: keyof ReturnType<typeof handleResponseType>,\n\tparser: ExtraOptions[\"responseParser\"]\n) => {\n\tconst RESPONSE_TYPE_LOOKUP = handleResponseType<TResponse>(response, parser);\n\n\tif (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {\n\t\tthrow new Error(`Invalid response type: ${responseType}`);\n\t}\n\n\treturn RESPONSE_TYPE_LOOKUP[responseType]();\n};\n\ntype data = {\n\tsuccessData: unknown;\n\toptions: ExtraOptions;\n\tresponse: Response;\n};\n\n// == The CallApiResult type is used to cast all return statements due to a design limitation in ts.\n// LINK - See https://www.zhenghao.io/posts/type-functions for more info\nexport const resolveSuccessResult = <CallApiResult>(info: data): CallApiResult => {\n\tconst { options, response, successData } = info;\n\n\tconst apiDetails = {\n\t\tdata: successData,\n\t\terrorInfo: null,\n\t\tresponse,\n\t};\n\n\tif (options.resultMode === undefined || options.resultMode === \"all\") {\n\t\treturn apiDetails as CallApiResult;\n\t}\n\n\treturn {\n\t\tonlySuccess: apiDetails.data,\n\t\tonlyError: apiDetails.errorInfo,\n\t\tonlyResponse: apiDetails.response,\n\t}[options.resultMode] as CallApiResult;\n};\n\n// == Using curring here so error and options are not required to be passed every time, instead to be captured once by way of closure\nexport const $resolveErrorResult = <CallApiResult>($info: { error?: unknown; options: ExtraOptions }) => {\n\tconst { error, options } = $info;\n\n\ttype ErrorInfo = {\n\t\tresponse?: Response;\n\t\terrorData?: unknown;\n\t\tmessage?: string;\n\t};\n\n\tconst resolveErrorResult = (info: ErrorInfo = {}): CallApiResult => {\n\t\tconst { errorData, message, response } = info;\n\n\t\tconst shouldThrowOnError = isFunction(options.throwOnError)\n\t\t\t? options.throwOnError(error as Error)\n\t\t\t: options.throwOnError;\n\n\t\tif (shouldThrowOnError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn {\n\t\t\tdata: null,\n\t\t\terror: {\n\t\t\t\terrorName: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? null,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.errorName === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\tresponse: Response & { errorData: TErrorResponse };\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response } = errorDetails;\n\n\t\tsuper((response.errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n"]}
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@zayne-labs/callapi",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
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
- "main": "./dist/src/index.js",
6
+ "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
10
  "import": {
11
11
  "types": "./dist/index.d.ts",
12
- "default": "./dist/src/index.js"
12
+ "default": "./dist/index.js"
13
13
  },
14
14
  "require": {
15
15
  "types": "./dist/index.d.cts",
16
- "default": "./dist/src/index.cjs"
16
+ "default": "./dist/index.cjs"
17
17
  }
18
18
  }
19
19
  },
@@ -44,7 +44,6 @@
44
44
  "@changesets/cli": "^2.27.5",
45
45
  "@eslint/js": "^9.5.0",
46
46
  "@zayne-labs/tsconfig": "^0.0.7",
47
- "changelogen": "^0.5.5",
48
47
  "eslint": "^9.5.0",
49
48
  "eslint-plugin-import-x": "^0.5.1",
50
49
  "eslint-plugin-jsdoc": "^48.2.12",
@@ -1,11 +0,0 @@
1
- 'use strict';
2
-
3
- var _typeof = require('./typeof');
4
- var utils = require('./utils');
5
-
6
- const b=m=>{const l=new Map,[D,C]=utils.splitConfig(m??{}),{headers:p,body:w,signal:A,...M}=D,c=async(s,f)=>{const[q,B]=utils.splitConfig(f??{}),e={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:utils.defaultRetryCodes,retryMethods:utils.defaultRetryMethods,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...C,...B},{signal:O=A,body:o=w,headers:g,...x}=q,R=l.get(s);if(R&&e.cancelRedundantRequests){const t=new DOMException("Cancelled the previous unfinished request","AbortError");R.abort(t);}const u=new AbortController;l.set(s,u);const S=e.timeout?AbortSignal.timeout(e.timeout):null,T=AbortSignal.any([u.signal,S??u.signal,O??u.signal]),a={signal:T,method:"GET",body:_typeof.isObject(o)?e.bodySerializer(o):o,headers:p||g||e.auth||_typeof.isObject(o)?{..._typeof.isObject(o)&&{"Content-Type":"application/json",Accept:"application/json"},..._typeof.isFormData(o)&&{"Content-Type":"multipart/form-data"},..._typeof.isString(o)&&{"Content-Type":"application/x-www-form-urlencoded"},...!!e.auth&&{Authorization:`Bearer ${e.auth}`},...utils.objectifyHeaders(p),...utils.objectifyHeaders(g)}:void 0,...M,...x};try{await e.onRequest?.({request:a,options:e});const t=await fetch(`${e.baseURL}${utils.mergeUrlWithParams(s,e.query)}`,a);if(!t.ok&&!T.aborted&&e.retries>0&&e.retryCodes.includes(t.status)&&e.retryMethods.includes(a.method))return await utils.waitUntil(e.retryDelay),await c(s,{...f,retries:e.retries-1});if(!t.ok){const F=await utils.getResponseData(t,e.responseType,e.responseParser);throw new utils.HTTPError({response:{...t,errorData:F},defaultErrorMessage:e.defaultErrorMessage})}const r=await utils.getResponseData(t,e.responseType,e.responseParser),i=e.responseValidator?e.responseValidator(r):r;return await e.onResponse?.({response:{...t,data:i},request:a,options:e}),utils.resolveSuccessResult({successData:i,response:t,options:e})}catch(t){const n=utils.$resolveErrorResult({error:t,options:e});if(t instanceof DOMException&&t.name==="TimeoutError"){const r=`Request timed out after ${e.timeout}ms`;return console.info(`%cTimeoutError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),n({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){const r="Request was cancelled";return console.info(`%AbortError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),n({message:r})}if(utils.isHTTPErrorInstance(t)){const{errorData:r,...i}=t.response;return await e.onResponseError?.({response:{...i,errorData:r},request:a,options:e}),n({errorData:r,response:i,message:r?.message})}return await e.onRequestError?.({request:a,error:t,options:e}),n()}finally{l.delete(s);}};return c.create=b,c.cancel=s=>l.get(s)?.abort(),c},V=b();
7
-
8
- exports.callApi = V;
9
- exports.createFetchClient = b;
10
- //# sourceMappingURL=out.js.map
11
- //# sourceMappingURL=createFetchClient.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/createFetchClient.ts"],"names":["isFormData","isObject","isString","$resolveErrorResult","HTTPError","defaultRetryCodes","defaultRetryMethods","getResponseData","isHTTPErrorInstance","mergeUrlWithParams","objectifyHeaders","resolveSuccessResult","splitConfig","waitUntil","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","options","signal","body","headers","restOfFetchConfig","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","response","errorData","successData","validSuccessData","error","resolveErrorResult","message"],"mappings":"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","sourcesContent":["import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tAbortSignalWithAny,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\t/** Default Options */\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst { signal = baseSignal, body = baseBody, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\"Cancelled the previous unfinished request\", \"AbortError\");\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\t// FIXME - Remove this type cast once TS updates its lib-dom types for AbortSignal to include the any() method\n\t\tconst combinedSignal = (AbortSignal as AbortSignalWithAny).any([\n\t\t\tnewFetchController.signal,\n\t\t\ttimeoutSignal ?? newFetchController.signal,\n\t\t\tsignal ?? newFetchController.signal,\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(Boolean(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestOptions;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait waitUntil(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse,\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse,\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\tresponse: { ...response, data: validSuccessData },\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, response, options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.info(`%cTimeoutError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"TimeoutError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request was cancelled`;\n\n\t\t\t\tconsole.info(`%AbortError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"AbortError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, ...response } = error.response;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"]}
@@ -1,8 +0,0 @@
1
- import { isObject, isFormData, isString } from './typeof';
2
- import { splitConfig, defaultRetryCodes, defaultRetryMethods, objectifyHeaders, mergeUrlWithParams, waitUntil, getResponseData, HTTPError, resolveSuccessResult, $resolveErrorResult, isHTTPErrorInstance } from './utils';
3
-
4
- const b=m=>{const l=new Map,[D,C]=splitConfig(m??{}),{headers:p,body:w,signal:A,...M}=D,c=async(s,f)=>{const[q,B]=splitConfig(f??{}),e={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:defaultRetryCodes,retryMethods:defaultRetryMethods,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...C,...B},{signal:O=A,body:o=w,headers:g,...x}=q,R=l.get(s);if(R&&e.cancelRedundantRequests){const t=new DOMException("Cancelled the previous unfinished request","AbortError");R.abort(t);}const u=new AbortController;l.set(s,u);const S=e.timeout?AbortSignal.timeout(e.timeout):null,T=AbortSignal.any([u.signal,S??u.signal,O??u.signal]),a={signal:T,method:"GET",body:isObject(o)?e.bodySerializer(o):o,headers:p||g||e.auth||isObject(o)?{...isObject(o)&&{"Content-Type":"application/json",Accept:"application/json"},...isFormData(o)&&{"Content-Type":"multipart/form-data"},...isString(o)&&{"Content-Type":"application/x-www-form-urlencoded"},...!!e.auth&&{Authorization:`Bearer ${e.auth}`},...objectifyHeaders(p),...objectifyHeaders(g)}:void 0,...M,...x};try{await e.onRequest?.({request:a,options:e});const t=await fetch(`${e.baseURL}${mergeUrlWithParams(s,e.query)}`,a);if(!t.ok&&!T.aborted&&e.retries>0&&e.retryCodes.includes(t.status)&&e.retryMethods.includes(a.method))return await waitUntil(e.retryDelay),await c(s,{...f,retries:e.retries-1});if(!t.ok){const F=await getResponseData(t,e.responseType,e.responseParser);throw new HTTPError({response:{...t,errorData:F},defaultErrorMessage:e.defaultErrorMessage})}const r=await getResponseData(t,e.responseType,e.responseParser),i=e.responseValidator?e.responseValidator(r):r;return await e.onResponse?.({response:{...t,data:i},request:a,options:e}),resolveSuccessResult({successData:i,response:t,options:e})}catch(t){const n=$resolveErrorResult({error:t,options:e});if(t instanceof DOMException&&t.name==="TimeoutError"){const r=`Request timed out after ${e.timeout}ms`;return console.info(`%cTimeoutError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("TimeoutError"),n({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){const r="Request was cancelled";return console.info(`%AbortError: ${r}`,"color: red; font-weight: 500; font-size: 14px;"),console.trace("AbortError"),n({message:r})}if(isHTTPErrorInstance(t)){const{errorData:r,...i}=t.response;return await e.onResponseError?.({response:{...i,errorData:r},request:a,options:e}),n({errorData:r,response:i,message:r?.message})}return await e.onRequestError?.({request:a,error:t,options:e}),n()}finally{l.delete(s);}};return c.create=b,c.cancel=s=>l.get(s)?.abort(),c},V=b();
5
-
6
- export { V as callApi, b as createFetchClient };
7
- //# sourceMappingURL=out.js.map
8
- //# sourceMappingURL=createFetchClient.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/createFetchClient.ts"],"names":["isFormData","isObject","isString","$resolveErrorResult","HTTPError","defaultRetryCodes","defaultRetryMethods","getResponseData","isHTTPErrorInstance","mergeUrlWithParams","objectifyHeaders","resolveSuccessResult","splitConfig","waitUntil","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","options","signal","body","headers","restOfFetchConfig","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","response","errorData","successData","validSuccessData","error","resolveErrorResult","message"],"mappings":"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","sourcesContent":["import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tAbortSignalWithAny,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\t/** Default Options */\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst { signal = baseSignal, body = baseBody, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\"Cancelled the previous unfinished request\", \"AbortError\");\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\t// FIXME - Remove this type cast once TS updates its lib-dom types for AbortSignal to include the any() method\n\t\tconst combinedSignal = (AbortSignal as AbortSignalWithAny).any([\n\t\t\tnewFetchController.signal,\n\t\t\ttimeoutSignal ?? newFetchController.signal,\n\t\t\tsignal ?? newFetchController.signal,\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(Boolean(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestOptions;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait waitUntil(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse,\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse,\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\tresponse: { ...response, data: validSuccessData },\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, response, options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.info(`%cTimeoutError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"TimeoutError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request was cancelled`;\n\n\t\t\t\tconsole.info(`%AbortError: ${message}`, \"color: red; font-weight: 500; font-size: 14px;\");\n\t\t\t\tconsole.trace(\"AbortError\");\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, ...response } = error.response;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: { ...response, errorData },\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/index.ts"],"names":["callApi","createFetchClient","HTTPError","isHTTPError","isHTTPErrorInstance","toQueryString"],"mappings":"AAAA,OAAS,WAAAA,EAAS,qBAAAC,MAAyB,sBAU3C,OAAS,aAAAC,EAAW,eAAAC,EAAa,uBAAAC,EAAqB,iBAAAC,MAAqB","sourcesContent":["export { callApi, createFetchClient } from \"./createFetchClient\";\n\nexport type {\n\tFetchConfig,\n\t$RequestOptions,\n\tExtraOptions,\n\tResponseContext,\n\tResponseErrorContext,\n} from \"./types\";\n\nexport { HTTPError, isHTTPError, isHTTPErrorInstance, toQueryString } from \"./utils\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/index.ts"],"names":["callApi","createFetchClient","HTTPError","isHTTPError","isHTTPErrorInstance","toQueryString"],"mappings":"AAAA,OAAS,WAAAA,EAAS,qBAAAC,MAAyB,sBAU3C,OAAS,aAAAC,EAAW,eAAAC,EAAa,uBAAAC,EAAqB,iBAAAC,MAAqB","sourcesContent":["export { callApi, createFetchClient } from \"./createFetchClient\";\n\nexport type {\n\tFetchConfig,\n\t$RequestOptions,\n\tExtraOptions,\n\tResponseContext,\n\tResponseErrorContext,\n} from \"./types\";\n\nexport { HTTPError, isHTTPError, isHTTPErrorInstance, toQueryString } from \"./utils\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/typeof.ts"],"names":["isArray","value","isFormData","isObject","isFunction","isString"],"mappings":"AAEO,MAAMA,EAAmBC,GAAsC,MAAM,QAAQA,CAAK,EAE5EC,EAAcD,GAAmBA,aAAiB,SAElDE,EAAqDF,GAC1D,OAAOA,GAAU,UAAYA,IAAU,MAAQ,CAACC,EAAWD,CAAK,GAAK,CAAC,MAAM,QAAQA,CAAK,EAGpFG,EAA6CH,GACzD,OAAOA,GAAU,WAELI,EAAYJ,GAAmB,OAAOA,GAAU","sourcesContent":["import type { AnyFunction } from \"./type-helpers\";\n\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\n\nexport const isFormData = (value: unknown) => value instanceof FormData;\n\nexport const isObject = <TObject extends Record<string, unknown>>(value: unknown): value is TObject => {\n\treturn typeof value === \"object\" && value !== null && !isFormData(value) && !Array.isArray(value);\n};\n\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\n\ttypeof value === \"function\";\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/typeof.ts"],"names":["isArray","value","isFormData","isObject","isFunction","isString"],"mappings":"AAEO,MAAMA,EAAmBC,GAAsC,MAAM,QAAQA,CAAK,EAE5EC,EAAcD,GAAmBA,aAAiB,SAElDE,EAAqDF,GAC1D,OAAOA,GAAU,UAAYA,IAAU,MAAQ,CAACC,EAAWD,CAAK,GAAK,CAAC,MAAM,QAAQA,CAAK,EAGpFG,EAA6CH,GACzD,OAAOA,GAAU,WAELI,EAAYJ,GAAmB,OAAOA,GAAU","sourcesContent":["import type { AnyFunction } from \"./type-helpers\";\n\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\n\nexport const isFormData = (value: unknown) => value instanceof FormData;\n\nexport const isObject = <TObject extends Record<string, unknown>>(value: unknown): value is TObject => {\n\treturn typeof value === \"object\" && value !== null && !isFormData(value) && !Array.isArray(value);\n};\n\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\n\ttypeof value === \"function\";\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utils.ts"],"names":["isArray","isFunction","isObject","toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve"],"mappings":"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","sourcesContent":["import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n\t(params: ExtraOptions[\"query\"]): string | null;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"No query params provided\");\n\t\treturn null as never;\n\t}\n\n\treturn new URLSearchParams(params as Record<string, string>).toString();\n};\n\nexport const mergeUrlWithParams = (url: string, params: ExtraOptions[\"query\"]): string => {\n\tif (!params) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(params);\n\n\tif (url.includes(\"?\") && !url.endsWith(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\nconst omitKeys = <TObject extends Record<string, unknown>, const TOmitArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToOmit: TOmitArray\n) => {\n\tconst arrayFromFilteredObject = Object.entries(initialObject).filter(\n\t\t([key]) => !keysToOmit.includes(key)\n\t);\n\n\tconst updatedObject = Object.fromEntries(arrayFromFilteredObject);\n\n\treturn updatedObject as Omit<TObject, keyof TOmitArray>;\n};\n\nconst pickKeys = <TObject extends Record<string, unknown>, const TPickArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToPick: TPickArray\n) => {\n\tconst keysToPickSet = new Set(keysToPick);\n\n\tconst arrayFromInitObject = Object.entries(initialObject);\n\n\tconst filteredArray = arrayFromInitObject.filter(([objectKey]) => keysToPickSet.has(objectKey));\n\n\tconst updatedObject = Object.fromEntries(filteredArray);\n\n\treturn updatedObject as Pick<TObject, TPickArray[number]>;\n};\n\nexport const splitConfig = <TObject extends object>(\n\tconfig: TObject\n): [\"body\" extends keyof TObject ? $RequestOptions : $BaseRequestOptions, ExtraOptions] => [\n\tpickKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n\tomitKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n];\n\nexport const handleResponseType = <TResponse>(\n\tresponse: Response,\n\tparser?: Required<ExtraOptions>[\"responseParser\"]\n) => ({\n\tjson: async () => {\n\t\tif (parser) {\n\t\t\treturn parser(await response.text());\n\t\t}\n\n\t\treturn response.json() as Promise<TResponse>;\n\t},\n\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\n\tblob: () => response.blob() as Promise<TResponse>,\n\tformData: () => response.formData() as Promise<TResponse>,\n\ttext: () => response.text() as Promise<TResponse>,\n});\n\nexport const getResponseData = <TResponse>(\n\tresponse: Response,\n\tresponseType: keyof ReturnType<typeof handleResponseType>,\n\tparser: ExtraOptions[\"responseParser\"]\n) => {\n\tconst RESPONSE_TYPE_LOOKUP = handleResponseType<TResponse>(response, parser);\n\n\tif (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {\n\t\tthrow new Error(`Invalid response type: ${responseType}`);\n\t}\n\n\treturn RESPONSE_TYPE_LOOKUP[responseType]();\n};\n\ntype data = {\n\tsuccessData: unknown;\n\toptions: ExtraOptions;\n\tresponse: Response;\n};\n\n// == The CallApiResult type is used to cast all return statements due to a design limitation in ts.\n// LINK - See https://www.zhenghao.io/posts/type-functions for more info\nexport const resolveSuccessResult = <CallApiResult>(info: data): CallApiResult => {\n\tconst { options, response, successData } = info;\n\n\tconst apiDetails = {\n\t\tdata: successData,\n\t\terrorInfo: null,\n\t\tresponse,\n\t};\n\n\tif (options.resultMode === undefined || options.resultMode === \"all\") {\n\t\treturn apiDetails as CallApiResult;\n\t}\n\n\treturn {\n\t\tonlySuccess: apiDetails.data,\n\t\tonlyError: apiDetails.errorInfo,\n\t\tonlyResponse: apiDetails.response,\n\t}[options.resultMode] as CallApiResult;\n};\n\n// == Using curring here so error and options are not required to be passed every time, instead to be captured once by way of closure\nexport const $resolveErrorResult = <CallApiResult>($info: { error?: unknown; options: ExtraOptions }) => {\n\tconst { error, options } = $info;\n\n\ttype ErrorInfo = {\n\t\tresponse?: Response;\n\t\terrorData?: unknown;\n\t\tmessage?: string;\n\t};\n\n\tconst resolveErrorResult = (info: ErrorInfo = {}): CallApiResult => {\n\t\tconst { errorData, message, response } = info;\n\n\t\tconst shouldThrowOnError = isFunction(options.throwOnError)\n\t\t\t? options.throwOnError(error as Error)\n\t\t\t: options.throwOnError;\n\n\t\tif (shouldThrowOnError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn {\n\t\t\tdata: null,\n\t\t\terror: {\n\t\t\t\terrorName: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? null,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.errorName === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\tresponse: Response & { errorData: TErrorResponse };\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response } = errorDetails;\n\n\t\tsuper((response.errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utils.ts"],"names":["isArray","isFunction","isObject","toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve"],"mappings":"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","sourcesContent":["import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n\t(params: ExtraOptions[\"query\"]): string | null;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"No query params provided\");\n\t\treturn null as never;\n\t}\n\n\treturn new URLSearchParams(params as Record<string, string>).toString();\n};\n\nexport const mergeUrlWithParams = (url: string, params: ExtraOptions[\"query\"]): string => {\n\tif (!params) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(params);\n\n\tif (url.includes(\"?\") && !url.endsWith(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\nconst omitKeys = <TObject extends Record<string, unknown>, const TOmitArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToOmit: TOmitArray\n) => {\n\tconst arrayFromFilteredObject = Object.entries(initialObject).filter(\n\t\t([key]) => !keysToOmit.includes(key)\n\t);\n\n\tconst updatedObject = Object.fromEntries(arrayFromFilteredObject);\n\n\treturn updatedObject as Omit<TObject, keyof TOmitArray>;\n};\n\nconst pickKeys = <TObject extends Record<string, unknown>, const TPickArray extends Array<keyof TObject>>(\n\tinitialObject: TObject,\n\tkeysToPick: TPickArray\n) => {\n\tconst keysToPickSet = new Set(keysToPick);\n\n\tconst arrayFromInitObject = Object.entries(initialObject);\n\n\tconst filteredArray = arrayFromInitObject.filter(([objectKey]) => keysToPickSet.has(objectKey));\n\n\tconst updatedObject = Object.fromEntries(filteredArray);\n\n\treturn updatedObject as Pick<TObject, TPickArray[number]>;\n};\n\nexport const splitConfig = <TObject extends object>(\n\tconfig: TObject\n): [\"body\" extends keyof TObject ? $RequestOptions : $BaseRequestOptions, ExtraOptions] => [\n\tpickKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n\tomitKeys(config as Record<string, unknown>, fetchSpecificKeys) as never,\n];\n\nexport const handleResponseType = <TResponse>(\n\tresponse: Response,\n\tparser?: Required<ExtraOptions>[\"responseParser\"]\n) => ({\n\tjson: async () => {\n\t\tif (parser) {\n\t\t\treturn parser(await response.text());\n\t\t}\n\n\t\treturn response.json() as Promise<TResponse>;\n\t},\n\tarrayBuffer: () => response.arrayBuffer() as Promise<TResponse>,\n\tblob: () => response.blob() as Promise<TResponse>,\n\tformData: () => response.formData() as Promise<TResponse>,\n\ttext: () => response.text() as Promise<TResponse>,\n});\n\nexport const getResponseData = <TResponse>(\n\tresponse: Response,\n\tresponseType: keyof ReturnType<typeof handleResponseType>,\n\tparser: ExtraOptions[\"responseParser\"]\n) => {\n\tconst RESPONSE_TYPE_LOOKUP = handleResponseType<TResponse>(response, parser);\n\n\tif (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {\n\t\tthrow new Error(`Invalid response type: ${responseType}`);\n\t}\n\n\treturn RESPONSE_TYPE_LOOKUP[responseType]();\n};\n\ntype data = {\n\tsuccessData: unknown;\n\toptions: ExtraOptions;\n\tresponse: Response;\n};\n\n// == The CallApiResult type is used to cast all return statements due to a design limitation in ts.\n// LINK - See https://www.zhenghao.io/posts/type-functions for more info\nexport const resolveSuccessResult = <CallApiResult>(info: data): CallApiResult => {\n\tconst { options, response, successData } = info;\n\n\tconst apiDetails = {\n\t\tdata: successData,\n\t\terrorInfo: null,\n\t\tresponse,\n\t};\n\n\tif (options.resultMode === undefined || options.resultMode === \"all\") {\n\t\treturn apiDetails as CallApiResult;\n\t}\n\n\treturn {\n\t\tonlySuccess: apiDetails.data,\n\t\tonlyError: apiDetails.errorInfo,\n\t\tonlyResponse: apiDetails.response,\n\t}[options.resultMode] as CallApiResult;\n};\n\n// == Using curring here so error and options are not required to be passed every time, instead to be captured once by way of closure\nexport const $resolveErrorResult = <CallApiResult>($info: { error?: unknown; options: ExtraOptions }) => {\n\tconst { error, options } = $info;\n\n\ttype ErrorInfo = {\n\t\tresponse?: Response;\n\t\terrorData?: unknown;\n\t\tmessage?: string;\n\t};\n\n\tconst resolveErrorResult = (info: ErrorInfo = {}): CallApiResult => {\n\t\tconst { errorData, message, response } = info;\n\n\t\tconst shouldThrowOnError = isFunction(options.throwOnError)\n\t\t\t? options.throwOnError(error as Error)\n\t\t\t: options.throwOnError;\n\n\t\tif (shouldThrowOnError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn {\n\t\t\tdata: null,\n\t\t\terror: {\n\t\t\t\terrorName: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? null,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.errorName === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\tresponse: Response & { errorData: TErrorResponse };\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response } = errorDetails;\n\n\t\tsuper((response.errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n"]}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes