@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 +60 -17
- package/dist/createFetchClient.cjs +11 -0
- package/dist/createFetchClient.cjs.map +1 -0
- package/dist/createFetchClient.d.cts +1 -1
- package/dist/createFetchClient.d.ts +1 -1
- package/dist/createFetchClient.js +8 -0
- package/dist/createFetchClient.js.map +1 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -0
- package/dist/typeof.cjs.map +1 -0
- package/dist/typeof.js.map +1 -0
- package/dist/{types-D17lSUr9.d.ts → types-BFhLAZSM.d.ts} +5 -1
- package/dist/{types-DfUaOAyg.d.cts → types-CAxhtQRR.d.cts} +5 -1
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.cts +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js.map +1 -0
- package/package.json +4 -5
- package/dist/src/createFetchClient.cjs +0 -11
- package/dist/src/createFetchClient.cjs.map +0 -1
- package/dist/src/createFetchClient.js +0 -8
- package/dist/src/createFetchClient.js.map +0 -1
- package/dist/src/index.cjs.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/dist/src/typeof.cjs.map +0 -1
- package/dist/src/typeof.js.map +0 -1
- package/dist/src/utils.cjs.map +0 -1
- package/dist/src/utils.js.map +0 -1
- /package/dist/{src/index.cjs → index.cjs} +0 -0
- /package/dist/{src/index.js → index.js} +0 -0
- /package/dist/{src/typeof.cjs → typeof.cjs} +0 -0
- /package/dist/{src/typeof.js → typeof.js} +0 -0
- /package/dist/{src/utils.cjs → utils.cjs} +0 -0
- /package/dist/{src/utils.js → utils.js} +0 -0
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
|
|
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
|
|
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()
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
##
|
|
99
|
+
## ✔️ Automatic Cancellation of Redundant Requests (No more race conditions🤩)
|
|
88
100
|
|
|
89
|
-
`CallApi`
|
|
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
|
-
|
|
103
|
+
**How this feature Works in detail**:
|
|
92
104
|
|
|
93
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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":"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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/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-
|
|
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-
|
|
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.
|
|
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/
|
|
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/
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
13
|
},
|
|
14
14
|
"require": {
|
|
15
15
|
"types": "./dist/index.d.cts",
|
|
16
|
-
"default": "./dist/
|
|
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"]}
|
package/dist/src/index.cjs.map
DELETED
|
@@ -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"]}
|
package/dist/src/index.js.map
DELETED
|
@@ -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"]}
|
package/dist/src/typeof.cjs.map
DELETED
|
@@ -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"]}
|
package/dist/src/typeof.js.map
DELETED
|
@@ -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"]}
|
package/dist/src/utils.cjs.map
DELETED
|
@@ -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"]}
|
package/dist/src/utils.js.map
DELETED
|
@@ -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
|