@superutils/fetch 0.1.0 → 1.0.0

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,10 @@ 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
+ - [`fetchDeferred()`](#fetch-deferred): cancellable and debounced or throttled `fetch()`
14
+ - [`post()`](#post): make post-like requests
15
+ - [`postDeferred()`](#post-deferred) cancellable and debounced or throttled `post()`
14
16
 
15
17
  ## Features
16
18
 
@@ -39,13 +41,18 @@ Make a simple GET request. No need for `response.json()` or `result.data.theActu
39
41
  ```typescript
40
42
  import { fetch } from '@superutils/fetch'
41
43
 
42
- const theActualData = await fetch('https://dummyjson.com/products/1')
44
+ const theActualData = await fetch('https://dummyjson.com/products/1', {
45
+ method: 'get', // default
46
+ })
47
+ console.log(theActualData)
48
+ // Alternative:
49
+ const theActualData = await fetch.get('https://dummyjson.com/products/1')
43
50
  console.log(theActualData)
44
51
  ```
45
52
 
46
- <div id="fetchDeferred"></div>
53
+ <div id="fetch-deferred"></div>
47
54
 
48
- ### `fetchDeferred(deferOptions, url, fetchOptions)`
55
+ ### `fetch.get.deferred(deferOptions, url, fetchOptions)`
49
56
 
50
57
  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
58
 
@@ -53,7 +60,7 @@ A practical utility that combines `PromisE.deferred()` from the `@superutils/pro
53
60
  import { fetchDeferred, ResolveIgnored } from '@superutils/fetch'
54
61
 
55
62
  // Create a debounced search function with a 300ms delay.
56
- const searchProducts = fetchDeferred({
63
+ const searchProducts = fetch.get.deferred({
57
64
  delayMs: 300, // Debounce delay
58
65
  resolveIgnored: ResolveIgnored.WITH_UNDEFINED, // Ignored (aborted) promises will resolve with `undefined`
59
66
  })
@@ -126,8 +133,10 @@ getRandomQuote().then(quote => console.log('Call 3 resolved:', quote.id))
126
133
 
127
134
  <div id="post"></div>
128
135
 
129
- ### `post(url, options)`
136
+ ### `fetch.post(url, options)`
137
+
138
+ <div id="post-deferred"></div>
130
139
 
131
- <div id="postDeferred"></div>
140
+ ### `fetch.post.deferred(deferOptions, url, postOptions)`
132
141
 
133
- ### `postDeferred(deferOptions, url, postOptions)`
142
+ <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];
@@ -239,7 +239,7 @@ type FetchOptionsInterceptor = Omit<FetchOptions, 'as' | 'errMsgs' | 'intercepto
239
239
  errMsgs: Required<FetchErrMsgs>;
240
240
  headers: Headers;
241
241
  interceptors: Required<FetchInterceptors>;
242
- } & Required<FetchRetryOptions>;
242
+ } & FetchRetryOptions;
243
243
  /**
244
244
  * Result types for specific parsers ("as": FetchAs)
245
245
  */
@@ -255,7 +255,7 @@ interface FetchResult<T> {
255
255
  /**
256
256
  * Fetch retry options
257
257
  */
258
- type FetchRetryOptions = Partial<RetryOptions> & {
258
+ type FetchRetryOptions = Omit<Partial<RetryOptions>, 'retry' | 'retryIf'> & {
259
259
  /**
260
260
  * Maximum number of retries.
261
261
  *
@@ -264,6 +264,7 @@ type FetchRetryOptions = Partial<RetryOptions> & {
264
264
  * Default: `0`
265
265
  */
266
266
  retry?: number;
267
+ retryIf?: null | ((response: Response | undefined, retryCount: number, error?: unknown) => boolean | Promise<boolean>);
267
268
  };
268
269
  /**
269
270
  * Generic fetch interceptor type
@@ -273,44 +274,48 @@ type PostBody = Record<string, unknown> | BodyInit | null;
273
274
  type PostArgs = [
274
275
  url: string | URL,
275
276
  data?: PostBody,
276
- options?: Omit<FetchOptions, 'method'> & {
277
- /** Default: `'post'` */
278
- method?: 'post' | 'put' | 'patch' | 'delete';
279
- }
277
+ options?: PostOptions
280
278
  ];
281
-
282
- type Config = Config$1 & {
283
- fetchOptions: FetchOptionsInterceptor;
284
- };
285
- declare const config: Config;
286
-
287
279
  /**
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
280
  *
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
- *
305
- * @example Make a simple HTTP requests
281
+ * @example
306
282
  * ```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))
283
+ * // test with types
284
+ * type T1 = PostDeferredCallbackArgs<string | URL, undefined> // expected: [data, options]
285
+ * type T2 = PostDeferredCallbackArgs<undefined, string> // expected: [url, options]
286
+ * type T3 = PostDeferredCallbackArgs // expected: [url, data, options]
287
+ * type T4 = PostDeferredCallbackArgs<string, string> // expected: [options]
288
+ *
289
+ * const data = { name: 'test' }
290
+ * const url = 'https://domain.com'
291
+ * // test with postDeferred()
292
+ * const f1 = postDeferred({}, 'https://domain.com')
293
+ * // expected: [data, options]
294
+ * f1({data: 1}).then(console.log, console.warn)
295
+ *
296
+ * const f2 = postDeferred({}, undefined, 'dome data')
297
+ * // expected: [url, options]
298
+ * f2('https').then(console.log, console.warn)
299
+ *
300
+ * const f3 = postDeferred({})
301
+ * // expected: [url, data, options]
302
+ * f3('https://domain.com').then(console.log, console.warn)
303
+ *
304
+ * const f4 = postDeferred({}, 'url', 'data')
305
+ * // expected: [options]
306
+ * f4().then(console.log, console.warn)
311
307
  * ```
312
308
  */
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>;
309
+ 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;
310
+ type PostOptions = Omit<FetchOptions, 'method'> & {
311
+ /** Default: `'post'` */
312
+ method?: 'post' | 'put' | 'patch' | 'delete';
313
+ };
314
+
315
+ declare const fetch$1: {
316
+ <TJSON, TOptions extends FetchOptions = FetchOptions, TReturn = TOptions["as"] extends FetchAs ? FetchResult<TJSON>[TOptions["as"]] : TJSON>(url: string | URL, options?: TOptions): IPromisE<TReturn>;
317
+ defaults: Omit<FetchOptionsInterceptor, "retryIf" | "method">;
318
+ };
314
319
 
315
320
  /**
316
321
  * Creates a deferred/throttled version of {@link fetch}, powered by {@link PromisE.deferred}.
@@ -321,10 +326,11 @@ declare function fetch$1<TJSON, TOptions extends FetchOptions = FetchOptions, TR
321
326
  * `fetchDeferred` uses this to automatically abort pending requests when a new one is initiated, preventing race conditions and redundant network traffic.
322
327
  *
323
328
  * @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
- *
329
+ * See {@link DeferredAsyncOptions} for details.
330
+ * @param globalUrl (optional) If a global URL is `undefined`, returned callback will always require an URL.
331
+ * @param defaultOptions (optional) Default {@link FetchOptions} to be used by the returned function.
332
+ * Default options will be merged with the options provided in the callback.
333
+ * If the same property is provided in both cases, defaults will be overriden by the callback.
328
334
  *
329
335
  * @example Debounce/Throttle requests for an auto-complete search input
330
336
  * ```typescript
@@ -397,7 +403,7 @@ declare function fetch$1<TJSON, TOptions extends FetchOptions = FetchOptions, TR
397
403
  * // Console output will show the same quote ID for all three calls.
398
404
  * ```
399
405
  */
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>;
406
+ 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
407
 
402
408
  /**
403
409
  * Creates a deferred/throttled function for making `POST`, `PUT`, or `PATCH` requests, powered by
@@ -408,6 +414,16 @@ declare function fetchDeferred<ThisArg, DefaultUrl extends string | URL>(deferOp
408
414
  * Like `fetchDeferred`, it automatically aborts pending requests when a new one is initiated, ensuring only
409
415
  * the most recent or relevant action is executed.
410
416
  *
417
+ *
418
+ * @param deferOptions Configuration for the deferred execution behavior (e.g., `delayMs`, `throttle`).
419
+ * See {@link DeferredAsyncOptions} for details.
420
+ * @param globalUrl (optional) If global URL is `undefined`, returned callback will always require an URL.
421
+ * @param globalData (optional) If global data is `undefined`, returned callback will allow a data parameter.
422
+ * @param defaultOptions (optional) Default {@link FetchOptions} to be used by the returned function.
423
+ * Default options will be merged with the options provided in the callback.
424
+ * If the same property is provided in both cases, defaults will be overriden by the callback.
425
+ *
426
+ *
411
427
  * @example Debouncing an authentication token refresh
412
428
  * ```typescript
413
429
  * import { postDeferred } from '@superutils/fetch'
@@ -485,12 +501,12 @@ declare function fetchDeferred<ThisArg, DefaultUrl extends string | URL>(deferOp
485
501
  * // This results in only two network requests instead of four.
486
502
  * ```
487
503
  */
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>;
504
+ 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
505
+ globalData?: GlobalData, // The default data for all calls
506
+ defaultOptions?: PostOptions): <TResult = unknown>(...args: Args) => _superutils_promise.IPromisE<TResult>;
491
507
 
492
508
  /**
493
- * Merge one or more {@link FetchOptions} with global fetch options ({@link config.fetchOptions}).
509
+ * Merge one or more {@link FetchOptions}
494
510
  *
495
511
  * Notes:
496
512
  * - {@link config.fetchOptions} will be added as the base and not necessary to be included
@@ -505,16 +521,50 @@ declare function postDeferred<ThisArg, DefaultUrl extends string | URL>(deferOpt
505
521
  */
506
522
  declare const mergeFetchOptions: (...allOptions: FetchOptions[]) => FetchOptionsInterceptor;
507
523
 
508
- type Func = <T, Options extends Omit<FetchOptions, 'method'>>(url: string | URL, options?: Options) => ReturnType<typeof fetch$1<T, Options>>;
509
- type MethodFunc = Func & ({
524
+ type FetchWithoutMethods = typeof fetch$1;
525
+ /** Describes method-specific fetch-functions that includes `.deferred()` function for deferred/throttled requests */
526
+ type FetchMethodFunc = (<T, Options extends Omit<FetchOptions, 'method'>>(url: string | URL, options?: Options) => ReturnType<typeof fetch$1<T, Options>>) & {
510
527
  deferred: typeof fetchDeferred;
511
- } | {
528
+ };
529
+ /** Describes method-specific fetch-functions that includes `.deferred()` function for deferred/throttled requests */
530
+ type PostMethodFunc = (<T, Options extends Omit<FetchOptions, 'method'>>(url: string | URL, data?: PostBody, options?: Options) => ReturnType<typeof fetch$1<T, Options>>) & {
512
531
  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>>;
532
+ };
533
+ interface FetchWithMethods extends FetchWithoutMethods {
534
+ get: FetchMethodFunc;
535
+ head: FetchMethodFunc;
536
+ options: FetchMethodFunc;
537
+ delete: PostMethodFunc;
538
+ patch: PostMethodFunc;
539
+ post: PostMethodFunc;
540
+ put: PostMethodFunc;
517
541
  }
518
- declare const fetch: DefaultFetch;
542
+ /**
543
+ * A `fetch()` replacement that simplifies data fetching with automatic JSON parsing, request timeouts, retries,
544
+ * and powerful interceptors. It also includes deferred and throttled request capabilities for complex asynchronous
545
+ * control flows.
546
+ *
547
+ * Will reject promise if response status code is not 2xx (200 <= status < 300).
548
+ *
549
+ * @param url
550
+ * @param options (optional) all built-in `fetch()` options such as "method", "headers" and the additionals below.
551
+ * @param options.abortCtrl (optional) if not provided `AbortController` will be instantiated when `timeout` used.
552
+ * @param options.headers (optional) request headers. Default: `{ 'content-type' : 'application/json'}`
553
+ * @param options.interceptors (optional) request interceptor callbacks. See {@link FetchInterceptors} for details.
554
+ * @param options.method (optional) Default: `"get"`
555
+ * @param options.timeout (optional) duration in milliseconds to abort the request if it takes longer.
556
+ * @param options.parse (optional) specify how to parse the result.
557
+ * Default: {@link FetchAs.json}
558
+ * For raw `Response` use {@link FetchAs.response}
559
+ *
560
+ * @example Make a simple HTTP requests
561
+ * ```typescript
562
+ * import { fetch } from '@superutils/fetch'
563
+ *
564
+ * // no need for `response.json()` or `result.data.theActualData` drilling
565
+ * fetch('https://dummyjson.com/products/1').then(theActualData => console.log(theActualData))
566
+ * ```
567
+ */
568
+ declare const fetch: FetchWithMethods;
519
569
 
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 };
570
+ export { type FetchArgs, type FetchArgsInterceptor, FetchAs, type FetchConf, type FetchDeferredArgs, type FetchErrMsgs, FetchError, type FetchInterceptorError, type FetchInterceptorRequest, type FetchInterceptorResponse, type FetchInterceptorResult, type FetchInterceptors, type FetchMethodFunc, type FetchOptions, 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, err) => {
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, err)) === 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(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,54 @@ function fetch(url, options = {}) {
227
201
  });
228
202
  promise.onEarlyFinalize.push(() => abortCtrl == null ? void 0 : abortCtrl.abort());
229
203
  return promise;
230
- }
204
+ };
205
+ var 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
+ };
229
+ fetch.defaults = defaults;
231
230
  var fetch_default = fetch;
