@scirexs/fetchy 0.4.2 → 0.5.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.
Files changed (3) hide show
  1. package/README.md +13 -13
  2. package/esm/main.js +1 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -146,7 +146,7 @@ interface FetchyOptions extends RequestInit {
146
146
  // Error throwing behavior
147
147
  onError?: {
148
148
  onNative?: boolean; // Throw on native errors (default: true)
149
- onStatus?: boolean; // Throw on 4xx/5xx status (default: false)
149
+ onStatus?: boolean; // Throw on 4xx/5xx status (default: true)
150
150
  } | boolean; // Set to true to throw on all errors
151
151
 
152
152
  // Initial jitter delay in seconds
@@ -171,7 +171,7 @@ interface FetchyOptions extends RequestInit {
171
171
  },
172
172
  onError: {
173
173
  onNative: true, // Throw native errors
174
- onStatus: false // Don't throw on HTTP errors
174
+ onStatus: true // Don't throw on HTTP errors
175
175
  },
176
176
  }
177
177
  ```
@@ -206,7 +206,7 @@ If the timeout duration specified in the `timeout` option is exceeded, the reque
206
206
 
207
207
  ### HTTPStatusError
208
208
 
209
- If `onStatus` is set to `true`, an `HTTPStatusError` will be thrown when the response status is outside the 2xx range. You can access the status and body through this error object. The error message format is: `404 Not Found: (no response body)`.
209
+ If `onStatus` is set to `true` (default), an `HTTPStatusError` will be thrown when the response status is outside the 2xx range. You can access the status and body through this error object. The error message format is: `404 Not Found: (no response body)`.
210
210
 
211
211
  ### RedirectError
212
212
 
@@ -290,11 +290,15 @@ const response = await fetchy("https://api.example.com/data", {
290
290
  ```ts
291
291
  import { fetchy, HTTPStatusError, RedirectError } from "@scirexs/fetchy";
292
292
 
293
- // Throw on error (default behavior, same as native fetch)
293
+ // Throw on error and not ok response (default behavior)
294
294
  try {
295
295
  const response = await fetchy("https://api.example.com/data");
296
296
  } catch (error) {
297
- console.error("Request failed:", error);
297
+ if (error instanceof HTTPStatusError) {
298
+ console.error("HTTP error:", error.message); // e.g., "404 Not Found: (no response body)"
299
+ console.error("Status:", error.status);
300
+ console.error("Body:", error.body);
301
+ }
298
302
  }
299
303
 
300
304
  // Return null on error
@@ -305,17 +309,13 @@ if (response === null) {
305
309
  console.log("Request failed");
306
310
  }
307
311
 
308
- // Throw only on HTTP errors
312
+ // Throw only on native errors
309
313
  try {
310
314
  const response = await fetchy("https://api.example.com/data", {
311
- onError: { onNative: false, onStatus: true }
315
+ onError: { onNative: true, onStatus: false } as const
312
316
  });
313
317
  } catch (error) {
314
- if (error instanceof HTTPStatusError) {
315
- console.error("HTTP error:", error.message); // e.g., "404 Not Found: (no response body)"
316
- console.error("Status:", error.status);
317
- console.error("Body:", error.body);
318
- }
318
+ console.error("Request failed:", error);
319
319
  }
320
320
 
321
321
  // Handle redirects
@@ -408,7 +408,7 @@ interface User {
408
408
  name: string;
409
409
  }
410
410
 
411
- const options = { timeout: 5, onError: true as const }; // Add `as const`
411
+ const options = { timeout: 5, onError: { onNative: true, onStatus: false } as const }; // Add `as const`
412
412
  const response = await fetchy("https://api.example.com/todos/1", "json", options);
413
413
  // `response` is User (not User | null)
