tryo 0.13.0 → 0.13.3
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/dist/index.cjs +1 -1
- package/dist/index.d.cts +148 -95
- package/dist/index.d.ts +148 -95
- package/dist/index.js +1 -1
- package/package.json +27 -27
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var D=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var me=Object.prototype.hasOwnProperty;var de=(e,r,t)=>r in e?D(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var ye=(e,r)=>{for(var t in r)D(e,t,{get:r[t],enumerable:!0})},pe=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of le(r))!me.call(e,o)&&o!==t&&D(e,o,{get:()=>r[o],enumerable:!(n=ce(r,o))||n.enumerable});return e};var fe=e=>pe(D({},"__esModule",{value:!0}),e);var m=(e,r,t)=>de(e,typeof r!="symbol"?r+"":r,t);var Ae={};ye(Ae,{RetryStrategies:()=>X,all:()=>ne,allOrThrow:()=>oe,asConcurrencyLimit:()=>B,asMilliseconds:()=>C,asPercentage:()=>J,asRetryCount:()=>R,asStatusCode:()=>K,errorRule:()=>G,orThrow:()=>te,run:()=>ee,runOrThrow:()=>re,tryo:()=>z});module.exports=fe(Ae);var T=class extends Error{constructor(t,n){var o;super(t);m(this,"cause");m(this,"meta");m(this,"status");m(this,"timestamp");m(this,"retryable");this.timestamp=Date.now(),this.retryable=(o=n==null?void 0:n.retryable)!=null?o:!0,this.name=this.constructor.name,this.cause=n==null?void 0:n.cause,this.meta=n==null?void 0:n.meta,this.status=n==null?void 0:n.status,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}is(t){return this.code===t}withMeta(t){return Object.assign(this,{meta:t})}withStatus(t){return Object.assign(this,{status:t})}withCause(t){return Object.assign(this,{cause:t})}withRetryable(t){return this.retryable=t,this}toJSON(){return{name:this.name,code:this.code,message:this.message,timestamp:this.timestamp,retryable:this.retryable,cause:this.cause,stack:this.stack}}},v=class extends T{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0});m(this,"code","TIMEOUT")}};var A=class extends T{constructor(t,n){super(`Circuit breaker is open, reset after ${t}ms`,{cause:n,retryable:!1});m(this,"code","CIRCUIT_OPEN")}},I=class extends T{constructor(t,n,o){super(t,{cause:o,meta:{validationErrors:n},retryable:!1});this.validationErrors=n;m(this,"code","VALIDATION")}};var F=class extends T{constructor(t,n){super(t,{cause:n});m(this,"code","UNKNOWN")}};var C=e=>{if(e<0||!Number.isFinite(e))throw new Error(`Invalid milliseconds: must be a non-negative finite number, got ${e}`);return e},R=e=>{if(e<0||!Number.isInteger(e))throw new Error(`Invalid retry count: must be a non-negative integer, got ${e}`);return e},B=e=>{if(e<1||!Number.isInteger(e))throw new Error(`Invalid concurrency limit: must be a positive integer, got ${e}`);return e},J=e=>{if(e<0||e>100||!Number.isFinite(e))throw new Error(`Invalid percentage: must be between 0 and 100, got ${e}`);return e},K=e=>{if(!Number.isInteger(e)||e<100||e>599)throw new Error(`Invalid status code: must be an integer between 100-599, got ${e}`);return e};var L=class{constructor(r){m(this,"state");m(this,"config");this.config={failureThreshold:R(r.failureThreshold),resetTimeout:C(r.resetTimeout),halfOpenRequests:R(r.halfOpenRequests),shouldCountAsFailure:r.shouldCountAsFailure},this.state={state:"closed",failureCount:0,halfOpenCount:0}}async canExecute(){if(this.updateStateIfNeeded(),this.state.state==="open")return!1;if(this.state.state==="half-open"){if(this.state.halfOpenCount>=this.config.halfOpenRequests)return!1;this.state={...this.state,halfOpenCount:this.state.halfOpenCount+1}}return!0}async recordSuccess(){switch(this.state.state){case"closed":this.state={...this.state,failureCount:0};break;case"half-open":this.state={state:"closed",failureCount:0,halfOpenCount:0};break;case"open":break}}async recordFailure(r){var o,a,i;if(!((i=(a=(o=this.config).shouldCountAsFailure)==null?void 0:a.call(o,r))!=null?i:!0))return;let n=new Date;switch(this.state.state){case"closed":{let s=this.state.failureCount+1;s>=this.config.failureThreshold?this.state={state:"open",failureCount:s,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)}:this.state={...this.state,failureCount:s,lastFailureTime:n};break}case"half-open":this.state={state:"open",failureCount:this.state.failureCount+1,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)};break;case"open":this.state={...this.state,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)};break}}getState(){return this.updateStateIfNeeded(),{...this.state,canExecute:this.state.state!=="open"}}createOpenError(){let r=this.state.nextAttemptTime?C(Math.max(0,this.state.nextAttemptTime.getTime()-Date.now())):this.config.resetTimeout;return new A(r)}forceState(r){this.state={state:r,failureCount:0,halfOpenCount:0}}reset(){this.state={state:"closed",failureCount:0,halfOpenCount:0}}updateStateIfNeeded(){this.state.state==="open"&&this.state.nextAttemptTime&&new Date>=this.state.nextAttemptTime&&(this.state={...this.state,state:"half-open",halfOpenCount:0})}};var q=(e,r)=>t=>{for(let n of e){let o=n(t);if(o!==null)return o}return r(t)},Y=e=>r=>r instanceof T?r:r instanceof Error?new e(r.message,r):typeof r=="string"?new e(r):new e("Unknown error occurred",r);var Te=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.status=="number"||typeof r.statusCode=="number"},Ee=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.code=="string"&&r.code.length>0},we=e=>{var r;if(e instanceof Error){if(e.name==="TypeError")return!0;let t=e.message.toLowerCase();if(t.includes("fetch")&&t.includes("failed")||t.includes("network"))return!0}if(Ee(e)){let t=((r=e.code)!=null?r:"").toUpperCase();return t==="ECONNRESET"||t==="ECONNREFUSED"||t==="ETIMEDOUT"||t==="ENOTFOUND"||t==="EAI_AGAIN"}return!1},U=class{constructor(r){this.predicate=r}toCode(r){return new _(this.predicate,r)}toError(r){return t=>{if(!this.predicate(t))return null;let n=r(t),o=n.code;class a extends T{constructor(){var u;super(n.message,{cause:(u=n.cause)!=null?u:t,meta:n.meta,status:n.status,retryable:n.retryable});m(this,"code",o)}}return new a}}},_=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){return t=>{if(!this.predicate(t))return null;let n=r(t),o=this.errorCode;class a extends T{constructor(){super(n.message,{cause:n.cause,meta:n.meta,status:n.status,retryable:n.retryable});m(this,"code",o)}}return new a}}},E={when:e=>new U(e),instance:e=>new U(r=>r instanceof e),code:e=>({for:r=>new _(r,e)}),string:(e,r)=>({when:t=>E.when(t).toCode(r).with(n=>({message:n===e?e:`${r}: ${n}`,cause:n}))}),httpStatus:(e,r)=>({for:t=>E.when(t).toCode(r!=null?r:`HTTP_${e}`).with(n=>({message:`HTTP ${e} error`,cause:n}))})};var w={typed:(e=>e instanceof T?e:null),abort:E.when(e=>e instanceof DOMException&&e.name==="AbortError").toCode("ABORTED").with(e=>({message:e.message||"Operation was aborted",cause:e,retryable:!1})),timeout:E.when(e=>e instanceof Error&&e.name==="TimeoutError").toCode("TIMEOUT").with(e=>({message:e.message||"Operation timed out",cause:e})),network:E.when(e=>we(e)).toCode("NETWORK").with(e=>({message:(e instanceof Error,e.message||"Network error"),cause:e})),http:E.when(e=>{var n;if(!Te(e))return!1;let r=e,t=(n=r.status)!=null?n:r.statusCode;return typeof t=="number"&&t>=400}).toCode("HTTP").with(e=>{var n;let r=(n=e.status)!=null?n:e.statusCode,t=typeof r=="number"&&(r>=500||r===429);return{message:e.message||`HTTP ${r!=null?r:"error"} error`,cause:e,status:typeof r=="number"?r:void 0,retryable:t}}),unknown:E.when(e=>e instanceof Error&&!(e instanceof T)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e}))};var be=[w.typed,w.abort,w.timeout,w.http,w.network,w.unknown];var G={when:e=>E.when(e),instance:e=>E.instance(e)},Q=be,He=w.timeout,Ve=w.abort,We=w.network,Je=w.http,Ke=E.when(e=>e instanceof A).toCode("CIRCUIT_OPEN").with(e=>({message:e.message,cause:e})),qe=E.when(e=>e instanceof I).toCode("VALIDATION").with(e=>({message:e.message,cause:e}));var X={fixed:e=>({type:"fixed",delay:e}),exponential:(e,r=2,t)=>({type:"exponential",base:e,factor:r,maxDelay:t}),fibonacci:(e,r)=>({type:"fibonacci",base:e,maxDelay:r}),custom:e=>({type:"custom",calculate:e})},Z=(e,r,t)=>{switch(e.type){case"fixed":return e.delay;case"exponential":{let n=e.base*e.factor**(Number(r)-1);return e.maxDelay?Math.min(n,e.maxDelay):n}case"fibonacci":{let n=e.base*he(Number(r));return e.maxDelay?Math.min(n,e.maxDelay):n}case"custom":return e.calculate(r,t);default:return e}},he=e=>{if(e<=1)return 1;let r=1,t=1;for(let n=2;n<=e;n++){let o=r+t;r=t,t=o}return t};var j=(e,r)=>new Promise((t,n)=>{if(r!=null&&r.aborted){n(new DOMException("Aborted","AbortError"));return}let o=s=>{r&&s&&r.removeEventListener("abort",s)},a,i=()=>{a&&clearTimeout(a),o(i),n(new DOMException("Aborted","AbortError"))};a=setTimeout(()=>{o(i),t()},e),r==null||r.addEventListener("abort",i,{once:!0})});var Ce=e=>{var i,s,u;if(e.toError)return e.toError;let r=(i=e.rulesMode)!=null?i:"extend",t=(s=e.rules)!=null?s:[],n=Q,o=(u=e.fallback)!=null?u:(c=>Y(F)(c)),a=r==="replace"?t:[...t,...n];return q(a,o)},H=class e{constructor(r={}){m(this,"circuitBreaker");m(this,"config");m(this,"lastCircuitState");let{rules:t,rulesMode:n,fallback:o,toError:a,mapError:i,...s}=r,u=Ce(r),c={...s,errorHandling:{normalizer:u,mapError:i}};this.config=c,c.circuitBreaker&&(this.circuitBreaker=new L(c.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var a,i,s,u,c,b;let n={...this.config,...t};if(this.circuitBreaker){let l=(a=this.lastCircuitState)!=null?a:this.circuitBreaker.getState().state,y=await this.circuitBreaker.canExecute(),p=this.circuitBreaker.getState().state;if(l!==p)try{(s=(i=n.hooks)==null?void 0:i.onCircuitStateChange)==null||s.call(i,l,p)}catch{}if(this.lastCircuitState=p,!y)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let o=await xe(r,n);if(this.circuitBreaker){let l=(u=this.lastCircuitState)!=null?u:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let y=this.circuitBreaker.getState().state;if(l!==y)try{(b=(c=n.hooks)==null?void 0:c.onCircuitStateChange)==null||b.call(c,l,y)}catch{}this.lastCircuitState=y}return o}async runOrThrow(r,t={}){let n=await this.run(r,t);if(n.ok)return n.data;throw n.error}async orThrow(r,t={}){let n=await this.run(r,t);if(n.ok)return n.data;throw n.error}async all(r,t={}){var b;let n={...this.config,...t},o=(b=n.concurrency)!=null?b:Number.POSITIVE_INFINITY,a=Number.isFinite(o)?Number(B(o)):Number.POSITIVE_INFINITY,i=new Array(r.length),s=0,u=async()=>{var l;for(;s<r.length&&!((l=n.signal)!=null&&l.aborted);){let y=s++;if(y>=r.length)break;let p=r[y];p&&(i[y]=await this.run(p,n))}},c=Array.from({length:Math.min(a,r.length)},()=>u());await Promise.all(c);for(let l=0;l<r.length;l++)if(!(l in i)){let y=r[l];y&&(i[l]=await this.run(y,n))}return i}async allOrThrow(r,t={}){let n=await this.all(r,t);for(let o of n)if(!o.ok)throw o.error;return n.map(o=>{if(!o.ok)throw o.error;return o.data})}partitionAll(r){let t=[],n=[],o=[],a=[],i=[];for(let s of r){if(s.type==="success"){t.push(s);continue}switch(n.push(s),s.type){case"failure":o.push(s);break;case"aborted":a.push(s);break;case"timeout":i.push(s);break}}return{ok:t,errors:n,failure:o,aborted:a,timeout:i}}getCircuitBreakerState(){var r;return(r=this.circuitBreaker)==null?void 0:r.getState()}resetCircuitBreaker(){var r;(r=this.circuitBreaker)==null||r.reset()}getConfig(){return{...this.config}}withConfig(r){var u,c;let{errorHandling:t,...n}=this.config,{errorHandling:o,...a}=r,i=(u=o==null?void 0:o.normalizer)!=null?u:t.normalizer,s=(c=o==null?void 0:o.mapError)!=null?c:t.mapError;return new e({...n,...a,toError:i,mapError:s})}withErrorType(r={}){return new e(r)}};function z(e={}){let r=new H(e);return{run:(t,n)=>r.run(t,n),orThrow:(t,n)=>r.orThrow(t,n),runOrThrow:(t,n)=>r.runOrThrow(t,n),all:(t,n)=>r.all(t,n),allOrThrow:(t,n)=>r.allOrThrow(t,n),partitionAll:t=>r.partitionAll(t),withConfig:t=>r.withConfig(t)}}async function xe(e,r){let{signal:t,ignoreAbort:n=!0,timeout:o,retry:a,errorHandling:i,hooks:s,logger:u}=r,c=(g,...d)=>{try{g==null||g(...d)}catch{}},b,l=0,y=0,p=[],k=Date.now(),{signal:x,cleanup:se}=Re(t);try{if(x.aborted){c(s==null?void 0:s.onAbort,x);let d=i.normalizer(new DOMException("Aborted","AbortError")),f=i.mapError?i.mapError(d):d;return{type:"aborted",ok:!1,data:null,error:f,metrics:{totalAttempts:l,totalRetries:y,totalDuration:Date.now()-k,lastError:f,retryHistory:p}}}let g=async d=>{var f,P,O;l=d;try{let M=e({signal:x}),S=o?await ke(M,o,x):await M;return c(s==null?void 0:s.onSuccess,S),c(u==null?void 0:u.info,`Task succeeded on attempt ${d}`),S}catch(M){let S=i.normalizer(M),h=i.mapError?i.mapError(S):S;b=h,h.code==="ABORTED"&&c(s==null?void 0:s.onAbort,x),n&&h.code==="ABORTED"||(c(s==null?void 0:s.onError,h),c(u==null?void 0:u.error,`Task failed on attempt ${d}`,h));let ae=R((f=a==null?void 0:a.maxRetries)!=null?f:0);if(d<=Number(ae)){let V=a==null?void 0:a.shouldRetry,ie={totalAttempts:Number(l),elapsedTime:Date.now()-k,startTime:new Date(k),lastDelay:(P=p[p.length-1])!=null&&P.delay?Number((O=p[p.length-1])==null?void 0:O.delay):void 0};if(!V||V(d,h,ie)){let ue=a?Z(a.strategy,d,h):0,$=ge(ue,a==null?void 0:a.jitter),W=C($);return p.push({attempt:d,error:h,delay:W,timestamp:new Date}),c(s==null?void 0:s.onRetry,d,h,$),c(u==null?void 0:u.info,`Retrying in ${$}ms (attempt ${d+1})`),await j(W,x),g(d+1)}}throw h}};try{let d=await g(1);y=l>0?Number(l)-1:0;let f={totalAttempts:l,totalRetries:y,totalDuration:Date.now()-k,retryHistory:p};return c(s==null?void 0:s.onFinally,f),{type:"success",ok:!0,data:d,error:null,metrics:f}}catch(d){let f=b!=null?b:i.normalizer(d),P=f.code==="TIMEOUT"?"timeout":f.code==="ABORTED"?"aborted":"failure";y=l>0?Number(l)-1:0;let O={totalAttempts:l,totalRetries:y,totalDuration:Date.now()-k,lastError:f,retryHistory:p};return c(s==null?void 0:s.onFinally,O),{type:P,ok:!1,data:null,error:f,metrics:O}}}finally{se()}}function Re(e){let r=new AbortController;if(!e)return{signal:r.signal,cleanup:()=>{}};let t=()=>r.abort();return e.aborted?(t(),{signal:r.signal,cleanup:()=>{}}):(e.addEventListener("abort",t,{once:!0}),{signal:r.signal,cleanup:()=>e.removeEventListener("abort",t)})}async function ke(e,r,t){return new Promise((n,o)=>{if(t!=null&&t.aborted){o(new DOMException("Aborted","AbortError"));return}let a=!1,i=setTimeout(()=>{a=!0,u(),o(new v(C(r)))},r),s=()=>{a||(a=!0,u(),o(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(i),t==null||t.removeEventListener("abort",s)};t==null||t.addEventListener("abort",s,{once:!0}),e.then(c=>{a||(a=!0,u(),n(c))},c=>{a||(a=!0,u(),o(c))})})}function ge(e,r){if(!r||r.type==="none"||e<=0)return e;switch(r.type){case"full":{let t=Number(r.ratio)/100,n=Math.max(0,Number(e)*(1-t)),o=Number(e);return n+Math.random()*(o-n)}case"equal":{let t=Number(r.ratio)/100,n=Number(e)*t/2;return Number(e)-n+Math.random()*n}case"custom":return r.calculate(e);default:return r}}var N=z(),ee=N.run,re=N.runOrThrow,te=N.orThrow,ne=N.all,oe=N.allOrThrow;0&&(module.exports={RetryStrategies,all,allOrThrow,asConcurrencyLimit,asMilliseconds,asPercentage,asRetryCount,asStatusCode,errorRule,orThrow,run,runOrThrow,tryo});
|
|
1
|
+
"use strict";var I=Object.defineProperty;var fe=Object.getOwnPropertyDescriptor;var Ee=Object.getOwnPropertyNames;var Te=Object.prototype.hasOwnProperty;var be=(e,r,t)=>r in e?I(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var we=(e,r)=>{for(var t in r)I(e,t,{get:r[t],enumerable:!0})},he=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of Ee(r))!Te.call(e,o)&&o!==t&&I(e,o,{get:()=>r[o],enumerable:!(n=fe(r,o))||n.enumerable});return e};var Ce=e=>he(I({},"__esModule",{value:!0}),e);var y=(e,r,t)=>be(e,typeof r!="symbol"?r+"":r,t);var ve={};we(ve,{RetryStrategies:()=>ee,TypedError:()=>T,all:()=>le,allOrThrow:()=>ce,asMilliseconds:()=>w,asRetryCount:()=>h,default:()=>S,errorRule:()=>Z,orThrow:()=>ue,run:()=>ae,runOrThrow:()=>ie,tryo:()=>S});module.exports=Ce(ve);var T=class extends Error{constructor(t,n){var o,a;super(t);y(this,"cause");y(this,"meta");y(this,"status");y(this,"raw");y(this,"path");y(this,"timestamp");y(this,"retryable");this.timestamp=Date.now(),this.retryable=(o=n.retryable)!=null?o:!0,this.name=this.constructor.name,this.cause=n.cause,this.meta=(a=n.meta)!=null?a:{},this.status=n.status,this.raw=n.raw,this.path=n.path,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}is(t){return this.code===t}withMeta(t){return Object.assign(this,{meta:t})}withStatus(t){return Object.assign(this,{status:t})}withCause(t){return Object.assign(this,{cause:t})}withPath(t){return this.path=t,this}withRaw(t){return Object.assign(this,{raw:t})}withRetryable(t){return this.retryable=t,this}toJSON(){return{name:this.name,code:this.code,message:this.message,timestamp:this.timestamp,retryable:this.retryable,cause:this.cause,raw:this.raw,path:this.path,stack:this.stack}}},P=class extends T{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0,raw:n});y(this,"code","TIMEOUT")}};var F=class extends T{constructor(t,n){super(`Circuit breaker is open, reset after ${t}ms`,{cause:n,retryable:!1,raw:n});y(this,"code","CIRCUIT_OPEN")}};var _=class extends T{constructor(t,n){super(t,{cause:n,raw:n});y(this,"code","UNKNOWN")}};var w=e=>{if(e<0||!Number.isFinite(e))throw new Error(`Invalid milliseconds: must be a non-negative finite number, got ${e}`);return e},h=e=>{if(e<0||!Number.isInteger(e))throw new Error(`Invalid retry count: must be a non-negative integer, got ${e}`);return e},H=e=>{if(e<1||!Number.isInteger(e))throw new Error(`Invalid concurrency limit: must be a positive integer, got ${e}`);return e},G=e=>{if(e<0||e>100||!Number.isFinite(e))throw new Error(`Invalid percentage: must be between 0 and 100, got ${e}`);return e};var B=class{constructor(r){y(this,"state");y(this,"config");this.config={failureThreshold:h(r.failureThreshold),resetTimeout:w(r.resetTimeout),halfOpenRequests:h(r.halfOpenRequests),shouldCountAsFailure:r.shouldCountAsFailure},this.state={state:"closed",failureCount:0,halfOpenCount:0}}async canExecute(){if(this.updateStateIfNeeded(),this.state.state==="open")return!1;if(this.state.state==="half-open"){if(this.state.halfOpenCount>=this.config.halfOpenRequests)return!1;this.state={...this.state,halfOpenCount:this.state.halfOpenCount+1}}return!0}async recordSuccess(){switch(this.state.state){case"closed":this.state={...this.state,failureCount:0};break;case"half-open":this.state={state:"closed",failureCount:0,halfOpenCount:0};break;case"open":break}}async recordFailure(r){var o,a,i;if(!((i=(a=(o=this.config).shouldCountAsFailure)==null?void 0:a.call(o,r))!=null?i:!0))return;let n=new Date;switch(this.state.state){case"closed":{let s=this.state.failureCount+1;s>=this.config.failureThreshold?this.state={state:"open",failureCount:s,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)}:this.state={...this.state,failureCount:s,lastFailureTime:n};break}case"half-open":this.state={state:"open",failureCount:this.state.failureCount+1,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)};break;case"open":this.state={...this.state,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)};break}}getState(){return this.updateStateIfNeeded(),{...this.state,canExecute:this.state.state!=="open"}}createOpenError(){let r=this.state.nextAttemptTime?w(Math.max(0,this.state.nextAttemptTime.getTime()-Date.now())):this.config.resetTimeout;return new F(r)}forceState(r){this.state={state:r,failureCount:0,halfOpenCount:0}}reset(){this.state={state:"closed",failureCount:0,halfOpenCount:0}}updateStateIfNeeded(){this.state.state==="open"&&this.state.nextAttemptTime&&new Date>=this.state.nextAttemptTime&&(this.state={...this.state,state:"half-open",halfOpenCount:0})}};var Q=(e,r)=>t=>{for(let n of e){let o=n(t);if(o!==null)return o}return r(t)},X=e=>r=>r instanceof T?r:r instanceof Error?new e(r.message,r):typeof r=="string"?new e(r):new e("Unknown error occurred",r);var Re=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.status=="number"||typeof r.statusCode=="number"},xe=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.code=="string"&&r.code.length>0},ge=e=>{var r;if(e instanceof Error){let t=e.message.toLowerCase();if(e.name==="TypeError"&&(t.includes("fetch")&&t.includes("failed")||t.includes("network"))||t.includes("fetch")&&t.includes("failed")||t.includes("network"))return!0}if(xe(e)){let t=((r=e.code)!=null?r:"").toUpperCase();return t==="ECONNRESET"||t==="ECONNREFUSED"||t==="ETIMEDOUT"||t==="ENOTFOUND"||t==="EAI_AGAIN"}return!1},U=class{constructor(r){this.predicate=r}toCode(r){return new $(this.predicate,r)}toError(r){return t=>{if(!this.predicate(t))return null;let n=r(t),o=n.code;class a extends T{constructor(){var l,u,d;super(n.message,{cause:(l=n.cause)!=null?l:t,meta:(u=n.meta)!=null?u:{},status:n.status,retryable:n.retryable,raw:(d=n.raw)!=null?d:t,path:n.path});y(this,"code",o)}}return new a}}},$=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){return t=>{if(!this.predicate(t))return null;let n=r(t),o=this.errorCode;class a extends T{constructor(){var u;let l=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{cause:n.cause,meta:(u=n.meta)!=null?u:{},status:n.status,retryable:n.retryable,raw:l,path:n.path});y(this,"code",o)}}return new a}}},C={when:e=>new U(e),instance:e=>new U(r=>r instanceof e)},x={typed:(e=>e instanceof T?e:null),abort:C.when(e=>e instanceof Error&&e.name==="AbortError").toCode("ABORTED").with(e=>({message:e.message||"Operation was aborted",cause:e,retryable:!1,raw:e})),timeout:C.when(e=>e instanceof Error&&e.name==="TimeoutError").toCode("TIMEOUT").with(e=>({message:e.message||"Operation timed out",cause:e,raw:e})),network:C.when(e=>ge(e)).toCode("NETWORK").with(e=>({message:(e instanceof Error,e.message||"Network error"),cause:e,raw:e})),http:C.when(e=>{var n;if(!Re(e))return!1;let r=e,t=(n=r.status)!=null?n:r.statusCode;return typeof t=="number"&&t>=400}).toCode("HTTP").with(e=>{var n;let r=(n=e.status)!=null?n:e.statusCode,t=typeof r=="number"&&(r>=500||r===429);return{message:e.message||`HTTP ${r!=null?r:"error"} error`,cause:e,status:typeof r=="number"?r:void 0,retryable:t,raw:e}}),unknown:C.when(e=>e instanceof Error&&!(e instanceof T)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e,raw:e}))};var ke=[x.typed,x.abort,x.timeout,x.http,x.network,x.unknown];var Z={when:e=>C.when(e),instance:e=>C.instance(e)},j=ke;var ee={fixed:e=>({type:"fixed",delay:e}),exponential:(e,r=2,t)=>({type:"exponential",base:e,factor:r,maxDelay:t}),fibonacci:(e,r)=>({type:"fibonacci",base:e,maxDelay:r}),custom:e=>({type:"custom",calculate:e})},re=(e,r,t)=>{switch(e.type){case"fixed":return e.delay;case"exponential":{let n=e.base*e.factor**(Number(r)-1);return e.maxDelay!==void 0?Math.min(n,e.maxDelay):n}case"fibonacci":{let n=e.base*Ae(Number(r));return e.maxDelay!==void 0?Math.min(n,e.maxDelay):n}case"custom":return e.calculate(r,t);default:return e}},Ae=e=>{if(e<=1)return 1;let r=1,t=1;for(let n=2;n<=e;n++){let o=r+t;r=t,t=o}return t},te=e=>{switch(e.type){case"fixed":{if(e.delay<0)throw new Error("Fixed delay must be non-negative");break}case"exponential":if(e.base<=0)throw new Error("Exponential base delay must be positive");if(e.factor<=1)throw new Error("Exponential factor must be greater than 1");if(e.maxDelay!==void 0&&e.maxDelay<=0)throw new Error("Exponential max delay must be positive");break;case"fibonacci":if(e.base<=0)throw new Error("Fibonacci base delay must be positive");if(e.maxDelay!==void 0&&e.maxDelay<=0)throw new Error("Fibonacci max delay must be positive");break;case"custom":break;default:{let r=e;throw new Error(`Unknown strategy type: ${r}`)}}};var ne=(e,r)=>new Promise((t,n)=>{if(r!=null&&r.aborted){n(new DOMException("Aborted","AbortError"));return}let o=s=>{r&&s&&r.removeEventListener("abort",s)},a,i=()=>{a&&clearTimeout(a),o(i),n(new DOMException("Aborted","AbortError"))};a=setTimeout(()=>{o(i),t()},e),r==null||r.addEventListener("abort",i,{once:!0})}),oe=(e,r,t,n)=>new Promise((o,a)=>{if(t!=null&&t.aborted){a(new DOMException("Aborted","AbortError"));return}let i=!1,s=setTimeout(()=>{var d;i=!0,u(),a((d=n==null?void 0:n())!=null?d:new Error(`Operation timed out after ${r}ms`))},r),l=()=>{i||(i=!0,u(),a(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(s),t==null||t.removeEventListener("abort",l)};t==null||t.addEventListener("abort",l,{once:!0}),e.then(d=>{i||(i=!0,u(),o(d))},d=>{i||(i=!0,u(),a(d))})});var Oe=e=>{var i,s,l;if(e.toError)return e.toError;let r=(i=e.rulesMode)!=null?i:"extend",t=(s=e.rules)!=null?s:[],n=j,o=(l=e.fallback)!=null?l:(u=>X(_)(u)),a=r==="replace"?t:[...t,...n];return Q(a,o)},Se=e=>{if(e)switch(e.type){case"none":return;case"full":case"equal":G(e.ratio);return;case"custom":return;default:{let r=e;throw new Error(`Unsupported jitter configuration: ${r}`)}}},q=e=>{let r=e;return r.timeout!==void 0&&(r={...r,timeout:Number(w(r.timeout))}),r.concurrency!==void 0&&(r={...r,concurrency:Number.isFinite(r.concurrency)?Number(H(r.concurrency)):r.concurrency}),r.retry&&(te(r.retry.strategy),Se(r.retry.jitter),r={...r,retry:{...r.retry,maxRetries:Number(h(r.retry.maxRetries))}}),r},V=class e{constructor(r={}){y(this,"circuitBreaker");y(this,"config");y(this,"lastCircuitState");let{rules:t,rulesMode:n,fallback:o,toError:a,mapError:i,...s}=r,l=Oe(r),u={...s,errorHandling:{normalizer:l,mapError:i}};this.config=q(u),u.circuitBreaker&&(this.circuitBreaker=new B(u.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var a,i,s,l,u,d;let n=q({...this.config,...t});if(this.circuitBreaker){let c=(a=this.lastCircuitState)!=null?a:this.circuitBreaker.getState().state,m=await this.circuitBreaker.canExecute(),E=this.circuitBreaker.getState().state;if(c!==E)try{(s=(i=n.hooks)==null?void 0:i.onCircuitStateChange)==null||s.call(i,c,E)}catch{}if(this.lastCircuitState=E,!m)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let o=await Ne(r,n);if(this.circuitBreaker){let c=(l=this.lastCircuitState)!=null?l:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let m=this.circuitBreaker.getState().state;if(c!==m)try{(d=(u=n.hooks)==null?void 0:u.onCircuitStateChange)==null||d.call(u,c,m)}catch{}this.lastCircuitState=m}return o}async runOrThrow(r,t={}){return this.orThrow(r,t)}async orThrow(r,t={}){let n=await this.run(r,t);if(n.ok)return n.data;throw n.error}async all(r,t={}){var d;let n=q({...this.config,...t}),o=(d=n.concurrency)!=null?d:Number.POSITIVE_INFINITY,a=Number.isFinite(o)?Number(H(o)):Number.POSITIVE_INFINITY;if(a===Number.POSITIVE_INFINITY)return Promise.all(r.map(c=>this.run(c,n)));let i=new Array(r.length),s=0,l=async()=>{var c;for(;s<r.length&&!((c=n.signal)!=null&&c.aborted);){let m=s++;if(m>=r.length)break;let E=r[m];E&&(i[m]=await this.run(E,n))}},u=Array.from({length:Math.min(a,r.length)},()=>l());await Promise.all(u);for(let c=0;c<r.length;c++)if(!(c in i)){let m=r[c];m&&(i[c]=await this.run(m,n))}return i}async allOrThrow(r,t={}){return(await this.all(r,t)).map(o=>{if(!o.ok)throw o.error;return o.data})}partitionAll(r){let t=[],n=[],o=[],a=[],i=[];for(let s of r){if(s.type==="success"){t.push(s);continue}switch(n.push(s),s.type){case"failure":o.push(s);break;case"aborted":a.push(s);break;case"timeout":i.push(s);break}}return{ok:t,errors:n,failure:o,aborted:a,timeout:i}}getCircuitBreakerState(){var r;return(r=this.circuitBreaker)==null?void 0:r.getState()}resetCircuitBreaker(){var r;(r=this.circuitBreaker)==null||r.reset()}getConfig(){return{...this.config}}withConfig(r){var l,u;let{errorHandling:t,...n}=this.config,{errorHandling:o,...a}=r,i=(l=o==null?void 0:o.normalizer)!=null?l:t.normalizer,s=(u=o==null?void 0:o.mapError)!=null?u:t.mapError;return new e({...n,...a,toError:i,mapError:s})}};function S(e={}){let r=new V(e);return{run:(t,n)=>r.run(t,n),orThrow:(t,n)=>r.orThrow(t,n),runOrThrow:(t,n)=>r.runOrThrow(t,n),all:(t,n)=>r.all(t,n),allOrThrow:(t,n)=>r.allOrThrow(t,n),partitionAll:t=>r.partitionAll(t),withConfig:t=>r.withConfig(t)}}async function Ne(e,r){let{signal:t,ignoreAbort:n=!0,timeout:o,retry:a,errorHandling:i,hooks:s,logger:l}=r,u=(R,...p)=>{try{R==null||R(...p)}catch{}},d,c=0,m=0,E=[],k=Date.now(),{signal:A,cleanup:ye}=se(t),M=R=>({totalAttempts:c,totalRetries:c>0?Number(c)-1:0,totalDuration:Date.now()-k,lastError:R,retryHistory:E});try{if(A.aborted){u(s==null?void 0:s.onAbort,A);let p=i.normalizer(new DOMException("Aborted","AbortError")),b=i.mapError?i.mapError(p):p;return{type:"aborted",ok:!1,data:null,error:b,metrics:{totalAttempts:c,totalRetries:m,totalDuration:Date.now()-k,lastError:b,retryHistory:E}}}let R=async()=>{var v,g,W;let p=1,b=h((v=a==null?void 0:a.maxRetries)!=null?v:0);for(;;){c=p;let K=new AbortController,{signal:z,cleanup:de}=se(A,K.signal);try{let D=Promise.resolve(e({signal:z})),O=o?await oe(D,o,z,()=>(K.abort(),new P(w(o)))):await D;return u(s==null?void 0:s.onSuccess,O,M()),u(l==null?void 0:l.info,`Task succeeded on attempt ${p}`),O}catch(D){let O=i.normalizer(D),f=i.mapError?i.mapError(O):O;if(d=f,f.code==="ABORTED"&&u(s==null?void 0:s.onAbort,z),n&&f.code==="ABORTED"||(u(s==null?void 0:s.onError,f,M(f)),u(l==null?void 0:l.error,`Task failed on attempt ${p}`,f)),f.code==="ABORTED"||f.retryable===!1)throw f;if(p<=Number(b)){let J=a==null?void 0:a.shouldRetry,pe={totalAttempts:Number(c),elapsedTime:Date.now()-k,startTime:new Date(k),lastDelay:(g=E[E.length-1])!=null&&g.delay?Number((W=E[E.length-1])==null?void 0:W.delay):void 0};if(!J||J(p,f,pe)){let me=a?re(a.strategy,p,f):0,L=Me(me,a==null?void 0:a.jitter),Y=w(L);E.push({attempt:p,error:f,delay:Y,timestamp:new Date}),u(s==null?void 0:s.onRetry,p,f,L),u(l==null?void 0:l.info,`Retrying in ${L}ms (attempt ${p+1})`),p+=1,await ne(Y,A);continue}}throw f}finally{de()}}};try{let p=await R(),b=M();return m=b.totalRetries,u(s==null?void 0:s.onFinally,b),{type:"success",ok:!0,data:p,error:null,metrics:b}}catch(p){let b=d!=null?d:i.normalizer(p),v=b.code==="TIMEOUT"?"timeout":b.code==="ABORTED"?"aborted":"failure",g=M(b);return m=g.totalRetries,u(s==null?void 0:s.onFinally,g),{type:v,ok:!1,data:null,error:b,metrics:g}}}finally{ye()}}function se(...e){let r=new AbortController,t=e.filter(o=>o!==void 0);if(t.length===0)return{signal:r.signal,cleanup:()=>{}};if(t.some(o=>o.aborted))return r.abort(),{signal:r.signal,cleanup:()=>{}};let n=[];for(let o of t){let a=()=>r.abort();o.addEventListener("abort",a,{once:!0}),n.push({signal:o,abort:a})}return{signal:r.signal,cleanup:()=>{for(let o of n)o.signal.removeEventListener("abort",o.abort)}}}function Me(e,r){if(!r||r.type==="none"||e<=0)return e;switch(r.type){case"full":{let t=Number(r.ratio)/100,n=Math.max(0,Number(e)*(1-t)),o=Number(e);return n+Math.random()*(o-n)}case"equal":{let t=Number(r.ratio)/100,n=Number(e)*t/2;return Number(e)-n+Math.random()*n}case"custom":return r.calculate(e);default:return r}}var N=S(),ae=N.run,ie=N.runOrThrow,ue=N.orThrow,le=N.all,ce=N.allOrThrow;0&&(module.exports={RetryStrategies,TypedError,all,allOrThrow,asMilliseconds,asRetryCount,errorRule,orThrow,run,runOrThrow,tryo});
|
package/dist/index.d.cts
CHANGED
|
@@ -8,38 +8,30 @@ type Milliseconds = number & {
|
|
|
8
8
|
type RetryCount = number & {
|
|
9
9
|
readonly __brand: 'RetryCount';
|
|
10
10
|
};
|
|
11
|
-
type ConcurrencyLimit = number & {
|
|
12
|
-
readonly __brand: 'ConcurrencyLimit';
|
|
13
|
-
};
|
|
14
|
-
type Percentage = number & {
|
|
15
|
-
readonly __brand: 'Percentage';
|
|
16
|
-
};
|
|
17
|
-
type StatusCode = number & {
|
|
18
|
-
readonly __brand: 'StatusCode';
|
|
19
|
-
};
|
|
20
11
|
declare const asMilliseconds: (n: number) => Milliseconds;
|
|
21
12
|
declare const asRetryCount: (n: number) => RetryCount;
|
|
22
|
-
declare const asConcurrencyLimit: (n: number) => ConcurrencyLimit;
|
|
23
|
-
declare const asPercentage: (n: number) => Percentage;
|
|
24
|
-
declare const asStatusCode: (n: number) => StatusCode;
|
|
25
13
|
|
|
26
14
|
/**
|
|
27
15
|
* Modern typed error hierarchy with enhanced capabilities
|
|
28
16
|
* Provides type-safe error handling with fluent API and metadata support
|
|
29
17
|
*/
|
|
30
18
|
|
|
31
|
-
declare abstract class TypedError<Code extends string = string, Meta = unknown> extends Error {
|
|
19
|
+
declare abstract class TypedError<Code extends string = string, Meta extends Record<string, unknown> = Record<string, unknown>, Raw = unknown> extends Error {
|
|
32
20
|
abstract readonly code: Code;
|
|
33
21
|
readonly cause?: unknown;
|
|
34
|
-
readonly meta
|
|
22
|
+
readonly meta: Meta;
|
|
35
23
|
readonly status?: number;
|
|
24
|
+
readonly raw: Raw;
|
|
25
|
+
readonly path?: string;
|
|
36
26
|
readonly timestamp: number;
|
|
37
27
|
readonly retryable: boolean;
|
|
38
|
-
constructor(message: string, opts
|
|
28
|
+
constructor(message: string, opts: {
|
|
39
29
|
cause?: unknown;
|
|
40
30
|
meta?: Meta;
|
|
41
31
|
status?: number;
|
|
42
32
|
retryable?: boolean;
|
|
33
|
+
raw: Raw;
|
|
34
|
+
path?: string;
|
|
43
35
|
});
|
|
44
36
|
is<C extends string>(code: C): this is TypedError<C> & {
|
|
45
37
|
code: C;
|
|
@@ -53,107 +45,88 @@ declare abstract class TypedError<Code extends string = string, Meta = unknown>
|
|
|
53
45
|
withCause(cause: unknown): this & {
|
|
54
46
|
cause: unknown;
|
|
55
47
|
};
|
|
48
|
+
withPath(path: string): this;
|
|
49
|
+
withRaw<const R>(raw: R): this & {
|
|
50
|
+
raw: R;
|
|
51
|
+
};
|
|
56
52
|
withRetryable(retryable: boolean): this;
|
|
57
53
|
toJSON(): Record<string, unknown>;
|
|
58
54
|
}
|
|
59
|
-
|
|
55
|
+
type AnyTypedError = TypedError<string, any, unknown>;
|
|
56
|
+
declare class TimeoutError extends TypedError<'TIMEOUT', Record<string, unknown>, unknown> {
|
|
60
57
|
readonly code: "TIMEOUT";
|
|
61
58
|
constructor(timeout: Milliseconds, cause?: unknown);
|
|
62
59
|
}
|
|
63
|
-
declare class AbortedError extends TypedError<'ABORTED'> {
|
|
60
|
+
declare class AbortedError extends TypedError<'ABORTED', Record<string, unknown>, unknown> {
|
|
64
61
|
readonly code: "ABORTED";
|
|
65
62
|
constructor(reason?: string, cause?: unknown);
|
|
66
63
|
}
|
|
67
|
-
declare class CircuitOpenError extends TypedError<'CIRCUIT_OPEN'> {
|
|
64
|
+
declare class CircuitOpenError extends TypedError<'CIRCUIT_OPEN', Record<string, unknown>, unknown> {
|
|
68
65
|
readonly code: "CIRCUIT_OPEN";
|
|
69
66
|
constructor(resetAfter: Milliseconds, cause?: unknown);
|
|
70
67
|
}
|
|
71
|
-
type ValidationMeta = {
|
|
68
|
+
type ValidationMeta = Record<string, unknown> & {
|
|
72
69
|
validationErrors: unknown[];
|
|
73
70
|
};
|
|
74
|
-
declare class ValidationError extends TypedError<'VALIDATION', ValidationMeta> {
|
|
71
|
+
declare class ValidationError extends TypedError<'VALIDATION', ValidationMeta, unknown> {
|
|
75
72
|
readonly validationErrors: unknown[];
|
|
76
73
|
readonly code: "VALIDATION";
|
|
77
74
|
constructor(message: string, validationErrors: unknown[], cause?: unknown);
|
|
78
75
|
}
|
|
79
|
-
declare class NetworkError extends TypedError<'NETWORK'> {
|
|
76
|
+
declare class NetworkError extends TypedError<'NETWORK', Record<string, unknown>, unknown> {
|
|
80
77
|
readonly statusCode?: number | undefined;
|
|
81
78
|
readonly code: "NETWORK";
|
|
82
79
|
constructor(message: string, statusCode?: number | undefined, cause?: unknown);
|
|
83
80
|
}
|
|
84
|
-
type HttpMeta = {
|
|
81
|
+
type HttpMeta = Record<string, unknown> & {
|
|
85
82
|
response?: unknown;
|
|
86
83
|
};
|
|
87
|
-
declare class HttpError extends TypedError<'HTTP', HttpMeta> {
|
|
84
|
+
declare class HttpError extends TypedError<'HTTP', HttpMeta, unknown> {
|
|
88
85
|
readonly status: number;
|
|
89
86
|
readonly response?: unknown | undefined;
|
|
90
87
|
readonly code: "HTTP";
|
|
91
88
|
constructor(message: string, status: number, response?: unknown | undefined, cause?: unknown);
|
|
92
89
|
}
|
|
93
|
-
declare class UnknownError extends TypedError<'UNKNOWN'> {
|
|
90
|
+
declare class UnknownError extends TypedError<'UNKNOWN', Record<string, unknown>, unknown> {
|
|
94
91
|
readonly code: "UNKNOWN";
|
|
95
92
|
constructor(message: string, cause?: unknown);
|
|
96
93
|
}
|
|
97
94
|
|
|
98
|
-
/**
|
|
99
|
-
* Modern circuit breaker implementation with enhanced state management
|
|
100
|
-
* Provides circuit breaker pattern with type safety and observability
|
|
101
|
-
*/
|
|
102
|
-
|
|
103
|
-
interface CircuitBreakerConfig<E extends TypedError = TypedError> {
|
|
104
|
-
/** Number of consecutive failures before opening circuit */
|
|
105
|
-
readonly failureThreshold: number;
|
|
106
|
-
/** How long to wait before attempting to close circuit */
|
|
107
|
-
readonly resetTimeout: number;
|
|
108
|
-
/** Number of requests allowed in half-open state */
|
|
109
|
-
readonly halfOpenRequests: number;
|
|
110
|
-
/** Optional function to determine if error should count as failure */
|
|
111
|
-
readonly shouldCountAsFailure?: (error: E) => boolean;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Modern error normalization system
|
|
116
|
-
* Provides type-safe error transformation and normalization
|
|
117
|
-
*/
|
|
118
|
-
|
|
119
|
-
type ErrorNormalizer<E extends TypedError = TypedError> = (error: unknown) => E;
|
|
120
|
-
type ErrorRule<E extends TypedError = TypedError> = (error: unknown) => E | null;
|
|
121
|
-
|
|
122
95
|
/**
|
|
123
96
|
* Modern result types with enhanced discriminated unions
|
|
124
97
|
* Provides better type safety and more granular result categorization
|
|
125
98
|
*/
|
|
126
99
|
|
|
127
|
-
type TryoResult<T, E extends
|
|
128
|
-
interface SuccessResult<T, E extends
|
|
100
|
+
type TryoResult<T, E extends AnyTypedError = AnyTypedError> = SuccessResult<T, E> | FailureResult<E> | AbortedResult<E> | TimeoutResult<E>;
|
|
101
|
+
interface SuccessResult<T, E extends AnyTypedError> {
|
|
129
102
|
readonly type: 'success';
|
|
130
103
|
readonly ok: true;
|
|
131
104
|
readonly data: T;
|
|
132
105
|
readonly error: null;
|
|
133
|
-
readonly metrics
|
|
106
|
+
readonly metrics: TryoMetrics$1<E>;
|
|
134
107
|
}
|
|
135
|
-
interface FailureResult<E extends
|
|
108
|
+
interface FailureResult<E extends AnyTypedError> {
|
|
136
109
|
readonly type: 'failure';
|
|
137
110
|
readonly ok: false;
|
|
138
111
|
readonly data: null;
|
|
139
112
|
readonly error: E;
|
|
140
|
-
readonly metrics
|
|
113
|
+
readonly metrics: TryoMetrics$1<E>;
|
|
141
114
|
}
|
|
142
|
-
interface AbortedResult<E extends
|
|
115
|
+
interface AbortedResult<E extends AnyTypedError> {
|
|
143
116
|
readonly type: 'aborted';
|
|
144
117
|
readonly ok: false;
|
|
145
118
|
readonly data: null;
|
|
146
119
|
readonly error: E;
|
|
147
|
-
readonly metrics
|
|
120
|
+
readonly metrics: TryoMetrics$1<E>;
|
|
148
121
|
}
|
|
149
|
-
interface TimeoutResult<E extends
|
|
122
|
+
interface TimeoutResult<E extends AnyTypedError> {
|
|
150
123
|
readonly type: 'timeout';
|
|
151
124
|
readonly ok: false;
|
|
152
125
|
readonly data: null;
|
|
153
126
|
readonly error: E;
|
|
154
|
-
readonly metrics
|
|
127
|
+
readonly metrics: TryoMetrics$1<E>;
|
|
155
128
|
}
|
|
156
|
-
interface TryoMetrics$1<E extends
|
|
129
|
+
interface TryoMetrics$1<E extends AnyTypedError> {
|
|
157
130
|
readonly totalAttempts: RetryCount;
|
|
158
131
|
readonly totalRetries: RetryCount;
|
|
159
132
|
readonly totalDuration: Milliseconds;
|
|
@@ -166,6 +139,31 @@ interface TryoMetrics$1<E extends TypedError> {
|
|
|
166
139
|
}>;
|
|
167
140
|
}
|
|
168
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Modern circuit breaker implementation with enhanced state management
|
|
144
|
+
* Provides circuit breaker pattern with type safety and observability
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
interface CircuitBreakerConfig<E extends AnyTypedError = AnyTypedError> {
|
|
148
|
+
/** Number of consecutive failures before opening circuit */
|
|
149
|
+
readonly failureThreshold: number;
|
|
150
|
+
/** How long to wait before attempting to close circuit */
|
|
151
|
+
readonly resetTimeout: number;
|
|
152
|
+
/** Number of requests allowed in half-open state */
|
|
153
|
+
readonly halfOpenRequests: number;
|
|
154
|
+
/** Optional function to determine if error should count as failure */
|
|
155
|
+
readonly shouldCountAsFailure?: (error: E) => boolean;
|
|
156
|
+
}
|
|
157
|
+
type CircuitState = 'closed' | 'open' | 'half-open';
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Modern error normalization system
|
|
161
|
+
* Provides type-safe error transformation and normalization
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
type ErrorNormalizer<E extends AnyTypedError = AnyTypedError> = (error: unknown) => E;
|
|
165
|
+
type ErrorRule<E extends AnyTypedError = AnyTypedError> = (error: unknown) => E | null;
|
|
166
|
+
|
|
169
167
|
/**
|
|
170
168
|
* Modern retry strategies with enhanced capabilities
|
|
171
169
|
* Provides various retry patterns with type safety
|
|
@@ -198,7 +196,7 @@ declare const RetryStrategies: {
|
|
|
198
196
|
readonly custom: (calculate: (attempt: RetryCount, error: unknown) => number) => CustomDelayStrategy;
|
|
199
197
|
};
|
|
200
198
|
|
|
201
|
-
interface TryoConfig<E extends
|
|
199
|
+
interface TryoConfig<E extends AnyTypedError = AnyTypedError> {
|
|
202
200
|
/** Abort signal passed to tasks */
|
|
203
201
|
readonly signal?: AbortSignal;
|
|
204
202
|
/** If true, aborts are treated as non-throwing failures */
|
|
@@ -218,7 +216,7 @@ interface TryoConfig<E extends TypedError = TypedError> {
|
|
|
218
216
|
/** Callback hooks */
|
|
219
217
|
readonly hooks?: HookConfig<E>;
|
|
220
218
|
}
|
|
221
|
-
interface RetryConfig<E extends
|
|
219
|
+
interface RetryConfig<E extends AnyTypedError> {
|
|
222
220
|
/** Maximum number of retry attempts */
|
|
223
221
|
readonly maxRetries: number;
|
|
224
222
|
/** Base delay strategy */
|
|
@@ -228,7 +226,7 @@ interface RetryConfig<E extends TypedError> {
|
|
|
228
226
|
/** Function to determine if retry should be attempted */
|
|
229
227
|
readonly shouldRetry?: ShouldRetryPredicate<E>;
|
|
230
228
|
}
|
|
231
|
-
type ShouldRetryPredicate<E extends
|
|
229
|
+
type ShouldRetryPredicate<E extends AnyTypedError> = (attempt: number, error: E, context: RetryContext) => boolean;
|
|
232
230
|
interface RetryContext {
|
|
233
231
|
/** Total attempts made so far */
|
|
234
232
|
readonly totalAttempts: number;
|
|
@@ -239,13 +237,13 @@ interface RetryContext {
|
|
|
239
237
|
/** Last delay applied */
|
|
240
238
|
readonly lastDelay?: number;
|
|
241
239
|
}
|
|
242
|
-
interface ErrorHandlingConfig<E extends
|
|
240
|
+
interface ErrorHandlingConfig<E extends AnyTypedError> {
|
|
243
241
|
/** Error normalizer function */
|
|
244
242
|
readonly normalizer: ErrorNormalizer<E>;
|
|
245
243
|
/** Optional error mapping/transformation */
|
|
246
244
|
readonly mapError?: (error: E) => E;
|
|
247
245
|
}
|
|
248
|
-
interface LoggerConfig<E extends
|
|
246
|
+
interface LoggerConfig<E extends AnyTypedError> {
|
|
249
247
|
/** Debug logging function */
|
|
250
248
|
readonly debug?: (message: string, meta?: unknown) => void;
|
|
251
249
|
/** Error logging function */
|
|
@@ -255,7 +253,7 @@ interface LoggerConfig<E extends TypedError> {
|
|
|
255
253
|
/** Warning logging function */
|
|
256
254
|
readonly warn?: (message: string, meta?: unknown) => void;
|
|
257
255
|
}
|
|
258
|
-
interface HookConfig<E extends
|
|
256
|
+
interface HookConfig<E extends AnyTypedError> {
|
|
259
257
|
/** Called on successful execution */
|
|
260
258
|
readonly onSuccess?: <T>(data: T, metrics?: TryoMetrics<E>) => void;
|
|
261
259
|
/** Called on failed execution */
|
|
@@ -269,8 +267,7 @@ interface HookConfig<E extends TypedError> {
|
|
|
269
267
|
/** Called when circuit breaker state changes */
|
|
270
268
|
readonly onCircuitStateChange?: (from: CircuitState, to: CircuitState) => void;
|
|
271
269
|
}
|
|
272
|
-
type
|
|
273
|
-
type TryoMetrics<E extends TypedError> = TryoMetrics$1<E>;
|
|
270
|
+
type TryoMetrics<E extends AnyTypedError> = TryoMetrics$1<E>;
|
|
274
271
|
type JitterConfig = {
|
|
275
272
|
type: 'none';
|
|
276
273
|
} | {
|
|
@@ -292,40 +289,51 @@ declare const JitterConfig: {
|
|
|
292
289
|
|
|
293
290
|
type RulesMode = 'extend' | 'replace';
|
|
294
291
|
type DefaultError = AbortedError | TimeoutError | NetworkError | HttpError | CircuitOpenError | ValidationError | UnknownError;
|
|
292
|
+
type RunOptions<E extends AnyTypedError> = Omit<Partial<TryoConfig<E>>, 'circuitBreaker'>;
|
|
293
|
+
type AllOptions<E extends AnyTypedError> = Omit<Partial<TryoConfig<E> & {
|
|
294
|
+
concurrency?: number;
|
|
295
|
+
}>, 'circuitBreaker'>;
|
|
296
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
295
297
|
type NonNull<T> = T extends null ? never : T;
|
|
296
298
|
type RuleReturn<R> = R extends (err: unknown) => infer Out ? NonNull<Out> : never;
|
|
297
|
-
type InferErrorFromRules<TRules extends readonly ErrorRule<
|
|
298
|
-
type TryoOptions<E extends
|
|
299
|
+
type InferErrorFromRules<TRules extends readonly ErrorRule<AnyTypedError>[]> = TRules extends readonly [] ? TypedError : RuleReturn<TRules[number]> | UnknownError;
|
|
300
|
+
type TryoOptions<E extends AnyTypedError = AnyTypedError> = Omit<Partial<TryoConfig<E>>, 'errorHandling' | 'signal'> & {
|
|
299
301
|
rules?: Array<ErrorRule<E>>;
|
|
300
302
|
rulesMode?: RulesMode;
|
|
301
303
|
fallback?: (err: unknown) => E;
|
|
302
304
|
toError?: (err: unknown) => E;
|
|
303
305
|
mapError?: (error: E) => E;
|
|
304
306
|
};
|
|
305
|
-
|
|
306
|
-
|
|
307
|
+
type ExtractCode<R> = R extends (...args: any[]) => infer E ? E extends TypedError<any, any, any> | null ? E extends null ? never : E extends TypedError<infer C, any, any> ? string extends C ? never : C : never : never : never;
|
|
308
|
+
type CheckUniqueCodes<T extends readonly any[], Seen = never> = T extends readonly [infer Head, ...infer Tail] ? ExtractCode<Head> extends infer C ? [C] extends [never] ? CheckUniqueCodes<Tail, Seen> : [C] extends [Seen] ? {
|
|
309
|
+
error: '❌ ERROR: Código duplicado detectado';
|
|
310
|
+
code: C;
|
|
311
|
+
} : CheckUniqueCodes<Tail, Seen | C> : never : true;
|
|
312
|
+
declare function tryo<const TRules extends readonly ErrorRule<AnyTypedError>[]>(options: Omit<TryoOptions<InferErrorFromRules<TRules>>, 'rules' | 'rulesMode'> & {
|
|
313
|
+
rules: TRules & (CheckUniqueCodes<TRules> extends true ? unknown : CheckUniqueCodes<TRules>);
|
|
314
|
+
rulesMode: 'replace';
|
|
307
315
|
}): Tryo<InferErrorFromRules<TRules>>;
|
|
308
|
-
declare function tryo<
|
|
309
|
-
|
|
316
|
+
declare function tryo<const TRules extends readonly ErrorRule<AnyTypedError>[]>(options: Omit<TryoOptions<InferErrorFromRules<TRules> | DefaultError>, 'rules'> & {
|
|
317
|
+
rules: TRules & (CheckUniqueCodes<TRules> extends true ? unknown : CheckUniqueCodes<TRules>);
|
|
318
|
+
rulesMode?: 'extend';
|
|
319
|
+
}): Tryo<InferErrorFromRules<TRules> | DefaultError>;
|
|
320
|
+
declare function tryo<E extends AnyTypedError = DefaultError>(options?: TryoOptions<E>): Tryo<E>;
|
|
321
|
+
type Tryo<E extends AnyTypedError = AnyTypedError> = {
|
|
310
322
|
run: <T>(task: (ctx: {
|
|
311
323
|
signal: AbortSignal;
|
|
312
|
-
}) =>
|
|
324
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<TryoResult<T, E>>;
|
|
313
325
|
runOrThrow: <T>(task: (ctx: {
|
|
314
326
|
signal: AbortSignal;
|
|
315
|
-
}) =>
|
|
327
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
|
|
316
328
|
orThrow: <T>(task: (ctx: {
|
|
317
329
|
signal: AbortSignal;
|
|
318
|
-
}) =>
|
|
330
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
|
|
319
331
|
all: <T>(tasks: Array<(ctx: {
|
|
320
332
|
signal: AbortSignal;
|
|
321
|
-
}) =>
|
|
322
|
-
concurrency?: number;
|
|
323
|
-
}>) => Promise<Array<TryoResult<T, E>>>;
|
|
333
|
+
}) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<Array<TryoResult<T, E>>>;
|
|
324
334
|
allOrThrow: <T>(tasks: Array<(ctx: {
|
|
325
335
|
signal: AbortSignal;
|
|
326
|
-
}) =>
|
|
327
|
-
concurrency?: number;
|
|
328
|
-
}>) => Promise<T[]>;
|
|
336
|
+
}) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<T[]>;
|
|
329
337
|
partitionAll: <T>(results: Array<TryoResult<T, E>>) => {
|
|
330
338
|
ok: Array<SuccessResult<T, E>>;
|
|
331
339
|
errors: Array<FailureResult<E> | AbortedResult<E> | TimeoutResult<E>>;
|
|
@@ -338,23 +346,64 @@ type Tryo<E extends TypedError = TypedError> = {
|
|
|
338
346
|
|
|
339
347
|
declare const run: <T>(task: (ctx: {
|
|
340
348
|
signal: AbortSignal;
|
|
341
|
-
}) => Promise<T>, options?:
|
|
349
|
+
}) => T | Promise<T>, options?: {
|
|
350
|
+
readonly timeout?: number | undefined;
|
|
351
|
+
readonly signal?: AbortSignal | undefined;
|
|
352
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
353
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
354
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
355
|
+
readonly concurrency?: number | undefined;
|
|
356
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
357
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
358
|
+
} | undefined) => Promise<TryoResult<T, DefaultError>>;
|
|
342
359
|
declare const runOrThrow: <T>(task: (ctx: {
|
|
343
360
|
signal: AbortSignal;
|
|
344
|
-
}) => Promise<T>, options?:
|
|
361
|
+
}) => T | Promise<T>, options?: {
|
|
362
|
+
readonly timeout?: number | undefined;
|
|
363
|
+
readonly signal?: AbortSignal | undefined;
|
|
364
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
365
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
366
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
367
|
+
readonly concurrency?: number | undefined;
|
|
368
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
369
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
370
|
+
} | undefined) => Promise<T>;
|
|
345
371
|
declare const orThrow: <T>(task: (ctx: {
|
|
346
372
|
signal: AbortSignal;
|
|
347
|
-
}) => Promise<T>, options?:
|
|
373
|
+
}) => T | Promise<T>, options?: {
|
|
374
|
+
readonly timeout?: number | undefined;
|
|
375
|
+
readonly signal?: AbortSignal | undefined;
|
|
376
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
377
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
378
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
379
|
+
readonly concurrency?: number | undefined;
|
|
380
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
381
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
382
|
+
} | undefined) => Promise<T>;
|
|
348
383
|
declare const all: <T>(tasks: ((ctx: {
|
|
349
384
|
signal: AbortSignal;
|
|
350
|
-
}) => Promise<T>)[], options?:
|
|
351
|
-
|
|
352
|
-
|
|
385
|
+
}) => T | Promise<T>)[], options?: {
|
|
386
|
+
readonly timeout?: number | undefined;
|
|
387
|
+
readonly signal?: AbortSignal | undefined;
|
|
388
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
389
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
390
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
391
|
+
concurrency?: number | undefined;
|
|
392
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
393
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
394
|
+
} | undefined) => Promise<TryoResult<T, DefaultError>[]>;
|
|
353
395
|
declare const allOrThrow: <T>(tasks: ((ctx: {
|
|
354
396
|
signal: AbortSignal;
|
|
355
|
-
}) => Promise<T>)[], options?:
|
|
356
|
-
|
|
357
|
-
|
|
397
|
+
}) => T | Promise<T>)[], options?: {
|
|
398
|
+
readonly timeout?: number | undefined;
|
|
399
|
+
readonly signal?: AbortSignal | undefined;
|
|
400
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
401
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
402
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
403
|
+
concurrency?: number | undefined;
|
|
404
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
405
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
406
|
+
} | undefined) => Promise<T[]>;
|
|
358
407
|
|
|
359
408
|
/**
|
|
360
409
|
* Modern fluent error rule builder
|
|
@@ -368,23 +417,27 @@ declare class ErrorRuleBuilder<T> {
|
|
|
368
417
|
toError<const Out extends {
|
|
369
418
|
code: string;
|
|
370
419
|
message: string;
|
|
371
|
-
meta?: unknown
|
|
420
|
+
meta?: Record<string, unknown>;
|
|
372
421
|
status?: number;
|
|
373
422
|
cause?: unknown;
|
|
374
423
|
retryable?: boolean;
|
|
375
|
-
|
|
424
|
+
raw?: any;
|
|
425
|
+
path?: string;
|
|
426
|
+
}>(mapper: (err: T) => Out): ErrorRule<TypedError<Out['code'], Out['meta'] extends Record<string, unknown> ? Out['meta'] : Record<string, unknown>, unknown extends Out['raw'] ? T : Out['raw']>>;
|
|
376
427
|
}
|
|
377
428
|
declare class ErrorMapper<T, C extends string> {
|
|
378
429
|
private readonly predicate;
|
|
379
430
|
private readonly errorCode;
|
|
380
431
|
constructor(predicate: (err: unknown) => err is T, errorCode: C);
|
|
381
|
-
with<const M = unknown>(mapper: (err: T) => {
|
|
432
|
+
with<const M extends Record<string, unknown> = Record<string, unknown>, const R = '__NOT_SET__'>(mapper: (err: T) => {
|
|
382
433
|
message: string;
|
|
383
434
|
cause?: unknown;
|
|
384
435
|
meta?: M;
|
|
385
436
|
status?: number;
|
|
386
437
|
retryable?: boolean;
|
|
387
|
-
|
|
438
|
+
raw?: R;
|
|
439
|
+
path?: string;
|
|
440
|
+
}): ErrorRule<TypedError<C, M, R extends '__NOT_SET__' ? T : R>>;
|
|
388
441
|
}
|
|
389
442
|
|
|
390
443
|
declare const errorRule: {
|
|
@@ -392,4 +445,4 @@ declare const errorRule: {
|
|
|
392
445
|
readonly instance: <T extends new (...args: unknown[]) => unknown>(ErrorClass: T) => ErrorRuleBuilder<InstanceType<T>>;
|
|
393
446
|
};
|
|
394
447
|
|
|
395
|
-
export { type AbortedResult, type
|
|
448
|
+
export { type AbortedResult, type FailureResult, type Milliseconds, type RetryCount, RetryStrategies, type RulesMode, type SuccessResult, type TimeoutResult, type TryoConfig, type TryoMetrics$1 as TryoMetrics, type TryoOptions, type TryoResult, TypedError, all, allOrThrow, asMilliseconds, asRetryCount, tryo as default, errorRule, orThrow, run, runOrThrow, tryo };
|
package/dist/index.d.ts
CHANGED
|
@@ -8,38 +8,30 @@ type Milliseconds = number & {
|
|
|
8
8
|
type RetryCount = number & {
|
|
9
9
|
readonly __brand: 'RetryCount';
|
|
10
10
|
};
|
|
11
|
-
type ConcurrencyLimit = number & {
|
|
12
|
-
readonly __brand: 'ConcurrencyLimit';
|
|
13
|
-
};
|
|
14
|
-
type Percentage = number & {
|
|
15
|
-
readonly __brand: 'Percentage';
|
|
16
|
-
};
|
|
17
|
-
type StatusCode = number & {
|
|
18
|
-
readonly __brand: 'StatusCode';
|
|
19
|
-
};
|
|
20
11
|
declare const asMilliseconds: (n: number) => Milliseconds;
|
|
21
12
|
declare const asRetryCount: (n: number) => RetryCount;
|
|
22
|
-
declare const asConcurrencyLimit: (n: number) => ConcurrencyLimit;
|
|
23
|
-
declare const asPercentage: (n: number) => Percentage;
|
|
24
|
-
declare const asStatusCode: (n: number) => StatusCode;
|
|
25
13
|
|
|
26
14
|
/**
|
|
27
15
|
* Modern typed error hierarchy with enhanced capabilities
|
|
28
16
|
* Provides type-safe error handling with fluent API and metadata support
|
|
29
17
|
*/
|
|
30
18
|
|
|
31
|
-
declare abstract class TypedError<Code extends string = string, Meta = unknown> extends Error {
|
|
19
|
+
declare abstract class TypedError<Code extends string = string, Meta extends Record<string, unknown> = Record<string, unknown>, Raw = unknown> extends Error {
|
|
32
20
|
abstract readonly code: Code;
|
|
33
21
|
readonly cause?: unknown;
|
|
34
|
-
readonly meta
|
|
22
|
+
readonly meta: Meta;
|
|
35
23
|
readonly status?: number;
|
|
24
|
+
readonly raw: Raw;
|
|
25
|
+
readonly path?: string;
|
|
36
26
|
readonly timestamp: number;
|
|
37
27
|
readonly retryable: boolean;
|
|
38
|
-
constructor(message: string, opts
|
|
28
|
+
constructor(message: string, opts: {
|
|
39
29
|
cause?: unknown;
|
|
40
30
|
meta?: Meta;
|
|
41
31
|
status?: number;
|
|
42
32
|
retryable?: boolean;
|
|
33
|
+
raw: Raw;
|
|
34
|
+
path?: string;
|
|
43
35
|
});
|
|
44
36
|
is<C extends string>(code: C): this is TypedError<C> & {
|
|
45
37
|
code: C;
|
|
@@ -53,107 +45,88 @@ declare abstract class TypedError<Code extends string = string, Meta = unknown>
|
|
|
53
45
|
withCause(cause: unknown): this & {
|
|
54
46
|
cause: unknown;
|
|
55
47
|
};
|
|
48
|
+
withPath(path: string): this;
|
|
49
|
+
withRaw<const R>(raw: R): this & {
|
|
50
|
+
raw: R;
|
|
51
|
+
};
|
|
56
52
|
withRetryable(retryable: boolean): this;
|
|
57
53
|
toJSON(): Record<string, unknown>;
|
|
58
54
|
}
|
|
59
|
-
|
|
55
|
+
type AnyTypedError = TypedError<string, any, unknown>;
|
|
56
|
+
declare class TimeoutError extends TypedError<'TIMEOUT', Record<string, unknown>, unknown> {
|
|
60
57
|
readonly code: "TIMEOUT";
|
|
61
58
|
constructor(timeout: Milliseconds, cause?: unknown);
|
|
62
59
|
}
|
|
63
|
-
declare class AbortedError extends TypedError<'ABORTED'> {
|
|
60
|
+
declare class AbortedError extends TypedError<'ABORTED', Record<string, unknown>, unknown> {
|
|
64
61
|
readonly code: "ABORTED";
|
|
65
62
|
constructor(reason?: string, cause?: unknown);
|
|
66
63
|
}
|
|
67
|
-
declare class CircuitOpenError extends TypedError<'CIRCUIT_OPEN'> {
|
|
64
|
+
declare class CircuitOpenError extends TypedError<'CIRCUIT_OPEN', Record<string, unknown>, unknown> {
|
|
68
65
|
readonly code: "CIRCUIT_OPEN";
|
|
69
66
|
constructor(resetAfter: Milliseconds, cause?: unknown);
|
|
70
67
|
}
|
|
71
|
-
type ValidationMeta = {
|
|
68
|
+
type ValidationMeta = Record<string, unknown> & {
|
|
72
69
|
validationErrors: unknown[];
|
|
73
70
|
};
|
|
74
|
-
declare class ValidationError extends TypedError<'VALIDATION', ValidationMeta> {
|
|
71
|
+
declare class ValidationError extends TypedError<'VALIDATION', ValidationMeta, unknown> {
|
|
75
72
|
readonly validationErrors: unknown[];
|
|
76
73
|
readonly code: "VALIDATION";
|
|
77
74
|
constructor(message: string, validationErrors: unknown[], cause?: unknown);
|
|
78
75
|
}
|
|
79
|
-
declare class NetworkError extends TypedError<'NETWORK'> {
|
|
76
|
+
declare class NetworkError extends TypedError<'NETWORK', Record<string, unknown>, unknown> {
|
|
80
77
|
readonly statusCode?: number | undefined;
|
|
81
78
|
readonly code: "NETWORK";
|
|
82
79
|
constructor(message: string, statusCode?: number | undefined, cause?: unknown);
|
|
83
80
|
}
|
|
84
|
-
type HttpMeta = {
|
|
81
|
+
type HttpMeta = Record<string, unknown> & {
|
|
85
82
|
response?: unknown;
|
|
86
83
|
};
|
|
87
|
-
declare class HttpError extends TypedError<'HTTP', HttpMeta> {
|
|
84
|
+
declare class HttpError extends TypedError<'HTTP', HttpMeta, unknown> {
|
|
88
85
|
readonly status: number;
|
|
89
86
|
readonly response?: unknown | undefined;
|
|
90
87
|
readonly code: "HTTP";
|
|
91
88
|
constructor(message: string, status: number, response?: unknown | undefined, cause?: unknown);
|
|
92
89
|
}
|
|
93
|
-
declare class UnknownError extends TypedError<'UNKNOWN'> {
|
|
90
|
+
declare class UnknownError extends TypedError<'UNKNOWN', Record<string, unknown>, unknown> {
|
|
94
91
|
readonly code: "UNKNOWN";
|
|
95
92
|
constructor(message: string, cause?: unknown);
|
|
96
93
|
}
|
|
97
94
|
|
|
98
|
-
/**
|
|
99
|
-
* Modern circuit breaker implementation with enhanced state management
|
|
100
|
-
* Provides circuit breaker pattern with type safety and observability
|
|
101
|
-
*/
|
|
102
|
-
|
|
103
|
-
interface CircuitBreakerConfig<E extends TypedError = TypedError> {
|
|
104
|
-
/** Number of consecutive failures before opening circuit */
|
|
105
|
-
readonly failureThreshold: number;
|
|
106
|
-
/** How long to wait before attempting to close circuit */
|
|
107
|
-
readonly resetTimeout: number;
|
|
108
|
-
/** Number of requests allowed in half-open state */
|
|
109
|
-
readonly halfOpenRequests: number;
|
|
110
|
-
/** Optional function to determine if error should count as failure */
|
|
111
|
-
readonly shouldCountAsFailure?: (error: E) => boolean;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Modern error normalization system
|
|
116
|
-
* Provides type-safe error transformation and normalization
|
|
117
|
-
*/
|
|
118
|
-
|
|
119
|
-
type ErrorNormalizer<E extends TypedError = TypedError> = (error: unknown) => E;
|
|
120
|
-
type ErrorRule<E extends TypedError = TypedError> = (error: unknown) => E | null;
|
|
121
|
-
|
|
122
95
|
/**
|
|
123
96
|
* Modern result types with enhanced discriminated unions
|
|
124
97
|
* Provides better type safety and more granular result categorization
|
|
125
98
|
*/
|
|
126
99
|
|
|
127
|
-
type TryoResult<T, E extends
|
|
128
|
-
interface SuccessResult<T, E extends
|
|
100
|
+
type TryoResult<T, E extends AnyTypedError = AnyTypedError> = SuccessResult<T, E> | FailureResult<E> | AbortedResult<E> | TimeoutResult<E>;
|
|
101
|
+
interface SuccessResult<T, E extends AnyTypedError> {
|
|
129
102
|
readonly type: 'success';
|
|
130
103
|
readonly ok: true;
|
|
131
104
|
readonly data: T;
|
|
132
105
|
readonly error: null;
|
|
133
|
-
readonly metrics
|
|
106
|
+
readonly metrics: TryoMetrics$1<E>;
|
|
134
107
|
}
|
|
135
|
-
interface FailureResult<E extends
|
|
108
|
+
interface FailureResult<E extends AnyTypedError> {
|
|
136
109
|
readonly type: 'failure';
|
|
137
110
|
readonly ok: false;
|
|
138
111
|
readonly data: null;
|
|
139
112
|
readonly error: E;
|
|
140
|
-
readonly metrics
|
|
113
|
+
readonly metrics: TryoMetrics$1<E>;
|
|
141
114
|
}
|
|
142
|
-
interface AbortedResult<E extends
|
|
115
|
+
interface AbortedResult<E extends AnyTypedError> {
|
|
143
116
|
readonly type: 'aborted';
|
|
144
117
|
readonly ok: false;
|
|
145
118
|
readonly data: null;
|
|
146
119
|
readonly error: E;
|
|
147
|
-
readonly metrics
|
|
120
|
+
readonly metrics: TryoMetrics$1<E>;
|
|
148
121
|
}
|
|
149
|
-
interface TimeoutResult<E extends
|
|
122
|
+
interface TimeoutResult<E extends AnyTypedError> {
|
|
150
123
|
readonly type: 'timeout';
|
|
151
124
|
readonly ok: false;
|
|
152
125
|
readonly data: null;
|
|
153
126
|
readonly error: E;
|
|
154
|
-
readonly metrics
|
|
127
|
+
readonly metrics: TryoMetrics$1<E>;
|
|
155
128
|
}
|
|
156
|
-
interface TryoMetrics$1<E extends
|
|
129
|
+
interface TryoMetrics$1<E extends AnyTypedError> {
|
|
157
130
|
readonly totalAttempts: RetryCount;
|
|
158
131
|
readonly totalRetries: RetryCount;
|
|
159
132
|
readonly totalDuration: Milliseconds;
|
|
@@ -166,6 +139,31 @@ interface TryoMetrics$1<E extends TypedError> {
|
|
|
166
139
|
}>;
|
|
167
140
|
}
|
|
168
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Modern circuit breaker implementation with enhanced state management
|
|
144
|
+
* Provides circuit breaker pattern with type safety and observability
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
interface CircuitBreakerConfig<E extends AnyTypedError = AnyTypedError> {
|
|
148
|
+
/** Number of consecutive failures before opening circuit */
|
|
149
|
+
readonly failureThreshold: number;
|
|
150
|
+
/** How long to wait before attempting to close circuit */
|
|
151
|
+
readonly resetTimeout: number;
|
|
152
|
+
/** Number of requests allowed in half-open state */
|
|
153
|
+
readonly halfOpenRequests: number;
|
|
154
|
+
/** Optional function to determine if error should count as failure */
|
|
155
|
+
readonly shouldCountAsFailure?: (error: E) => boolean;
|
|
156
|
+
}
|
|
157
|
+
type CircuitState = 'closed' | 'open' | 'half-open';
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Modern error normalization system
|
|
161
|
+
* Provides type-safe error transformation and normalization
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
type ErrorNormalizer<E extends AnyTypedError = AnyTypedError> = (error: unknown) => E;
|
|
165
|
+
type ErrorRule<E extends AnyTypedError = AnyTypedError> = (error: unknown) => E | null;
|
|
166
|
+
|
|
169
167
|
/**
|
|
170
168
|
* Modern retry strategies with enhanced capabilities
|
|
171
169
|
* Provides various retry patterns with type safety
|
|
@@ -198,7 +196,7 @@ declare const RetryStrategies: {
|
|
|
198
196
|
readonly custom: (calculate: (attempt: RetryCount, error: unknown) => number) => CustomDelayStrategy;
|
|
199
197
|
};
|
|
200
198
|
|
|
201
|
-
interface TryoConfig<E extends
|
|
199
|
+
interface TryoConfig<E extends AnyTypedError = AnyTypedError> {
|
|
202
200
|
/** Abort signal passed to tasks */
|
|
203
201
|
readonly signal?: AbortSignal;
|
|
204
202
|
/** If true, aborts are treated as non-throwing failures */
|
|
@@ -218,7 +216,7 @@ interface TryoConfig<E extends TypedError = TypedError> {
|
|
|
218
216
|
/** Callback hooks */
|
|
219
217
|
readonly hooks?: HookConfig<E>;
|
|
220
218
|
}
|
|
221
|
-
interface RetryConfig<E extends
|
|
219
|
+
interface RetryConfig<E extends AnyTypedError> {
|
|
222
220
|
/** Maximum number of retry attempts */
|
|
223
221
|
readonly maxRetries: number;
|
|
224
222
|
/** Base delay strategy */
|
|
@@ -228,7 +226,7 @@ interface RetryConfig<E extends TypedError> {
|
|
|
228
226
|
/** Function to determine if retry should be attempted */
|
|
229
227
|
readonly shouldRetry?: ShouldRetryPredicate<E>;
|
|
230
228
|
}
|
|
231
|
-
type ShouldRetryPredicate<E extends
|
|
229
|
+
type ShouldRetryPredicate<E extends AnyTypedError> = (attempt: number, error: E, context: RetryContext) => boolean;
|
|
232
230
|
interface RetryContext {
|
|
233
231
|
/** Total attempts made so far */
|
|
234
232
|
readonly totalAttempts: number;
|
|
@@ -239,13 +237,13 @@ interface RetryContext {
|
|
|
239
237
|
/** Last delay applied */
|
|
240
238
|
readonly lastDelay?: number;
|
|
241
239
|
}
|
|
242
|
-
interface ErrorHandlingConfig<E extends
|
|
240
|
+
interface ErrorHandlingConfig<E extends AnyTypedError> {
|
|
243
241
|
/** Error normalizer function */
|
|
244
242
|
readonly normalizer: ErrorNormalizer<E>;
|
|
245
243
|
/** Optional error mapping/transformation */
|
|
246
244
|
readonly mapError?: (error: E) => E;
|
|
247
245
|
}
|
|
248
|
-
interface LoggerConfig<E extends
|
|
246
|
+
interface LoggerConfig<E extends AnyTypedError> {
|
|
249
247
|
/** Debug logging function */
|
|
250
248
|
readonly debug?: (message: string, meta?: unknown) => void;
|
|
251
249
|
/** Error logging function */
|
|
@@ -255,7 +253,7 @@ interface LoggerConfig<E extends TypedError> {
|
|
|
255
253
|
/** Warning logging function */
|
|
256
254
|
readonly warn?: (message: string, meta?: unknown) => void;
|
|
257
255
|
}
|
|
258
|
-
interface HookConfig<E extends
|
|
256
|
+
interface HookConfig<E extends AnyTypedError> {
|
|
259
257
|
/** Called on successful execution */
|
|
260
258
|
readonly onSuccess?: <T>(data: T, metrics?: TryoMetrics<E>) => void;
|
|
261
259
|
/** Called on failed execution */
|
|
@@ -269,8 +267,7 @@ interface HookConfig<E extends TypedError> {
|
|
|
269
267
|
/** Called when circuit breaker state changes */
|
|
270
268
|
readonly onCircuitStateChange?: (from: CircuitState, to: CircuitState) => void;
|
|
271
269
|
}
|
|
272
|
-
type
|
|
273
|
-
type TryoMetrics<E extends TypedError> = TryoMetrics$1<E>;
|
|
270
|
+
type TryoMetrics<E extends AnyTypedError> = TryoMetrics$1<E>;
|
|
274
271
|
type JitterConfig = {
|
|
275
272
|
type: 'none';
|
|
276
273
|
} | {
|
|
@@ -292,40 +289,51 @@ declare const JitterConfig: {
|
|
|
292
289
|
|
|
293
290
|
type RulesMode = 'extend' | 'replace';
|
|
294
291
|
type DefaultError = AbortedError | TimeoutError | NetworkError | HttpError | CircuitOpenError | ValidationError | UnknownError;
|
|
292
|
+
type RunOptions<E extends AnyTypedError> = Omit<Partial<TryoConfig<E>>, 'circuitBreaker'>;
|
|
293
|
+
type AllOptions<E extends AnyTypedError> = Omit<Partial<TryoConfig<E> & {
|
|
294
|
+
concurrency?: number;
|
|
295
|
+
}>, 'circuitBreaker'>;
|
|
296
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
295
297
|
type NonNull<T> = T extends null ? never : T;
|
|
296
298
|
type RuleReturn<R> = R extends (err: unknown) => infer Out ? NonNull<Out> : never;
|
|
297
|
-
type InferErrorFromRules<TRules extends readonly ErrorRule<
|
|
298
|
-
type TryoOptions<E extends
|
|
299
|
+
type InferErrorFromRules<TRules extends readonly ErrorRule<AnyTypedError>[]> = TRules extends readonly [] ? TypedError : RuleReturn<TRules[number]> | UnknownError;
|
|
300
|
+
type TryoOptions<E extends AnyTypedError = AnyTypedError> = Omit<Partial<TryoConfig<E>>, 'errorHandling' | 'signal'> & {
|
|
299
301
|
rules?: Array<ErrorRule<E>>;
|
|
300
302
|
rulesMode?: RulesMode;
|
|
301
303
|
fallback?: (err: unknown) => E;
|
|
302
304
|
toError?: (err: unknown) => E;
|
|
303
305
|
mapError?: (error: E) => E;
|
|
304
306
|
};
|
|
305
|
-
|
|
306
|
-
|
|
307
|
+
type ExtractCode<R> = R extends (...args: any[]) => infer E ? E extends TypedError<any, any, any> | null ? E extends null ? never : E extends TypedError<infer C, any, any> ? string extends C ? never : C : never : never : never;
|
|
308
|
+
type CheckUniqueCodes<T extends readonly any[], Seen = never> = T extends readonly [infer Head, ...infer Tail] ? ExtractCode<Head> extends infer C ? [C] extends [never] ? CheckUniqueCodes<Tail, Seen> : [C] extends [Seen] ? {
|
|
309
|
+
error: '❌ ERROR: Código duplicado detectado';
|
|
310
|
+
code: C;
|
|
311
|
+
} : CheckUniqueCodes<Tail, Seen | C> : never : true;
|
|
312
|
+
declare function tryo<const TRules extends readonly ErrorRule<AnyTypedError>[]>(options: Omit<TryoOptions<InferErrorFromRules<TRules>>, 'rules' | 'rulesMode'> & {
|
|
313
|
+
rules: TRules & (CheckUniqueCodes<TRules> extends true ? unknown : CheckUniqueCodes<TRules>);
|
|
314
|
+
rulesMode: 'replace';
|
|
307
315
|
}): Tryo<InferErrorFromRules<TRules>>;
|
|
308
|
-
declare function tryo<
|
|
309
|
-
|
|
316
|
+
declare function tryo<const TRules extends readonly ErrorRule<AnyTypedError>[]>(options: Omit<TryoOptions<InferErrorFromRules<TRules> | DefaultError>, 'rules'> & {
|
|
317
|
+
rules: TRules & (CheckUniqueCodes<TRules> extends true ? unknown : CheckUniqueCodes<TRules>);
|
|
318
|
+
rulesMode?: 'extend';
|
|
319
|
+
}): Tryo<InferErrorFromRules<TRules> | DefaultError>;
|
|
320
|
+
declare function tryo<E extends AnyTypedError = DefaultError>(options?: TryoOptions<E>): Tryo<E>;
|
|
321
|
+
type Tryo<E extends AnyTypedError = AnyTypedError> = {
|
|
310
322
|
run: <T>(task: (ctx: {
|
|
311
323
|
signal: AbortSignal;
|
|
312
|
-
}) =>
|
|
324
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<TryoResult<T, E>>;
|
|
313
325
|
runOrThrow: <T>(task: (ctx: {
|
|
314
326
|
signal: AbortSignal;
|
|
315
|
-
}) =>
|
|
327
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
|
|
316
328
|
orThrow: <T>(task: (ctx: {
|
|
317
329
|
signal: AbortSignal;
|
|
318
|
-
}) =>
|
|
330
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
|
|
319
331
|
all: <T>(tasks: Array<(ctx: {
|
|
320
332
|
signal: AbortSignal;
|
|
321
|
-
}) =>
|
|
322
|
-
concurrency?: number;
|
|
323
|
-
}>) => Promise<Array<TryoResult<T, E>>>;
|
|
333
|
+
}) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<Array<TryoResult<T, E>>>;
|
|
324
334
|
allOrThrow: <T>(tasks: Array<(ctx: {
|
|
325
335
|
signal: AbortSignal;
|
|
326
|
-
}) =>
|
|
327
|
-
concurrency?: number;
|
|
328
|
-
}>) => Promise<T[]>;
|
|
336
|
+
}) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<T[]>;
|
|
329
337
|
partitionAll: <T>(results: Array<TryoResult<T, E>>) => {
|
|
330
338
|
ok: Array<SuccessResult<T, E>>;
|
|
331
339
|
errors: Array<FailureResult<E> | AbortedResult<E> | TimeoutResult<E>>;
|
|
@@ -338,23 +346,64 @@ type Tryo<E extends TypedError = TypedError> = {
|
|
|
338
346
|
|
|
339
347
|
declare const run: <T>(task: (ctx: {
|
|
340
348
|
signal: AbortSignal;
|
|
341
|
-
}) => Promise<T>, options?:
|
|
349
|
+
}) => T | Promise<T>, options?: {
|
|
350
|
+
readonly timeout?: number | undefined;
|
|
351
|
+
readonly signal?: AbortSignal | undefined;
|
|
352
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
353
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
354
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
355
|
+
readonly concurrency?: number | undefined;
|
|
356
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
357
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
358
|
+
} | undefined) => Promise<TryoResult<T, DefaultError>>;
|
|
342
359
|
declare const runOrThrow: <T>(task: (ctx: {
|
|
343
360
|
signal: AbortSignal;
|
|
344
|
-
}) => Promise<T>, options?:
|
|
361
|
+
}) => T | Promise<T>, options?: {
|
|
362
|
+
readonly timeout?: number | undefined;
|
|
363
|
+
readonly signal?: AbortSignal | undefined;
|
|
364
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
365
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
366
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
367
|
+
readonly concurrency?: number | undefined;
|
|
368
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
369
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
370
|
+
} | undefined) => Promise<T>;
|
|
345
371
|
declare const orThrow: <T>(task: (ctx: {
|
|
346
372
|
signal: AbortSignal;
|
|
347
|
-
}) => Promise<T>, options?:
|
|
373
|
+
}) => T | Promise<T>, options?: {
|
|
374
|
+
readonly timeout?: number | undefined;
|
|
375
|
+
readonly signal?: AbortSignal | undefined;
|
|
376
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
377
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
378
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
379
|
+
readonly concurrency?: number | undefined;
|
|
380
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
381
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
382
|
+
} | undefined) => Promise<T>;
|
|
348
383
|
declare const all: <T>(tasks: ((ctx: {
|
|
349
384
|
signal: AbortSignal;
|
|
350
|
-
}) => Promise<T>)[], options?:
|
|
351
|
-
|
|
352
|
-
|
|
385
|
+
}) => T | Promise<T>)[], options?: {
|
|
386
|
+
readonly timeout?: number | undefined;
|
|
387
|
+
readonly signal?: AbortSignal | undefined;
|
|
388
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
389
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
390
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
391
|
+
concurrency?: number | undefined;
|
|
392
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
393
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
394
|
+
} | undefined) => Promise<TryoResult<T, DefaultError>[]>;
|
|
353
395
|
declare const allOrThrow: <T>(tasks: ((ctx: {
|
|
354
396
|
signal: AbortSignal;
|
|
355
|
-
}) => Promise<T>)[], options?:
|
|
356
|
-
|
|
357
|
-
|
|
397
|
+
}) => T | Promise<T>)[], options?: {
|
|
398
|
+
readonly timeout?: number | undefined;
|
|
399
|
+
readonly signal?: AbortSignal | undefined;
|
|
400
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
401
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
402
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
403
|
+
concurrency?: number | undefined;
|
|
404
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
405
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
406
|
+
} | undefined) => Promise<T[]>;
|
|
358
407
|
|
|
359
408
|
/**
|
|
360
409
|
* Modern fluent error rule builder
|
|
@@ -368,23 +417,27 @@ declare class ErrorRuleBuilder<T> {
|
|
|
368
417
|
toError<const Out extends {
|
|
369
418
|
code: string;
|
|
370
419
|
message: string;
|
|
371
|
-
meta?: unknown
|
|
420
|
+
meta?: Record<string, unknown>;
|
|
372
421
|
status?: number;
|
|
373
422
|
cause?: unknown;
|
|
374
423
|
retryable?: boolean;
|
|
375
|
-
|
|
424
|
+
raw?: any;
|
|
425
|
+
path?: string;
|
|
426
|
+
}>(mapper: (err: T) => Out): ErrorRule<TypedError<Out['code'], Out['meta'] extends Record<string, unknown> ? Out['meta'] : Record<string, unknown>, unknown extends Out['raw'] ? T : Out['raw']>>;
|
|
376
427
|
}
|
|
377
428
|
declare class ErrorMapper<T, C extends string> {
|
|
378
429
|
private readonly predicate;
|
|
379
430
|
private readonly errorCode;
|
|
380
431
|
constructor(predicate: (err: unknown) => err is T, errorCode: C);
|
|
381
|
-
with<const M = unknown>(mapper: (err: T) => {
|
|
432
|
+
with<const M extends Record<string, unknown> = Record<string, unknown>, const R = '__NOT_SET__'>(mapper: (err: T) => {
|
|
382
433
|
message: string;
|
|
383
434
|
cause?: unknown;
|
|
384
435
|
meta?: M;
|
|
385
436
|
status?: number;
|
|
386
437
|
retryable?: boolean;
|
|
387
|
-
|
|
438
|
+
raw?: R;
|
|
439
|
+
path?: string;
|
|
440
|
+
}): ErrorRule<TypedError<C, M, R extends '__NOT_SET__' ? T : R>>;
|
|
388
441
|
}
|
|
389
442
|
|
|
390
443
|
declare const errorRule: {
|
|
@@ -392,4 +445,4 @@ declare const errorRule: {
|
|
|
392
445
|
readonly instance: <T extends new (...args: unknown[]) => unknown>(ErrorClass: T) => ErrorRuleBuilder<InstanceType<T>>;
|
|
393
446
|
};
|
|
394
447
|
|
|
395
|
-
export { type AbortedResult, type
|
|
448
|
+
export { type AbortedResult, type FailureResult, type Milliseconds, type RetryCount, RetryStrategies, type RulesMode, type SuccessResult, type TimeoutResult, type TryoConfig, type TryoMetrics$1 as TryoMetrics, type TryoOptions, type TryoResult, TypedError, all, allOrThrow, asMilliseconds, asRetryCount, tryo as default, errorRule, orThrow, run, runOrThrow, tryo };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var j=Object.defineProperty;var ee=(e,r,t)=>r in e?j(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var m=(e,r,t)=>ee(e,typeof r!="symbol"?r+"":r,t);var T=class extends Error{constructor(t,n){var o;super(t);m(this,"cause");m(this,"meta");m(this,"status");m(this,"timestamp");m(this,"retryable");this.timestamp=Date.now(),this.retryable=(o=n==null?void 0:n.retryable)!=null?o:!0,this.name=this.constructor.name,this.cause=n==null?void 0:n.cause,this.meta=n==null?void 0:n.meta,this.status=n==null?void 0:n.status,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}is(t){return this.code===t}withMeta(t){return Object.assign(this,{meta:t})}withStatus(t){return Object.assign(this,{status:t})}withCause(t){return Object.assign(this,{cause:t})}withRetryable(t){return this.retryable=t,this}toJSON(){return{name:this.name,code:this.code,message:this.message,timestamp:this.timestamp,retryable:this.retryable,cause:this.cause,stack:this.stack}}},D=class extends T{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0});m(this,"code","TIMEOUT")}};var g=class extends T{constructor(t,n){super(`Circuit breaker is open, reset after ${t}ms`,{cause:n,retryable:!1});m(this,"code","CIRCUIT_OPEN")}},v=class extends T{constructor(t,n,o){super(t,{cause:o,meta:{validationErrors:n},retryable:!1});this.validationErrors=n;m(this,"code","VALIDATION")}};var I=class extends T{constructor(t,n){super(t,{cause:n});m(this,"code","UNKNOWN")}};var x=e=>{if(e<0||!Number.isFinite(e))throw new Error(`Invalid milliseconds: must be a non-negative finite number, got ${e}`);return e},A=e=>{if(e<0||!Number.isInteger(e))throw new Error(`Invalid retry count: must be a non-negative integer, got ${e}`);return e},_=e=>{if(e<1||!Number.isInteger(e))throw new Error(`Invalid concurrency limit: must be a positive integer, got ${e}`);return e},re=e=>{if(e<0||e>100||!Number.isFinite(e))throw new Error(`Invalid percentage: must be between 0 and 100, got ${e}`);return e},te=e=>{if(!Number.isInteger(e)||e<100||e>599)throw new Error(`Invalid status code: must be an integer between 100-599, got ${e}`);return e};var F=class{constructor(r){m(this,"state");m(this,"config");this.config={failureThreshold:A(r.failureThreshold),resetTimeout:x(r.resetTimeout),halfOpenRequests:A(r.halfOpenRequests),shouldCountAsFailure:r.shouldCountAsFailure},this.state={state:"closed",failureCount:0,halfOpenCount:0}}async canExecute(){if(this.updateStateIfNeeded(),this.state.state==="open")return!1;if(this.state.state==="half-open"){if(this.state.halfOpenCount>=this.config.halfOpenRequests)return!1;this.state={...this.state,halfOpenCount:this.state.halfOpenCount+1}}return!0}async recordSuccess(){switch(this.state.state){case"closed":this.state={...this.state,failureCount:0};break;case"half-open":this.state={state:"closed",failureCount:0,halfOpenCount:0};break;case"open":break}}async recordFailure(r){var o,a,i;if(!((i=(a=(o=this.config).shouldCountAsFailure)==null?void 0:a.call(o,r))!=null?i:!0))return;let n=new Date;switch(this.state.state){case"closed":{let s=this.state.failureCount+1;s>=this.config.failureThreshold?this.state={state:"open",failureCount:s,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)}:this.state={...this.state,failureCount:s,lastFailureTime:n};break}case"half-open":this.state={state:"open",failureCount:this.state.failureCount+1,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)};break;case"open":this.state={...this.state,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)};break}}getState(){return this.updateStateIfNeeded(),{...this.state,canExecute:this.state.state!=="open"}}createOpenError(){let r=this.state.nextAttemptTime?x(Math.max(0,this.state.nextAttemptTime.getTime()-Date.now())):this.config.resetTimeout;return new g(r)}forceState(r){this.state={state:r,failureCount:0,halfOpenCount:0}}reset(){this.state={state:"closed",failureCount:0,halfOpenCount:0}}updateStateIfNeeded(){this.state.state==="open"&&this.state.nextAttemptTime&&new Date>=this.state.nextAttemptTime&&(this.state={...this.state,state:"half-open",halfOpenCount:0})}};var W=(e,r)=>t=>{for(let n of e){let o=n(t);if(o!==null)return o}return r(t)},J=e=>r=>r instanceof T?r:r instanceof Error?new e(r.message,r):typeof r=="string"?new e(r):new e("Unknown error occurred",r);var ne=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.status=="number"||typeof r.statusCode=="number"},oe=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.code=="string"&&r.code.length>0},se=e=>{var r;if(e instanceof Error){if(e.name==="TypeError")return!0;let t=e.message.toLowerCase();if(t.includes("fetch")&&t.includes("failed")||t.includes("network"))return!0}if(oe(e)){let t=((r=e.code)!=null?r:"").toUpperCase();return t==="ECONNRESET"||t==="ECONNREFUSED"||t==="ETIMEDOUT"||t==="ENOTFOUND"||t==="EAI_AGAIN"}return!1},B=class{constructor(r){this.predicate=r}toCode(r){return new L(this.predicate,r)}toError(r){return t=>{if(!this.predicate(t))return null;let n=r(t),o=n.code;class a extends T{constructor(){var u;super(n.message,{cause:(u=n.cause)!=null?u:t,meta:n.meta,status:n.status,retryable:n.retryable});m(this,"code",o)}}return new a}}},L=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){return t=>{if(!this.predicate(t))return null;let n=r(t),o=this.errorCode;class a extends T{constructor(){super(n.message,{cause:n.cause,meta:n.meta,status:n.status,retryable:n.retryable});m(this,"code",o)}}return new a}}},E={when:e=>new B(e),instance:e=>new B(r=>r instanceof e),code:e=>({for:r=>new L(r,e)}),string:(e,r)=>({when:t=>E.when(t).toCode(r).with(n=>({message:n===e?e:`${r}: ${n}`,cause:n}))}),httpStatus:(e,r)=>({for:t=>E.when(t).toCode(r!=null?r:`HTTP_${e}`).with(n=>({message:`HTTP ${e} error`,cause:n}))})};var w={typed:(e=>e instanceof T?e:null),abort:E.when(e=>e instanceof DOMException&&e.name==="AbortError").toCode("ABORTED").with(e=>({message:e.message||"Operation was aborted",cause:e,retryable:!1})),timeout:E.when(e=>e instanceof Error&&e.name==="TimeoutError").toCode("TIMEOUT").with(e=>({message:e.message||"Operation timed out",cause:e})),network:E.when(e=>se(e)).toCode("NETWORK").with(e=>({message:(e instanceof Error,e.message||"Network error"),cause:e})),http:E.when(e=>{var n;if(!ne(e))return!1;let r=e,t=(n=r.status)!=null?n:r.statusCode;return typeof t=="number"&&t>=400}).toCode("HTTP").with(e=>{var n;let r=(n=e.status)!=null?n:e.statusCode,t=typeof r=="number"&&(r>=500||r===429);return{message:e.message||`HTTP ${r!=null?r:"error"} error`,cause:e,status:typeof r=="number"?r:void 0,retryable:t}}),unknown:E.when(e=>e instanceof Error&&!(e instanceof T)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e}))};var ae=[w.typed,w.abort,w.timeout,w.http,w.network,w.unknown];var ie={when:e=>E.when(e),instance:e=>E.instance(e)},K=ae,Fe=w.timeout,Be=w.abort,Le=w.network,Ue=w.http,_e=E.when(e=>e instanceof g).toCode("CIRCUIT_OPEN").with(e=>({message:e.message,cause:e})),ze=E.when(e=>e instanceof v).toCode("VALIDATION").with(e=>({message:e.message,cause:e}));var ue={fixed:e=>({type:"fixed",delay:e}),exponential:(e,r=2,t)=>({type:"exponential",base:e,factor:r,maxDelay:t}),fibonacci:(e,r)=>({type:"fibonacci",base:e,maxDelay:r}),custom:e=>({type:"custom",calculate:e})},q=(e,r,t)=>{switch(e.type){case"fixed":return e.delay;case"exponential":{let n=e.base*e.factor**(Number(r)-1);return e.maxDelay?Math.min(n,e.maxDelay):n}case"fibonacci":{let n=e.base*ce(Number(r));return e.maxDelay?Math.min(n,e.maxDelay):n}case"custom":return e.calculate(r,t);default:return e}},ce=e=>{if(e<=1)return 1;let r=1,t=1;for(let n=2;n<=e;n++){let o=r+t;r=t,t=o}return t};var Y=(e,r)=>new Promise((t,n)=>{if(r!=null&&r.aborted){n(new DOMException("Aborted","AbortError"));return}let o=s=>{r&&s&&r.removeEventListener("abort",s)},a,i=()=>{a&&clearTimeout(a),o(i),n(new DOMException("Aborted","AbortError"))};a=setTimeout(()=>{o(i),t()},e),r==null||r.addEventListener("abort",i,{once:!0})});var le=e=>{var i,s,u;if(e.toError)return e.toError;let r=(i=e.rulesMode)!=null?i:"extend",t=(s=e.rules)!=null?s:[],n=K,o=(u=e.fallback)!=null?u:(c=>J(I)(c)),a=r==="replace"?t:[...t,...n];return W(a,o)},z=class e{constructor(r={}){m(this,"circuitBreaker");m(this,"config");m(this,"lastCircuitState");let{rules:t,rulesMode:n,fallback:o,toError:a,mapError:i,...s}=r,u=le(r),c={...s,errorHandling:{normalizer:u,mapError:i}};this.config=c,c.circuitBreaker&&(this.circuitBreaker=new F(c.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var a,i,s,u,c,b;let n={...this.config,...t};if(this.circuitBreaker){let l=(a=this.lastCircuitState)!=null?a:this.circuitBreaker.getState().state,y=await this.circuitBreaker.canExecute(),p=this.circuitBreaker.getState().state;if(l!==p)try{(s=(i=n.hooks)==null?void 0:i.onCircuitStateChange)==null||s.call(i,l,p)}catch{}if(this.lastCircuitState=p,!y)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let o=await me(r,n);if(this.circuitBreaker){let l=(u=this.lastCircuitState)!=null?u:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let y=this.circuitBreaker.getState().state;if(l!==y)try{(b=(c=n.hooks)==null?void 0:c.onCircuitStateChange)==null||b.call(c,l,y)}catch{}this.lastCircuitState=y}return o}async runOrThrow(r,t={}){let n=await this.run(r,t);if(n.ok)return n.data;throw n.error}async orThrow(r,t={}){let n=await this.run(r,t);if(n.ok)return n.data;throw n.error}async all(r,t={}){var b;let n={...this.config,...t},o=(b=n.concurrency)!=null?b:Number.POSITIVE_INFINITY,a=Number.isFinite(o)?Number(_(o)):Number.POSITIVE_INFINITY,i=new Array(r.length),s=0,u=async()=>{var l;for(;s<r.length&&!((l=n.signal)!=null&&l.aborted);){let y=s++;if(y>=r.length)break;let p=r[y];p&&(i[y]=await this.run(p,n))}},c=Array.from({length:Math.min(a,r.length)},()=>u());await Promise.all(c);for(let l=0;l<r.length;l++)if(!(l in i)){let y=r[l];y&&(i[l]=await this.run(y,n))}return i}async allOrThrow(r,t={}){let n=await this.all(r,t);for(let o of n)if(!o.ok)throw o.error;return n.map(o=>{if(!o.ok)throw o.error;return o.data})}partitionAll(r){let t=[],n=[],o=[],a=[],i=[];for(let s of r){if(s.type==="success"){t.push(s);continue}switch(n.push(s),s.type){case"failure":o.push(s);break;case"aborted":a.push(s);break;case"timeout":i.push(s);break}}return{ok:t,errors:n,failure:o,aborted:a,timeout:i}}getCircuitBreakerState(){var r;return(r=this.circuitBreaker)==null?void 0:r.getState()}resetCircuitBreaker(){var r;(r=this.circuitBreaker)==null||r.reset()}getConfig(){return{...this.config}}withConfig(r){var u,c;let{errorHandling:t,...n}=this.config,{errorHandling:o,...a}=r,i=(u=o==null?void 0:o.normalizer)!=null?u:t.normalizer,s=(c=o==null?void 0:o.mapError)!=null?c:t.mapError;return new e({...n,...a,toError:i,mapError:s})}withErrorType(r={}){return new e(r)}};function $(e={}){let r=new z(e);return{run:(t,n)=>r.run(t,n),orThrow:(t,n)=>r.orThrow(t,n),runOrThrow:(t,n)=>r.runOrThrow(t,n),all:(t,n)=>r.all(t,n),allOrThrow:(t,n)=>r.allOrThrow(t,n),partitionAll:t=>r.partitionAll(t),withConfig:t=>r.withConfig(t)}}async function me(e,r){let{signal:t,ignoreAbort:n=!0,timeout:o,retry:a,errorHandling:i,hooks:s,logger:u}=r,c=(k,...d)=>{try{k==null||k(...d)}catch{}},b,l=0,y=0,p=[],R=Date.now(),{signal:C,cleanup:G}=de(t);try{if(C.aborted){c(s==null?void 0:s.onAbort,C);let d=i.normalizer(new DOMException("Aborted","AbortError")),f=i.mapError?i.mapError(d):d;return{type:"aborted",ok:!1,data:null,error:f,metrics:{totalAttempts:l,totalRetries:y,totalDuration:Date.now()-R,lastError:f,retryHistory:p}}}let k=async d=>{var f,P,O;l=d;try{let M=e({signal:C}),S=o?await ye(M,o,C):await M;return c(s==null?void 0:s.onSuccess,S),c(u==null?void 0:u.info,`Task succeeded on attempt ${d}`),S}catch(M){let S=i.normalizer(M),h=i.mapError?i.mapError(S):S;b=h,h.code==="ABORTED"&&c(s==null?void 0:s.onAbort,C),n&&h.code==="ABORTED"||(c(s==null?void 0:s.onError,h),c(u==null?void 0:u.error,`Task failed on attempt ${d}`,h));let Q=A((f=a==null?void 0:a.maxRetries)!=null?f:0);if(d<=Number(Q)){let H=a==null?void 0:a.shouldRetry,X={totalAttempts:Number(l),elapsedTime:Date.now()-R,startTime:new Date(R),lastDelay:(P=p[p.length-1])!=null&&P.delay?Number((O=p[p.length-1])==null?void 0:O.delay):void 0};if(!H||H(d,h,X)){let Z=a?q(a.strategy,d,h):0,U=pe(Z,a==null?void 0:a.jitter),V=x(U);return p.push({attempt:d,error:h,delay:V,timestamp:new Date}),c(s==null?void 0:s.onRetry,d,h,U),c(u==null?void 0:u.info,`Retrying in ${U}ms (attempt ${d+1})`),await Y(V,C),k(d+1)}}throw h}};try{let d=await k(1);y=l>0?Number(l)-1:0;let f={totalAttempts:l,totalRetries:y,totalDuration:Date.now()-R,retryHistory:p};return c(s==null?void 0:s.onFinally,f),{type:"success",ok:!0,data:d,error:null,metrics:f}}catch(d){let f=b!=null?b:i.normalizer(d),P=f.code==="TIMEOUT"?"timeout":f.code==="ABORTED"?"aborted":"failure";y=l>0?Number(l)-1:0;let O={totalAttempts:l,totalRetries:y,totalDuration:Date.now()-R,lastError:f,retryHistory:p};return c(s==null?void 0:s.onFinally,O),{type:P,ok:!1,data:null,error:f,metrics:O}}}finally{G()}}function de(e){let r=new AbortController;if(!e)return{signal:r.signal,cleanup:()=>{}};let t=()=>r.abort();return e.aborted?(t(),{signal:r.signal,cleanup:()=>{}}):(e.addEventListener("abort",t,{once:!0}),{signal:r.signal,cleanup:()=>e.removeEventListener("abort",t)})}async function ye(e,r,t){return new Promise((n,o)=>{if(t!=null&&t.aborted){o(new DOMException("Aborted","AbortError"));return}let a=!1,i=setTimeout(()=>{a=!0,u(),o(new D(x(r)))},r),s=()=>{a||(a=!0,u(),o(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(i),t==null||t.removeEventListener("abort",s)};t==null||t.addEventListener("abort",s,{once:!0}),e.then(c=>{a||(a=!0,u(),n(c))},c=>{a||(a=!0,u(),o(c))})})}function pe(e,r){if(!r||r.type==="none"||e<=0)return e;switch(r.type){case"full":{let t=Number(r.ratio)/100,n=Math.max(0,Number(e)*(1-t)),o=Number(e);return n+Math.random()*(o-n)}case"equal":{let t=Number(r.ratio)/100,n=Number(e)*t/2;return Number(e)-n+Math.random()*n}case"custom":return r.calculate(e);default:return r}}var N=$(),fe=N.run,Te=N.runOrThrow,Ee=N.orThrow,we=N.all,be=N.allOrThrow;export{ue as RetryStrategies,we as all,be as allOrThrow,_ as asConcurrencyLimit,x as asMilliseconds,re as asPercentage,A as asRetryCount,te as asStatusCode,ie as errorRule,Ee as orThrow,fe as run,Te as runOrThrow,$ as tryo};
|
|
1
|
+
var ie=Object.defineProperty;var ue=(e,r,t)=>r in e?ie(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var y=(e,r,t)=>ue(e,typeof r!="symbol"?r+"":r,t);var b=class extends Error{constructor(t,n){var o,a;super(t);y(this,"cause");y(this,"meta");y(this,"status");y(this,"raw");y(this,"path");y(this,"timestamp");y(this,"retryable");this.timestamp=Date.now(),this.retryable=(o=n.retryable)!=null?o:!0,this.name=this.constructor.name,this.cause=n.cause,this.meta=(a=n.meta)!=null?a:{},this.status=n.status,this.raw=n.raw,this.path=n.path,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}is(t){return this.code===t}withMeta(t){return Object.assign(this,{meta:t})}withStatus(t){return Object.assign(this,{status:t})}withCause(t){return Object.assign(this,{cause:t})}withPath(t){return this.path=t,this}withRaw(t){return Object.assign(this,{raw:t})}withRetryable(t){return this.retryable=t,this}toJSON(){return{name:this.name,code:this.code,message:this.message,timestamp:this.timestamp,retryable:this.retryable,cause:this.cause,raw:this.raw,path:this.path,stack:this.stack}}},D=class extends b{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0,raw:n});y(this,"code","TIMEOUT")}};var I=class extends b{constructor(t,n){super(`Circuit breaker is open, reset after ${t}ms`,{cause:n,retryable:!1,raw:n});y(this,"code","CIRCUIT_OPEN")}};var P=class extends b{constructor(t,n){super(t,{cause:n,raw:n});y(this,"code","UNKNOWN")}};var w=e=>{if(e<0||!Number.isFinite(e))throw new Error(`Invalid milliseconds: must be a non-negative finite number, got ${e}`);return e},R=e=>{if(e<0||!Number.isInteger(e))throw new Error(`Invalid retry count: must be a non-negative integer, got ${e}`);return e},L=e=>{if(e<1||!Number.isInteger(e))throw new Error(`Invalid concurrency limit: must be a positive integer, got ${e}`);return e},Y=e=>{if(e<0||e>100||!Number.isFinite(e))throw new Error(`Invalid percentage: must be between 0 and 100, got ${e}`);return e};var F=class{constructor(r){y(this,"state");y(this,"config");this.config={failureThreshold:R(r.failureThreshold),resetTimeout:w(r.resetTimeout),halfOpenRequests:R(r.halfOpenRequests),shouldCountAsFailure:r.shouldCountAsFailure},this.state={state:"closed",failureCount:0,halfOpenCount:0}}async canExecute(){if(this.updateStateIfNeeded(),this.state.state==="open")return!1;if(this.state.state==="half-open"){if(this.state.halfOpenCount>=this.config.halfOpenRequests)return!1;this.state={...this.state,halfOpenCount:this.state.halfOpenCount+1}}return!0}async recordSuccess(){switch(this.state.state){case"closed":this.state={...this.state,failureCount:0};break;case"half-open":this.state={state:"closed",failureCount:0,halfOpenCount:0};break;case"open":break}}async recordFailure(r){var o,a,i;if(!((i=(a=(o=this.config).shouldCountAsFailure)==null?void 0:a.call(o,r))!=null?i:!0))return;let n=new Date;switch(this.state.state){case"closed":{let s=this.state.failureCount+1;s>=this.config.failureThreshold?this.state={state:"open",failureCount:s,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)}:this.state={...this.state,failureCount:s,lastFailureTime:n};break}case"half-open":this.state={state:"open",failureCount:this.state.failureCount+1,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)};break;case"open":this.state={...this.state,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)};break}}getState(){return this.updateStateIfNeeded(),{...this.state,canExecute:this.state.state!=="open"}}createOpenError(){let r=this.state.nextAttemptTime?w(Math.max(0,this.state.nextAttemptTime.getTime()-Date.now())):this.config.resetTimeout;return new I(r)}forceState(r){this.state={state:r,failureCount:0,halfOpenCount:0}}reset(){this.state={state:"closed",failureCount:0,halfOpenCount:0}}updateStateIfNeeded(){this.state.state==="open"&&this.state.nextAttemptTime&&new Date>=this.state.nextAttemptTime&&(this.state={...this.state,state:"half-open",halfOpenCount:0})}};var G=(e,r)=>t=>{for(let n of e){let o=n(t);if(o!==null)return o}return r(t)},Q=e=>r=>r instanceof b?r:r instanceof Error?new e(r.message,r):typeof r=="string"?new e(r):new e("Unknown error occurred",r);var le=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.status=="number"||typeof r.statusCode=="number"},ce=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.code=="string"&&r.code.length>0},ye=e=>{var r;if(e instanceof Error){let t=e.message.toLowerCase();if(e.name==="TypeError"&&(t.includes("fetch")&&t.includes("failed")||t.includes("network"))||t.includes("fetch")&&t.includes("failed")||t.includes("network"))return!0}if(ce(e)){let t=((r=e.code)!=null?r:"").toUpperCase();return t==="ECONNRESET"||t==="ECONNREFUSED"||t==="ETIMEDOUT"||t==="ENOTFOUND"||t==="EAI_AGAIN"}return!1},_=class{constructor(r){this.predicate=r}toCode(r){return new H(this.predicate,r)}toError(r){return t=>{if(!this.predicate(t))return null;let n=r(t),o=n.code;class a extends b{constructor(){var l,u,d;super(n.message,{cause:(l=n.cause)!=null?l:t,meta:(u=n.meta)!=null?u:{},status:n.status,retryable:n.retryable,raw:(d=n.raw)!=null?d:t,path:n.path});y(this,"code",o)}}return new a}}},H=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){return t=>{if(!this.predicate(t))return null;let n=r(t),o=this.errorCode;class a extends b{constructor(){var u;let l=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{cause:n.cause,meta:(u=n.meta)!=null?u:{},status:n.status,retryable:n.retryable,raw:l,path:n.path});y(this,"code",o)}}return new a}}},h={when:e=>new _(e),instance:e=>new _(r=>r instanceof e)},x={typed:(e=>e instanceof b?e:null),abort:h.when(e=>e instanceof Error&&e.name==="AbortError").toCode("ABORTED").with(e=>({message:e.message||"Operation was aborted",cause:e,retryable:!1,raw:e})),timeout:h.when(e=>e instanceof Error&&e.name==="TimeoutError").toCode("TIMEOUT").with(e=>({message:e.message||"Operation timed out",cause:e,raw:e})),network:h.when(e=>ye(e)).toCode("NETWORK").with(e=>({message:(e instanceof Error,e.message||"Network error"),cause:e,raw:e})),http:h.when(e=>{var n;if(!le(e))return!1;let r=e,t=(n=r.status)!=null?n:r.statusCode;return typeof t=="number"&&t>=400}).toCode("HTTP").with(e=>{var n;let r=(n=e.status)!=null?n:e.statusCode,t=typeof r=="number"&&(r>=500||r===429);return{message:e.message||`HTTP ${r!=null?r:"error"} error`,cause:e,status:typeof r=="number"?r:void 0,retryable:t,raw:e}}),unknown:h.when(e=>e instanceof Error&&!(e instanceof b)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e,raw:e}))};var de=[x.typed,x.abort,x.timeout,x.http,x.network,x.unknown];var pe={when:e=>h.when(e),instance:e=>h.instance(e)},X=de;var me={fixed:e=>({type:"fixed",delay:e}),exponential:(e,r=2,t)=>({type:"exponential",base:e,factor:r,maxDelay:t}),fibonacci:(e,r)=>({type:"fibonacci",base:e,maxDelay:r}),custom:e=>({type:"custom",calculate:e})},Z=(e,r,t)=>{switch(e.type){case"fixed":return e.delay;case"exponential":{let n=e.base*e.factor**(Number(r)-1);return e.maxDelay!==void 0?Math.min(n,e.maxDelay):n}case"fibonacci":{let n=e.base*fe(Number(r));return e.maxDelay!==void 0?Math.min(n,e.maxDelay):n}case"custom":return e.calculate(r,t);default:return e}},fe=e=>{if(e<=1)return 1;let r=1,t=1;for(let n=2;n<=e;n++){let o=r+t;r=t,t=o}return t},j=e=>{switch(e.type){case"fixed":{if(e.delay<0)throw new Error("Fixed delay must be non-negative");break}case"exponential":if(e.base<=0)throw new Error("Exponential base delay must be positive");if(e.factor<=1)throw new Error("Exponential factor must be greater than 1");if(e.maxDelay!==void 0&&e.maxDelay<=0)throw new Error("Exponential max delay must be positive");break;case"fibonacci":if(e.base<=0)throw new Error("Fibonacci base delay must be positive");if(e.maxDelay!==void 0&&e.maxDelay<=0)throw new Error("Fibonacci max delay must be positive");break;case"custom":break;default:{let r=e;throw new Error(`Unknown strategy type: ${r}`)}}};var ee=(e,r)=>new Promise((t,n)=>{if(r!=null&&r.aborted){n(new DOMException("Aborted","AbortError"));return}let o=s=>{r&&s&&r.removeEventListener("abort",s)},a,i=()=>{a&&clearTimeout(a),o(i),n(new DOMException("Aborted","AbortError"))};a=setTimeout(()=>{o(i),t()},e),r==null||r.addEventListener("abort",i,{once:!0})}),re=(e,r,t,n)=>new Promise((o,a)=>{if(t!=null&&t.aborted){a(new DOMException("Aborted","AbortError"));return}let i=!1,s=setTimeout(()=>{var d;i=!0,u(),a((d=n==null?void 0:n())!=null?d:new Error(`Operation timed out after ${r}ms`))},r),l=()=>{i||(i=!0,u(),a(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(s),t==null||t.removeEventListener("abort",l)};t==null||t.addEventListener("abort",l,{once:!0}),e.then(d=>{i||(i=!0,u(),o(d))},d=>{i||(i=!0,u(),a(d))})});var Ee=e=>{var i,s,l;if(e.toError)return e.toError;let r=(i=e.rulesMode)!=null?i:"extend",t=(s=e.rules)!=null?s:[],n=X,o=(l=e.fallback)!=null?l:(u=>Q(P)(u)),a=r==="replace"?t:[...t,...n];return G(a,o)},Te=e=>{if(e)switch(e.type){case"none":return;case"full":case"equal":Y(e.ratio);return;case"custom":return;default:{let r=e;throw new Error(`Unsupported jitter configuration: ${r}`)}}},$=e=>{let r=e;return r.timeout!==void 0&&(r={...r,timeout:Number(w(r.timeout))}),r.concurrency!==void 0&&(r={...r,concurrency:Number.isFinite(r.concurrency)?Number(L(r.concurrency)):r.concurrency}),r.retry&&(j(r.retry.strategy),Te(r.retry.jitter),r={...r,retry:{...r.retry,maxRetries:Number(R(r.retry.maxRetries))}}),r},q=class e{constructor(r={}){y(this,"circuitBreaker");y(this,"config");y(this,"lastCircuitState");let{rules:t,rulesMode:n,fallback:o,toError:a,mapError:i,...s}=r,l=Ee(r),u={...s,errorHandling:{normalizer:l,mapError:i}};this.config=$(u),u.circuitBreaker&&(this.circuitBreaker=new F(u.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var a,i,s,l,u,d;let n=$({...this.config,...t});if(this.circuitBreaker){let c=(a=this.lastCircuitState)!=null?a:this.circuitBreaker.getState().state,m=await this.circuitBreaker.canExecute(),E=this.circuitBreaker.getState().state;if(c!==E)try{(s=(i=n.hooks)==null?void 0:i.onCircuitStateChange)==null||s.call(i,c,E)}catch{}if(this.lastCircuitState=E,!m)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let o=await be(r,n);if(this.circuitBreaker){let c=(l=this.lastCircuitState)!=null?l:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let m=this.circuitBreaker.getState().state;if(c!==m)try{(d=(u=n.hooks)==null?void 0:u.onCircuitStateChange)==null||d.call(u,c,m)}catch{}this.lastCircuitState=m}return o}async runOrThrow(r,t={}){return this.orThrow(r,t)}async orThrow(r,t={}){let n=await this.run(r,t);if(n.ok)return n.data;throw n.error}async all(r,t={}){var d;let n=$({...this.config,...t}),o=(d=n.concurrency)!=null?d:Number.POSITIVE_INFINITY,a=Number.isFinite(o)?Number(L(o)):Number.POSITIVE_INFINITY;if(a===Number.POSITIVE_INFINITY)return Promise.all(r.map(c=>this.run(c,n)));let i=new Array(r.length),s=0,l=async()=>{var c;for(;s<r.length&&!((c=n.signal)!=null&&c.aborted);){let m=s++;if(m>=r.length)break;let E=r[m];E&&(i[m]=await this.run(E,n))}},u=Array.from({length:Math.min(a,r.length)},()=>l());await Promise.all(u);for(let c=0;c<r.length;c++)if(!(c in i)){let m=r[c];m&&(i[c]=await this.run(m,n))}return i}async allOrThrow(r,t={}){return(await this.all(r,t)).map(o=>{if(!o.ok)throw o.error;return o.data})}partitionAll(r){let t=[],n=[],o=[],a=[],i=[];for(let s of r){if(s.type==="success"){t.push(s);continue}switch(n.push(s),s.type){case"failure":o.push(s);break;case"aborted":a.push(s);break;case"timeout":i.push(s);break}}return{ok:t,errors:n,failure:o,aborted:a,timeout:i}}getCircuitBreakerState(){var r;return(r=this.circuitBreaker)==null?void 0:r.getState()}resetCircuitBreaker(){var r;(r=this.circuitBreaker)==null||r.reset()}getConfig(){return{...this.config}}withConfig(r){var l,u;let{errorHandling:t,...n}=this.config,{errorHandling:o,...a}=r,i=(l=o==null?void 0:o.normalizer)!=null?l:t.normalizer,s=(u=o==null?void 0:o.mapError)!=null?u:t.mapError;return new e({...n,...a,toError:i,mapError:s})}};function B(e={}){let r=new q(e);return{run:(t,n)=>r.run(t,n),orThrow:(t,n)=>r.orThrow(t,n),runOrThrow:(t,n)=>r.runOrThrow(t,n),all:(t,n)=>r.all(t,n),allOrThrow:(t,n)=>r.allOrThrow(t,n),partitionAll:t=>r.partitionAll(t),withConfig:t=>r.withConfig(t)}}async function be(e,r){let{signal:t,ignoreAbort:n=!0,timeout:o,retry:a,errorHandling:i,hooks:s,logger:l}=r,u=(C,...p)=>{try{C==null||C(...p)}catch{}},d,c=0,m=0,E=[],k=Date.now(),{signal:A,cleanup:ne}=te(t),N=C=>({totalAttempts:c,totalRetries:c>0?Number(c)-1:0,totalDuration:Date.now()-k,lastError:C,retryHistory:E});try{if(A.aborted){u(s==null?void 0:s.onAbort,A);let p=i.normalizer(new DOMException("Aborted","AbortError")),T=i.mapError?i.mapError(p):p;return{type:"aborted",ok:!1,data:null,error:T,metrics:{totalAttempts:c,totalRetries:m,totalDuration:Date.now()-k,lastError:T,retryHistory:E}}}let C=async()=>{var M,g,V;let p=1,T=R((M=a==null?void 0:a.maxRetries)!=null?M:0);for(;;){c=p;let W=new AbortController,{signal:U,cleanup:oe}=te(A,W.signal);try{let v=Promise.resolve(e({signal:U})),O=o?await re(v,o,U,()=>(W.abort(),new D(w(o)))):await v;return u(s==null?void 0:s.onSuccess,O,N()),u(l==null?void 0:l.info,`Task succeeded on attempt ${p}`),O}catch(v){let O=i.normalizer(v),f=i.mapError?i.mapError(O):O;if(d=f,f.code==="ABORTED"&&u(s==null?void 0:s.onAbort,U),n&&f.code==="ABORTED"||(u(s==null?void 0:s.onError,f,N(f)),u(l==null?void 0:l.error,`Task failed on attempt ${p}`,f)),f.code==="ABORTED"||f.retryable===!1)throw f;if(p<=Number(T)){let K=a==null?void 0:a.shouldRetry,se={totalAttempts:Number(c),elapsedTime:Date.now()-k,startTime:new Date(k),lastDelay:(g=E[E.length-1])!=null&&g.delay?Number((V=E[E.length-1])==null?void 0:V.delay):void 0};if(!K||K(p,f,se)){let ae=a?Z(a.strategy,p,f):0,z=we(ae,a==null?void 0:a.jitter),J=w(z);E.push({attempt:p,error:f,delay:J,timestamp:new Date}),u(s==null?void 0:s.onRetry,p,f,z),u(l==null?void 0:l.info,`Retrying in ${z}ms (attempt ${p+1})`),p+=1,await ee(J,A);continue}}throw f}finally{oe()}}};try{let p=await C(),T=N();return m=T.totalRetries,u(s==null?void 0:s.onFinally,T),{type:"success",ok:!0,data:p,error:null,metrics:T}}catch(p){let T=d!=null?d:i.normalizer(p),M=T.code==="TIMEOUT"?"timeout":T.code==="ABORTED"?"aborted":"failure",g=N(T);return m=g.totalRetries,u(s==null?void 0:s.onFinally,g),{type:M,ok:!1,data:null,error:T,metrics:g}}}finally{ne()}}function te(...e){let r=new AbortController,t=e.filter(o=>o!==void 0);if(t.length===0)return{signal:r.signal,cleanup:()=>{}};if(t.some(o=>o.aborted))return r.abort(),{signal:r.signal,cleanup:()=>{}};let n=[];for(let o of t){let a=()=>r.abort();o.addEventListener("abort",a,{once:!0}),n.push({signal:o,abort:a})}return{signal:r.signal,cleanup:()=>{for(let o of n)o.signal.removeEventListener("abort",o.abort)}}}function we(e,r){if(!r||r.type==="none"||e<=0)return e;switch(r.type){case"full":{let t=Number(r.ratio)/100,n=Math.max(0,Number(e)*(1-t)),o=Number(e);return n+Math.random()*(o-n)}case"equal":{let t=Number(r.ratio)/100,n=Number(e)*t/2;return Number(e)-n+Math.random()*n}case"custom":return r.calculate(e);default:return r}}var S=B(),he=S.run,Ce=S.runOrThrow,Re=S.orThrow,xe=S.all,ge=S.allOrThrow;export{me as RetryStrategies,b as TypedError,xe as all,ge as allOrThrow,w as asMilliseconds,R as asRetryCount,B as default,pe as errorRule,Re as orThrow,he as run,Ce as runOrThrow,B as tryo};
|
package/package.json
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tryo",
|
|
3
|
-
"version": "0.13.
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
"version": "0.13.3",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "sebasxsala",
|
|
6
|
+
"url": "https://github.com/sebasxsala"
|
|
7
|
+
},
|
|
7
8
|
"main": "./dist/index.cjs",
|
|
8
9
|
"module": "./dist/index.js",
|
|
9
|
-
"
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@biomejs/biome": "2.3.13",
|
|
12
|
+
"@types/bun": "latest",
|
|
13
|
+
"husky": "^9.1.7",
|
|
14
|
+
"lint-staged": "^16.2.7",
|
|
15
|
+
"mint": "latest",
|
|
16
|
+
"tsup": "^8.5.1",
|
|
17
|
+
"typescript": "^5.0.0"
|
|
18
|
+
},
|
|
10
19
|
"exports": {
|
|
11
20
|
".": {
|
|
12
21
|
"types": "./dist/index.d.ts",
|
|
@@ -14,11 +23,20 @@
|
|
|
14
23
|
"require": "./dist/index.cjs"
|
|
15
24
|
}
|
|
16
25
|
},
|
|
26
|
+
"bugs": "https://github.com/sebasxsala/tryo/issues",
|
|
27
|
+
"description": "Run sync/async functions and return a typed Result instead of throwing.",
|
|
17
28
|
"files": [
|
|
18
29
|
"dist",
|
|
19
30
|
"README.md",
|
|
20
31
|
"LICENSE"
|
|
21
32
|
],
|
|
33
|
+
"homepage": "https://tryo-docs.pages.dev/",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"lint-staged": {
|
|
36
|
+
"*.{ts,tsx,js,jsx,json}": [
|
|
37
|
+
"biome check --write"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
22
40
|
"scripts": {
|
|
23
41
|
"build": "tsup",
|
|
24
42
|
"typecheck": "tsc --noEmit",
|
|
@@ -33,27 +51,9 @@
|
|
|
33
51
|
"lint:fix": "biome lint --fix",
|
|
34
52
|
"check": "biome check",
|
|
35
53
|
"check:fix": "biome check --write",
|
|
36
|
-
"prepare": "
|
|
37
|
-
},
|
|
38
|
-
"lint-staged": {
|
|
39
|
-
"*.{ts,tsx,js,jsx,json}": [
|
|
40
|
-
"biome check --write"
|
|
41
|
-
]
|
|
54
|
+
"prepare": ""
|
|
42
55
|
},
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"husky": "^9.1.7",
|
|
47
|
-
"lint-staged": "^16.2.7",
|
|
48
|
-
"mint": "latest",
|
|
49
|
-
"tsup": "^8.5.1",
|
|
50
|
-
"typescript": "^5.0.0"
|
|
51
|
-
},
|
|
52
|
-
"license": "MIT",
|
|
53
|
-
"homepage": "https://tryo-docs.pages.dev/",
|
|
54
|
-
"bugs": "https://github.com/sebasxsala/tryo/issues",
|
|
55
|
-
"author": {
|
|
56
|
-
"name": "sebasxsala",
|
|
57
|
-
"url": "https://github.com/sebasxsala"
|
|
58
|
-
}
|
|
56
|
+
"sideEffects": false,
|
|
57
|
+
"type": "module",
|
|
58
|
+
"types": "./dist/index.d.ts"
|
|
59
59
|
}
|