UNPKG

nice-retry

Version:

Type-safe, lightweight (zero-dependency) retry utility for async operations that simply works.

1 lines 2.96 kB
'use strict';var u=class extends Error{constructor(e,t,a=[]){super(e),this.name="RetryOperationError",this.attempts=t,this.errors=a,this.cause=a[a.length-1];}},m=class extends u{constructor(e,t=[]){super(`Failed after ${e} attempts`,e,t),this.name="MaxRetriesExceededError";}},p=class extends u{constructor(e,t=[]){super("Operation failed and retry condition prevented further attempts",e,t),this.name="RetryConditionFailedError";}},y=class extends u{constructor(e,t=[]){super("Operation was cancelled before completion",e,t),this.name="RetryAbortedError";}};var l=(r,e,t)=>{if(r?.aborted)throw new y(e,t)};var R={maxAttempts:3,initialDelay:1e3,maxDelay:3e4,jitterStrategy:"full",backoffStrategy:"exponential"},g=[408,409,429,500,502,503,504,520,521,522,523,524,525,526],h={retryStatusCodes:g,retryNetworkErrors:true};var d=r=>r instanceof Error?r:new Error(String(r)),x=r=>new Promise(e=>setTimeout(e,r)),f=(r,e,t)=>Math.max(e,Math.min(r,t)),A=(r,e)=>Math.random()*(e-r)+r;var k=(r,e,t,a)=>{let o;switch(a){case "fixed":o=e;break;case "linear":o=e*r;break;case "aggressive":o=e*Math.pow(3,r-1);break;case "exponential":default:o=e*Math.pow(2,r-1);break}return Math.min(o,t)},F=(r,e,t,a,o)=>{switch(e){case "full":{let s=Math.random()*r;return f(s,0,o)}case "equal":{let s=r/2,n=s+Math.random()*s;return f(n,0,o)}case "decorrelated":{let s=Math.min(r,o),n=3*t,c=Math.min(n,o);if(c<=s)return s;let i=A(s,c);return f(Math.max(i,a),0,o)}case "none":default:return f(r,0,o)}},O=(r,e,t,a,o,s)=>{let n=k(r,e,t,o);return F(n,a,s??e,e,t)};var E=async(r,e={})=>{let t={...R,...e},a=Date.now(),o=[],s=t.initialDelay;l(t.signal,0,o);for(let n=1;n<=t.maxAttempts;n++)try{return l(t.signal,n,o),{data:await r(),attempts:n,totalTime:Date.now()-a,errors:o}}catch(c){let i=d(c);if(o.push(i),l(t.signal,n,o),t.onRetry?.(i,n),n===t.maxAttempts)break;if(t.retryIf&&!t.retryIf(i))throw new p(n,o);let b=O(n,t.initialDelay,t.maxDelay,t.jitterStrategy,t.backoffStrategy,s);s=b,await x(b);}if(t.fallback){let n=Array.isArray(t.fallback)?t.fallback:[t.fallback];for(let c of n)try{return l(t.signal,t.maxAttempts,o),{data:await c(),attempts:t.maxAttempts,totalTime:Date.now()-a,errors:o}}catch(i){o.push(d(i));}}throw new m(t.maxAttempts,o)};var N=new Set(["ECONNRESET","ECONNREFUSED","ECONNABORTED","ETIMEDOUT","ENETUNREACH","EHOSTUNREACH"]),w=r=>"code"in r&&typeof r.code=="string"?N.has(r.code):false,T=(r,e)=>e&&"status"in r&&typeof r.status=="number"?e.includes(r.status):false;var S=async(r,e)=>{let{retry:t,...a}=e||{},o={...h,...t},s=await E(async()=>{let n=await fetch(r,a);if(!n.ok){let c=new Error(`HTTP Error ${n.status}: ${n.statusText}`);throw c.status=n.status,c}return n},{...o,retryIf:n=>o.retryNetworkErrors&&w(n)||T(n,o.retryStatusCodes)||(o.retryIf?.(n)??false)});return {...s,response:s.data}};var Q={fetch:S,async:E};exports.MaxRetriesExceededError=m;exports.RetryAbortedError=y;exports.RetryConditionFailedError=p;exports.RetryOperationError=u;exports.retry=Q;