232
231
 
233
232
  // src/fetchDeferred.ts
234
- import { forceCast } from "@superutils/core";
235
233
  import PromisE3 from "@superutils/promise";
236
234
  import {
237
235
  ResolveError,
238
236
  ResolveIgnored
239
237
  } from "@superutils/promise";
240
- function fetchDeferred(deferOptions = {}, defaultUrl, defaultOptions) {
238
+ function fetchDeferred(deferOptions = {}, globalUrl, defaultOptions) {
241
239
  let _abortCtrl;
242
240
  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;
241
+ var _a, _b, _c;
242
+ let options = {
243
+ ...(_a = globalUrl === void 0 ? args[1] : args[0]) != null ? _a : {}
244
+ };
245
+ if (defaultOptions) options = mergeFetchOptions_default(defaultOptions, options);
246
+ (_b = options.abortCtrl) != null ? _b : options.abortCtrl = new AbortController();
247
+ (_c = _abortCtrl == null ? void 0 : _abortCtrl.abort) == null ? void 0 : _c.call(_abortCtrl);
248
+ _abortCtrl = options.abortCtrl;
251
249
  const promise = fetch_default(
252
- ...forceCast([
253
- url != null ? url : defaultUrl,
254
- mergeFetchOptions_default(defaultOptions != null ? defaultOptions : {}, options)
255
- ])
250
+ globalUrl != null ? globalUrl : args[0],
251
+ options
256
252
  );
257
253
  promise.onEarlyFinalize.push(() => _abortCtrl == null ? void 0 : _abortCtrl.abort());
258
254
  return promise;
@@ -262,7 +258,6 @@ function fetchDeferred(deferOptions = {}, defaultUrl, defaultOptions) {
262
258
  var fetchDeferred_default = fetchDeferred;
263
259
 
264
260
  // src/postDeferred.ts
265
- import { forceCast as forceCast2 } from "@superutils/core";
266
261
  import PromisE4 from "@superutils/promise";
267
262
 
268
263
  // src/post.ts
@@ -285,21 +280,22 @@ import {
285
280
  ResolveError as ResolveError2,
286
281
  ResolveIgnored as ResolveIgnored2
287
282
  } from "@superutils/promise";
288
- function postDeferred(deferOptions = {}, defaultUrl, defaultData, defaultOptions) {
283
+ function postDeferred(deferOptions = {}, globalUrl, globalData, defaultOptions) {
289
284
  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
- ])
285
+ const doPost = (...args) => {
286
+ var _a, _b, _c;
287
+ if (globalUrl !== void 0) args.splice(0, 0, globalUrl);
288
+ if (globalData !== void 0) args.splice(1, 0, globalData);
289
+ const url = args[0];
290
+ const data = args[1];
291
+ const options = mergeFetchOptions_default(
292
+ defaultOptions != null ? defaultOptions : {},
293
+ (_a = args[2]) != null ? _a : {}
302
294
  );
295
+ (_b = options.abortCtrl) != null ? _b : options.abortCtrl = new AbortController();
296
+ (_c = _abortCtrl == null ? void 0 : _abortCtrl.abort) == null ? void 0 : _c.call(_abortCtrl);
297
+ _abortCtrl = options.abortCtrl;
298
+ const promise = post(url, data, options);
303
299
  promise.onEarlyFinalize.push(() => _abortCtrl == null ? void 0 : _abortCtrl.abort());
304
300
  return promise;
305
301
  };
@@ -308,37 +304,37 @@ function postDeferred(deferOptions = {}, defaultUrl, defaultData, defaultOptions
308
304
  var postDeferred_default = postDeferred;
309
305
 
310
306
  // src/index.ts
311
- var fetchGet = (method = "get") => {
312
- const methodFunc = ((url, options = {}) => {
313
- ;
314
- options.method = method;
315
- return fetch_default(url, options);
307
+ import { isObj } from "@superutils/core";
308
+ var createFetchMethodFunc = (method = "get") => {
309
+ const methodFunc = ((url, options) => {
310
+ const _options = isObj(options) ? options : {};
311
+ _options.method = method;
312
+ return fetch_default(url, _options);
316
313
  });
317
- methodFunc.deferred = (...args) => fetchDeferred_default(...args);
314
+ methodFunc.deferred = ((...args) => fetchDeferred_default(...args));
318
315
  return methodFunc;
319
316
  };
320
- var fetchPost = (method = "post") => {
321
- const methodFunc = ((url, options = {}) => {
322
- ;
323
- options.method = method;
324
- return post(url, options);
317
+ var createPostMethodFunc = (method = "post") => {
318
+ const methodFunc = ((url, data, options) => {
319
+ const _options = isObj(options) ? options : {};
320
+ _options.method = method;
321
+ return post(url, data, _options);
325
322
  });
326
- methodFunc.deferred = (...args) => postDeferred_default(...args);
323
+ methodFunc.deferred = ((...args) => postDeferred_default(...args));
327
324
  return methodFunc;
328
325
  };
329
326
  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");
327
+ fetch2.get = createFetchMethodFunc("get");
328
+ fetch2.head = createFetchMethodFunc("head");
329
+ fetch2.options = createFetchMethodFunc("options");
330
+ fetch2.delete = createPostMethodFunc("delete");
331
+ fetch2.patch = createPostMethodFunc("patch");
332
+ fetch2.post = createPostMethodFunc("post");
333
+ fetch2.put = createPostMethodFunc("put");
337
334
  var index_default = fetch2;
338
335
  export {
339
336
  FetchAs,
340
337
  FetchError,
341
- config,
342
338
  index_default as default,
343
339
  fetch2 as fetch,
344
340
  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.5"
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.5"
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.0"
40
41
  }