@alwatr/fetch 3.1.0 → 3.1.2

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/CHANGELOG.md CHANGED
@@ -3,34 +3,48 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [3.1.2](https://github.com/Alwatr/nanolib/compare/@alwatr/fetch@3.1.1...@alwatr/fetch@3.1.2) (2024-03-28)
7
+
8
+ **Note:** Version bump only for package @alwatr/fetch
9
+
10
+ ## [3.1.1](https://github.com/Alwatr/nanolib/compare/@alwatr/fetch@3.1.0...@alwatr/fetch@3.1.1) (2024-01-31)
11
+
12
+ ### Bug Fixes
13
+
14
+ * exported types by add .js extensions to all imports ([fc3d83e](https://github.com/Alwatr/nanolib/commit/fc3d83e8f375da97ba276314b2e6966aa82c9b3f)) by @AliMD
15
+
16
+ ### Miscellaneous Chores
17
+
18
+ * **deps:** update ([1a45030](https://github.com/Alwatr/nanolib/commit/1a450305440b710a300787d4ca24b1ed8c6a39d7)) by @AliMD
19
+
6
20
  ## [3.1.0](https://github.com/Alwatr/nanolib/compare/@alwatr/fetch@3.0.0...@alwatr/fetch@3.1.0) (2024-01-24)
7
21
 
8
22
  ### Features
9
23
 
10
- * **fetch:** fetch json ([b089f12](https://github.com/Alwatr/nanolib/commit/b089f12cef6f1f3b60bc7559dc5e9b8b63c57273)) by @njfamirm
24
+ - **fetch:** fetch json ([b089f12](https://github.com/Alwatr/nanolib/commit/b089f12cef6f1f3b60bc7559dc5e9b8b63c57273)) by @njfamirm
11
25
 
12
26
  ### Bug Fixes
13
27
 
14
- * **fetch:** better error handling on handleRetryPattern_ when user is offline ([b867f30](https://github.com/Alwatr/nanolib/commit/b867f30b3eba529ec1aae0026f0ded252ce54332)) by @AliMD
28
+ - **fetch:** better error handling on handleRetryPattern\_ when user is offline ([b867f30](https://github.com/Alwatr/nanolib/commit/b867f30b3eba529ec1aae0026f0ded252ce54332)) by @AliMD
15
29
 
16
30
  ### Code Refactoring
17
31
 
18
- * **fetch:** separate core files ([c7e6b09](https://github.com/Alwatr/nanolib/commit/c7e6b096d747f868a2a1bfde1ffd3fd2a64dc7f3)) by @njfamirm
32
+ - **fetch:** separate core files ([c7e6b09](https://github.com/Alwatr/nanolib/commit/c7e6b096d747f868a2a1bfde1ffd3fd2a64dc7f3)) by @njfamirm
19
33
 
20
34
  ## 3.0.0 (2024-01-20)
21
35
 
22
36
  ### ⚠ BREAKING CHANGES
23
37
 
24
- * **fetch:** remove serviceRequest
38
+ - **fetch:** remove serviceRequest
25
39
 
26
40
  Co-authored-by: Ali Mihandoost <ali@mihandoost.com>
27
41
 
28
42
  ### Features
29
43
 
30
- * **fetch:** alwatrAuth ([28e365c](https://github.com/Alwatr/nanolib/commit/28e365c839b0ea80060c0f44ed4dc4473468d5c4)) by @AliMD
31
- * **fetch:** move from last repo ([4b86bb5](https://github.com/Alwatr/nanolib/commit/4b86bb542af296c91bc1db36b4e08fdbad501db2)) by @njfamirm
32
- * **fetch:** Update fetch type definitions with document ([38398cc](https://github.com/Alwatr/nanolib/commit/38398cc33f311a569a53cc3e06c3191e17dbd45b)) by @AliMD
44
+ - **fetch:** alwatrAuth ([28e365c](https://github.com/Alwatr/nanolib/commit/28e365c839b0ea80060c0f44ed4dc4473468d5c4)) by @AliMD
45
+ - **fetch:** move from last repo ([4b86bb5](https://github.com/Alwatr/nanolib/commit/4b86bb542af296c91bc1db36b4e08fdbad501db2)) by @njfamirm
46
+ - **fetch:** Update fetch type definitions with document ([38398cc](https://github.com/Alwatr/nanolib/commit/38398cc33f311a569a53cc3e06c3191e17dbd45b)) by @AliMD
33
47
 
34
48
  ### Code Refactoring
35
49
 
36
- * **fetch:** review and update everything ([61ec38b](https://github.com/Alwatr/nanolib/commit/61ec38b2fde28ba26a7973fcd60a30c861faf4dd)) by @AliMD
50
+ - **fetch:** review and update everything ([61ec38b](https://github.com/Alwatr/nanolib/commit/61ec38b2fde28ba26a7973fcd60a30c861faf4dd)) by @AliMD
package/dist/core.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { FetchOptions } from './type';
1
+ import type { FetchOptions } from './type.js';
2
2
  export declare const logger_: import("@alwatr/logger").AlwatrLogger;
3
3
  export declare const cacheSupported: boolean;
4
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,QAAQ,CAAC;AAGzC,eAAO,MAAM,OAAO,uCAAsD,CAAC;AAG3E,eAAO,MAAM,cAAc,SAAuC,CAAC;AAInE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC,CA+C7E;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoF7F;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2B/F;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8B5F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoCvE"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,WAAW,CAAC;AAG5C,eAAO,MAAM,OAAO,uCAAsD,CAAC;AAG3E,eAAO,MAAM,cAAc,SAAuC,CAAC;AAInE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC,CA+C7E;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoF7F;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2B/F;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8B5F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoCvE"}
package/dist/main.cjs CHANGED
@@ -1,3 +1,3 @@
1
- /* @alwatr/fetch v3.1.0 */
2
- "use strict";var m=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var x=(e,r)=>{for(var t in r)m(e,t,{get:r[t],enumerable:!0})},T=(e,r,t,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let c of O(r))!k.call(e,c)&&c!==t&&m(e,c,{get:()=>r[c],enumerable:!(a=b(r,c))||a.enumerable});return e};var v=e=>T(m({},"__esModule",{value:!0}),e);var C={};x(C,{cacheSupported:()=>s,fetch:()=>S,fetchJson:()=>P});module.exports=v(C);var f=require("@alwatr/global-scope"),_=require("@alwatr/logger"),w=require("@alwatr/wait"),n=(0,_.definePackage)("@alwatr/fetch","3.1.0"),i,s=Object.hasOwn(f.globalScope,"caches"),u={};function g(e){if(e.method??(e.method="GET"),e.window??(e.window=null),e.timeout??(e.timeout=8e3),e.retry??(e.retry=3),e.retryDelay??(e.retryDelay=1e3),e.cacheStrategy??(e.cacheStrategy="network_only"),e.removeDuplicate??(e.removeDuplicate="never"),e.headers??(e.headers={}),e.cacheStrategy!=="network_only"&&s!==!0&&(n.incident?.("fetch","fetch_cache_strategy_unsupported",{cacheSupported:s}),e.cacheStrategy="network_only"),e.removeDuplicate==="auto"&&(e.removeDuplicate=s?"until_load":"always"),e.url.lastIndexOf("?")===-1&&e.queryParameters!=null){let r=e.queryParameters,t=Object.keys(r).map(a=>`${a}=${String(r[a])}`);t.length>0&&(e.url+="?"+t.join("&"))}return e.bodyJson!==void 0&&(e.body=JSON.stringify(e.bodyJson),e.headers["Content-Type"]="application/json"),e.bearerToken!==void 0?e.headers.Authorization=`Bearer ${e.bearerToken}`:e.alwatrAuth!==void 0&&(e.headers.Authorization=`Alwatr ${e.alwatrAuth.userId}:${e.alwatrAuth.userToken}`),e}async function R(e){if(e.cacheStrategy==="network_only")return h(e);n.logMethod?.("_handleCacheStrategy"),i==null&&e.cacheStorageName==null&&(i=await caches.open("fetch_cache"));let r=e.cacheStorageName!=null?await caches.open(e.cacheStorageName):i,t=new Request(e.url,e);switch(e.cacheStrategy){case"cache_first":{let a=await r.match(t);if(a!=null)return a;let c=await h(e);return c.ok&&r.put(t,c.clone()),c}case"cache_only":{let a=await r.match(t);if(a==null)throw n.accident("_handleCacheStrategy","fetch_cache_not_found",{url:t.url}),new Error("fetch_cache_not_found");return a}case"network_first":try{let a=await h(e);return a.ok&&r.put(t,a.clone()),a}catch(a){let c=await r.match(t);if(c!=null)return c;throw a}case"update_cache":{let a=await h(e);return a.ok&&r.put(t,a.clone()),a}case"stale_while_revalidate":{let a=await r.match(t),c=h(e).then(l=>(l.ok&&(r.put(t,l.clone()),typeof e.revalidateCallback=="function"&&setTimeout(e.revalidateCallback,0,l.clone())),l));return a??c}default:return h(e)}}async function h(e){if(e.removeDuplicate==="never")return o(e);n.logMethod?.("handleRemoveDuplicate_");let r=e.method+" "+e.url;u[r]??(u[r]=o(e));try{let t=await u[r];return u[r]!=null&&(t.ok!==!0||e.removeDuplicate==="until_load")&&delete u[r],t.clone()}catch(t){throw delete u[r],t}}async function o(e){if(!(e.retry>1))return y(e);n.logMethod?.("_handleRetryPattern"),e.retry--;let r=e.signal;try{let t=await y(e);if(t.status<500)return t;throw new Error("fetch_server_error")}catch(t){if(n.accident("fetch","fetch_failed_retry",t),f.globalScope.navigator?.onLine===!1)throw n.accident("handleRetryPattern_","offline","Skip retry because offline"),t;return await(0,w.waitForTimeout)(e.retryDelay),e.signal=r,o(e)}}function y(e){return e.timeout===0?f.globalScope.fetch(e.url,e):(n.logMethod?.("handleTimeout_"),new Promise((r,t)=>{let a=typeof AbortController=="function"?new AbortController:null,c=e.signal;e.signal=a?.signal,a!==null&&c!=null&&c.addEventListener("abort",()=>a.abort(),{once:!0});let l=setTimeout(()=>{t(new Error("fetch_timeout")),a?.abort("fetch_timeout")},e.timeout);f.globalScope.fetch(e.url,e).then(d=>r(d)).catch(d=>t(d)).finally(()=>{delete e.signal,clearTimeout(l)})}))}async function P(e){let r,t,a;try{return r=await S(e),t=await r.text(),a=JSON.parse(t),a.ok=!0,a.statusCode=r.status,a}catch(c){let l={ok:!1,statusCode:r?.status,statusText:r?.statusText,errorCode:a?.errorCode??c.message,responseText:t,meta:a?.meta};return n.accident("fetchJson","fetch_failed",{responseError:l,error:c}),l}}function S(e){return e=g(e),n.logMethodArgs?.("fetch",{options:e}),R(e)}0&&(module.exports={cacheSupported,fetch,fetchJson});
1
+ /* @alwatr/fetch v3.1.2 */
2
+ "use strict";var m=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var x=(e,r)=>{for(var t in r)m(e,t,{get:r[t],enumerable:!0})},T=(e,r,t,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let c of O(r))!k.call(e,c)&&c!==t&&m(e,c,{get:()=>r[c],enumerable:!(a=b(r,c))||a.enumerable});return e};var v=e=>T(m({},"__esModule",{value:!0}),e);var C={};x(C,{cacheSupported:()=>s,fetch:()=>S,fetchJson:()=>P});module.exports=v(C);var f=require("@alwatr/global-scope"),_=require("@alwatr/logger"),w=require("@alwatr/wait"),n=(0,_.definePackage)("@alwatr/fetch","3.1.2"),i,s=Object.hasOwn(f.globalScope,"caches"),u={};function g(e){if(e.method??(e.method="GET"),e.window??(e.window=null),e.timeout??(e.timeout=8e3),e.retry??(e.retry=3),e.retryDelay??(e.retryDelay=1e3),e.cacheStrategy??(e.cacheStrategy="network_only"),e.removeDuplicate??(e.removeDuplicate="never"),e.headers??(e.headers={}),e.cacheStrategy!=="network_only"&&s!==!0&&(n.incident?.("fetch","fetch_cache_strategy_unsupported",{cacheSupported:s}),e.cacheStrategy="network_only"),e.removeDuplicate==="auto"&&(e.removeDuplicate=s?"until_load":"always"),e.url.lastIndexOf("?")===-1&&e.queryParameters!=null){let r=e.queryParameters,t=Object.keys(r).map(a=>`${a}=${String(r[a])}`);t.length>0&&(e.url+="?"+t.join("&"))}return e.bodyJson!==void 0&&(e.body=JSON.stringify(e.bodyJson),e.headers["Content-Type"]="application/json"),e.bearerToken!==void 0?e.headers.Authorization=`Bearer ${e.bearerToken}`:e.alwatrAuth!==void 0&&(e.headers.Authorization=`Alwatr ${e.alwatrAuth.userId}:${e.alwatrAuth.userToken}`),e}async function R(e){if(e.cacheStrategy==="network_only")return h(e);n.logMethod?.("_handleCacheStrategy"),i==null&&e.cacheStorageName==null&&(i=await caches.open("fetch_cache"));let r=e.cacheStorageName!=null?await caches.open(e.cacheStorageName):i,t=new Request(e.url,e);switch(e.cacheStrategy){case"cache_first":{let a=await r.match(t);if(a!=null)return a;let c=await h(e);return c.ok&&r.put(t,c.clone()),c}case"cache_only":{let a=await r.match(t);if(a==null)throw n.accident("_handleCacheStrategy","fetch_cache_not_found",{url:t.url}),new Error("fetch_cache_not_found");return a}case"network_first":try{let a=await h(e);return a.ok&&r.put(t,a.clone()),a}catch(a){let c=await r.match(t);if(c!=null)return c;throw a}case"update_cache":{let a=await h(e);return a.ok&&r.put(t,a.clone()),a}case"stale_while_revalidate":{let a=await r.match(t),c=h(e).then(l=>(l.ok&&(r.put(t,l.clone()),typeof e.revalidateCallback=="function"&&setTimeout(e.revalidateCallback,0,l.clone())),l));return a??c}default:return h(e)}}async function h(e){if(e.removeDuplicate==="never")return o(e);n.logMethod?.("handleRemoveDuplicate_");let r=e.method+" "+e.url;u[r]??(u[r]=o(e));try{let t=await u[r];return u[r]!=null&&(t.ok!==!0||e.removeDuplicate==="until_load")&&delete u[r],t.clone()}catch(t){throw delete u[r],t}}async function o(e){if(!(e.retry>1))return y(e);n.logMethod?.("_handleRetryPattern"),e.retry--;let r=e.signal;try{let t=await y(e);if(t.status<500)return t;throw new Error("fetch_server_error")}catch(t){if(n.accident("fetch","fetch_failed_retry",t),f.globalScope.navigator?.onLine===!1)throw n.accident("handleRetryPattern_","offline","Skip retry because offline"),t;return await(0,w.waitForTimeout)(e.retryDelay),e.signal=r,o(e)}}function y(e){return e.timeout===0?f.globalScope.fetch(e.url,e):(n.logMethod?.("handleTimeout_"),new Promise((r,t)=>{let a=typeof AbortController=="function"?new AbortController:null,c=e.signal;e.signal=a?.signal,a!==null&&c!=null&&c.addEventListener("abort",()=>a.abort(),{once:!0});let l=setTimeout(()=>{t(new Error("fetch_timeout")),a?.abort("fetch_timeout")},e.timeout);f.globalScope.fetch(e.url,e).then(d=>r(d)).catch(d=>t(d)).finally(()=>{delete e.signal,clearTimeout(l)})}))}async function P(e){let r,t,a;try{return r=await S(e),t=await r.text(),a=JSON.parse(t),a.ok=!0,a.statusCode=r.status,a}catch(c){let l={ok:!1,statusCode:r?.status,statusText:r?.statusText,errorCode:a?.errorCode??c.message,responseText:t,meta:a?.meta};return n.accident("fetchJson","fetch_failed",{responseError:l,error:c}),l}}function S(e){return e=g(e),n.logMethodArgs?.("fetch",{options:e}),R(e)}0&&(module.exports={cacheSupported,fetch,fetchJson});
3
3
  //# sourceMappingURL=main.cjs.map
package/dist/main.cjs.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/main.ts", "../src/core.ts"],
4
- "sourcesContent": ["import {handleCacheStrategy_, logger_, processOptions_, cacheSupported} from './core';\n\nimport type {FetchOptions, ResponseError, ResponseSuccess} from './type';\nimport type {JsonObject} from '@alwatr/type-helper';\n\nexport {cacheSupported};\nexport type * from './type';\n\n/**\n * It's a wrapper around the browser's `fetch` function that adds retry pattern, timeout, cacheStrategy,\n * remove duplicates, etc.\n *\n * @see {@link FetchOptions}\n * @see {@link ResponseSuccess}\n * @see {@link ResponseError}\n *\n * @param options Fetch options.\n *\n * @returns A success or error response.\n *\n * @example\n * ```typescript\n * const responseJson = await fetchJson({\n * url: '/api/products',\n * queryParameters: {limit: 10},\n * timeout: 8_000,\n * retry: 3,\n * cacheStrategy: 'stale_while_revalidate',\n * cacheDuplicate: 'auto',\n * });\n * ```\n */\nexport async function fetchJson<T extends JsonObject>(options: FetchOptions): Promise<ResponseSuccess<T> | ResponseError> {\n let response;\n let responseText;\n let responseJson;\n try {\n response = await fetch(options);\n responseText = await response.text();\n responseJson = JSON.parse(responseText) as ResponseSuccess<T>;\n responseJson.ok = true;\n responseJson.statusCode = response.status;\n return responseJson;\n }\n catch (error) {\n const responseError: ResponseError = {\n ok: false,\n statusCode: response?.status,\n statusText: response?.statusText,\n errorCode: (responseJson?.errorCode as string) ?? (error as Error).message,\n responseText,\n meta: responseJson?.meta as JsonObject,\n };\n\n logger_.accident('fetchJson', 'fetch_failed', {responseError, error});\n return responseError;\n }\n}\n\n/**\n * It's a wrapper around the browser's `fetch` function that adds retry pattern, timeout, cacheStrategy,\n * remove duplicates, etc.\n *\n * @see {@link FetchOptions}\n *\n * @param options Fetch options.\n *\n * @returns A promise that resolves to the Response to that request, whether it is successful or not.\n *\n * @example\n * ```typescript\n * const response = await fetch({\n * url: '/api/products',\n * queryParameters: {limit: 10},\n * timeout: 8_000,\n * retry: 3,\n * cacheStrategy: 'stale_while_revalidate',\n * cacheDuplicate: 'auto',\n * });\n * ```\n */\nexport function fetch(options: FetchOptions): Promise<Response> {\n options = processOptions_(options);\n logger_.logMethodArgs?.('fetch', {options});\n return handleCacheStrategy_(options as Required<FetchOptions>);\n}\n", "import {globalScope} from '@alwatr/global-scope';\nimport {definePackage} from '@alwatr/logger';\nimport {waitForTimeout} from '@alwatr/wait';\n\nimport type {FetchOptions} from './type';\nimport type {} from '@alwatr/nano-build';\n\nexport const logger_ = definePackage('@alwatr/fetch', __package_version__);\n\nlet cacheStorage_: Cache;\nexport const cacheSupported = Object.hasOwn(globalScope, 'caches');\n\nconst duplicateRequestStorage_: Record<string, Promise<Response>> = {};\n\n/**\n * Process fetch options and set defaults, etc.\n *\n * @param options Fetch options.\n *\n * @returns Required fetch options.\n */\nexport function processOptions_(options: FetchOptions): Required<FetchOptions> {\n options.method ??= 'GET';\n options.window ??= null;\n\n options.timeout ??= 8_000;\n options.retry ??= 3;\n options.retryDelay ??= 1_000;\n options.cacheStrategy ??= 'network_only';\n options.removeDuplicate ??= 'never';\n options.headers ??= {};\n\n if (options.cacheStrategy !== 'network_only' && cacheSupported !== true) {\n logger_.incident?.('fetch', 'fetch_cache_strategy_unsupported', {\n cacheSupported,\n });\n options.cacheStrategy = 'network_only';\n }\n\n if (options.removeDuplicate === 'auto') {\n options.removeDuplicate = cacheSupported ? 'until_load' : 'always';\n }\n\n if (options.url.lastIndexOf('?') === -1 && options.queryParameters != null) {\n const queryParameters = options.queryParameters;\n // prettier-ignore\n const queryArray = Object\n .keys(queryParameters)\n .map((key) => `${key}=${String(queryParameters[key])}`);\n\n if (queryArray.length > 0) {\n options.url += '?' + queryArray.join('&');\n }\n }\n\n if (options.bodyJson !== undefined) {\n options.body = JSON.stringify(options.bodyJson);\n options.headers['Content-Type'] = 'application/json';\n }\n\n if (options.bearerToken !== undefined) {\n options.headers.Authorization = `Bearer ${options.bearerToken}`;\n }\n else if (options.alwatrAuth !== undefined) {\n options.headers.Authorization = `Alwatr ${options.alwatrAuth.userId}:${options.alwatrAuth.userToken}`;\n }\n\n return options as Required<FetchOptions>;\n}\n\n/**\n * Handle Cache Strategy over `handleRemoveDuplicate_`.\n */\nexport async function handleCacheStrategy_(options: Required<FetchOptions>): Promise<Response> {\n if (options.cacheStrategy === 'network_only') {\n return handleRemoveDuplicate_(options);\n }\n // else handle cache strategies!\n logger_.logMethod?.('_handleCacheStrategy');\n\n if (cacheStorage_ == null && options.cacheStorageName == null) {\n cacheStorage_ = await caches.open('fetch_cache');\n }\n\n const cacheStorage = options.cacheStorageName != null ? await caches.open(options.cacheStorageName) : cacheStorage_;\n\n const request = new Request(options.url, options);\n\n switch (options.cacheStrategy) {\n case 'cache_first': {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse != null) {\n return cachedResponse;\n }\n // else\n const response = await handleRemoveDuplicate_(options);\n if (response.ok) {\n cacheStorage.put(request, response.clone());\n }\n return response;\n }\n\n case 'cache_only': {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse == null) {\n logger_.accident('_handleCacheStrategy', 'fetch_cache_not_found', {url: request.url});\n throw new Error('fetch_cache_not_found');\n }\n // else\n return cachedResponse;\n }\n\n case 'network_first': {\n try {\n const networkResponse = await handleRemoveDuplicate_(options);\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n }\n return networkResponse;\n }\n catch (err) {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse != null) {\n return cachedResponse;\n }\n // else\n throw err;\n }\n }\n\n case 'update_cache': {\n const networkResponse = await handleRemoveDuplicate_(options);\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n }\n return networkResponse;\n }\n\n case 'stale_while_revalidate': {\n const cachedResponse = await cacheStorage.match(request);\n const fetchedResponsePromise = handleRemoveDuplicate_(options).then((networkResponse) => {\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n if (typeof options.revalidateCallback === 'function') {\n setTimeout(options.revalidateCallback, 0, networkResponse.clone());\n }\n }\n return networkResponse;\n });\n\n return cachedResponse ?? fetchedResponsePromise;\n }\n\n default: {\n return handleRemoveDuplicate_(options);\n }\n }\n}\n\n/**\n * Handle Remove Duplicates over `_handleRetryPattern`.\n */\nexport async function handleRemoveDuplicate_(options: Required<FetchOptions>): Promise<Response> {\n if (options.removeDuplicate === 'never') return handleRetryPattern_(options);\n\n logger_.logMethod?.('handleRemoveDuplicate_');\n\n const cacheKey = options.method + ' ' + options.url;\n\n // We must cache fetch promise without await for handle other parallel requests.\n duplicateRequestStorage_[cacheKey] ??= handleRetryPattern_(options);\n\n try {\n // For all requests need to await for clone responses.\n const response = await duplicateRequestStorage_[cacheKey];\n\n if (duplicateRequestStorage_[cacheKey] != null) {\n if (response.ok !== true || options.removeDuplicate === 'until_load') {\n delete duplicateRequestStorage_[cacheKey];\n }\n }\n\n return response.clone();\n }\n catch (err) {\n // clean cache on any error.\n delete duplicateRequestStorage_[cacheKey];\n throw err;\n }\n}\n\n/**\n * Handle retry pattern over `handleTimeout_`.\n */\nexport async function handleRetryPattern_(options: Required<FetchOptions>): Promise<Response> {\n if (!(options.retry > 1)) return handleTimeout_(options);\n\n logger_.logMethod?.('_handleRetryPattern');\n options.retry--;\n\n const externalAbortSignal = options.signal;\n\n try {\n const response = await handleTimeout_(options);\n\n if (response.status < 500) {\n return response;\n }\n // else\n throw new Error('fetch_server_error');\n }\n catch (err) {\n logger_.accident('fetch', 'fetch_failed_retry', err);\n\n if (globalScope.navigator?.onLine === false) {\n logger_.accident('handleRetryPattern_', 'offline', 'Skip retry because offline');\n throw err;\n }\n\n await waitForTimeout(options.retryDelay);\n\n options.signal = externalAbortSignal;\n return handleRetryPattern_(options);\n }\n}\n\n/**\n * It's a wrapper around the browser's `fetch` with timeout.\n */\nexport function handleTimeout_(options: FetchOptions): Promise<Response> {\n if (options.timeout === 0) {\n return globalScope.fetch(options.url, options);\n }\n // else\n logger_.logMethod?.('handleTimeout_');\n return new Promise((resolved, reject) => {\n const abortController = typeof AbortController === 'function' ? new AbortController() : null;\n const externalAbortSignal = options.signal;\n options.signal = abortController?.signal;\n\n if (abortController !== null && externalAbortSignal != null) {\n // Respect external abort signal\n externalAbortSignal.addEventListener('abort', () => abortController.abort(), {once: true});\n }\n\n const timeoutId = setTimeout(() => {\n reject(new Error('fetch_timeout'));\n abortController?.abort('fetch_timeout');\n }, options.timeout);\n\n // abortController.signal.addEventListener('abort', () => {\n // logger.incident('fetch', 'fetch_abort_signal', {\n // reason: abortController.signal.reason,\n // });\n // });\n\n globalScope\n .fetch(options.url, options)\n .then((response) => resolved(response))\n .catch((reason) => reject(reason))\n .finally(() => {\n delete options.signal; // try to avoid memory leak in nodejs!\n clearTimeout(timeoutId);\n });\n });\n}\n"],
4
+ "sourcesContent": ["import {handleCacheStrategy_, logger_, processOptions_, cacheSupported} from './core.js';\n\nimport type {FetchOptions, ResponseError, ResponseSuccess} from './type.js';\nimport type {JsonObject} from '@alwatr/type-helper';\n\nexport {cacheSupported};\nexport type * from './type.js';\n\n/**\n * It's a wrapper around the browser's `fetch` function that adds retry pattern, timeout, cacheStrategy,\n * remove duplicates, etc.\n *\n * @see {@link FetchOptions}\n * @see {@link ResponseSuccess}\n * @see {@link ResponseError}\n *\n * @param options Fetch options.\n *\n * @returns A success or error response.\n *\n * @example\n * ```typescript\n * const responseJson = await fetchJson({\n * url: '/api/products',\n * queryParameters: {limit: 10},\n * timeout: 8_000,\n * retry: 3,\n * cacheStrategy: 'stale_while_revalidate',\n * cacheDuplicate: 'auto',\n * });\n * ```\n */\nexport async function fetchJson<T extends JsonObject>(options: FetchOptions): Promise<ResponseSuccess<T> | ResponseError> {\n let response;\n let responseText;\n let responseJson;\n try {\n response = await fetch(options);\n responseText = await response.text();\n responseJson = JSON.parse(responseText) as ResponseSuccess<T>;\n responseJson.ok = true;\n responseJson.statusCode = response.status;\n return responseJson;\n }\n catch (error) {\n const responseError: ResponseError = {\n ok: false,\n statusCode: response?.status,\n statusText: response?.statusText,\n errorCode: (responseJson?.errorCode as string) ?? (error as Error).message,\n responseText,\n meta: responseJson?.meta as JsonObject,\n };\n\n logger_.accident('fetchJson', 'fetch_failed', {responseError, error});\n return responseError;\n }\n}\n\n/**\n * It's a wrapper around the browser's `fetch` function that adds retry pattern, timeout, cacheStrategy,\n * remove duplicates, etc.\n *\n * @see {@link FetchOptions}\n *\n * @param options Fetch options.\n *\n * @returns A promise that resolves to the Response to that request, whether it is successful or not.\n *\n * @example\n * ```typescript\n * const response = await fetch({\n * url: '/api/products',\n * queryParameters: {limit: 10},\n * timeout: 8_000,\n * retry: 3,\n * cacheStrategy: 'stale_while_revalidate',\n * cacheDuplicate: 'auto',\n * });\n * ```\n */\nexport function fetch(options: FetchOptions): Promise<Response> {\n options = processOptions_(options);\n logger_.logMethodArgs?.('fetch', {options});\n return handleCacheStrategy_(options as Required<FetchOptions>);\n}\n", "import {globalScope} from '@alwatr/global-scope';\nimport {definePackage} from '@alwatr/logger';\nimport {waitForTimeout} from '@alwatr/wait';\n\nimport type {FetchOptions} from './type.js';\nimport type {} from '@alwatr/nano-build';\n\nexport const logger_ = definePackage('@alwatr/fetch', __package_version__);\n\nlet cacheStorage_: Cache;\nexport const cacheSupported = Object.hasOwn(globalScope, 'caches');\n\nconst duplicateRequestStorage_: Record<string, Promise<Response>> = {};\n\n/**\n * Process fetch options and set defaults, etc.\n *\n * @param options Fetch options.\n *\n * @returns Required fetch options.\n */\nexport function processOptions_(options: FetchOptions): Required<FetchOptions> {\n options.method ??= 'GET';\n options.window ??= null;\n\n options.timeout ??= 8_000;\n options.retry ??= 3;\n options.retryDelay ??= 1_000;\n options.cacheStrategy ??= 'network_only';\n options.removeDuplicate ??= 'never';\n options.headers ??= {};\n\n if (options.cacheStrategy !== 'network_only' && cacheSupported !== true) {\n logger_.incident?.('fetch', 'fetch_cache_strategy_unsupported', {\n cacheSupported,\n });\n options.cacheStrategy = 'network_only';\n }\n\n if (options.removeDuplicate === 'auto') {\n options.removeDuplicate = cacheSupported ? 'until_load' : 'always';\n }\n\n if (options.url.lastIndexOf('?') === -1 && options.queryParameters != null) {\n const queryParameters = options.queryParameters;\n // prettier-ignore\n const queryArray = Object\n .keys(queryParameters)\n .map((key) => `${key}=${String(queryParameters[key])}`);\n\n if (queryArray.length > 0) {\n options.url += '?' + queryArray.join('&');\n }\n }\n\n if (options.bodyJson !== undefined) {\n options.body = JSON.stringify(options.bodyJson);\n options.headers['Content-Type'] = 'application/json';\n }\n\n if (options.bearerToken !== undefined) {\n options.headers.Authorization = `Bearer ${options.bearerToken}`;\n }\n else if (options.alwatrAuth !== undefined) {\n options.headers.Authorization = `Alwatr ${options.alwatrAuth.userId}:${options.alwatrAuth.userToken}`;\n }\n\n return options as Required<FetchOptions>;\n}\n\n/**\n * Handle Cache Strategy over `handleRemoveDuplicate_`.\n */\nexport async function handleCacheStrategy_(options: Required<FetchOptions>): Promise<Response> {\n if (options.cacheStrategy === 'network_only') {\n return handleRemoveDuplicate_(options);\n }\n // else handle cache strategies!\n logger_.logMethod?.('_handleCacheStrategy');\n\n if (cacheStorage_ == null && options.cacheStorageName == null) {\n cacheStorage_ = await caches.open('fetch_cache');\n }\n\n const cacheStorage = options.cacheStorageName != null ? await caches.open(options.cacheStorageName) : cacheStorage_;\n\n const request = new Request(options.url, options);\n\n switch (options.cacheStrategy) {\n case 'cache_first': {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse != null) {\n return cachedResponse;\n }\n // else\n const response = await handleRemoveDuplicate_(options);\n if (response.ok) {\n cacheStorage.put(request, response.clone());\n }\n return response;\n }\n\n case 'cache_only': {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse == null) {\n logger_.accident('_handleCacheStrategy', 'fetch_cache_not_found', {url: request.url});\n throw new Error('fetch_cache_not_found');\n }\n // else\n return cachedResponse;\n }\n\n case 'network_first': {\n try {\n const networkResponse = await handleRemoveDuplicate_(options);\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n }\n return networkResponse;\n }\n catch (err) {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse != null) {\n return cachedResponse;\n }\n // else\n throw err;\n }\n }\n\n case 'update_cache': {\n const networkResponse = await handleRemoveDuplicate_(options);\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n }\n return networkResponse;\n }\n\n case 'stale_while_revalidate': {\n const cachedResponse = await cacheStorage.match(request);\n const fetchedResponsePromise = handleRemoveDuplicate_(options).then((networkResponse) => {\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n if (typeof options.revalidateCallback === 'function') {\n setTimeout(options.revalidateCallback, 0, networkResponse.clone());\n }\n }\n return networkResponse;\n });\n\n return cachedResponse ?? fetchedResponsePromise;\n }\n\n default: {\n return handleRemoveDuplicate_(options);\n }\n }\n}\n\n/**\n * Handle Remove Duplicates over `_handleRetryPattern`.\n */\nexport async function handleRemoveDuplicate_(options: Required<FetchOptions>): Promise<Response> {\n if (options.removeDuplicate === 'never') return handleRetryPattern_(options);\n\n logger_.logMethod?.('handleRemoveDuplicate_');\n\n const cacheKey = options.method + ' ' + options.url;\n\n // We must cache fetch promise without await for handle other parallel requests.\n duplicateRequestStorage_[cacheKey] ??= handleRetryPattern_(options);\n\n try {\n // For all requests need to await for clone responses.\n const response = await duplicateRequestStorage_[cacheKey];\n\n if (duplicateRequestStorage_[cacheKey] != null) {\n if (response.ok !== true || options.removeDuplicate === 'until_load') {\n delete duplicateRequestStorage_[cacheKey];\n }\n }\n\n return response.clone();\n }\n catch (err) {\n // clean cache on any error.\n delete duplicateRequestStorage_[cacheKey];\n throw err;\n }\n}\n\n/**\n * Handle retry pattern over `handleTimeout_`.\n */\nexport async function handleRetryPattern_(options: Required<FetchOptions>): Promise<Response> {\n if (!(options.retry > 1)) return handleTimeout_(options);\n\n logger_.logMethod?.('_handleRetryPattern');\n options.retry--;\n\n const externalAbortSignal = options.signal;\n\n try {\n const response = await handleTimeout_(options);\n\n if (response.status < 500) {\n return response;\n }\n // else\n throw new Error('fetch_server_error');\n }\n catch (err) {\n logger_.accident('fetch', 'fetch_failed_retry', err);\n\n if (globalScope.navigator?.onLine === false) {\n logger_.accident('handleRetryPattern_', 'offline', 'Skip retry because offline');\n throw err;\n }\n\n await waitForTimeout(options.retryDelay);\n\n options.signal = externalAbortSignal;\n return handleRetryPattern_(options);\n }\n}\n\n/**\n * It's a wrapper around the browser's `fetch` with timeout.\n */\nexport function handleTimeout_(options: FetchOptions): Promise<Response> {\n if (options.timeout === 0) {\n return globalScope.fetch(options.url, options);\n }\n // else\n logger_.logMethod?.('handleTimeout_');\n return new Promise((resolved, reject) => {\n const abortController = typeof AbortController === 'function' ? new AbortController() : null;\n const externalAbortSignal = options.signal;\n options.signal = abortController?.signal;\n\n if (abortController !== null && externalAbortSignal != null) {\n // Respect external abort signal\n externalAbortSignal.addEventListener('abort', () => abortController.abort(), {once: true});\n }\n\n const timeoutId = setTimeout(() => {\n reject(new Error('fetch_timeout'));\n abortController?.abort('fetch_timeout');\n }, options.timeout);\n\n // abortController.signal.addEventListener('abort', () => {\n // logger.incident('fetch', 'fetch_abort_signal', {\n // reason: abortController.signal.reason,\n // });\n // });\n\n globalScope\n .fetch(options.url, options)\n .then((response) => resolved(response))\n .catch((reason) => reject(reason))\n .finally(() => {\n delete options.signal; // try to avoid memory leak in nodejs!\n clearTimeout(timeoutId);\n });\n });\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["main_exports", "__export", "cacheSupported", "fetch", "fetchJson", "__toCommonJS", "import_global_scope", "import_logger", "import_wait", "logger_", "cacheStorage_", "cacheSupported", "duplicateRequestStorage_", "processOptions_", "options", "queryParameters", "queryArray", "key", "handleCacheStrategy_", "handleRemoveDuplicate_", "cacheStorage", "request", "cachedResponse", "response", "networkResponse", "err", "fetchedResponsePromise", "handleRetryPattern_", "cacheKey", "handleTimeout_", "externalAbortSignal", "resolved", "reject", "abortController", "timeoutId", "reason", "fetchJson", "options", "response", "responseText", "responseJson", "fetch", "error", "responseError", "logger_", "processOptions_", "handleCacheStrategy_"]
7
7
  }
package/dist/main.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { cacheSupported } from './core';
2
- import type { FetchOptions, ResponseError, ResponseSuccess } from './type';
1
+ import { cacheSupported } from './core.js';
2
+ import type { FetchOptions, ResponseError, ResponseSuccess } from './type.js';
3
3
  import type { JsonObject } from '@alwatr/type-helper';
4
4
  export { cacheSupported };
5
- export type * from './type';
5
+ export type * from './type.js';
6
6
  /**
7
7
  * It's a wrapper around the browser's `fetch` function that adds retry pattern, timeout, cacheStrategy,
8
8
  * remove duplicates, etc.
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiD,cAAc,EAAC,MAAM,QAAQ,CAAC;AAEtF,OAAO,KAAK,EAAC,YAAY,EAAE,aAAa,EAAE,eAAe,EAAC,MAAM,QAAQ,CAAC;AACzE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAC,cAAc,EAAC,CAAC;AACxB,mBAAmB,QAAQ,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,SAAS,CAAC,CAAC,SAAS,UAAU,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAyBxH;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAI9D"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiD,cAAc,EAAC,MAAM,WAAW,CAAC;AAEzF,OAAO,KAAK,EAAC,YAAY,EAAE,aAAa,EAAE,eAAe,EAAC,MAAM,WAAW,CAAC;AAC5E,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAC,cAAc,EAAC,CAAC;AACxB,mBAAmB,WAAW,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,SAAS,CAAC,CAAC,SAAS,UAAU,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAyBxH;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAI9D"}
package/dist/main.mjs CHANGED
@@ -1,3 +1,3 @@
1
- /* @alwatr/fetch v3.1.0 */
2
- import{globalScope as f}from"@alwatr/global-scope";import{definePackage as w}from"@alwatr/logger";import{waitForTimeout as g}from"@alwatr/wait";var n=w("@alwatr/fetch","3.1.0"),m,s=Object.hasOwn(f,"caches"),u={};function y(e){if(e.method??(e.method="GET"),e.window??(e.window=null),e.timeout??(e.timeout=8e3),e.retry??(e.retry=3),e.retryDelay??(e.retryDelay=1e3),e.cacheStrategy??(e.cacheStrategy="network_only"),e.removeDuplicate??(e.removeDuplicate="never"),e.headers??(e.headers={}),e.cacheStrategy!=="network_only"&&s!==!0&&(n.incident?.("fetch","fetch_cache_strategy_unsupported",{cacheSupported:s}),e.cacheStrategy="network_only"),e.removeDuplicate==="auto"&&(e.removeDuplicate=s?"until_load":"always"),e.url.lastIndexOf("?")===-1&&e.queryParameters!=null){let a=e.queryParameters,t=Object.keys(a).map(r=>`${r}=${String(a[r])}`);t.length>0&&(e.url+="?"+t.join("&"))}return e.bodyJson!==void 0&&(e.body=JSON.stringify(e.bodyJson),e.headers["Content-Type"]="application/json"),e.bearerToken!==void 0?e.headers.Authorization=`Bearer ${e.bearerToken}`:e.alwatrAuth!==void 0&&(e.headers.Authorization=`Alwatr ${e.alwatrAuth.userId}:${e.alwatrAuth.userToken}`),e}async function _(e){if(e.cacheStrategy==="network_only")return h(e);n.logMethod?.("_handleCacheStrategy"),m==null&&e.cacheStorageName==null&&(m=await caches.open("fetch_cache"));let a=e.cacheStorageName!=null?await caches.open(e.cacheStorageName):m,t=new Request(e.url,e);switch(e.cacheStrategy){case"cache_first":{let r=await a.match(t);if(r!=null)return r;let c=await h(e);return c.ok&&a.put(t,c.clone()),c}case"cache_only":{let r=await a.match(t);if(r==null)throw n.accident("_handleCacheStrategy","fetch_cache_not_found",{url:t.url}),new Error("fetch_cache_not_found");return r}case"network_first":try{let r=await h(e);return r.ok&&a.put(t,r.clone()),r}catch(r){let c=await a.match(t);if(c!=null)return c;throw r}case"update_cache":{let r=await h(e);return r.ok&&a.put(t,r.clone()),r}case"stale_while_revalidate":{let r=await a.match(t),c=h(e).then(l=>(l.ok&&(a.put(t,l.clone()),typeof e.revalidateCallback=="function"&&setTimeout(e.revalidateCallback,0,l.clone())),l));return r??c}default:return h(e)}}async function h(e){if(e.removeDuplicate==="never")return i(e);n.logMethod?.("handleRemoveDuplicate_");let a=e.method+" "+e.url;u[a]??(u[a]=i(e));try{let t=await u[a];return u[a]!=null&&(t.ok!==!0||e.removeDuplicate==="until_load")&&delete u[a],t.clone()}catch(t){throw delete u[a],t}}async function i(e){if(!(e.retry>1))return o(e);n.logMethod?.("_handleRetryPattern"),e.retry--;let a=e.signal;try{let t=await o(e);if(t.status<500)return t;throw new Error("fetch_server_error")}catch(t){if(n.accident("fetch","fetch_failed_retry",t),f.navigator?.onLine===!1)throw n.accident("handleRetryPattern_","offline","Skip retry because offline"),t;return await g(e.retryDelay),e.signal=a,i(e)}}function o(e){return e.timeout===0?f.fetch(e.url,e):(n.logMethod?.("handleTimeout_"),new Promise((a,t)=>{let r=typeof AbortController=="function"?new AbortController:null,c=e.signal;e.signal=r?.signal,r!==null&&c!=null&&c.addEventListener("abort",()=>r.abort(),{once:!0});let l=setTimeout(()=>{t(new Error("fetch_timeout")),r?.abort("fetch_timeout")},e.timeout);f.fetch(e.url,e).then(d=>a(d)).catch(d=>t(d)).finally(()=>{delete e.signal,clearTimeout(l)})}))}async function T(e){let a,t,r;try{return a=await R(e),t=await a.text(),r=JSON.parse(t),r.ok=!0,r.statusCode=a.status,r}catch(c){let l={ok:!1,statusCode:a?.status,statusText:a?.statusText,errorCode:r?.errorCode??c.message,responseText:t,meta:r?.meta};return n.accident("fetchJson","fetch_failed",{responseError:l,error:c}),l}}function R(e){return e=y(e),n.logMethodArgs?.("fetch",{options:e}),_(e)}export{s as cacheSupported,R as fetch,T as fetchJson};
1
+ /* @alwatr/fetch v3.1.2 */
2
+ import{globalScope as f}from"@alwatr/global-scope";import{definePackage as w}from"@alwatr/logger";import{waitForTimeout as g}from"@alwatr/wait";var n=w("@alwatr/fetch","3.1.2"),m,s=Object.hasOwn(f,"caches"),u={};function y(e){if(e.method??(e.method="GET"),e.window??(e.window=null),e.timeout??(e.timeout=8e3),e.retry??(e.retry=3),e.retryDelay??(e.retryDelay=1e3),e.cacheStrategy??(e.cacheStrategy="network_only"),e.removeDuplicate??(e.removeDuplicate="never"),e.headers??(e.headers={}),e.cacheStrategy!=="network_only"&&s!==!0&&(n.incident?.("fetch","fetch_cache_strategy_unsupported",{cacheSupported:s}),e.cacheStrategy="network_only"),e.removeDuplicate==="auto"&&(e.removeDuplicate=s?"until_load":"always"),e.url.lastIndexOf("?")===-1&&e.queryParameters!=null){let a=e.queryParameters,t=Object.keys(a).map(r=>`${r}=${String(a[r])}`);t.length>0&&(e.url+="?"+t.join("&"))}return e.bodyJson!==void 0&&(e.body=JSON.stringify(e.bodyJson),e.headers["Content-Type"]="application/json"),e.bearerToken!==void 0?e.headers.Authorization=`Bearer ${e.bearerToken}`:e.alwatrAuth!==void 0&&(e.headers.Authorization=`Alwatr ${e.alwatrAuth.userId}:${e.alwatrAuth.userToken}`),e}async function _(e){if(e.cacheStrategy==="network_only")return h(e);n.logMethod?.("_handleCacheStrategy"),m==null&&e.cacheStorageName==null&&(m=await caches.open("fetch_cache"));let a=e.cacheStorageName!=null?await caches.open(e.cacheStorageName):m,t=new Request(e.url,e);switch(e.cacheStrategy){case"cache_first":{let r=await a.match(t);if(r!=null)return r;let c=await h(e);return c.ok&&a.put(t,c.clone()),c}case"cache_only":{let r=await a.match(t);if(r==null)throw n.accident("_handleCacheStrategy","fetch_cache_not_found",{url:t.url}),new Error("fetch_cache_not_found");return r}case"network_first":try{let r=await h(e);return r.ok&&a.put(t,r.clone()),r}catch(r){let c=await a.match(t);if(c!=null)return c;throw r}case"update_cache":{let r=await h(e);return r.ok&&a.put(t,r.clone()),r}case"stale_while_revalidate":{let r=await a.match(t),c=h(e).then(l=>(l.ok&&(a.put(t,l.clone()),typeof e.revalidateCallback=="function"&&setTimeout(e.revalidateCallback,0,l.clone())),l));return r??c}default:return h(e)}}async function h(e){if(e.removeDuplicate==="never")return i(e);n.logMethod?.("handleRemoveDuplicate_");let a=e.method+" "+e.url;u[a]??(u[a]=i(e));try{let t=await u[a];return u[a]!=null&&(t.ok!==!0||e.removeDuplicate==="until_load")&&delete u[a],t.clone()}catch(t){throw delete u[a],t}}async function i(e){if(!(e.retry>1))return o(e);n.logMethod?.("_handleRetryPattern"),e.retry--;let a=e.signal;try{let t=await o(e);if(t.status<500)return t;throw new Error("fetch_server_error")}catch(t){if(n.accident("fetch","fetch_failed_retry",t),f.navigator?.onLine===!1)throw n.accident("handleRetryPattern_","offline","Skip retry because offline"),t;return await g(e.retryDelay),e.signal=a,i(e)}}function o(e){return e.timeout===0?f.fetch(e.url,e):(n.logMethod?.("handleTimeout_"),new Promise((a,t)=>{let r=typeof AbortController=="function"?new AbortController:null,c=e.signal;e.signal=r?.signal,r!==null&&c!=null&&c.addEventListener("abort",()=>r.abort(),{once:!0});let l=setTimeout(()=>{t(new Error("fetch_timeout")),r?.abort("fetch_timeout")},e.timeout);f.fetch(e.url,e).then(d=>a(d)).catch(d=>t(d)).finally(()=>{delete e.signal,clearTimeout(l)})}))}async function T(e){let a,t,r;try{return a=await R(e),t=await a.text(),r=JSON.parse(t),r.ok=!0,r.statusCode=a.status,r}catch(c){let l={ok:!1,statusCode:a?.status,statusText:a?.statusText,errorCode:r?.errorCode??c.message,responseText:t,meta:r?.meta};return n.accident("fetchJson","fetch_failed",{responseError:l,error:c}),l}}function R(e){return e=y(e),n.logMethodArgs?.("fetch",{options:e}),_(e)}export{s as cacheSupported,R as fetch,T as fetchJson};
3
3
  //# sourceMappingURL=main.mjs.map
package/dist/main.mjs.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/core.ts", "../src/main.ts"],
4
- "sourcesContent": ["import {globalScope} from '@alwatr/global-scope';\nimport {definePackage} from '@alwatr/logger';\nimport {waitForTimeout} from '@alwatr/wait';\n\nimport type {FetchOptions} from './type';\nimport type {} from '@alwatr/nano-build';\n\nexport const logger_ = definePackage('@alwatr/fetch', __package_version__);\n\nlet cacheStorage_: Cache;\nexport const cacheSupported = Object.hasOwn(globalScope, 'caches');\n\nconst duplicateRequestStorage_: Record<string, Promise<Response>> = {};\n\n/**\n * Process fetch options and set defaults, etc.\n *\n * @param options Fetch options.\n *\n * @returns Required fetch options.\n */\nexport function processOptions_(options: FetchOptions): Required<FetchOptions> {\n options.method ??= 'GET';\n options.window ??= null;\n\n options.timeout ??= 8_000;\n options.retry ??= 3;\n options.retryDelay ??= 1_000;\n options.cacheStrategy ??= 'network_only';\n options.removeDuplicate ??= 'never';\n options.headers ??= {};\n\n if (options.cacheStrategy !== 'network_only' && cacheSupported !== true) {\n logger_.incident?.('fetch', 'fetch_cache_strategy_unsupported', {\n cacheSupported,\n });\n options.cacheStrategy = 'network_only';\n }\n\n if (options.removeDuplicate === 'auto') {\n options.removeDuplicate = cacheSupported ? 'until_load' : 'always';\n }\n\n if (options.url.lastIndexOf('?') === -1 && options.queryParameters != null) {\n const queryParameters = options.queryParameters;\n // prettier-ignore\n const queryArray = Object\n .keys(queryParameters)\n .map((key) => `${key}=${String(queryParameters[key])}`);\n\n if (queryArray.length > 0) {\n options.url += '?' + queryArray.join('&');\n }\n }\n\n if (options.bodyJson !== undefined) {\n options.body = JSON.stringify(options.bodyJson);\n options.headers['Content-Type'] = 'application/json';\n }\n\n if (options.bearerToken !== undefined) {\n options.headers.Authorization = `Bearer ${options.bearerToken}`;\n }\n else if (options.alwatrAuth !== undefined) {\n options.headers.Authorization = `Alwatr ${options.alwatrAuth.userId}:${options.alwatrAuth.userToken}`;\n }\n\n return options as Required<FetchOptions>;\n}\n\n/**\n * Handle Cache Strategy over `handleRemoveDuplicate_`.\n */\nexport async function handleCacheStrategy_(options: Required<FetchOptions>): Promise<Response> {\n if (options.cacheStrategy === 'network_only') {\n return handleRemoveDuplicate_(options);\n }\n // else handle cache strategies!\n logger_.logMethod?.('_handleCacheStrategy');\n\n if (cacheStorage_ == null && options.cacheStorageName == null) {\n cacheStorage_ = await caches.open('fetch_cache');\n }\n\n const cacheStorage = options.cacheStorageName != null ? await caches.open(options.cacheStorageName) : cacheStorage_;\n\n const request = new Request(options.url, options);\n\n switch (options.cacheStrategy) {\n case 'cache_first': {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse != null) {\n return cachedResponse;\n }\n // else\n const response = await handleRemoveDuplicate_(options);\n if (response.ok) {\n cacheStorage.put(request, response.clone());\n }\n return response;\n }\n\n case 'cache_only': {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse == null) {\n logger_.accident('_handleCacheStrategy', 'fetch_cache_not_found', {url: request.url});\n throw new Error('fetch_cache_not_found');\n }\n // else\n return cachedResponse;\n }\n\n case 'network_first': {\n try {\n const networkResponse = await handleRemoveDuplicate_(options);\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n }\n return networkResponse;\n }\n catch (err) {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse != null) {\n return cachedResponse;\n }\n // else\n throw err;\n }\n }\n\n case 'update_cache': {\n const networkResponse = await handleRemoveDuplicate_(options);\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n }\n return networkResponse;\n }\n\n case 'stale_while_revalidate': {\n const cachedResponse = await cacheStorage.match(request);\n const fetchedResponsePromise = handleRemoveDuplicate_(options).then((networkResponse) => {\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n if (typeof options.revalidateCallback === 'function') {\n setTimeout(options.revalidateCallback, 0, networkResponse.clone());\n }\n }\n return networkResponse;\n });\n\n return cachedResponse ?? fetchedResponsePromise;\n }\n\n default: {\n return handleRemoveDuplicate_(options);\n }\n }\n}\n\n/**\n * Handle Remove Duplicates over `_handleRetryPattern`.\n */\nexport async function handleRemoveDuplicate_(options: Required<FetchOptions>): Promise<Response> {\n if (options.removeDuplicate === 'never') return handleRetryPattern_(options);\n\n logger_.logMethod?.('handleRemoveDuplicate_');\n\n const cacheKey = options.method + ' ' + options.url;\n\n // We must cache fetch promise without await for handle other parallel requests.\n duplicateRequestStorage_[cacheKey] ??= handleRetryPattern_(options);\n\n try {\n // For all requests need to await for clone responses.\n const response = await duplicateRequestStorage_[cacheKey];\n\n if (duplicateRequestStorage_[cacheKey] != null) {\n if (response.ok !== true || options.removeDuplicate === 'until_load') {\n delete duplicateRequestStorage_[cacheKey];\n }\n }\n\n return response.clone();\n }\n catch (err) {\n // clean cache on any error.\n delete duplicateRequestStorage_[cacheKey];\n throw err;\n }\n}\n\n/**\n * Handle retry pattern over `handleTimeout_`.\n */\nexport async function handleRetryPattern_(options: Required<FetchOptions>): Promise<Response> {\n if (!(options.retry > 1)) return handleTimeout_(options);\n\n logger_.logMethod?.('_handleRetryPattern');\n options.retry--;\n\n const externalAbortSignal = options.signal;\n\n try {\n const response = await handleTimeout_(options);\n\n if (response.status < 500) {\n return response;\n }\n // else\n throw new Error('fetch_server_error');\n }\n catch (err) {\n logger_.accident('fetch', 'fetch_failed_retry', err);\n\n if (globalScope.navigator?.onLine === false) {\n logger_.accident('handleRetryPattern_', 'offline', 'Skip retry because offline');\n throw err;\n }\n\n await waitForTimeout(options.retryDelay);\n\n options.signal = externalAbortSignal;\n return handleRetryPattern_(options);\n }\n}\n\n/**\n * It's a wrapper around the browser's `fetch` with timeout.\n */\nexport function handleTimeout_(options: FetchOptions): Promise<Response> {\n if (options.timeout === 0) {\n return globalScope.fetch(options.url, options);\n }\n // else\n logger_.logMethod?.('handleTimeout_');\n return new Promise((resolved, reject) => {\n const abortController = typeof AbortController === 'function' ? new AbortController() : null;\n const externalAbortSignal = options.signal;\n options.signal = abortController?.signal;\n\n if (abortController !== null && externalAbortSignal != null) {\n // Respect external abort signal\n externalAbortSignal.addEventListener('abort', () => abortController.abort(), {once: true});\n }\n\n const timeoutId = setTimeout(() => {\n reject(new Error('fetch_timeout'));\n abortController?.abort('fetch_timeout');\n }, options.timeout);\n\n // abortController.signal.addEventListener('abort', () => {\n // logger.incident('fetch', 'fetch_abort_signal', {\n // reason: abortController.signal.reason,\n // });\n // });\n\n globalScope\n .fetch(options.url, options)\n .then((response) => resolved(response))\n .catch((reason) => reject(reason))\n .finally(() => {\n delete options.signal; // try to avoid memory leak in nodejs!\n clearTimeout(timeoutId);\n });\n });\n}\n", "import {handleCacheStrategy_, logger_, processOptions_, cacheSupported} from './core';\n\nimport type {FetchOptions, ResponseError, ResponseSuccess} from './type';\nimport type {JsonObject} from '@alwatr/type-helper';\n\nexport {cacheSupported};\nexport type * from './type';\n\n/**\n * It's a wrapper around the browser's `fetch` function that adds retry pattern, timeout, cacheStrategy,\n * remove duplicates, etc.\n *\n * @see {@link FetchOptions}\n * @see {@link ResponseSuccess}\n * @see {@link ResponseError}\n *\n * @param options Fetch options.\n *\n * @returns A success or error response.\n *\n * @example\n * ```typescript\n * const responseJson = await fetchJson({\n * url: '/api/products',\n * queryParameters: {limit: 10},\n * timeout: 8_000,\n * retry: 3,\n * cacheStrategy: 'stale_while_revalidate',\n * cacheDuplicate: 'auto',\n * });\n * ```\n */\nexport async function fetchJson<T extends JsonObject>(options: FetchOptions): Promise<ResponseSuccess<T> | ResponseError> {\n let response;\n let responseText;\n let responseJson;\n try {\n response = await fetch(options);\n responseText = await response.text();\n responseJson = JSON.parse(responseText) as ResponseSuccess<T>;\n responseJson.ok = true;\n responseJson.statusCode = response.status;\n return responseJson;\n }\n catch (error) {\n const responseError: ResponseError = {\n ok: false,\n statusCode: response?.status,\n statusText: response?.statusText,\n errorCode: (responseJson?.errorCode as string) ?? (error as Error).message,\n responseText,\n meta: responseJson?.meta as JsonObject,\n };\n\n logger_.accident('fetchJson', 'fetch_failed', {responseError, error});\n return responseError;\n }\n}\n\n/**\n * It's a wrapper around the browser's `fetch` function that adds retry pattern, timeout, cacheStrategy,\n * remove duplicates, etc.\n *\n * @see {@link FetchOptions}\n *\n * @param options Fetch options.\n *\n * @returns A promise that resolves to the Response to that request, whether it is successful or not.\n *\n * @example\n * ```typescript\n * const response = await fetch({\n * url: '/api/products',\n * queryParameters: {limit: 10},\n * timeout: 8_000,\n * retry: 3,\n * cacheStrategy: 'stale_while_revalidate',\n * cacheDuplicate: 'auto',\n * });\n * ```\n */\nexport function fetch(options: FetchOptions): Promise<Response> {\n options = processOptions_(options);\n logger_.logMethodArgs?.('fetch', {options});\n return handleCacheStrategy_(options as Required<FetchOptions>);\n}\n"],
4
+ "sourcesContent": ["import {globalScope} from '@alwatr/global-scope';\nimport {definePackage} from '@alwatr/logger';\nimport {waitForTimeout} from '@alwatr/wait';\n\nimport type {FetchOptions} from './type.js';\nimport type {} from '@alwatr/nano-build';\n\nexport const logger_ = definePackage('@alwatr/fetch', __package_version__);\n\nlet cacheStorage_: Cache;\nexport const cacheSupported = Object.hasOwn(globalScope, 'caches');\n\nconst duplicateRequestStorage_: Record<string, Promise<Response>> = {};\n\n/**\n * Process fetch options and set defaults, etc.\n *\n * @param options Fetch options.\n *\n * @returns Required fetch options.\n */\nexport function processOptions_(options: FetchOptions): Required<FetchOptions> {\n options.method ??= 'GET';\n options.window ??= null;\n\n options.timeout ??= 8_000;\n options.retry ??= 3;\n options.retryDelay ??= 1_000;\n options.cacheStrategy ??= 'network_only';\n options.removeDuplicate ??= 'never';\n options.headers ??= {};\n\n if (options.cacheStrategy !== 'network_only' && cacheSupported !== true) {\n logger_.incident?.('fetch', 'fetch_cache_strategy_unsupported', {\n cacheSupported,\n });\n options.cacheStrategy = 'network_only';\n }\n\n if (options.removeDuplicate === 'auto') {\n options.removeDuplicate = cacheSupported ? 'until_load' : 'always';\n }\n\n if (options.url.lastIndexOf('?') === -1 && options.queryParameters != null) {\n const queryParameters = options.queryParameters;\n // prettier-ignore\n const queryArray = Object\n .keys(queryParameters)\n .map((key) => `${key}=${String(queryParameters[key])}`);\n\n if (queryArray.length > 0) {\n options.url += '?' + queryArray.join('&');\n }\n }\n\n if (options.bodyJson !== undefined) {\n options.body = JSON.stringify(options.bodyJson);\n options.headers['Content-Type'] = 'application/json';\n }\n\n if (options.bearerToken !== undefined) {\n options.headers.Authorization = `Bearer ${options.bearerToken}`;\n }\n else if (options.alwatrAuth !== undefined) {\n options.headers.Authorization = `Alwatr ${options.alwatrAuth.userId}:${options.alwatrAuth.userToken}`;\n }\n\n return options as Required<FetchOptions>;\n}\n\n/**\n * Handle Cache Strategy over `handleRemoveDuplicate_`.\n */\nexport async function handleCacheStrategy_(options: Required<FetchOptions>): Promise<Response> {\n if (options.cacheStrategy === 'network_only') {\n return handleRemoveDuplicate_(options);\n }\n // else handle cache strategies!\n logger_.logMethod?.('_handleCacheStrategy');\n\n if (cacheStorage_ == null && options.cacheStorageName == null) {\n cacheStorage_ = await caches.open('fetch_cache');\n }\n\n const cacheStorage = options.cacheStorageName != null ? await caches.open(options.cacheStorageName) : cacheStorage_;\n\n const request = new Request(options.url, options);\n\n switch (options.cacheStrategy) {\n case 'cache_first': {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse != null) {\n return cachedResponse;\n }\n // else\n const response = await handleRemoveDuplicate_(options);\n if (response.ok) {\n cacheStorage.put(request, response.clone());\n }\n return response;\n }\n\n case 'cache_only': {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse == null) {\n logger_.accident('_handleCacheStrategy', 'fetch_cache_not_found', {url: request.url});\n throw new Error('fetch_cache_not_found');\n }\n // else\n return cachedResponse;\n }\n\n case 'network_first': {\n try {\n const networkResponse = await handleRemoveDuplicate_(options);\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n }\n return networkResponse;\n }\n catch (err) {\n const cachedResponse = await cacheStorage.match(request);\n if (cachedResponse != null) {\n return cachedResponse;\n }\n // else\n throw err;\n }\n }\n\n case 'update_cache': {\n const networkResponse = await handleRemoveDuplicate_(options);\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n }\n return networkResponse;\n }\n\n case 'stale_while_revalidate': {\n const cachedResponse = await cacheStorage.match(request);\n const fetchedResponsePromise = handleRemoveDuplicate_(options).then((networkResponse) => {\n if (networkResponse.ok) {\n cacheStorage.put(request, networkResponse.clone());\n if (typeof options.revalidateCallback === 'function') {\n setTimeout(options.revalidateCallback, 0, networkResponse.clone());\n }\n }\n return networkResponse;\n });\n\n return cachedResponse ?? fetchedResponsePromise;\n }\n\n default: {\n return handleRemoveDuplicate_(options);\n }\n }\n}\n\n/**\n * Handle Remove Duplicates over `_handleRetryPattern`.\n */\nexport async function handleRemoveDuplicate_(options: Required<FetchOptions>): Promise<Response> {\n if (options.removeDuplicate === 'never') return handleRetryPattern_(options);\n\n logger_.logMethod?.('handleRemoveDuplicate_');\n\n const cacheKey = options.method + ' ' + options.url;\n\n // We must cache fetch promise without await for handle other parallel requests.\n duplicateRequestStorage_[cacheKey] ??= handleRetryPattern_(options);\n\n try {\n // For all requests need to await for clone responses.\n const response = await duplicateRequestStorage_[cacheKey];\n\n if (duplicateRequestStorage_[cacheKey] != null) {\n if (response.ok !== true || options.removeDuplicate === 'until_load') {\n delete duplicateRequestStorage_[cacheKey];\n }\n }\n\n return response.clone();\n }\n catch (err) {\n // clean cache on any error.\n delete duplicateRequestStorage_[cacheKey];\n throw err;\n }\n}\n\n/**\n * Handle retry pattern over `handleTimeout_`.\n */\nexport async function handleRetryPattern_(options: Required<FetchOptions>): Promise<Response> {\n if (!(options.retry > 1)) return handleTimeout_(options);\n\n logger_.logMethod?.('_handleRetryPattern');\n options.retry--;\n\n const externalAbortSignal = options.signal;\n\n try {\n const response = await handleTimeout_(options);\n\n if (response.status < 500) {\n return response;\n }\n // else\n throw new Error('fetch_server_error');\n }\n catch (err) {\n logger_.accident('fetch', 'fetch_failed_retry', err);\n\n if (globalScope.navigator?.onLine === false) {\n logger_.accident('handleRetryPattern_', 'offline', 'Skip retry because offline');\n throw err;\n }\n\n await waitForTimeout(options.retryDelay);\n\n options.signal = externalAbortSignal;\n return handleRetryPattern_(options);\n }\n}\n\n/**\n * It's a wrapper around the browser's `fetch` with timeout.\n */\nexport function handleTimeout_(options: FetchOptions): Promise<Response> {\n if (options.timeout === 0) {\n return globalScope.fetch(options.url, options);\n }\n // else\n logger_.logMethod?.('handleTimeout_');\n return new Promise((resolved, reject) => {\n const abortController = typeof AbortController === 'function' ? new AbortController() : null;\n const externalAbortSignal = options.signal;\n options.signal = abortController?.signal;\n\n if (abortController !== null && externalAbortSignal != null) {\n // Respect external abort signal\n externalAbortSignal.addEventListener('abort', () => abortController.abort(), {once: true});\n }\n\n const timeoutId = setTimeout(() => {\n reject(new Error('fetch_timeout'));\n abortController?.abort('fetch_timeout');\n }, options.timeout);\n\n // abortController.signal.addEventListener('abort', () => {\n // logger.incident('fetch', 'fetch_abort_signal', {\n // reason: abortController.signal.reason,\n // });\n // });\n\n globalScope\n .fetch(options.url, options)\n .then((response) => resolved(response))\n .catch((reason) => reject(reason))\n .finally(() => {\n delete options.signal; // try to avoid memory leak in nodejs!\n clearTimeout(timeoutId);\n });\n });\n}\n", "import {handleCacheStrategy_, logger_, processOptions_, cacheSupported} from './core.js';\n\nimport type {FetchOptions, ResponseError, ResponseSuccess} from './type.js';\nimport type {JsonObject} from '@alwatr/type-helper';\n\nexport {cacheSupported};\nexport type * from './type.js';\n\n/**\n * It's a wrapper around the browser's `fetch` function that adds retry pattern, timeout, cacheStrategy,\n * remove duplicates, etc.\n *\n * @see {@link FetchOptions}\n * @see {@link ResponseSuccess}\n * @see {@link ResponseError}\n *\n * @param options Fetch options.\n *\n * @returns A success or error response.\n *\n * @example\n * ```typescript\n * const responseJson = await fetchJson({\n * url: '/api/products',\n * queryParameters: {limit: 10},\n * timeout: 8_000,\n * retry: 3,\n * cacheStrategy: 'stale_while_revalidate',\n * cacheDuplicate: 'auto',\n * });\n * ```\n */\nexport async function fetchJson<T extends JsonObject>(options: FetchOptions): Promise<ResponseSuccess<T> | ResponseError> {\n let response;\n let responseText;\n let responseJson;\n try {\n response = await fetch(options);\n responseText = await response.text();\n responseJson = JSON.parse(responseText) as ResponseSuccess<T>;\n responseJson.ok = true;\n responseJson.statusCode = response.status;\n return responseJson;\n }\n catch (error) {\n const responseError: ResponseError = {\n ok: false,\n statusCode: response?.status,\n statusText: response?.statusText,\n errorCode: (responseJson?.errorCode as string) ?? (error as Error).message,\n responseText,\n meta: responseJson?.meta as JsonObject,\n };\n\n logger_.accident('fetchJson', 'fetch_failed', {responseError, error});\n return responseError;\n }\n}\n\n/**\n * It's a wrapper around the browser's `fetch` function that adds retry pattern, timeout, cacheStrategy,\n * remove duplicates, etc.\n *\n * @see {@link FetchOptions}\n *\n * @param options Fetch options.\n *\n * @returns A promise that resolves to the Response to that request, whether it is successful or not.\n *\n * @example\n * ```typescript\n * const response = await fetch({\n * url: '/api/products',\n * queryParameters: {limit: 10},\n * timeout: 8_000,\n * retry: 3,\n * cacheStrategy: 'stale_while_revalidate',\n * cacheDuplicate: 'auto',\n * });\n * ```\n */\nexport function fetch(options: FetchOptions): Promise<Response> {\n options = processOptions_(options);\n logger_.logMethodArgs?.('fetch', {options});\n return handleCacheStrategy_(options as Required<FetchOptions>);\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["globalScope", "definePackage", "waitForTimeout", "logger_", "cacheStorage_", "cacheSupported", "duplicateRequestStorage_", "processOptions_", "options", "queryParameters", "queryArray", "key", "handleCacheStrategy_", "handleRemoveDuplicate_", "cacheStorage", "request", "cachedResponse", "response", "networkResponse", "err", "fetchedResponsePromise", "handleRetryPattern_", "cacheKey", "handleTimeout_", "externalAbortSignal", "resolved", "reject", "abortController", "timeoutId", "reason", "fetchJson", "options", "response", "responseText", "responseJson", "fetch", "error", "responseError", "logger_", "processOptions_", "handleCacheStrategy_"]
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../src/type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAE,IAAI,EAAE,UAAU,EAAC,MAAM,qBAAqB,CAAC;AAEtE;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7G;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAEpE;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,eAAe,GAAG,YAAY,GAAG,aAAa,GAAG,cAAc,GAAG,wBAAwB,CAAC;AAExI;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAE7B;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,cAAc,CAAC;IAEjC;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAE9B;;OAEG;IACH,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElE;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,QAAQ,CAAC,EAAE,IAAI,CAAC;IAEhB;;OAEG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAElC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,EAAE;QACX,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,UAAU,IAAI,CAAC,GAAG;IACtD,EAAE,EAAE,IAAI,CAAC;IACT,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,KAAK,CAAC;IACV,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB,CAAA"}
1
+ {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../src/type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAE,IAAI,EAAE,UAAU,EAAC,MAAM,qBAAqB,CAAC;AAEtE;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7G;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAEpE;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,eAAe,GAAG,YAAY,GAAG,aAAa,GAAG,cAAc,GAAG,wBAAwB,CAAC;AAExI;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAE7B;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,cAAc,CAAC;IAEjC;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAE9B;;OAEG;IACH,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElE;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,QAAQ,CAAC,EAAE,IAAI,CAAC;IAEhB;;OAEG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAElC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,EAAE;QACX,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,UAAU,IAAI,CAAC,GAAG;IACtD,EAAE,EAAE,IAAI,CAAC;IACT,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,KAAK,CAAC;IACV,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alwatr/fetch",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "description": "Enhanced fetch API with cache strategy, retry pattern, timeout, helper methods and enhanced types.",
5
5
  "author": "S. Ali Mihandoost <ali.mihandoost@gmail.com>",
6
6
  "keywords": [
@@ -71,18 +71,18 @@
71
71
  "clean": "rm -rfv dist *.tsbuildinfo"
72
72
  },
73
73
  "dependencies": {
74
- "@alwatr/global-scope": "^1.1.12",
75
- "@alwatr/logger": "^3.2.4",
76
- "@alwatr/wait": "^1.1.7"
74
+ "@alwatr/global-scope": "^1.1.14",
75
+ "@alwatr/logger": "^3.2.6",
76
+ "@alwatr/wait": "^1.1.9"
77
77
  },
78
78
  "devDependencies": {
79
- "@alwatr/nano-build": "^1.3.2",
79
+ "@alwatr/nano-build": "^1.3.4",
80
80
  "@alwatr/prettier-config": "^1.0.4",
81
- "@alwatr/tsconfig-base": "^1.1.1",
82
- "@alwatr/type-helper": "^1.2.0",
83
- "@types/node": "^20.11.6",
81
+ "@alwatr/tsconfig-base": "^1.1.2",
82
+ "@alwatr/type-helper": "^1.2.2",
83
+ "@types/node": "^20.11.30",
84
84
  "jest": "^29.7.0",
85
- "typescript": "^5.3.3"
85
+ "typescript": "^5.4.3"
86
86
  },
87
- "gitHead": "3486060abc5f331a3b8c78da7f1d671ba1139b5d"
87
+ "gitHead": "df149736d1e85335aaeb9c547ff3bdd1161f9b33"
88
88
  }
package/src/main.test.js CHANGED
@@ -1,14 +1,17 @@
1
- import {fetch} from '@alwatr/fetch'
1
+ import {fetch, fetchJson} from '@alwatr/fetch'
2
2
 
3
3
  describe('fetch with search params', () => {
4
4
  it('should make a GET request to the specified URL', async () => {
5
+ /**
6
+ * @type {import('@alwatr/fetch').FetchOptions}
7
+ */
5
8
  const options = {
6
9
  url: 'http://httpbin.org/get',
7
10
  queryParameters: {
8
- a: 2
11
+ a: 2,
9
12
  },
10
13
  cacheStrategy: 'network_only',
11
- removeDuplicate: 'auto'
14
+ removeDuplicate: 'auto',
12
15
  };
13
16
 
14
17
  fetch(options);
@@ -19,3 +22,33 @@ describe('fetch with search params', () => {
19
22
  expect(responseJson.args.a).toBe('2');
20
23
  });
21
24
  });
25
+
26
+ describe('fetch json', () => {
27
+ it('should make a GET request to the specified URL with json body and parse valid json', async () => {
28
+ const options = {
29
+ url: 'http://httpbin.org/post',
30
+ method: 'POST',
31
+ bodyJson: {
32
+ a: 2
33
+ },
34
+ cacheStrategy: 'network_only',
35
+ };
36
+
37
+ const responseJson = await fetchJson(options);
38
+
39
+ expect(responseJson.ok).toBe(true);
40
+ expect(responseJson.json.a).toBe(2);
41
+ })
42
+
43
+ it('should make a GET request to the specified URL and parse json and handle 404 status code', async () => {
44
+ const options = {
45
+ url: 'https://httpbin.org/status/404',
46
+ cacheStrategy: 'network_only',
47
+ };
48
+
49
+ const responseJson = await fetchJson(options);
50
+
51
+ expect(responseJson.ok).toBe(false);
52
+ expect(responseJson.statusCode).toBe(404);
53
+ });
54
+ });