@superutils/fetch 0.1.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,8 +9,11 @@ This package enhances the native `fetch` API by providing a streamlined interfac
9
9
  - Features
10
10
  - Installation
11
11
  - Usage
12
- - `fetch(url, options)`
13
- - `fetchDeferred(deferOptions, url, fetchOptions)`
12
+ - [`fetch()`](#fetch): make HTTP requests just like built-in `fetch()`
13
+ - [`Method Specific Functions`](#methods)
14
+ - [`fetchDeferred()`](#fetch-deferred): cancellable and debounced or throttled `fetch()`
15
+ - [`post()`](#post): make post-like requests
16
+ - [`postDeferred()`](#post-deferred) cancellable and debounced or throttled `post()`
14
17
 
15
18
  ## Features
16
19
 
@@ -37,23 +40,52 @@ npm install @superutils/fetch
37
40
  Make a simple GET request. No need for `response.json()` or `result.data.theActualData` drilling.
38
41
 
39
42
  ```typescript
40
- import { fetch } from '@superutils/fetch'
43
+ import fetch from '@superutils/fetch'
41
44
 
42
- const theActualData = await fetch('https://dummyjson.com/products/1')
45
+ const theActualData = await fetch('https://dummyjson.com/products/1', {
46
+ method: 'get', // default
47
+ })
48
+ console.log(theActualData)
49
+ // Alternative:
50
+ const theActualData = await fetch.get('https://dummyjson.com/products/1')
43
51
  console.log(theActualData)
44
52
  ```
45
53
 
46
- <div id="fetchDeferred"></div>
54
+ <div id="methods"></div>
55
+
56
+ ### Method Specific Functions
57
+
58
+ While `fetch()` provides access to all HTTP request methods by specifying it in options (eg: `{ method: 'get' }`), for ease of use you can also use the following:
59
+
60
+ - `fetch.delete(...)`
61
+ - `fetch.get(...)`
62
+ - `fetch.head(...)`
63
+ - `fetch.options(...)`
64
+ - `fetch.patch(...)`
65
+ - `fetch.post(...)`
66
+ - `fetch.put(...)`
47
67
 
48
- ### `fetchDeferred(deferOptions, url, fetchOptions)`
68
+ **Deferred variants:** To debounce/throttle requests.
69
+
70
+ - `fetch.delete.deferred(...)`
71
+ - `fetch.get.deferred(...)`
72
+ - `fetch.head.deferred(...)`
73
+ - `fetch.options.deferred(...)`
74
+ - `fetch.patch.deferred(...)`
75
+ - `fetch.post.deferred(...)`
76
+ - `fetch.put.deferred(...)`
77
+
78
+ <div id="fetch-deferred"></div>
79
+
80
+ ### `fetch.get.deferred(deferOptions, defaultUrl, defaultOptions)`
49
81
 
50
82
  A practical utility that combines `PromisE.deferred()` from the `@superutils/promise` package with `fetch()`. It's perfect for implementing cancellable, debounced, or throttled search inputs.
51
83
 
52
84
  ```typescript
53
- import { fetchDeferred, ResolveIgnored } from '@superutils/fetch'
85
+ import fetch, { fetchDeferred, ResolveIgnored } from '@superutils/fetch'
54
86
 
55
87
  // Create a debounced search function with a 300ms delay.
56
- const searchProducts = fetchDeferred({
88
+ const searchProducts = fetch.get.deferred({
57
89
  delayMs: 300, // Debounce delay
58
90
  resolveIgnored: ResolveIgnored.WITH_UNDEFINED, // Ignored (aborted) promises will resolve with `undefined`
59
91
  })
@@ -97,11 +129,11 @@ setTimeout(() => {
97
129
  #### Using defaults to reduce redundancy
98
130
 
99
131
  ```typescript
100
- import { fetchDeferred, ResolveIgnored } from '@superutils/fetch'
132
+ import fetch, { ResolveIgnored } from '@superutils/fetch'
101
133
 
102
134
  // Create a throttled function to fetch a random quote.
103
135
  // The URL and a 3-second timeout are set as defaults, creating a reusable client.
104
- const getRandomQuote = fetchDeferred(
136
+ const getRandomQuote = fetch.get.deferred(
105
137
  {
106
138
  delayMs: 300, // Throttle window
107
139
  throttle: true,
@@ -126,8 +158,10 @@ getRandomQuote().then(quote => console.log('Call 3 resolved:', quote.id))
126
158
 
127
159
  <div id="post"></div>
128
160
 
129
- ### `post(url, options)`
161
+ ### `fetch.post(url, options)`
162
+
163
+ <div id="post-deferred"></div>
130
164
 
131
- <div id="postDeferred"></div>
165
+ ### `fetch.post.deferred(deferOptions, url, postOptions)`
132
166
 
133
- ### `postDeferred(deferOptions, url, postOptions)`
167
+ <div id="method-specific"></div>
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _superutils_promise from '@superutils/promise';
2
- import { RetryOptions, Config as Config$1, IPromisE, DeferredOptions } from '@superutils/promise';
3
- export { DeferredOptions, ResolveError, ResolveIgnored } from '@superutils/promise';
2
+ import { RetryOptions, IPromisE, DeferredAsyncOptions } from '@superutils/promise';
3
+ export { DeferredAsyncOptions, ResolveError, ResolveIgnored } from '@superutils/promise';
4
4
  import { ValueOrPromise } from '@superutils/core';
5
5
 
6
6
  type FetchArgs = [url: string | URL, options?: FetchOptions];
@@ -17,7 +17,8 @@ declare enum FetchAs {
17
17
  response = "response",
18
18
  text = "text"
19
19
  }
20
- type FetchConf = {
20
+ /** Custom fetch options (not used by built-in `fetch()`*/
21
+ type FetchCustomOptions = {
21
22
  /**
22
23
  * Specify how the parse the result. To get raw response use {@link FetchAs.response}.
23
24
  * Default: 'json'
@@ -28,7 +29,7 @@ type FetchConf = {
28
29
  interceptors?: FetchInterceptors;
29
30
  /** Request timeout in milliseconds. */
30
31
  timeout?: number;
31
- };
32
+ } & FetchRetryOptions;
32
33
  /** Default args */
33
34
  type FetchDeferredArgs = [
34
35
  url?: string | URL,
@@ -230,7 +231,8 @@ type FetchInterceptors = {
230
231
  /**
231
232
  * Fetch request options
232
233
  */
233
- type FetchOptions = RequestInit & FetchConf & FetchRetryOptions;
234
+ type FetchOptions = RequestInit & FetchCustomOptions;
235
+ type FetchOptionsDefaults = Omit<FetchOptionsInterceptor, 'method' | 'retryIf'>;
234
236
  /**
235
237
  * Fetch options available to interceptors
236
238
  */
@@ -239,7 +241,7 @@ type FetchOptionsInterceptor = Omit<FetchOptions, 'as' | 'errMsgs' | 'intercepto
239
241
  errMsgs: Required<FetchErrMsgs>;
240
242
  headers: Headers;
241
243
  interceptors: Required<FetchInterceptors>;
242
- } & Required<FetchRetryOptions>;
244
+ } & FetchRetryOptions;
243
245
  /**
244
246
  * Result types for specific parsers ("as": FetchAs)
245
247
  */
@@ -255,7 +257,7 @@ interface FetchResult<T> {
255
257
  /**
256
258
  * Fetch retry options
257
259
  */
258
- type FetchRetryOptions = Partial<RetryOptions> & {
260
+ type FetchRetryOptions = Omit<Partial<RetryOptions>, 'retry' | 'retryIf'> & {
259
261
  /**
260
262
  * Maximum number of retries.
261
263
  *
@@ -264,6 +266,7 @@ type FetchRetryOptions = Partial<RetryOptions> & {
264
266
  * Default: `0`
265
267
  */
266
268
  retry?: number;
269
+ retryIf?: null | ((response: Response | undefined, retryCount: number, error?: unknown) => boolean | Promise<boolean>);
267
270
  };
268
271
  /**
269
272
  * Generic fetch interceptor type
@@ -273,44 +276,49 @@ type PostBody = Record<string, unknown> | BodyInit | null;
273
276
  type PostArgs = [
274
277
  url: string | URL,
275
278
  data?: PostBody,
276
- options?: Omit<FetchOptions, 'method'> & {
277
- /** Default: `'post'` */
278
- method?: 'post' | 'put' | 'patch' | 'delete';
279
- }
279
+ options?: PostOptions
280
280
  ];
281
-
282
- type Config = Config$1 & {
283
- fetchOptions: FetchOptionsInterceptor;
284
- };
285
- declare const config: Config;
286
-
287
281
  /**
288
- * A `fetch()` replacement that simplifies data fetching with automatic JSON parsing, request timeouts, retries,
289
- * and powerful interceptors. It also includes deferred and throttled request capabilities for complex asynchronous
290
- * control flows.
291
- *
292
- * Will reject promise if response status code is not 2xx (200 <= status < 300).
293
- *
294
- * @param url
295
- * @param options (optional) all built-in `fetch()` options such as "method", "headers" and the additionals below.
296
- * @param options.abortCtrl (optional) if not provided `AbortController` will be instantiated when `timeout` used.
297
- * @param options.headers (optional) request headers. Default: `{ 'content-type' : 'application/json'}`
298
- * @param options.interceptors (optional) request interceptor callbacks. See {@link FetchInterceptors} for details.
299
- * @param options.method (optional) Default: `"get"`
300
- * @param options.timeout (optional) duration in milliseconds to abort the request if it takes longer.
301
- * @param options.parse (optional) specify how to parse the result.
302
- * Default: {@link FetchAs.json}
303
- * For raw `Response` use {@link FetchAs.response}
304
282
  *
305
- * @example Make a simple HTTP requests
283
+ * @example
306
284
  * ```typescript
307
- * import { fetch } from '@superutils/fetch'
308
- *
309
- * // no need for `response.json()` or `result.data.theActualData` drilling
310
- * fetch('https://dummyjson.com/products/1').then(theActualData => console.log(theActualData))
285
+ * // test with types
286
+ * type T1 = PostDeferredCallbackArgs<string | URL, undefined> // expected: [data, options]
287
+ * type T2 = PostDeferredCallbackArgs<undefined, string> // expected: [url, options]
288
+ * type T3 = PostDeferredCallbackArgs // expected: [url, data, options]
289
+ * type T4 = PostDeferredCallbackArgs<string, string> // expected: [options]
290
+ *
291
+ * const data = { name: 'test' }
292
+ * const url = 'https://domain.com'
293
+ * // test with postDeferred()
294
+ * const f1 = postDeferred({}, 'https://domain.com')
295
+ * // expected: [data, options]
296
+ * f1({data: 1}).then(console.log, console.warn)
297
+ *
298
+ * const f2 = postDeferred({}, undefined, 'dome data')
299
+ * // expected: [url, options]
300
+ * f2('https').then(console.log, console.warn)
301
+ *
302
+ * const f3 = postDeferred({})
303
+ * // expected: [url, data, options]
304
+ * f3('https://domain.com').then(console.log, console.warn)
305
+ *
306
+ * const f4 = postDeferred({}, 'url', 'data')
307
+ * // expected: [options]
308
+ * f4().then(console.log, console.warn)
311
309
  * ```
312
310
  */
313
- declare function fetch$1<TJSON, TOptions extends FetchOptions = FetchOptions, TReturn = TOptions['as'] extends FetchAs ? FetchResult<TJSON>[TOptions['as']] : TJSON>(url: string | URL, options?: TOptions): IPromisE<TReturn>;
311
+ type PostDeferredCallbackArgs<TUrl = undefined, TData = undefined, RPA extends unknown[] = Required<PostArgs>> = [TUrl, TData] extends [RPA[0], undefined] ? [data?: PostArgs[1], options?: PostArgs[2]] : [TUrl, TData] extends [undefined, RPA[1]] ? [url: PostArgs[0], options?: PostArgs[2]] : [TUrl, TData] extends [RPA[0], RPA[1]] ? [options?: PostArgs[2]] : PostArgs;
312
+ type PostOptions = Omit<FetchOptions, 'method'> & {
313
+ /** Default: `'post'` */
314
+ method?: 'post' | 'put' | 'patch' | 'delete';
315
+ };
316
+
317
+ declare const fetch$1: {
318
+ <T, TOptions extends FetchOptions = FetchOptions, TReturn = TOptions["as"] extends FetchAs ? FetchResult<T>[TOptions["as"]] : T>(url: string | URL, options?: TOptions): IPromisE<TReturn>;
319
+ /** Default fetch options */
320
+ defaults: FetchOptionsDefaults;
321
+ };
314
322
 
315
323
  /**
316
324
  * Creates a deferred/throttled version of {@link fetch}, powered by {@link PromisE.deferred}.
@@ -321,10 +329,11 @@ declare function fetch$1<TJSON, TOptions extends FetchOptions = FetchOptions, TR
321
329
  * `fetchDeferred` uses this to automatically abort pending requests when a new one is initiated, preventing race conditions and redundant network traffic.
322
330
  *
323
331
  * @param deferOptions Configuration for the deferred execution behavior (e.g., `delayMs`, `throttle`).
324
- * See {@link DeferredOptions} for details.
325
- * @param defaultFetchArgs (optional) Default `url` and `fetchOptions` to be used for every call made by the
326
- * returned function. This is useful for creating a reusable client for a specific endpoint.
327
- *
332
+ * See {@link DeferredAsyncOptions} for details.
333
+ * @param globalUrl (optional) If a global URL is `undefined`, returned callback will always require an URL.
334
+ * @param defaultOptions (optional) Default {@link FetchOptions} to be used by the returned function.
335
+ * Default options will be merged with the options provided in the callback.
336
+ * If the same property is provided in both cases, defaults will be overriden by the callback.
328
337
  *
329
338
  * @example Debounce/Throttle requests for an auto-complete search input
330
339
  * ```typescript
@@ -397,7 +406,7 @@ declare function fetch$1<TJSON, TOptions extends FetchOptions = FetchOptions, TR
397
406
  * // Console output will show the same quote ID for all three calls.
398
407
  * ```
399
408
  */
400
- declare function fetchDeferred<ThisArg, DefaultUrl extends string | URL>(deferOptions?: DeferredOptions<ThisArg>, defaultUrl?: DefaultUrl, defaultOptions?: FetchDeferredArgs[1]): <TResult = unknown>(...args: DefaultUrl extends undefined ? FetchArgs : [url?: string | URL | undefined, options?: FetchOptions | undefined]) => _superutils_promise.IPromisE<TResult>;
409
+ declare function fetchDeferred<ThisArg = unknown, Delay extends number = number, GlobalUrl extends string | URL | undefined = string | URL | undefined, Args extends unknown[] = undefined extends GlobalUrl ? FetchArgs : [options?: FetchOptions]>(deferOptions?: DeferredAsyncOptions<ThisArg, Delay>, globalUrl?: GlobalUrl, defaultOptions?: FetchDeferredArgs[1]): <TResult = unknown>(...args: Args) => _superutils_promise.IPromisE<TResult>;
401
410
 
402
411
  /**
403
412
  * Creates a deferred/throttled function for making `POST`, `PUT`, or `PATCH` requests, powered by
@@ -408,6 +417,16 @@ declare function fetchDeferred<ThisArg, DefaultUrl extends string | URL>(deferOp
408
417
  * Like `fetchDeferred`, it automatically aborts pending requests when a new one is initiated, ensuring only
409
418
  * the most recent or relevant action is executed.
410
419
  *
420
+ *
421
+ * @param deferOptions Configuration for the deferred execution behavior (e.g., `delayMs`, `throttle`).
422
+ * See {@link DeferredAsyncOptions} for details.
423
+ * @param globalUrl (optional) If global URL is `undefined`, returned callback will always require an URL.
424
+ * @param globalData (optional) If global data is `undefined`, returned callback will allow a data parameter.
425
+ * @param defaultOptions (optional) Default {@link FetchOptions} to be used by the returned function.
426
+ * Default options will be merged with the options provided in the callback.
427
+ * If the same property is provided in both cases, defaults will be overriden by the callback.
428
+ *
429
+ *
411
430
  * @example Debouncing an authentication token refresh
412
431
  * ```typescript
413
432
  * import { postDeferred } from '@superutils/fetch'
@@ -485,12 +504,12 @@ declare function fetchDeferred<ThisArg, DefaultUrl extends string | URL>(deferOp
485
504
  * // This results in only two network requests instead of four.
486
505
  * ```
487
506
  */
488
- declare function postDeferred<ThisArg, DefaultUrl extends string | URL>(deferOptions?: DeferredOptions<ThisArg>, defaultUrl?: DefaultUrl, defaultData?: PostArgs[1], defaultOptions?: PostArgs[2]): <TResult = unknown>(...args: DefaultUrl extends undefined ? PostArgs : [url?: string | URL | undefined, data?: PostBody | undefined, options?: (Omit<FetchOptions, "method"> & {
489
- method?: "post" | "put" | "patch" | "delete";
490
- }) | undefined]) => _superutils_promise.IPromisE<TResult>;
507
+ declare function postDeferred<ThisArg, Delay extends number = number, GlobalUrl extends PostArgs[0] | undefined = undefined, GlobalData extends PostArgs[1] | undefined = undefined, Args extends unknown[] = PostDeferredCallbackArgs<GlobalUrl, GlobalData>>(deferOptions?: DeferredAsyncOptions<ThisArg, Delay>, globalUrl?: GlobalUrl, // The default URL for all calls
508
+ globalData?: GlobalData, // The default data for all calls
509
+ defaultOptions?: PostOptions): <TResult = unknown>(...args: Args) => _superutils_promise.IPromisE<TResult>;
491
510
 
492
511
  /**
493
- * Merge one or more {@link FetchOptions} with global fetch options ({@link config.fetchOptions}).
512
+ * Merge one or more {@link FetchOptions}
494
513
  *
495
514
  * Notes:
496
515
  * - {@link config.fetchOptions} will be added as the base and not necessary to be included
@@ -505,16 +524,109 @@ declare function postDeferred<ThisArg, DefaultUrl extends string | URL>(deferOpt
505
524
  */
506
525
  declare const mergeFetchOptions: (...allOptions: FetchOptions[]) => FetchOptionsInterceptor;
507
526
 
508
- type Func = <T, Options extends Omit<FetchOptions, 'method'>>(url: string | URL, options?: Options) => ReturnType<typeof fetch$1<T, Options>>;
509
- type MethodFunc = Func & ({
527
+ type FetchWithoutMethods = typeof fetch$1;
528
+ /** Describes method-specific fetch-functions that includes `.deferred()` function for deferred/throttled requests */
529
+ type FetchMethodFunc = (<T, Options extends Omit<FetchOptions, 'method'>>(url: string | URL, options?: Options) => ReturnType<typeof fetch$1<T, Options>>) & {
510
530
  deferred: typeof fetchDeferred;
511
- } | {
531
+ };
532
+ /** Describes method-specific fetch-functions that includes `.deferred()` function for deferred/throttled requests */
533
+ type PostMethodFunc = (<T, Options extends Omit<FetchOptions, 'method'>>(url: string | URL, data?: PostBody, options?: Options) => ReturnType<typeof fetch$1<T, Options>>) & {
512
534
  deferred: typeof postDeferred;
513
- });
514
- type FetchDeferred = typeof fetchDeferred | typeof postDeferred;
515
- interface DefaultFetch extends Record<string, MethodFunc> {
516
- <T, O extends FetchOptions>(...params: Parameters<typeof fetch$1<T, O>>): ReturnType<typeof fetch$1<T, O>>;
535
+ };
536
+ interface FetchWithMethods extends FetchWithoutMethods {
537
+ get: FetchMethodFunc;
538
+ head: FetchMethodFunc;
539
+ options: FetchMethodFunc;
540
+ delete: PostMethodFunc;
541
+ patch: PostMethodFunc;
542
+ post: PostMethodFunc;
543
+ put: PostMethodFunc;
517
544
  }
518
- declare const fetch: DefaultFetch;
545
+ /**
546
+ * @function fetch
547
+ *
548
+ * A `fetch()` replacement that simplifies data fetching with automatic JSON parsing, request timeouts, retries,
549
+ * and powerful interceptors. It also includes deferred and throttled request capabilities for complex asynchronous
550
+ * control flows.
551
+ *
552
+ * Will reject promise if response status code is not 2xx (200 <= status < 300).
553
+ *
554
+ * ## Method Specific Functions
555
+ *
556
+ * While `fetch()` provides access to all HTTP request methods by specifying it in options (eg: `{ method: 'get' }`),
557
+ * for ease of use you can also use the following:
558
+ *
559
+ * - `fetch.delete(...)`
560
+ * - `fetch.get(...)`
561
+ * - `fetch.head(...)`
562
+ * - `fetch.options(...)`
563
+ * - `fetch.patch(...)`
564
+ * - `fetch.post(...)`
565
+ * - `fetch.put(...)`
566
+ *
567
+ * **Deferred variants:** To debounce/throttle requests.
568
+ *
569
+ * - `fetch.delete.deferred(...)`
570
+ * - `fetch.get.deferred(...)`
571
+ * - `fetch.head.deferred(...)`
572
+ * - `fetch.options.deferred(...)`
573
+ * - `fetch.patch.deferred(...)`
574
+ * - `fetch.post.deferred(...)`
575
+ * - `fetch.put.deferred(...)`
576
+ *
577
+ * @template T The type of the value that the `fetch` resolves to.
578
+ * @template TReturn Return value type.
579
+ *
580
+ * If `T` is not specified defaults to the following based on the value of `options.as`:
581
+ * - FetchAs.arrayBuffer: `ArrayBuffer`
582
+ * - FetchAs.blob: `Blob`
583
+ * - FetchAs.bytes: `Uint8Array<ArrayBuffer>`
584
+ * - FetchAs.formData: `FormData`
585
+ * - FetchAs.json: `unknown`
586
+ * - FetchAs.text: `string`
587
+ * - FetchAs.response: `Response`
588
+ * @param url
589
+ * @param options (optional) all built-in `fetch()` options such as "method", "headers" and the additionals below.
590
+ *
591
+ * Options' default values (excluding `method` and `retryIf`) can be configured to be EFFECTIVE GLOBALLY.
592
+ *
593
+ * ```typescript
594
+ * fetch.defaults = {
595
+ * as: FetchAs.json,
596
+ * errMsgs: {
597
+ * invalidUrl: 'Invalid URL',
598
+ * parseFailed: 'Failed to parse response as',
599
+ * reqTimedout: 'Request timed out',
600
+ * requestFailed: 'Request failed with status code:',
601
+ * },
602
+ * headers: new Headers([['content-type', 'application/json']]),
603
+ * interceptors: {
604
+ * error: [],
605
+ * request: [],
606
+ * response: [],
607
+ * result: [],
608
+ * },
609
+ * timeout: 0,
610
+ * ........
611
+ * }
612
+ * ```
613
+ * @param options.abortCtrl (optional) if not provided `AbortController` will be instantiated when `timeout` used.
614
+ * @param options.headers (optional) request headers. Default: `{ 'content-type' : 'application/json'}`
615
+ * @param options.interceptors (optional) request interceptor callbacks. See {@link FetchInterceptors} for details.
616
+ * @param options.method (optional) Default: `"get"`
617
+ * @param options.timeout (optional) duration in milliseconds to abort the request if it takes longer.
618
+ * @param options.parse (optional) specify how to parse the result.
619
+ * Default: {@link FetchAs.json}
620
+ * For raw `Response` use {@link FetchAs.response}
621
+ *
622
+ * @example Make a simple HTTP requests
623
+ * ```typescript
624
+ * import { fetch } from '@superutils/fetch'
625
+ *
626
+ * // no need for `response.json()` or `result.data.theActualData` drilling
627
+ * fetch('https://dummyjson.com/products/1').then(theActualData => console.log(theActualData))
628
+ * ```
629
+ */
630
+ declare const fetch: FetchWithMethods;
519
631
 
520
- export { type Config, type DefaultFetch, type FetchArgs, type FetchArgsInterceptor, FetchAs, type FetchConf, type FetchDeferred, type FetchDeferredArgs, type FetchErrMsgs, FetchError, type FetchInterceptorError, type FetchInterceptorRequest, type FetchInterceptorResponse, type FetchInterceptorResult, type FetchInterceptors, type FetchOptions, type FetchOptionsInterceptor, type FetchResult, type FetchRetryOptions, type Func, type Interceptor, type MethodFunc, type PostArgs, type PostBody, config, fetch as default, fetch, fetchDeferred, mergeFetchOptions, postDeferred };
632
+ export { type FetchArgs, type FetchArgsInterceptor, FetchAs, type FetchCustomOptions, type FetchDeferredArgs, type FetchErrMsgs, FetchError, type FetchInterceptorError, type FetchInterceptorRequest, type FetchInterceptorResponse, type FetchInterceptorResult, type FetchInterceptors, type FetchMethodFunc, type FetchOptions, type FetchOptionsDefaults, type FetchOptionsInterceptor, type FetchResult, type FetchRetryOptions, type FetchWithMethods, type FetchWithoutMethods, type Interceptor, type PostArgs, type PostBody, type PostDeferredCallbackArgs, type PostMethodFunc, type PostOptions, fetch as default, fetch, fetchDeferred, mergeFetchOptions, postDeferred };
package/dist/index.js CHANGED
@@ -1,62 +1,6 @@
1
- // src/config.ts
2
- import {
3
- config as promiseConfig
4
- } from "@superutils/promise";
5
-
6
- // src/types.ts
7
- var FetchAs = /* @__PURE__ */ ((FetchAs3) => {
8
- FetchAs3["arrayBuffer"] = "arrayBuffer";
9
- FetchAs3["blob"] = "blob";
10
- FetchAs3["bytes"] = "bytes";
11
- FetchAs3["formData"] = "formData";
12
- FetchAs3["json"] = "json";
13
- FetchAs3["response"] = "response";
14
- FetchAs3["text"] = "text";
15
- return FetchAs3;
16
- })(FetchAs || {});
17
- var FetchError = class extends Error {
18
- constructor(message, options) {
19
- super(message, { cause: options.cause });
20
- this.name = "FetchError";
21
- this.options = options.options;
22
- this.response = options.response;
23
- this.url = options.url;
24
- }
25
- };
26
-
27
- // src/config.ts
28
- var fetchOptions = {
29
- as: "json" /* json */,
30
- errMsgs: {
31
- invalidUrl: "Invalid URL",
32
- parseFailed: "Failed to parse response as",
33
- reqTimedout: "Request timed out",
34
- requestFailed: "Request failed with status code:"
35
- },
36
- // all error messages must be defined here
37
- headers: new Headers([["content-type", "application/json"]]),
38
- /** Global interceptors for fetch requests */
39
- interceptors: {
40
- /**
41
- * Global error interceptors to be invoked whenever an exception occurs
42
- * Returning an
43
- */
44
- error: [],
45
- /** Interceptors to be invoked before making fetch requests */
46
- request: [],
47
- response: [],
48
- result: []
49
- },
50
- ...promiseConfig.retryOptions,
51
- retryIf: null,
52
- timeout: 0
53
- };
54
- var config = promiseConfig;
55
- config.fetchOptions = fetchOptions;
56
- var config_default = config;
57
-
58
1
  // src/fetch.ts
59
2
  import {
3
+ isEmpty as isEmpty2,
60
4
  isFn as isFn2,
61
5
  isPositiveNumber,
62
6
  isPromise,
@@ -64,35 +8,6 @@ import {
64
8
  } from "@superutils/core";
65
9
  import PromisE2 from "@superutils/promise";
66
10
 
67
- // src/mergeFetchOptions.ts
68
- import { isEmpty, objKeys } from "@superutils/core";
69
- var mergeFetchOptions = (...allOptions) => allOptions.reduce((o1, o2) => {
70
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
71
- const { errMsgs = {}, headers, interceptors: ints1 = {} } = o1;
72
- const { errMsgs: msgs2 = {}, interceptors: ints2 = {} } = o2;
73
- o2.headers && new Headers(o2.headers).forEach(
74
- (value, key) => headers.set(key, value)
75
- );
76
- for (const key of objKeys(msgs2)) {
77
- if (!isEmpty(msgs2[key])) continue;
78
- errMsgs[key] = msgs2[key];
79
- }
80
- return {
81
- ...o1,
82
- ...o2,
83
- errMsgs,
84
- headers,
85
- interceptors: {
86
- error: (_c = (_b = ints1 == null ? void 0 : ints1.error) == null ? void 0 : _b.concat((_a = ints2 == null ? void 0 : ints2.error) != null ? _a : [])) != null ? _c : [],
87
- request: (_f = (_e = ints1 == null ? void 0 : ints1.request) == null ? void 0 : _e.concat((_d = ints2 == null ? void 0 : ints2.request) != null ? _d : [])) != null ? _f : [],
88
- response: (_i = (_h = ints1 == null ? void 0 : ints1.response) == null ? void 0 : _h.concat((_g = ints2 == null ? void 0 : ints2.response) != null ? _g : [])) != null ? _i : [],
89
- result: (_l = (_k = ints1 == null ? void 0 : ints1.result) == null ? void 0 : _k.concat((_j = ints2 == null ? void 0 : ints2.result) != null ? _j : [])) != null ? _l : []
90
- },
91
- timeout: (_n = (_m = o2.timeout) != null ? _m : o1.timeout) != null ? _n : 0
92
- };
93
- }, config_default.fetchOptions);
94
- var mergeFetchOptions_default = mergeFetchOptions;
95
-
96
11
  // src/executeInterceptors.ts
97
12
  import { fallbackIfFails, isFn } from "@superutils/core";
98
13
  var executeInterceptors = async (value, interceptors, ...args) => {
@@ -110,40 +25,99 @@ var executeInterceptors_default = executeInterceptors;
110
25
 
111
26
  // src/getResponse.ts
112
27
  import PromisE from "@superutils/promise";
113
- var getResponse = async (...[url, options]) => {
114
- const doFetch = () => globalThis.fetch(url, options).catch(
115
- (err) => err.message === "Failed to fetch" ? (
116
- // catch network errors to allow retries
117
- new Response(null, {
118
- status: 0,
119
- statusText: "Network Error"
120
- })
121
- ) : globalThis.Promise.reject(err)
122
- );
123
- const response = await PromisE.retry(doFetch, {
28
+ import { isPositiveInteger } from "@superutils/core";
29
+ var getResponse = async (...[url, options = {}]) => {
30
+ let attemptCount = 0;
31
+ const doFetch = async () => {
32
+ attemptCount++;
33
+ return globalThis.fetch(url, options).catch(
34
+ (err) => err.message === "Failed to fetch" ? (
35
+ // catch network errors to allow retries
36
+ new Response(null, {
37
+ status: 0,
38
+ statusText: "Network Error"
39
+ })
40
+ ) : Promise.reject(err)
41
+ );
42
+ };
43
+ if (!isPositiveInteger(options.retry)) return doFetch();
44
+ const response = PromisE.retry(doFetch, {
124
45
  ...options,
125
- retryIf: (res, count) => {
46
+ retryIf: async (res, count) => {
126
47
  var _a;
127
- return !(res == null ? void 0 : res.ok) && ((_a = options == null ? void 0 : options.retryIf) == null ? void 0 : _a.call(options, res, count)) !== false;
48
+ return (res == null ? void 0 : res.ok) === false || await ((_a = options == null ? void 0 : options.retryIf) == null ? void 0 : _a.call(options, res, count)) === true;
128
49
  }
129
- }).catch((err) => {
130
- if (!(options == null ? void 0 : options.retry)) return Promise.reject(err);
131
- const msg = `Request failed after attempt #${(options.retry || 0) + 1}`;
132
- return Promise.reject(new Error(msg, { cause: err }));
133
- });
50
+ }).catch(
51
+ (err) => Promise.reject(
52
+ new Error(`Request failed after attempt #${attemptCount}`, {
53
+ cause: err
54
+ })
55
+ )
56
+ );
134
57
  return response;
135
58
  };
136
59
  var getResponse_default = getResponse;
137
60
 
61
+ // src/mergeFetchOptions.ts
62
+ import { isEmpty, objKeys } from "@superutils/core";
63
+ var mergeFetchOptions = (...allOptions) => allOptions.reduce(
64
+ (o1, o2) => {
65
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
66
+ const { errMsgs = {}, headers, interceptors: ints1 = {} } = o1;
67
+ const { errMsgs: msgs2 = {}, interceptors: ints2 = {} } = o2;
68
+ o2.headers && new Headers(o2.headers).forEach((value, key) => {
69
+ headers && headers.set(key, value);
70
+ });
71
+ for (const key of objKeys(msgs2)) {
72
+ if (!isEmpty(msgs2[key])) errMsgs[key] = msgs2[key];
73
+ }
74
+ return {
75
+ ...o1,
76
+ ...o2,
77
+ errMsgs,
78
+ headers,
79
+ interceptors: {
80
+ error: (_c = (_b = ints1 == null ? void 0 : ints1.error) == null ? void 0 : _b.concat((_a = ints2 == null ? void 0 : ints2.error) != null ? _a : [])) != null ? _c : [],
81
+ request: (_f = (_e = ints1 == null ? void 0 : ints1.request) == null ? void 0 : _e.concat((_d = ints2 == null ? void 0 : ints2.request) != null ? _d : [])) != null ? _f : [],
82
+ response: (_i = (_h = ints1 == null ? void 0 : ints1.response) == null ? void 0 : _h.concat((_g = ints2 == null ? void 0 : ints2.response) != null ? _g : [])) != null ? _i : [],
83
+ result: (_l = (_k = ints1 == null ? void 0 : ints1.result) == null ? void 0 : _k.concat((_j = ints2 == null ? void 0 : ints2.result) != null ? _j : [])) != null ? _l : []
84
+ },
85
+ timeout: (_n = (_m = o2.timeout) != null ? _m : o1.timeout) != null ? _n : 0
86
+ };
87
+ },
88
+ { headers: new Headers() }
89
+ );
90
+ var mergeFetchOptions_default = mergeFetchOptions;
91
+
92
+ // src/types.ts
93
+ var FetchAs = /* @__PURE__ */ ((FetchAs2) => {
94
+ FetchAs2["arrayBuffer"] = "arrayBuffer";
95
+ FetchAs2["blob"] = "blob";
96
+ FetchAs2["bytes"] = "bytes";
97
+ FetchAs2["formData"] = "formData";
98
+ FetchAs2["json"] = "json";
99
+ FetchAs2["response"] = "response";
100
+ FetchAs2["text"] = "text";
101
+ return FetchAs2;
102
+ })(FetchAs || {});
103
+ var FetchError = class extends Error {
104
+ constructor(message, options) {
105
+ super(message, { cause: options.cause });
106
+ this.name = "FetchError";
107
+ this.options = options.options;
108
+ this.response = options.response;
109
+ this.url = options.url;
110
+ }
111
+ };
112
+
138
113
  // src/fetch.ts
139
- function fetch(url, options = {}) {
140
- var _a;
114
+ var fetch = (url, options = {}) => {
141
115
  let abortCtrl;
142
116
  let timeoutId;
143
- (_a = options.method) != null ? _a : options.method = "get";
144
117
  const promise = new PromisE2(async (resolve, reject) => {
145
- var _a2, _b;
146
- const _options = mergeFetchOptions_default(options);
118
+ var _a, _b;
119
+ const _options = mergeFetchOptions_default(fetch.defaults, options);
120
+ if (isEmpty2(_options.method)) _options.method = "get";
147
121
  const errorInterceptors = [..._options.interceptors.error];
148
122
  const requestInterceptors = [..._options.interceptors.request];
149
123
  const responseInterceptors = [..._options.interceptors.response];
@@ -151,10 +125,10 @@ function fetch(url, options = {}) {
151
125
  url = await executeInterceptors_default(url, requestInterceptors, url, _options);
152
126
  const { as: parseAs, errMsgs, timeout } = _options;
153
127
  if (isPositiveNumber(timeout)) {
154
- (_a2 = _options.abortCtrl) != null ? _a2 : _options.abortCtrl = new AbortController();
128
+ (_a = _options.abortCtrl) != null ? _a : _options.abortCtrl = new AbortController();
155
129
  timeoutId = setTimeout(() => {
156
- var _a3;
157
- return (_a3 = _options.abortCtrl) == null ? void 0 : _a3.abort();
130
+ var _a2;
131
+ return (_a2 = _options.abortCtrl) == null ? void 0 : _a2.abort();
158
132
  }, timeout);
159
133
  }
160
134
  abortCtrl = _options.abortCtrl;
@@ -183,12 +157,12 @@ function fetch(url, options = {}) {
183
157
  const parseFunc = response[parseAs];
184
158
  if (isFn2(parseFunc)) {
185
159
  const handleErr = (err) => {
186
- var _a3, _b2;
160
+ var _a2, _b2;
187
161
  err = new Error(
188
162
  [
189
163
  errMsgs.parseFailed,
190
164
  parseAs + ".",
191
- (_b2 = `${(_a3 = err == null ? void 0 : err.message) != null ? _a3 : err}`) == null ? void 0 : _b2.replace("Error: ", "")
165
+ (_b2 = `${(_a2 = err == null ? void 0 : err.message) != null ? _a2 : err}`) == null ? void 0 : _b2.replace("Error: ", "")
192
166
  ].join(" "),
193
167
  { cause: err }
194
168
  );
@@ -227,32 +201,53 @@ function fetch(url, options = {}) {
227
201
  });
228
202
  promise.onEarlyFinalize.push(() => abortCtrl == null ? void 0 : abortCtrl.abort());
229
203
  return promise;
230
- }
204
+ };
205
+ fetch.defaults = {
206
+ as: "json" /* json */,
207
+ errMsgs: {
208
+ invalidUrl: "Invalid URL",
209
+ parseFailed: "Failed to parse response as",
210
+ reqTimedout: "Request timed out",
211
+ requestFailed: "Request failed with status code:"
212
+ },
213
+ // all error messages must be defined here
214
+ headers: new Headers([["content-type", "application/json"]]),
215
+ /** Global interceptors for fetch requests */
216
+ interceptors: {
217
+ /**
218
+ * Global error interceptors to be invoked whenever an exception occurs
219
+ * Returning an
220
+ */
221
+ error: [],
222
+ /** Interceptors to be invoked before making fetch requests */
223
+ request: [],
224
+ response: [],
225
+ result: []
226
+ },
227
+ timeout: 0
228
+ };
231
229
  var fetch_default = fetch;
232
230
 
233
231
  // src/fetchDeferred.ts
234
- import { forceCast } from "@superutils/core";
235
232
  import PromisE3 from "@superutils/promise";
236
233
  import {
237
234
  ResolveError,
238
235
  ResolveIgnored
239
236
  } from "@superutils/promise";
240
- function fetchDeferred(deferOptions = {}, defaultUrl, defaultOptions) {
237
+ function fetchDeferred(deferOptions = {}, globalUrl, defaultOptions) {
241
238
  let _abortCtrl;
242
239
  const fetchCallback = (...args) => {
243
- var _a, _b;
244
- const [url, options = {}] = args;
245
- (_a = options.abortCtrl) != null ? _a : options.abortCtrl = new AbortController();
246
- (_b = options.timeout) != null ? _b : options.timeout = defaultOptions == null ? void 0 : defaultOptions.timeout;
247
- options.errMsgs = { ...defaultOptions == null ? void 0 : defaultOptions.errMsgs, ...options.errMsgs };
248
- const { abortCtrl } = options;
249
- _abortCtrl == null ? void 0 : _abortCtrl.abort();
250
- _abortCtrl = abortCtrl;
240
+ var _a, _b, _c;
241
+ let options = {
242
+ ...(_a = globalUrl === void 0 ? args[1] : args[0]) != null ? _a : {}
243
+ };
244
+ if (defaultOptions) options = mergeFetchOptions_default(defaultOptions, options);
245
+ (_b = options.abortCtrl) != null ? _b : options.abortCtrl = new AbortController();
246
+ (_c = _abortCtrl == null ? void 0 : _abortCtrl.abort) == null ? void 0 : _c.call(_abortCtrl);
247
+ _abortCtrl = options.abortCtrl;
251
248
  const promise = fetch_default(
252
- ...forceCast([
253
- url != null ? url : defaultUrl,
254
- mergeFetchOptions_default(defaultOptions != null ? defaultOptions : {}, options)
255
- ])
249
+ globalUrl != null ? globalUrl : args[0],
250
+ options
256
251
  );
257
252
  promise.onEarlyFinalize.push(() => _abortCtrl == null ? void 0 : _abortCtrl.abort());
258
253
  return promise;
@@ -262,7 +257,6 @@ function fetchDeferred(deferOptions = {}, defaultUrl, defaultOptions) {
262
257
  var fetchDeferred_default = fetchDeferred;
263
258
 
264
259
  // src/postDeferred.ts
265
- import { forceCast as forceCast2 } from "@superutils/core";
266
260
  import PromisE4 from "@superutils/promise";
267
261
 
268
262
  // src/post.ts
@@ -285,21 +279,22 @@ import {
285
279
  ResolveError as ResolveError2,
286
280
  ResolveIgnored as ResolveIgnored2
287
281
  } from "@superutils/promise";
288
- function postDeferred(deferOptions = {}, defaultUrl, defaultData, defaultOptions) {
282
+ function postDeferred(deferOptions = {}, globalUrl, globalData, defaultOptions) {
289
283
  let _abortCtrl;
290
- const doPost = (...[url, data, options = {}]) => {
291
- var _a;
292
- (_a = options.abortCtrl) != null ? _a : options.abortCtrl = new AbortController();
293
- _abortCtrl == null ? void 0 : _abortCtrl.abort();
294
- _abortCtrl = options.abortCtrl;
295
- const mergedOptions = mergeFetchOptions_default(options, defaultOptions != null ? defaultOptions : {});
296
- const promise = post(
297
- ...forceCast2([
298
- url != null ? url : defaultUrl,
299
- data != null ? data : defaultData,
300
- mergedOptions
301
- ])
284
+ const doPost = (...args) => {
285
+ var _a, _b, _c;
286
+ if (globalUrl !== void 0) args.splice(0, 0, globalUrl);
287
+ if (globalData !== void 0) args.splice(1, 0, globalData);
288
+ const url = args[0];
289
+ const data = args[1];
290
+ const options = mergeFetchOptions_default(
291
+ defaultOptions != null ? defaultOptions : {},
292
+ (_a = args[2]) != null ? _a : {}
302
293
  );
294
+ (_b = options.abortCtrl) != null ? _b : options.abortCtrl = new AbortController();
295
+ (_c = _abortCtrl == null ? void 0 : _abortCtrl.abort) == null ? void 0 : _c.call(_abortCtrl);
296
+ _abortCtrl = options.abortCtrl;
297
+ const promise = post(url, data, options);
303
298
  promise.onEarlyFinalize.push(() => _abortCtrl == null ? void 0 : _abortCtrl.abort());
304
299
  return promise;
305
300
  };
@@ -308,37 +303,37 @@ function postDeferred(deferOptions = {}, defaultUrl, defaultData, defaultOptions
308
303
  var postDeferred_default = postDeferred;
309
304
 
310
305
  // src/index.ts
311
- var fetchGet = (method = "get") => {
312
- const methodFunc = ((url, options = {}) => {
313
- ;
314
- options.method = method;
315
- return fetch_default(url, options);
306
+ import { isObj } from "@superutils/core";
307
+ var createFetchMethodFunc = (method = "get") => {
308
+ const methodFunc = ((url, options) => {
309
+ const _options = isObj(options) ? options : {};
310
+ _options.method = method;
311
+ return fetch_default(url, _options);
316
312
  });
317
- methodFunc.deferred = (...args) => fetchDeferred_default(...args);
313
+ methodFunc.deferred = ((...args) => fetchDeferred_default(...args));
318
314
  return methodFunc;
319
315
  };
320
- var fetchPost = (method = "post") => {
321
- const methodFunc = ((url, options = {}) => {
322
- ;
323
- options.method = method;
324
- return post(url, options);
316
+ var createPostMethodFunc = (method = "post") => {
317
+ const methodFunc = ((url, data, options) => {
318
+ const _options = isObj(options) ? options : {};
319
+ _options.method = method;
320
+ return post(url, data, _options);
325
321
  });
326
- methodFunc.deferred = (...args) => postDeferred_default(...args);
322
+ methodFunc.deferred = ((...args) => postDeferred_default(...args));
327
323
  return methodFunc;
328
324
  };
329
325
  var fetch2 = fetch_default;
330
- fetch2.get = fetchGet("get");
331
- fetch2.head = fetchGet("head");
332
- fetch2.delete = fetchGet("options");
333
- fetch2.delete = fetchPost("delete");
334
- fetch2.patch = fetchPost("patch");
335
- fetch2.post = fetchPost("post");
336
- fetch2.put = fetchPost("put");
326
+ fetch2.get = createFetchMethodFunc("get");
327
+ fetch2.head = createFetchMethodFunc("head");
328
+ fetch2.options = createFetchMethodFunc("options");
329
+ fetch2.delete = createPostMethodFunc("delete");
330
+ fetch2.patch = createPostMethodFunc("patch");
331
+ fetch2.post = createPostMethodFunc("post");
332
+ fetch2.put = createPostMethodFunc("put");
337
333
  var index_default = fetch2;
338
334
  export {
339
335
  FetchAs,
340
336
  FetchError,
341
- config,
342
337
  index_default as default,
343
338
  fetch2 as fetch,
344
339
  fetchDeferred,
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "author": "Toufiqur Rahaman Chowdhury",
3
- "description": "Fetch utilities",
3
+ "description": "A lightweight `fetch` wrapper for browsers and Node.js, designed to simplify data fetching and reduce boilerplate.",
4
4
  "dependencies": {
5
- "@superutils/core": "^1.0.1",
6
- "@superutils/promise": "^1.0.2"
5
+ "@superutils/core": "^1.0.5",
6
+ "@superutils/promise": "^1.0.6"
7
7
  },
8
8
  "files": [
9
9
  "dist",
@@ -20,8 +20,8 @@
20
20
  "main": "dist/index.js",
21
21
  "name": "@superutils/fetch",
22
22
  "peerDependencies": {
23
- "@superutils/core": "^1.0.1",
24
- "@superutils/promise": "^1.0.2"
23
+ "@superutils/core": "^1.0.5",
24
+ "@superutils/promise": "^1.0.6"
25
25
  },
26
26
  "publishConfig": {
27
27
  "access": "public"
@@ -31,10 +31,11 @@
31
31
  "_watch": "tsc -p tsconfig.json --watch",
32
32
  "build": "tsup src/index.ts --format esm --dts --clean --config ../../tsup.config.js",
33
33
  "dev": "npm run build -- --watch",
34
- "test": "cd ../../ && npm run test promise"
34
+ "start": "npm run build -- --watch",
35
+ "test": "cd ../../ && npm run test fetch"
35
36
  },
36
37
  "sideEffects": false,
37
38
  "type": "module",
38
39
  "types": "dist/index.d.ts",
39
- "version": "0.1.0"
40
+ "version": "1.0.1"
40
41
  }