@zayne-labs/callapi 0.4.4 → 0.4.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 +10 -9
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +14 -14
- package/dist/esm/chunk-LP7FW6GY.js +8 -0
- package/dist/esm/chunk-LP7FW6GY.js.map +1 -0
- package/dist/esm/{chunk-QCF5J2OT.js → chunk-YRSDB5GD.js} +1 -1
- package/dist/esm/{chunk-QCF5J2OT.js.map → chunk-YRSDB5GD.js.map} +1 -1
- package/dist/esm/createFetchClient.d.ts +4 -4
- package/dist/esm/createFetchClient.js +2 -2
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/{types-Bjfr6g7f.d.ts → types-BeDSv1wB.d.ts} +13 -12
- package/dist/esm/utils.d.ts +1 -1
- package/dist/esm/utils.js +1 -1
- package/package.json +4 -4
- package/dist/esm/chunk-UFIAMS26.js +0 -8
- package/dist/esm/chunk-UFIAMS26.js.map +0 -1
package/README.md
CHANGED
|
@@ -45,8 +45,6 @@ To do this, you first need to set your `script`'s type to `module`, then import
|
|
|
45
45
|
</script>
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
48
|
## Quick Start
|
|
51
49
|
|
|
52
50
|
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.
|
|
@@ -138,11 +136,11 @@ For extra convenience with typescript, visit the [Typescript section](#usage-wit
|
|
|
138
136
|
|
|
139
137
|
**Key takeaways**:
|
|
140
138
|
|
|
141
|
-
- Automatic Cancellation
|
|
142
|
-
- Race Condition Prevention
|
|
143
|
-
- Ideal for React Hooks
|
|
144
|
-
- Configurable
|
|
145
|
-
- Manual Cancellation
|
|
139
|
+
- *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.
|
|
140
|
+
- *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.
|
|
141
|
+
- *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.
|
|
142
|
+
- *Configurable*: If you prefer to handle request management differently, you can disable this feature by setting `{ cancelRedundantRequests: false }` in the fetch options. No pressure 👌.
|
|
143
|
+
- *Manual Cancellation*: You can manually cancel ongoing 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.
|
|
146
144
|
|
|
147
145
|
Using `callApi.cancel`:
|
|
148
146
|
|
|
@@ -150,6 +148,9 @@ Using `callApi.cancel`:
|
|
|
150
148
|
callApi("some-url");
|
|
151
149
|
|
|
152
150
|
callApi.cancel("some-url");
|
|
151
|
+
|
|
152
|
+
// With reason
|
|
153
|
+
callApi.cancel("some-url", "Cancelled due to some reason")
|
|
153
154
|
```
|
|
154
155
|
|
|
155
156
|
Using `AbortController`:
|
|
@@ -189,7 +190,7 @@ Data types eligible for this automatic setting include:
|
|
|
189
190
|
- Query Strings
|
|
190
191
|
- FormData
|
|
191
192
|
|
|
192
|
-
**Object/JSON Handling**: If you pass in an object, `CallApi` will set `Content-Type` to `application/json
|
|
193
|
+
**Object/JSON Handling**: If you pass in an object, `CallApi` will set `Content-Type` to `application/json` for you. It will also handle `JSON.stringify` process for you (although you can always pass in a custom stringifier/serializer if you want), so you don't have to do it yourself.
|
|
193
194
|
|
|
194
195
|
```js
|
|
195
196
|
callApi.post("some-url", {
|
|
@@ -204,7 +205,7 @@ fetch("some-url", {
|
|
|
204
205
|
});
|
|
205
206
|
```
|
|
206
207
|
|
|
207
|
-
**Query String Handling**: If you pass in a string, `CallApi` will
|
|
208
|
+
**Query String Handling**: If you pass in a string, `CallApi` will ensure the `Content-Type` is set to `application/x-www-form-urlencoded` for you.
|
|
208
209
|
|
|
209
210
|
`CallApi` also includes a `toQueryString` method that helps you convert objects to query strings, for extra convenience.
|
|
210
211
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var h=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Z=(e,r)=>{for(var s in r)h(e,s,{get:r[s],enumerable:!0})},ee=(e,r,s,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of Y(r))!X.call(e,n)&&n!==s&&h(e,n,{get:()=>r[n],enumerable:!(a=J(r,n))||a.enumerable});return e};var re=e=>ee(h({},"__esModule",{value:!0}),e);var ae={};Z(ae,{HTTPError:()=>d,callApi:()=>
|
|
1
|
+
"use strict";var h=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Z=(e,r)=>{for(var s in r)h(e,s,{get:r[s],enumerable:!0})},ee=(e,r,s,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of Y(r))!X.call(e,n)&&n!==s&&h(e,n,{get:()=>r[n],enumerable:!(a=J(r,n))||a.enumerable});return e};var re=e=>ee(h({},"__esModule",{value:!0}),e);var ae={};Z(ae,{HTTPError:()=>d,callApi:()=>N,createFetchClient:()=>g,isHTTPError:()=>z,isHTTPErrorInstance:()=>b,toQueryString:()=>w});module.exports=re(ae);var S=e=>Array.isArray(e),$=e=>e instanceof FormData,u=e=>typeof e=="object"&&e!==null&&!(e instanceof FormData)&&!Array.isArray(e),v=e=>typeof e=="function",x=e=>typeof e=="string";var w=e=>e?new URLSearchParams(e).toString():(console.error("No query params provided"),null),H=(e,r)=>{if(!r)return e;let s=w(r);return e.endsWith("?")?`${e}${s}`:e.includes("?")?`${e}&${s}`:`${e}?${s}`},D=e=>!e||u(e)?e:Object.fromEntries(S(e)?e:e.entries()),te={408:"Request Timeout",409:"Conflict",425:"Too Early",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},I=Object.keys(te).map(Number),U=["GET"],B=["body","integrity","method","headers","signal","cache","redirect","window","credentials","keepalive","referrer","priority","mode","referrerPolicy"],se=(e,r)=>{let s=Object.entries(e).filter(([n])=>!r.includes(n));return Object.fromEntries(s)},oe=(e,r)=>{let s=new Set(r),n=Object.entries(e).filter(([T])=>s.has(T));return Object.fromEntries(n)},A=e=>[oe(e,B),se(e,B)],ne=(e,r)=>({json:async()=>r?r(await e.text()):e.json(),arrayBuffer:()=>e.arrayBuffer(),blob:()=>e.blob(),formData:()=>e.formData(),text:()=>e.text()}),C=(e,r,s)=>{let a=ne(e,s);if(!Object.hasOwn(a,r))throw new Error(`Invalid response type: ${r}`);return a[r]()},G=e=>{let{options:r,response:s,successData:a}=e,n={data:a,errorInfo:null,response:s};return r.resultMode===void 0||r.resultMode==="all"?n:{onlySuccess:n.data,onlyError:n.errorInfo,onlyResponse:n.response}[r.resultMode]},L=e=>{let{error:r,options:s}=e;return(n={})=>{let{errorData:l,message:T,response:O}=n;if(v(s.throwOnError)?s.throwOnError(r):s.throwOnError)throw r;return{data:null,error:{name:r?.name??"UnknownError",errorData:l??r,message:T??r?.message??s.defaultErrorMessage},response:O??null}}},z=e=>u(e)&&e.name==="HTTPError",d=class extends Error{response;errorData;name="HTTPError";isHTTPError=!0;constructor(r,s){let{defaultErrorMessage:a,response:n,errorData:l}=r;super(l.message??a,s),this.errorData=l,this.response=n}},b=e=>e instanceof d||u(e)&&e.name==="HTTPError"&&e.isHTTPError===!0,K=e=>{if(e===0)return;let{promise:r,resolve:s}=Promise.withResolvers();return setTimeout(s,e),r};var g=e=>{let r=new Map,[s,a]=A(e??{}),{headers:n,body:l,signal:T,...O}=s,f=async(c,R)=>{let[Q,V]=A(R??{}),{signal:j=T,body:p=l,headers:P,...W}=Q,t={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:I,retryMethods:U,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...a,...V},k=r.get(c);if(k&&t.cancelRedundantRequests){let o=new DOMException(`Automatic cancelation of the previous unfinished request to this same url: ${c}`,"AbortError");k.abort(o)}let q=new AbortController;r.set(c,q);let F=t.timeout?AbortSignal.timeout(t.timeout):null,M=AbortSignal.any([q.signal,...F?[F]:[],...j?[j]:[]]),y={signal:M,method:"GET",body:u(p)?t.bodySerializer(p):p,headers:n||P||t.auth||u(p)?{...u(p)&&{"Content-Type":"application/json",Accept:"application/json"},...$(p)&&{"Content-Type":"multipart/form-data"},...x(p)&&{"Content-Type":"application/x-www-form-urlencoded"},...x(t.auth)&&{Authorization:`Bearer ${t.auth}`},...u(t.auth)&&{Authorization:"bearer"in t.auth?`Bearer ${t.auth.bearer}`:`Token ${t.auth.token}`},...D(n),...D(P)}:void 0,...O,...W};try{await t.onRequest?.({request:y,options:t});let o=await fetch(`${t.baseURL}${H(c,t.query)}`,y);if(!o.ok&&!M.aborted&&t.retries>0&&t.retryCodes.includes(o.status)&&t.retryMethods.includes(y.method))return await K(t.retryDelay),await f(c,{...R,retries:t.retries-1});if(!o.ok){let _=await C(o.clone(),t.responseType,t.responseParser);throw new d({errorData:_,response:o,defaultErrorMessage:t.defaultErrorMessage})}let i=await C(o.clone(),t.responseType,t.responseParser),m=t.responseValidator?t.responseValidator(i):i;return await t.onResponse?.({response:Object.assign(o.clone(),{data:m}),request:y,options:t}),G({successData:m,response:o,options:t})}catch(o){let E=L({error:o,options:t});if(o instanceof DOMException&&o.name==="TimeoutError"){let i=`Request timed out after ${t.timeout}ms`;return console.error(`${o.name}:`,i),E({message:i})}if(o instanceof DOMException&&o.name==="AbortError"){let i=`Request aborted due to ${o.message}`;return console.error(`${o.name}:`,i),E({message:i})}if(b(o)){let{errorData:i,response:m}=o;return await t.onResponseError?.({response:Object.assign(m.clone(),{errorData:i}),request:y,options:t}),E({errorData:i,message:i?.message,response:m})}return await t.onRequestError?.({request:y,error:o,options:t}),E()}finally{r.delete(c)}};return f.create=g,f.cancel=(c,R)=>{R?r.get(c)?.abort(R):r.get(c)?.abort()},f},N=g();0&&(module.exports={HTTPError,callApi,createFetchClient,isHTTPError,isHTTPErrorInstance,toQueryString});
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../src/typeof.ts","../../src/utils.ts","../../src/createFetchClient.ts"],"sourcesContent":["export { callApi, createFetchClient } from \"./createFetchClient\";\n\nexport type {\n\tFetchConfig,\n\t$RequestOptions,\n\tExtraOptions,\n\tResponseContext,\n\tResponseErrorContext,\n} from \"./types\";\n\nexport { HTTPError, isHTTPError, isHTTPErrorInstance, toQueryString } from \"./utils\";\n","import type { AnyFunction } from \"./type-helpers\";\n\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\n\nexport const isFormData = (value: unknown) => value instanceof FormData;\n\nexport const isObject = <TObject extends Record<string, unknown>>(value: unknown): value is TObject => {\n\treturn (\n\t\ttypeof value === \"object\" && value !== null && !(value instanceof FormData) && !Array.isArray(value)\n\t);\n};\n\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\n\ttypeof value === \"function\";\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n","import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n\t(params: ExtraOptions[\"query\"]): string | null;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"No query params provided\");\n\t\treturn null as never;\n\t}\n\n\treturn new URLSearchParams(params as Record<string, string>).toString();\n};\n\nexport const mergeUrlWithParams = (url: string, params: ExtraOptions[\"query\"]): string => {\n\tif (!params) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(params);\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\tif (url.includes(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\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\tname: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? error,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.name === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\terrorData: TErrorResponse;\n\tresponse: Response;\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response, errorData } = errorDetails;\n\n\t\tsuper((errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.errorData = errorData;\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n","import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\tconst { signal = baseSignal, body = baseBody, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\t// == Default Options\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\n\t\t\t\t`Automatic cancelation of the previous unfinished request to this same url: ${url}`,\n\t\t\t\t\"AbortError\"\n\t\t\t);\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\tconst combinedSignal = AbortSignal.any([\n\t\t\tnewFetchController.signal,\n\t\t\ttimeoutSignal ?? newFetchController.signal,\n\t\t\tsignal ?? newFetchController.signal,\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization:\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestOptions;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait waitUntil(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse.clone(),\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse.clone(),\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\t// == Workaround as opposed to using the spread operator, as it doesn't work on the response object. So using Object.assign instead on a clone of the response object.\n\t\t\t\tresponse: Object.assign(response.clone(), { data: validSuccessData }),\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, response, options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.error(`${error.name}:`, message);\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request aborted due to ${error.message}`;\n\n\t\t\t\tconsole.error(`${error.name}:`, message);\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, response } = error;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: Object.assign(response.clone(), { errorData }),\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t\tresponse,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"],"mappings":"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","names":["src_exports","__export","HTTPError","callApi","createFetchClient","isHTTPError","isHTTPErrorInstance","toQueryString","__toCommonJS","isArray","value","isFormData","isObject","isFunction","isString","toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","isObject","isArray","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isFunction","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","splitConfig","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","signal","body","headers","restOfFetchConfig","options","defaultRetryCodes","defaultRetryMethods","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","isObject","isFormData","isString","objectifyHeaders","response","mergeUrlWithParams","waitUntil","errorData","getResponseData","HTTPError","successData","validSuccessData","resolveSuccessResult","error","resolveErrorResult","$resolveErrorResult","message","isHTTPErrorInstance"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/typeof.ts","../../src/utils.ts","../../src/createFetchClient.ts"],"sourcesContent":["export { callApi, createFetchClient } from \"./createFetchClient\";\n\nexport type {\n\t$RequestOptions,\n\tExtraOptions,\n\tFetchConfig,\n\tResponseContext,\n\tResponseErrorContext,\n} from \"./types\";\n\nexport { HTTPError, isHTTPError, isHTTPErrorInstance, toQueryString } from \"./utils\";\n","import type { AnyFunction } from \"./type-helpers\";\n\nexport const isArray = <TArray>(value: unknown): value is TArray[] => Array.isArray(value);\n\nexport const isFormData = (value: unknown) => value instanceof FormData;\n\nexport const isObject = <TObject extends Record<string, unknown>>(value: unknown): value is TObject => {\n\treturn (\n\t\ttypeof value === \"object\" && value !== null && !(value instanceof FormData) && !Array.isArray(value)\n\t);\n};\n\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\n\ttypeof value === \"function\";\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n","import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: ExtraOptions[\"query\"]): string | null;\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"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.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\tif (url.includes(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\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\tname: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? error,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.name === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\terrorData: TErrorResponse;\n\tresponse: Response;\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response, errorData } = errorDetails;\n\n\t\tsuper((errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.errorData = errorData;\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n","import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\tconst { signal = baseSignal, body = baseBody, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\t// == Default Options\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\n\t\t\t\t`Automatic cancelation of the previous unfinished request to this same url: ${url}`,\n\t\t\t\t\"AbortError\"\n\t\t\t);\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\tconst combinedSignal = AbortSignal.any([\n\t\t\tnewFetchController.signal,\n\t\t\t...(timeoutSignal ? [timeoutSignal] : []),\n\t\t\t...(signal ? [signal] : []),\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization:\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestOptions;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait waitUntil(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse.clone(),\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse.clone(),\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\t// == Workaround as opposed to using the spread operator, as it doesn't work on the response object. So using Object.assign instead on a clone of the response object.\n\t\t\t\tresponse: Object.assign(response.clone(), { data: validSuccessData }),\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, response, options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.error(`${error.name}:`, message);\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request aborted due to ${error.message}`;\n\n\t\t\t\tconsole.error(`${error.name}:`, message);\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, response } = error;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: Object.assign(response.clone(), { errorData }),\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t\tresponse,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\n\tcallApi.cancel = (url: string, reason?: unknown) => {\n\t\treason ? abortControllerStore.get(url)?.abort(reason) : abortControllerStore.get(url)?.abort();\n\t};\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"],"mappings":"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","names":["src_exports","__export","HTTPError","callApi","createFetchClient","isHTTPError","isHTTPErrorInstance","toQueryString","__toCommonJS","isArray","value","isFormData","isObject","isFunction","isString","toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","isObject","isArray","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isFunction","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve","createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","splitConfig","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","signal","body","headers","restOfFetchConfig","options","defaultRetryCodes","defaultRetryMethods","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","isObject","isFormData","isString","objectifyHeaders","response","mergeUrlWithParams","waitUntil","errorData","getResponseData","HTTPError","successData","validSuccessData","resolveSuccessResult","error","resolveErrorResult","$resolveErrorResult","message","isHTTPErrorInstance"]}
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -6,8 +6,8 @@ type AnyNumber = number & {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
type ToQueryStringFn = {
|
|
9
|
-
(params: Required<ExtraOptions>["query"]): string;
|
|
10
9
|
(params: ExtraOptions["query"]): string | null;
|
|
10
|
+
(params: Required<ExtraOptions>["query"]): string;
|
|
11
11
|
};
|
|
12
12
|
declare const toQueryString: ToQueryStringFn;
|
|
13
13
|
declare const fetchSpecificKeys: ("body" | "method" | "headers" | "cache" | "credentials" | "integrity" | "keepalive" | "mode" | "priority" | "redirect" | "referrer" | "referrerPolicy" | "signal" | "window")[];
|
|
@@ -40,8 +40,9 @@ declare class HTTPError<TErrorResponse = Record<string, unknown>> extends Error
|
|
|
40
40
|
}
|
|
41
41
|
declare const isHTTPErrorInstance: <TErrorResponse>(error: unknown) => error is HTTPError<TErrorResponse>;
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
interface $RequestOptions extends Pick<FetchConfig, (typeof fetchSpecificKeys)[number]> {
|
|
44
|
+
}
|
|
45
|
+
interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = ResultModeUnion> {
|
|
45
46
|
/** Optional body of the request, can be a object or any other supported body type. */
|
|
46
47
|
body?: Record<string, unknown> | RequestInit["body"];
|
|
47
48
|
/**
|
|
@@ -66,7 +67,7 @@ type ExtraOptions<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode
|
|
|
66
67
|
/**
|
|
67
68
|
* @description Custom function to validate the response data.
|
|
68
69
|
*/
|
|
69
|
-
responseValidator?: (data:
|
|
70
|
+
responseValidator?: (data: TData) => TData;
|
|
70
71
|
/**
|
|
71
72
|
* @description Custom function to serialize the body object into a string.
|
|
72
73
|
*/
|
|
@@ -80,7 +81,7 @@ type ExtraOptions<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode
|
|
|
80
81
|
* Can be set to "all" | "onlySuccess" | "onlyError" | "onlyResponse".
|
|
81
82
|
* @default "all"
|
|
82
83
|
*/
|
|
83
|
-
resultMode?:
|
|
84
|
+
resultMode?: TResultMode;
|
|
84
85
|
/**
|
|
85
86
|
* @description If true, cancels previous unfinished requests to the same URL.
|
|
86
87
|
* @default true
|
|
@@ -104,7 +105,7 @@ type ExtraOptions<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode
|
|
|
104
105
|
* The function is passed the error object and can be used to conditionally throw the error
|
|
105
106
|
* @default false
|
|
106
107
|
*/
|
|
107
|
-
throwOnError?: boolean | ((error?: Error | HTTPError<
|
|
108
|
+
throwOnError?: boolean | ((error?: Error | HTTPError<TErrorData>) => boolean);
|
|
108
109
|
/**
|
|
109
110
|
* @description Expected response type, affects how response is parsed
|
|
110
111
|
* @default "json"
|
|
@@ -165,10 +166,10 @@ type ExtraOptions<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode
|
|
|
165
166
|
options: ExtraOptions;
|
|
166
167
|
}) => void | Promise<void>;
|
|
167
168
|
/** @description Interceptor to be called when a successful response is received from the api. */
|
|
168
|
-
onResponse?: (responseContext: ResponseContext<
|
|
169
|
+
onResponse?: (responseContext: ResponseContext<TData>) => void | Promise<void>;
|
|
169
170
|
/** @description Interceptor to be called when an error response is received from the api. */
|
|
170
|
-
onResponseError?: (responseErrorContext: ResponseErrorContext<
|
|
171
|
-
}
|
|
171
|
+
onResponseError?: (responseErrorContext: ResponseErrorContext<TErrorData>) => void | Promise<void>;
|
|
172
|
+
}
|
|
172
173
|
type ResponseContext<TData> = {
|
|
173
174
|
_: {
|
|
174
175
|
response: Response & {
|
|
@@ -189,8 +190,7 @@ type ResponseErrorContext<TErrorData> = {
|
|
|
189
190
|
}["_"];
|
|
190
191
|
interface FetchConfig<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = undefined> extends Omit<RequestInit, "method" | "body">, ExtraOptions<TData, TErrorData, TResultMode> {
|
|
191
192
|
}
|
|
192
|
-
|
|
193
|
-
}
|
|
193
|
+
type BaseConfig<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = undefined> = FetchConfig<TBaseData, TBaseErrorData, TBaseResultMode>;
|
|
194
194
|
type ApiSuccessVariant<TData> = {
|
|
195
195
|
data: TData;
|
|
196
196
|
error: null;
|
|
@@ -232,16 +232,16 @@ type GetCallApiResult<TData, TErrorData, TResultMode> = TResultMode extends NonN
|
|
|
232
232
|
declare const createFetchClient: <TBaseData, TBaseErrorData, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
|
|
233
233
|
<TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
|
|
234
234
|
create: any;
|
|
235
|
-
cancel(url: string): void
|
|
235
|
+
cancel(url: string, reason?: unknown): void;
|
|
236
236
|
};
|
|
237
237
|
declare const callApi: {
|
|
238
238
|
<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = undefined>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode> | undefined): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
|
|
239
239
|
create: <TBaseData, TBaseErrorData, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
|
|
240
240
|
<TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
|
|
241
241
|
create: any;
|
|
242
|
-
cancel(url: string): void
|
|
242
|
+
cancel(url: string, reason?: unknown): void;
|
|
243
243
|
};
|
|
244
|
-
cancel(url: string): void
|
|
244
|
+
cancel(url: string, reason?: unknown): void;
|
|
245
245
|
};
|
|
246
246
|
|
|
247
247
|
export { type $RequestOptions, type ExtraOptions, type FetchConfig, HTTPError, type ResponseContext, type ResponseErrorContext, callApi, createFetchClient, isHTTPError, isHTTPErrorInstance, toQueryString };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { g, d, e, c as c$1, b as b$1, o, i, m, j, k, n } from './chunk-YRSDB5GD.js';
|
|
2
|
+
import { c, b, e as e$1 } from './chunk-3E33QJL5.js';
|
|
3
|
+
|
|
4
|
+
var S=F=>{let n$1=new Map,[j$1,P]=g(F??{}),{headers:T,body:v,signal:U,...k$1}=j$1,d$1=async(s,i$1)=>{let[z,G]=g(i$1??{}),{signal:h=U,body:o$1=v,headers:m$1,...H}=z,e$2={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:d,retryMethods:e,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...P,...G},b$2=n$1.get(s);if(b$2&&e$2.cancelRedundantRequests){let t=new DOMException(`Automatic cancelation of the previous unfinished request to this same url: ${s}`,"AbortError");b$2.abort(t);}let y=new AbortController;n$1.set(s,y);let E=e$2.timeout?AbortSignal.timeout(e$2.timeout):null,D=AbortSignal.any([y.signal,...E?[E]:[],...h?[h]:[]]),a={signal:D,method:"GET",body:c(o$1)?e$2.bodySerializer(o$1):o$1,headers:T||m$1||e$2.auth||c(o$1)?{...c(o$1)&&{"Content-Type":"application/json",Accept:"application/json"},...b(o$1)&&{"Content-Type":"multipart/form-data"},...e$1(o$1)&&{"Content-Type":"application/x-www-form-urlencoded"},...e$1(e$2.auth)&&{Authorization:`Bearer ${e$2.auth}`},...c(e$2.auth)&&{Authorization:"bearer"in e$2.auth?`Bearer ${e$2.auth.bearer}`:`Token ${e$2.auth.token}`},...c$1(T),...c$1(m$1)}:void 0,...k$1,...H};try{await e$2.onRequest?.({request:a,options:e$2});let t=await fetch(`${e$2.baseURL}${b$1(s,e$2.query)}`,a);if(!t.ok&&!D.aborted&&e$2.retries>0&&e$2.retryCodes.includes(t.status)&&e$2.retryMethods.includes(a.method))return await o(e$2.retryDelay),await d$1(s,{...i$1,retries:e$2.retries-1});if(!t.ok){let I=await i(t.clone(),e$2.responseType,e$2.responseParser);throw new m({errorData:I,response:t,defaultErrorMessage:e$2.defaultErrorMessage})}let r=await i(t.clone(),e$2.responseType,e$2.responseParser),c=e$2.responseValidator?e$2.responseValidator(r):r;return await e$2.onResponse?.({response:Object.assign(t.clone(),{data:c}),request:a,options:e$2}),j({successData:c,response:t,options:e$2})}catch(t){let l=k({error:t,options:e$2});if(t instanceof DOMException&&t.name==="TimeoutError"){let r=`Request timed out after ${e$2.timeout}ms`;return console.error(`${t.name}:`,r),l({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){let r=`Request aborted due to ${t.message}`;return console.error(`${t.name}:`,r),l({message:r})}if(n(t)){let{errorData:r,response:c}=t;return await e$2.onResponseError?.({response:Object.assign(c.clone(),{errorData:r}),request:a,options:e$2}),l({errorData:r,message:r?.message,response:c})}return await e$2.onRequestError?.({request:a,error:t,options:e$2}),l()}finally{n$1.delete(s);}};return d$1.create=S,d$1.cancel=(s,i)=>{i?n$1.get(s)?.abort(i):n$1.get(s)?.abort();},d$1},J=S();
|
|
5
|
+
|
|
6
|
+
export { S as a, J as b };
|
|
7
|
+
//# sourceMappingURL=out.js.map
|
|
8
|
+
//# sourceMappingURL=chunk-LP7FW6GY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/createFetchClient.ts"],"names":["createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","splitConfig","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","signal","body","headers","restOfFetchConfig","options","defaultRetryCodes","defaultRetryMethods","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","isObject","isFormData","isString","objectifyHeaders","response","mergeUrlWithParams","waitUntil","errorData","getResponseData","HTTPError","successData","validSuccessData","resolveSuccessResult","error","resolveErrorResult","$resolveErrorResult","message","isHTTPErrorInstance"],"mappings":"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","sourcesContent":["import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\tconst { signal = baseSignal, body = baseBody, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\t// == Default Options\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\n\t\t\t\t`Automatic cancelation of the previous unfinished request to this same url: ${url}`,\n\t\t\t\t\"AbortError\"\n\t\t\t);\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\tconst combinedSignal = AbortSignal.any([\n\t\t\tnewFetchController.signal,\n\t\t\t...(timeoutSignal ? [timeoutSignal] : []),\n\t\t\t...(signal ? [signal] : []),\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization:\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestOptions;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait waitUntil(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse.clone(),\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse.clone(),\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\t// == Workaround as opposed to using the spread operator, as it doesn't work on the response object. So using Object.assign instead on a clone of the response object.\n\t\t\t\tresponse: Object.assign(response.clone(), { data: validSuccessData }),\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, response, options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.error(`${error.name}:`, message);\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request aborted due to ${error.message}`;\n\n\t\t\t\tconsole.error(`${error.name}:`, message);\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, response } = error;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: Object.assign(response.clone(), { errorData }),\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t\tresponse,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\n\tcallApi.cancel = (url: string, reason?: unknown) => {\n\t\treason ? abortControllerStore.get(url)?.abort(reason) : abortControllerStore.get(url)?.abort();\n\t};\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"]}
|
|
@@ -4,4 +4,4 @@ var E=r=>r?new URLSearchParams(r).toString():(console.error("No query params pro
|
|
|
4
4
|
|
|
5
5
|
export { E as a, b, g as c, x as d, j as e, l as f, w as g, O as h, P as i, k as j, h as k, A as l, c as m, D as n, C as o };
|
|
6
6
|
//# sourceMappingURL=out.js.map
|
|
7
|
-
//# sourceMappingURL=chunk-
|
|
7
|
+
//# sourceMappingURL=chunk-YRSDB5GD.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils.ts"],"names":["toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","isObject","isArray","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isFunction","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve"],"mappings":"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","sourcesContent":["import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n\t(params: ExtraOptions[\"query\"]): string | null;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"No query params provided\");\n\t\treturn null as never;\n\t}\n\n\treturn new URLSearchParams(params as Record<string, string>).toString();\n};\n\nexport const mergeUrlWithParams = (url: string, params: ExtraOptions[\"query\"]): string => {\n\tif (!params) {\n\t\treturn url;\n\t}\n\n\tconst paramsString = toQueryString(params);\n\n\tif (url.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\tif (url.includes(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\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\tname: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? error,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.name === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\terrorData: TErrorResponse;\n\tresponse: Response;\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response, errorData } = errorDetails;\n\n\t\tsuper((errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.errorData = errorData;\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils.ts"],"names":["toQueryString","params","mergeUrlWithParams","url","paramsString","objectifyHeaders","headers","isObject","isArray","retryCodesLookup","defaultRetryCodes","defaultRetryMethods","fetchSpecificKeys","omitKeys","initialObject","keysToOmit","arrayFromFilteredObject","key","pickKeys","keysToPick","keysToPickSet","filteredArray","objectKey","splitConfig","config","handleResponseType","response","parser","getResponseData","responseType","RESPONSE_TYPE_LOOKUP","resolveSuccessResult","info","options","successData","apiDetails","$resolveErrorResult","$info","error","errorData","message","isFunction","isHTTPError","HTTPError","errorDetails","errorOptions","defaultErrorMessage","isHTTPErrorInstance","waitUntil","delay","promise","resolve"],"mappings":"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","sourcesContent":["import { isArray, isFunction, isObject } from \"./typeof\";\nimport type {\n\t$BaseRequestOptions,\n\t$RequestOptions,\n\tApiErrorVariant,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tPossibleErrorObject,\n} from \"./types\";\n\ntype ToQueryStringFn = {\n\t(params: ExtraOptions[\"query\"]): string | null;\n\t(params: Required<ExtraOptions>[\"query\"]): string;\n};\n\nexport const toQueryString: ToQueryStringFn = (params) => {\n\tif (!params) {\n\t\tconsole.error(\"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.endsWith(\"?\")) {\n\t\treturn `${url}${paramsString}`;\n\t}\n\n\tif (url.includes(\"?\")) {\n\t\treturn `${url}&${paramsString}`;\n\t}\n\n\treturn `${url}?${paramsString}`;\n};\n\nexport const objectifyHeaders = (headers: RequestInit[\"headers\"]): Record<string, string> | undefined => {\n\tif (!headers || isObject(headers)) {\n\t\treturn headers;\n\t}\n\n\treturn Object.fromEntries(isArray(headers) ? headers : headers.entries());\n};\n\nconst retryCodesLookup = {\n\t408: \"Request Timeout\",\n\t409: \"Conflict\",\n\t425: \"Too Early\",\n\t429: \"Too Many Requests\",\n\t500: \"Internal Server Error\",\n\t502: \"Bad Gateway\",\n\t503: \"Service Unavailable\",\n\t504: \"Gateway Timeout\",\n};\n\nexport const defaultRetryCodes: Required<BaseConfig>[\"retryCodes\"] =\n\tObject.keys(retryCodesLookup).map(Number);\n\nexport const defaultRetryMethods: Required<BaseConfig>[\"retryMethods\"] = [\"GET\"];\n\nexport const fetchSpecificKeys = [\n\t\"body\",\n\t\"integrity\",\n\t\"method\",\n\t\"headers\",\n\t\"signal\",\n\t\"cache\",\n\t\"redirect\",\n\t\"window\",\n\t\"credentials\",\n\t\"keepalive\",\n\t\"referrer\",\n\t\"priority\",\n\t\"mode\",\n\t\"referrerPolicy\",\n] satisfies Array<keyof FetchConfig>;\n\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\tname: (error as PossibleErrorObject)?.name ?? \"UnknownError\",\n\t\t\t\terrorData: errorData ?? error,\n\t\t\t\tmessage: message ?? (error as PossibleErrorObject)?.message ?? options.defaultErrorMessage,\n\t\t\t},\n\t\t\tresponse: response ?? null,\n\t\t} as CallApiResult;\n\t};\n\n\treturn resolveErrorResult;\n};\n\nexport const isHTTPError = <TErrorData>(error: ApiErrorVariant<TErrorData>[\"error\"] | null) => {\n\treturn isObject(error) && error.name === \"HTTPError\";\n};\n\ntype ErrorDetails<TErrorResponse> = {\n\terrorData: TErrorResponse;\n\tresponse: Response;\n\tdefaultErrorMessage: string;\n};\n\ntype ErrorOptions = {\n\tcause?: unknown;\n};\n\nexport class HTTPError<TErrorResponse = Record<string, unknown>> extends Error {\n\tresponse: ErrorDetails<TErrorResponse>[\"response\"];\n\terrorData: ErrorDetails<TErrorResponse>[\"errorData\"];\n\n\toverride name = \"HTTPError\" as const;\n\n\tisHTTPError = true;\n\n\tconstructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultErrorMessage, response, errorData } = errorDetails;\n\n\t\tsuper((errorData as { message?: string }).message ?? defaultErrorMessage, errorOptions);\n\n\t\tthis.errorData = errorData;\n\t\tthis.response = response;\n\t}\n}\n\n// prettier-ignore\nexport const isHTTPErrorInstance = <TErrorResponse>(\n\terror: unknown\n): error is HTTPError<TErrorResponse> => {\n\treturn (\n\t\terror instanceof HTTPError || (isObject(error) && error.name === \"HTTPError\" && error.isHTTPError === true)\n\t);\n};\n\nexport const waitUntil = (delay: number) => {\n\tif (delay === 0) return;\n\n\tconst { promise, resolve } = Promise.withResolvers();\n\n\tsetTimeout(resolve, delay);\n\n\treturn promise;\n};\n"]}
|
|
@@ -1,19 +1,19 @@
|
|
|
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-BeDSv1wB.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>) => {
|
|
5
5
|
<TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
|
|
6
6
|
create: any;
|
|
7
|
-
cancel(url: string): void
|
|
7
|
+
cancel(url: string, reason?: unknown): void;
|
|
8
8
|
};
|
|
9
9
|
declare const callApi: {
|
|
10
10
|
<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = undefined>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode> | undefined): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
|
|
11
11
|
create: <TBaseData, TBaseErrorData, TBaseResultMode extends ResultModeUnion = undefined>(baseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>) => {
|
|
12
12
|
<TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode>(url: string, config?: FetchConfig<TData, TErrorData, TResultMode>): Promise<GetCallApiResult<TData, TErrorData, TResultMode>>;
|
|
13
13
|
create: any;
|
|
14
|
-
cancel(url: string): void
|
|
14
|
+
cancel(url: string, reason?: unknown): void;
|
|
15
15
|
};
|
|
16
|
-
cancel(url: string): void
|
|
16
|
+
cancel(url: string, reason?: unknown): void;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export { callApi, createFetchClient };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { b as callApi, a as createFetchClient } from './chunk-
|
|
2
|
-
import './chunk-
|
|
1
|
+
export { b as callApi, a as createFetchClient } from './chunk-LP7FW6GY.js';
|
|
2
|
+
import './chunk-YRSDB5GD.js';
|
|
3
3
|
import './chunk-3E33QJL5.js';
|
|
4
4
|
//# sourceMappingURL=out.js.map
|
|
5
5
|
//# sourceMappingURL=createFetchClient.js.map
|
package/dist/esm/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-BeDSv1wB.js';
|
|
3
3
|
import './type-helpers-Dibitydy.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { b as callApi, a as createFetchClient } from './chunk-
|
|
2
|
-
export { m as HTTPError, l as isHTTPError, n as isHTTPErrorInstance, a as toQueryString } from './chunk-
|
|
1
|
+
export { b as callApi, a as createFetchClient } from './chunk-LP7FW6GY.js';
|
|
2
|
+
export { m as HTTPError, l as isHTTPError, n as isHTTPErrorInstance, a as toQueryString } from './chunk-YRSDB5GD.js';
|
|
3
3
|
import './chunk-3E33QJL5.js';
|
|
4
4
|
//# sourceMappingURL=out.js.map
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { a as AnyString, b as AnyNumber } from './type-helpers-Dibitydy.js';
|
|
2
2
|
|
|
3
3
|
type ToQueryStringFn = {
|
|
4
|
-
(params: Required<ExtraOptions>["query"]): string;
|
|
5
4
|
(params: ExtraOptions["query"]): string | null;
|
|
5
|
+
(params: Required<ExtraOptions>["query"]): string;
|
|
6
6
|
};
|
|
7
7
|
declare const toQueryString: ToQueryStringFn;
|
|
8
8
|
declare const mergeUrlWithParams: (url: string, params: ExtraOptions["query"]) => string;
|
|
@@ -56,9 +56,11 @@ declare class HTTPError<TErrorResponse = Record<string, unknown>> extends Error
|
|
|
56
56
|
declare const isHTTPErrorInstance: <TErrorResponse>(error: unknown) => error is HTTPError<TErrorResponse>;
|
|
57
57
|
declare const waitUntil: (delay: number) => Promise<unknown> | undefined;
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
interface $RequestOptions extends Pick<FetchConfig, (typeof fetchSpecificKeys)[number]> {
|
|
60
|
+
}
|
|
61
|
+
interface $BaseRequestOptions extends Omit<$RequestOptions, "body"> {
|
|
62
|
+
}
|
|
63
|
+
interface ExtraOptions<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = ResultModeUnion> {
|
|
62
64
|
/** Optional body of the request, can be a object or any other supported body type. */
|
|
63
65
|
body?: Record<string, unknown> | RequestInit["body"];
|
|
64
66
|
/**
|
|
@@ -83,7 +85,7 @@ type ExtraOptions<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode
|
|
|
83
85
|
/**
|
|
84
86
|
* @description Custom function to validate the response data.
|
|
85
87
|
*/
|
|
86
|
-
responseValidator?: (data:
|
|
88
|
+
responseValidator?: (data: TData) => TData;
|
|
87
89
|
/**
|
|
88
90
|
* @description Custom function to serialize the body object into a string.
|
|
89
91
|
*/
|
|
@@ -97,7 +99,7 @@ type ExtraOptions<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode
|
|
|
97
99
|
* Can be set to "all" | "onlySuccess" | "onlyError" | "onlyResponse".
|
|
98
100
|
* @default "all"
|
|
99
101
|
*/
|
|
100
|
-
resultMode?:
|
|
102
|
+
resultMode?: TResultMode;
|
|
101
103
|
/**
|
|
102
104
|
* @description If true, cancels previous unfinished requests to the same URL.
|
|
103
105
|
* @default true
|
|
@@ -121,7 +123,7 @@ type ExtraOptions<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode
|
|
|
121
123
|
* The function is passed the error object and can be used to conditionally throw the error
|
|
122
124
|
* @default false
|
|
123
125
|
*/
|
|
124
|
-
throwOnError?: boolean | ((error?: Error | HTTPError<
|
|
126
|
+
throwOnError?: boolean | ((error?: Error | HTTPError<TErrorData>) => boolean);
|
|
125
127
|
/**
|
|
126
128
|
* @description Expected response type, affects how response is parsed
|
|
127
129
|
* @default "json"
|
|
@@ -182,10 +184,10 @@ type ExtraOptions<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode
|
|
|
182
184
|
options: ExtraOptions;
|
|
183
185
|
}) => void | Promise<void>;
|
|
184
186
|
/** @description Interceptor to be called when a successful response is received from the api. */
|
|
185
|
-
onResponse?: (responseContext: ResponseContext<
|
|
187
|
+
onResponse?: (responseContext: ResponseContext<TData>) => void | Promise<void>;
|
|
186
188
|
/** @description Interceptor to be called when an error response is received from the api. */
|
|
187
|
-
onResponseError?: (responseErrorContext: ResponseErrorContext<
|
|
188
|
-
}
|
|
189
|
+
onResponseError?: (responseErrorContext: ResponseErrorContext<TErrorData>) => void | Promise<void>;
|
|
190
|
+
}
|
|
189
191
|
type ResponseContext<TData> = {
|
|
190
192
|
_: {
|
|
191
193
|
response: Response & {
|
|
@@ -206,8 +208,7 @@ type ResponseErrorContext<TErrorData> = {
|
|
|
206
208
|
}["_"];
|
|
207
209
|
interface FetchConfig<TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = undefined> extends Omit<RequestInit, "method" | "body">, ExtraOptions<TData, TErrorData, TResultMode> {
|
|
208
210
|
}
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
+
type BaseConfig<TBaseData = unknown, TBaseErrorData = unknown, TBaseResultMode extends ResultModeUnion = undefined> = FetchConfig<TBaseData, TBaseErrorData, TBaseResultMode>;
|
|
211
212
|
type ApiSuccessVariant<TData> = {
|
|
212
213
|
data: TData;
|
|
213
214
|
error: null;
|
package/dist/esm/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-BeDSv1wB.js';
|
|
2
2
|
import './type-helpers-Dibitydy.js';
|
package/dist/esm/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { k as $resolveErrorResult, m as HTTPError, d as defaultRetryCodes, e as defaultRetryMethods, f as fetchSpecificKeys, i as getResponseData, h as handleResponseType, l as isHTTPError, n as isHTTPErrorInstance, b as mergeUrlWithParams, c as objectifyHeaders, j as resolveSuccessResult, g as splitConfig, a as toQueryString, o as waitUntil } from './chunk-
|
|
1
|
+
export { k as $resolveErrorResult, m as HTTPError, d as defaultRetryCodes, e as defaultRetryMethods, f as fetchSpecificKeys, i as getResponseData, h as handleResponseType, l as isHTTPError, n as isHTTPErrorInstance, b as mergeUrlWithParams, c as objectifyHeaders, j as resolveSuccessResult, g as splitConfig, a as toQueryString, o as waitUntil } from './chunk-YRSDB5GD.js';
|
|
2
2
|
import './chunk-3E33QJL5.js';
|
|
3
3
|
//# sourceMappingURL=out.js.map
|
|
4
4
|
//# sourceMappingURL=utils.js.map
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zayne-labs/callapi",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.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/esm/index.js",
|
|
7
7
|
"module": "./dist/esm/index.js",
|
|
8
8
|
"types": "./dist/esm/index.d.ts",
|
|
9
9
|
"exports": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"interceptor"
|
|
37
37
|
],
|
|
38
38
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
39
|
+
"node": ">=18.17.x"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@arethetypeswrong/cli": "^0.15.3",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"globals": "^15.8.0",
|
|
53
53
|
"husky": "^9.0.11",
|
|
54
54
|
"lint-staged": "^15.2.7",
|
|
55
|
-
"prettier": "^3.3.
|
|
55
|
+
"prettier": "^3.3.3",
|
|
56
56
|
"size-limit": "^11.1.4",
|
|
57
57
|
"tsup": "^8.1.0",
|
|
58
58
|
"typescript": "5.5.3",
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { g, d, e, c as c$1, b as b$1, o, i, m, j, k, n } from './chunk-QCF5J2OT.js';
|
|
2
|
-
import { c, b, e as e$1 } from './chunk-3E33QJL5.js';
|
|
3
|
-
|
|
4
|
-
var $=x=>{let c$2=new Map,[S,F]=g(x??{}),{headers:T,body:j$1,signal:P,...v}=S,u=async(s,h)=>{let[U,k$1]=g(h??{}),{signal:z=P,body:o$1=j$1,headers:m$1,...G}=U,e$2={bodySerializer:JSON.stringify,responseType:"json",baseURL:"",retries:0,retryDelay:0,retryCodes:d,retryMethods:e,defaultErrorMessage:"Failed to fetch data from server!",cancelRedundantRequests:!0,...F,...k$1},b$2=c$2.get(s);if(b$2&&e$2.cancelRedundantRequests){let t=new DOMException(`Automatic cancelation of the previous unfinished request to this same url: ${s}`,"AbortError");b$2.abort(t);}let d$1=new AbortController;c$2.set(s,d$1);let H=e$2.timeout?AbortSignal.timeout(e$2.timeout):null,y=AbortSignal.any([d$1.signal,H??d$1.signal,z??d$1.signal]),a={signal:y,method:"GET",body:c(o$1)?e$2.bodySerializer(o$1):o$1,headers:T||m$1||e$2.auth||c(o$1)?{...c(o$1)&&{"Content-Type":"application/json",Accept:"application/json"},...b(o$1)&&{"Content-Type":"multipart/form-data"},...e$1(o$1)&&{"Content-Type":"application/x-www-form-urlencoded"},...e$1(e$2.auth)&&{Authorization:`Bearer ${e$2.auth}`},...c(e$2.auth)&&{Authorization:"bearer"in e$2.auth?`Bearer ${e$2.auth.bearer}`:`Token ${e$2.auth.token}`},...c$1(T),...c$1(m$1)}:void 0,...v,...G};try{await e$2.onRequest?.({request:a,options:e$2});let t=await fetch(`${e$2.baseURL}${b$1(s,e$2.query)}`,a);if(!t.ok&&!y.aborted&&e$2.retries>0&&e$2.retryCodes.includes(t.status)&&e$2.retryMethods.includes(a.method))return await o(e$2.retryDelay),await u(s,{...h,retries:e$2.retries-1});if(!t.ok){let I=await i(t.clone(),e$2.responseType,e$2.responseParser);throw new m({errorData:I,response:t,defaultErrorMessage:e$2.defaultErrorMessage})}let r=await i(t.clone(),e$2.responseType,e$2.responseParser),i$1=e$2.responseValidator?e$2.responseValidator(r):r;return await e$2.onResponse?.({response:Object.assign(t.clone(),{data:i$1}),request:a,options:e$2}),j({successData:i$1,response:t,options:e$2})}catch(t){let n$1=k({error:t,options:e$2});if(t instanceof DOMException&&t.name==="TimeoutError"){let r=`Request timed out after ${e$2.timeout}ms`;return console.error(`${t.name}:`,r),n$1({message:r})}if(t instanceof DOMException&&t.name==="AbortError"){let r=`Request aborted due to ${t.message}`;return console.error(`${t.name}:`,r),n$1({message:r})}if(n(t)){let{errorData:r,response:i}=t;return await e$2.onResponseError?.({response:Object.assign(i.clone(),{errorData:r}),request:a,options:e$2}),n$1({errorData:r,message:r?.message,response:i})}return await e$2.onRequestError?.({request:a,error:t,options:e$2}),n$1()}finally{c$2.delete(s);}};return u.create=$,u.cancel=s=>c$2.get(s)?.abort(),u},J=$();
|
|
5
|
-
|
|
6
|
-
export { $ as a, J as b };
|
|
7
|
-
//# sourceMappingURL=out.js.map
|
|
8
|
-
//# sourceMappingURL=chunk-UFIAMS26.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/createFetchClient.ts"],"names":["createFetchClient","baseConfig","abortControllerStore","baseFetchConfig","baseExtraOptions","splitConfig","baseHeaders","baseBody","baseSignal","restOfBaseFetchConfig","callApi","url","config","fetchConfig","extraOptions","signal","body","headers","restOfFetchConfig","options","defaultRetryCodes","defaultRetryMethods","prevFetchController","reason","newFetchController","timeoutSignal","combinedSignal","requestInit","isObject","isFormData","isString","objectifyHeaders","response","mergeUrlWithParams","waitUntil","errorData","getResponseData","HTTPError","successData","validSuccessData","resolveSuccessResult","error","resolveErrorResult","$resolveErrorResult","message","isHTTPErrorInstance"],"mappings":"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","sourcesContent":["import { isFormData, isObject, isString } from \"./typeof\";\nimport type {\n\t$RequestOptions,\n\tBaseConfig,\n\tExtraOptions,\n\tFetchConfig,\n\tGetCallApiResult,\n\tPossibleErrorObject,\n\tResultModeUnion,\n} from \"./types\";\nimport {\n\t$resolveErrorResult,\n\tHTTPError,\n\tdefaultRetryCodes,\n\tdefaultRetryMethods,\n\tgetResponseData,\n\tisHTTPErrorInstance,\n\tmergeUrlWithParams,\n\tobjectifyHeaders,\n\tresolveSuccessResult,\n\tsplitConfig,\n\twaitUntil,\n} from \"./utils\";\n\nexport const createFetchClient = <\n\tTBaseData,\n\tTBaseErrorData,\n\tTBaseResultMode extends ResultModeUnion = undefined,\n>(\n\tbaseConfig?: BaseConfig<TBaseData, TBaseErrorData, TBaseResultMode>\n) => {\n\tconst abortControllerStore = new Map<string, AbortController>();\n\n\tconst [baseFetchConfig, baseExtraOptions] = splitConfig(baseConfig ?? {});\n\n\tconst {\n\t\theaders: baseHeaders,\n\t\tbody: baseBody,\n\t\tsignal: baseSignal,\n\t\t...restOfBaseFetchConfig\n\t} = baseFetchConfig;\n\n\t/* eslint-disable complexity */\n\tconst callApi = async <\n\t\tTData = TBaseData,\n\t\tTErrorData = TBaseErrorData,\n\t\tTResultMode extends ResultModeUnion = TBaseResultMode,\n\t>(\n\t\turl: string,\n\t\tconfig?: FetchConfig<TData, TErrorData, TResultMode>\n\t): Promise<GetCallApiResult<TData, TErrorData, TResultMode>> => {\n\t\ttype CallApiResult = GetCallApiResult<TData, TErrorData, TResultMode>;\n\n\t\tconst [fetchConfig, extraOptions] = splitConfig(config ?? {});\n\n\t\tconst { signal = baseSignal, body = baseBody, headers, ...restOfFetchConfig } = fetchConfig;\n\n\t\t// == Default Options\n\t\tconst options = {\n\t\t\tbodySerializer: JSON.stringify,\n\t\t\tresponseType: \"json\",\n\t\t\tbaseURL: \"\",\n\t\t\tretries: 0,\n\t\t\tretryDelay: 0,\n\t\t\tretryCodes: defaultRetryCodes,\n\t\t\tretryMethods: defaultRetryMethods,\n\t\t\tdefaultErrorMessage: \"Failed to fetch data from server!\",\n\t\t\tcancelRedundantRequests: true,\n\t\t\t...baseExtraOptions,\n\t\t\t...extraOptions,\n\t\t} satisfies ExtraOptions;\n\n\t\tconst prevFetchController = abortControllerStore.get(url);\n\n\t\tif (prevFetchController && options.cancelRedundantRequests) {\n\t\t\tconst reason = new DOMException(\n\t\t\t\t`Automatic cancelation of the previous unfinished request to this same url: ${url}`,\n\t\t\t\t\"AbortError\"\n\t\t\t);\n\t\t\tprevFetchController.abort(reason);\n\t\t}\n\n\t\tconst newFetchController = new AbortController();\n\n\t\tabortControllerStore.set(url, newFetchController);\n\n\t\tconst timeoutSignal = options.timeout ? AbortSignal.timeout(options.timeout) : null;\n\n\t\tconst combinedSignal = AbortSignal.any([\n\t\t\tnewFetchController.signal,\n\t\t\ttimeoutSignal ?? newFetchController.signal,\n\t\t\tsignal ?? newFetchController.signal,\n\t\t]);\n\n\t\tconst requestInit = {\n\t\t\tsignal: combinedSignal,\n\n\t\t\tmethod: \"GET\",\n\n\t\t\tbody: isObject(body) ? options.bodySerializer(body) : body,\n\n\t\t\t// == Return undefined if the following conditions are not met (so that native fetch would auto set the correct headers):\n\t\t\t// - headers are provided\n\t\t\t// - The body is an object\n\t\t\t// - The auth option is provided\n\t\t\theaders:\n\t\t\t\tbaseHeaders || headers || options.auth || isObject(body)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...(isObject(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isFormData(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"multipart/form-data\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(body) && {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isString(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${options.auth}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...(isObject(options.auth) && {\n\t\t\t\t\t\t\t\tAuthorization:\n\t\t\t\t\t\t\t\t\t\"bearer\" in options.auth\n\t\t\t\t\t\t\t\t\t\t? `Bearer ${options.auth.bearer}`\n\t\t\t\t\t\t\t\t\t\t: `Token ${options.auth.token}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t...objectifyHeaders(baseHeaders),\n\t\t\t\t\t\t\t...objectifyHeaders(headers),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\n\t\t\t...restOfBaseFetchConfig,\n\t\t\t...restOfFetchConfig,\n\t\t} satisfies $RequestOptions;\n\n\t\ttry {\n\t\t\tawait options.onRequest?.({ request: requestInit, options });\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${options.baseURL}${mergeUrlWithParams(url, options.query)}`,\n\t\t\t\trequestInit\n\t\t\t);\n\n\t\t\tconst shouldRetry =\n\t\t\t\t!response.ok &&\n\t\t\t\t!combinedSignal.aborted &&\n\t\t\t\toptions.retries > 0 &&\n\t\t\t\toptions.retryCodes.includes(response.status) &&\n\t\t\t\toptions.retryMethods.includes(requestInit.method);\n\n\t\t\tif (shouldRetry) {\n\t\t\t\tawait waitUntil(options.retryDelay);\n\n\t\t\t\treturn await callApi(url, { ...config, retries: options.retries - 1 });\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorData = await getResponseData<TErrorData>(\n\t\t\t\t\tresponse.clone(),\n\t\t\t\t\toptions.responseType,\n\t\t\t\t\toptions.responseParser\n\t\t\t\t);\n\n\t\t\t\t// == Pushing all error handling responsibilities to the catch block\n\t\t\t\tthrow new HTTPError({\n\t\t\t\t\terrorData,\n\t\t\t\t\tresponse,\n\t\t\t\t\tdefaultErrorMessage: options.defaultErrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst successData = await getResponseData<TData>(\n\t\t\t\tresponse.clone(),\n\t\t\t\toptions.responseType,\n\t\t\t\toptions.responseParser\n\t\t\t);\n\n\t\t\tconst validSuccessData = options.responseValidator\n\t\t\t\t? options.responseValidator(successData)\n\t\t\t\t: successData;\n\n\t\t\tawait options.onResponse?.({\n\t\t\t\t// == Workaround as opposed to using the spread operator, as it doesn't work on the response object. So using Object.assign instead on a clone of the response object.\n\t\t\t\tresponse: Object.assign(response.clone(), { data: validSuccessData }),\n\t\t\t\trequest: requestInit,\n\t\t\t\toptions,\n\t\t\t});\n\n\t\t\treturn resolveSuccessResult<CallApiResult>({ successData: validSuccessData, response, options });\n\n\t\t\t// == Exhaustive Error handling\n\t\t} catch (error) {\n\t\t\tconst resolveErrorResult = $resolveErrorResult<CallApiResult>({ error, options });\n\n\t\t\tif (error instanceof DOMException && error.name === \"TimeoutError\") {\n\t\t\t\tconst message = `Request timed out after ${options.timeout}ms`;\n\n\t\t\t\tconsole.error(`${error.name}:`, message);\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\n\t\t\t\tconst message = `Request aborted due to ${error.message}`;\n\n\t\t\t\tconsole.error(`${error.name}:`, message);\n\n\t\t\t\treturn resolveErrorResult({ message });\n\t\t\t}\n\n\t\t\tif (isHTTPErrorInstance<TErrorData>(error)) {\n\t\t\t\tconst { errorData, response } = error;\n\n\t\t\t\tawait options.onResponseError?.({\n\t\t\t\t\tresponse: Object.assign(response.clone(), { errorData }),\n\t\t\t\t\trequest: requestInit,\n\t\t\t\t\toptions,\n\t\t\t\t});\n\n\t\t\t\treturn resolveErrorResult({\n\t\t\t\t\terrorData,\n\t\t\t\t\tmessage: (errorData as PossibleErrorObject)?.message,\n\t\t\t\t\tresponse,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// == At this point only the request errors exist, so the request error interceptor is called\n\t\t\tawait options.onRequestError?.({ request: requestInit, error: error as Error, options });\n\n\t\t\treturn resolveErrorResult();\n\n\t\t\t// == Removing the now unneeded AbortController from store\n\t\t} finally {\n\t\t\tabortControllerStore.delete(url);\n\t\t}\n\t};\n\n\tcallApi.create = createFetchClient;\n\n\tcallApi.cancel = (url: string) => abortControllerStore.get(url)?.abort();\n\n\treturn callApi;\n};\n\nexport const callApi = createFetchClient();\n"]}
|