414
414
  ```
package/esm/main.js CHANGED
@@ -1 +1 @@
1
- export{m as fetchy,A as fetchyb,l as HTTPStatusError,h as RedirectError};const i={timeout:15,delay:0,interval:3,maxInterval:30,maxAttempts:3,retryAfter:!0,onNative:!0,onStatus:!1,redirect:"follow"};class l extends Error{static#t=80;status;body;constructor(e,r,n){super(e),this.name="HTTPStatusError",this.status=r,this.body=n}static async fromResponse(e){const r=await e.text(),n=r.length>this.#t?`${r.slice(0,this.#t)}... (more ${r.length-this.#t} chars)`:r||"(no response body)",a=`${e.status} ${e.statusText}: ${n}`;return new this(a,e.status,r)}}class h extends Error{status;constructor(e,r){super(e),this.name="RedirectError",this.status=r}static fromResponse(e){const r=`${e.status} ${e.statusText}`.trim();return new this(r,e.status)}}async function A(t,e="auto",r){const n=await m(t,r);if(!n||!n.ok)return null;const a=n.headers.get("Content-Type")??"";try{return e==="text"||e==="auto"&&a.startsWith("text/")?await n.text():e==="json"||e==="auto"&&a==="application/json"?await n.json():await n.bytes()}catch(s){if(o("onNative",r?.onError))throw s;return null}}async function m(t,e){try{t||(t=e?.url??new URL(""));const r=v(e),n=T(t,r,e),a=await P(t,n,r);if(!a.ok&&r.onStatus)throw await l.fromResponse(a);return a}catch(r){if(o("onNative",e?.onError))throw r;return null}}function R(t){return typeof t=="string"}function y(t){return typeof t=="number"}function d(t){return typeof t=="boolean"}function N(t){return!!(t&&typeof t=="object"&&Object.prototype.toString.call(t).slice(8,-1)==="Object"&&t.constructor===Object)}function o(t,e){return!!(e===void 0&&i[t]||typeof e=="boolean"&&e||typeof e=="object"&&(e[t]??i[t]))}function f(t,e,r=!1){return e===void 0||e<0?t:r?Math.trunc(e):e}function u(t,e,r){return d(r)?e:r===void 0||r[t]===void 0?i[t]:y(r[t])?f(i[t],r[t],t==="maxAttempts"):r[t]}function v(t){return{timeout:f(i.timeout,t?.timeout),delay:f(i.delay,t?.delay),interval:u("interval",0,t?.retry),maxInterval:u("maxInterval",0,t?.retry),maxAttempts:u("maxAttempts",0,t?.retry),retryAfter:u("retryAfter",!1,t?.retry),onStatus:o("onStatus",t?.onError),redirect:t?.redirect??i.redirect}}function T(t,e,r){const{method:n,body:a,timeout:s,retry:D,bearer:C,onError:H,delay:L,redirect:c,signal:k,...g}=r??{};return{headers:S(r),method:n||(t instanceof Request?t.method:a===void 0?"GET":"POST"),signal:I(t,e.timeout,r?.signal),...c&&{redirect:c==="error"?"manual":c},...a&&{body:E(a)},...g}}function E(t){return w(t)?JSON.stringify(t):t}function w(t){return!!(t===null||y(t)||d(t)||Array.isArray(t)||N(t))}function S(t){const e=new Headers(t?.headers);if(e.has("Accept")||e.append("Accept","application/json, text/plain"),!e.has("Content-Type")){const r=j(t?.body);r&&e.append("Content-Type",r)}return t?.bearer&&e.set("Authorization",`Bearer ${t.bearer}`),e}function j(t){if(!(t===void 0||R(t)||t instanceof FormData||t instanceof URLSearchParams)&&!(t instanceof Blob&&t.type))return w(t)?"application/json":"application/octet-stream"}function I(t,e,r){const n=[];return t instanceof Request&&t.signal&&n.push(t.signal),r&&n.push(r),e>0&&n.push(AbortSignal.timeout(e*1e3+1)),n.length?AbortSignal.any(n):void 0}async function b(t,e=!0){if(t<=0)return;const r=Math.trunc((e?Math.random():1)*t*1e3);await new Promise(n=>setTimeout(n,r))}function O(t){return t.status<400&&t.status>=300}async function x(t,e,r,n){if(t>=r.maxAttempts-1||e.signal?.aborted||n?.ok)return!0;if(n&&O(n)){if(r.redirect==="manual")return!0;if(r.redirect==="error")throw r.maxAttempts=0,h.fromResponse(n)}const a=M(t,r,n);return a>r.maxInterval?!0:(await b(a,!1),!1)}function M(t,e,r){return e.retryAfter&&r?Math.max(B(r.headers.get("Retry-After")?.trim()??""),e.interval):Math.min(Math.pow(Math.max(1,e.interval),t),e.maxInterval)}function B(t){if(!t)return 1/0;const e=Number.parseInt(t,10);if(!Number.isNaN(e))return e;const r=Math.ceil((new Date(t).getTime()-Date.now())/1e3);return Number.isNaN(r)?1/0:r}function $(t,e,r){return r.redirected?(r.status===303&&(e.method="GET"),t instanceof Request?new Request(r.url,t):r.url):t}function q(t,e){return t instanceof Request&&e?t.clone():t}async function P(t,e,r){for(let n=0;n<r.maxAttempts;n++)try{const a=q(t,n<r.maxAttempts-1),s=await _(a,e,r);if(await x(n,e,r,s))return s;t=$(t,e,s);continue}catch(a){if(await x(n,e,r))throw a;continue}return await _(t,e,r)}async function _(t,e,r){return await b(r.delay),await fetch(t,e)}
1
+ export{m as fetchy,A as fetchyb,c as HTTPStatusError,h as RedirectError};const s={timeout:15,delay:0,interval:3,maxInterval:30,maxAttempts:3,retryAfter:!0,onNative:!0,onStatus:!0,redirect:"follow"};class c extends Error{static#t=80;status;body;constructor(e,r,n){super(e),this.name="HTTPStatusError",this.status=r,this.body=n}static async fromResponse(e){const r=await e.text(),n=r.length>this.#t?`${r.slice(0,this.#t)}... (more ${r.length-this.#t} chars)`:r||"(no response body)",a=`${e.status} ${e.statusText}: ${n}`;return new this(a,e.status,r)}}class h extends Error{status;constructor(e,r){super(e),this.name="RedirectError",this.status=r}static fromResponse(e){const r=`${e.status} ${e.statusText}`.trim();return new this(r,e.status)}}async function A(t,e="auto",r){const n=await m(t,r);if(!n||!n.ok)return null;const a=n.headers.get("Content-Type")??"";try{return e==="text"||e==="auto"&&a.startsWith("text/")?await n.text():e==="json"||e==="auto"&&a==="application/json"?await n.json():await n.bytes()}catch(i){if(f("onNative",r?.onError)||i instanceof c)throw i;return null}}async function m(t,e){try{t||(t=e?.url??new URL(""));const r=v(e),n=T(t,r,e),a=await P(t,n,r);if(!a.ok&&r.onStatus)throw await c.fromResponse(a);return a}catch(r){if(f("onNative",e?.onError)||r instanceof c)throw r;return null}}function R(t){return typeof t=="string"}function y(t){return typeof t=="number"}function d(t){return typeof t=="boolean"}function N(t){return!!(t&&typeof t=="object"&&Object.prototype.toString.call(t).slice(8,-1)==="Object"&&t.constructor===Object)}function f(t,e){return!!(e===void 0&&s[t]||typeof e=="boolean"&&e||typeof e=="object"&&(e[t]??s[t]))}function l(t,e,r=!1){return e===void 0||e<0?t:r?Math.trunc(e):e}function u(t,e,r){return d(r)?e:r===void 0||r[t]===void 0?s[t]:y(r[t])?l(s[t],r[t],t==="maxAttempts"):r[t]}function v(t){return{timeout:l(s.timeout,t?.timeout),delay:l(s.delay,t?.delay),interval:u("interval",0,t?.retry),maxInterval:u("maxInterval",0,t?.retry),maxAttempts:u("maxAttempts",0,t?.retry),retryAfter:u("retryAfter",!1,t?.retry),onStatus:f("onStatus",t?.onError),redirect:t?.redirect??s.redirect}}function T(t,e,r){const{method:n,body:a,timeout:i,retry:D,bearer:C,onError:H,delay:L,redirect:o,signal:k,...g}=r??{};return{headers:S(r),method:n||(t instanceof Request?t.method:a===void 0?"GET":"POST"),signal:I(t,e.timeout,r?.signal),...o&&{redirect:o==="error"?"manual":o},...a&&{body:E(a)},...g}}function E(t){return w(t)?JSON.stringify(t):t}function w(t){return!!(t===null||y(t)||d(t)||Array.isArray(t)||N(t))}function S(t){const e=new Headers(t?.headers);if(e.has("Accept")||e.append("Accept","application/json, text/plain"),!e.has("Content-Type")){const r=j(t?.body);r&&e.append("Content-Type",r)}return t?.bearer&&e.set("Authorization",`Bearer ${t.bearer}`),e}function j(t){if(!(t===void 0||R(t)||t instanceof FormData||t instanceof URLSearchParams)&&!(t instanceof Blob&&t.type))return w(t)?"application/json":"application/octet-stream"}function I(t,e,r){const n=[];return t instanceof Request&&t.signal&&n.push(t.signal),r&&n.push(r),e>0&&n.push(AbortSignal.timeout(e*1e3+1)),n.length?AbortSignal.any(n):void 0}async function b(t,e=!0){if(t<=0)return;const r=Math.trunc((e?Math.random():1)*t*1e3);await new Promise(n=>setTimeout(n,r))}function O(t){return t.status<400&&t.status>=300}async function x(t,e,r,n){if(t>=r.maxAttempts-1||e.signal?.aborted||n?.ok)return!0;if(n&&O(n)){if(r.redirect==="manual")return!0;if(r.redirect==="error")throw r.maxAttempts=0,h.fromResponse(n)}const a=M(t,r,n);return a>r.maxInterval?!0:(await b(a,!1),!1)}function M(t,e,r){return e.retryAfter&&r?Math.max(B(r.headers.get("Retry-After")?.trim()??""),e.interval):Math.min(Math.pow(Math.max(1,e.interval),t),e.maxInterval)}function B(t){if(!t)return 1/0;const e=Number.parseInt(t,10);if(!Number.isNaN(e))return e;const r=Math.ceil((new Date(t).getTime()-Date.now())/1e3);return Number.isNaN(r)?1/0:r}function $(t,e,r){return r.redirected?(r.status===303&&(e.method="GET"),t instanceof Request?new Request(r.url,t):r.url):t}function q(t,e){return t instanceof Request&&e?t.clone():t}async function P(t,e,r){for(let n=0;n<r.maxAttempts;n++)try{const a=q(t,n<r.maxAttempts-1),i=await _(a,e,r);if(await x(n,e,r,i))return i;t=$(t,e,i);continue}catch(a){if(await x(n,e,r))throw a;continue}return await _(t,e,r)}async function _(t,e,r){return await b(r.delay),await fetch(t,e)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scirexs/fetchy",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "A lightweight fetch wrapper.",
5
5
  "keywords": [
6
6
  "fetch",