tryo 0.13.0 → 0.13.4
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 +171 -106
- package/dist/index.d.ts +171 -106
- package/dist/index.js +1 -1
- package/package.json +27 -28
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 Te=Object.getOwnPropertyNames;var Ee=Object.prototype.hasOwnProperty;var we=(e,r,t)=>r in e?I(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var be=(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 Te(r))!Ee.call(e,o)&&o!==t&&I(e,o,{get:()=>r[o],enumerable:!(n=fe(r,o))||n.enumerable});return e};var Re=e=>he(I({},"__esModule",{value:!0}),e);var d=(e,r,t)=>we(e,typeof r!="symbol"?r+"":r,t);var ve={};be(ve,{RetryStrategies:()=>ee,TypedError:()=>E,all:()=>le,allOrThrow:()=>ce,asMilliseconds:()=>b,asRetryCount:()=>h,default:()=>S,errorRule:()=>Z,orThrow:()=>ue,run:()=>ae,runOrThrow:()=>ie,tryo:()=>S});module.exports=Re(ve);var E=class extends Error{constructor(t,n){var o,a;super(t);d(this,"cause");d(this,"meta");d(this,"status");d(this,"raw");d(this,"path");d(this,"timestamp");d(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 E{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0,raw:n});d(this,"code","TIMEOUT")}};var F=class extends E{constructor(t,n){super(`Circuit breaker is open, reset after ${t}ms`,{cause:n,retryable:!1,raw:n});d(this,"code","CIRCUIT_OPEN")}};var B=class extends E{constructor(t,n){super(t,{cause:n,raw:n});d(this,"code","UNKNOWN")}};var b=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},q=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 _=class{constructor(r){d(this,"state");d(this,"config");this.config={failureThreshold:h(r.failureThreshold),resetTimeout:b(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?b(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 E?r:r instanceof Error?new e(r.message,r):typeof r=="string"?new e(r):new e("Unknown error occurred",r);var Ce=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 H(this.predicate,r)}toError(r){return t=>{var s;if(!this.predicate(t))return null;let n=r(t),o=n.code,a=(s=n.meta)!=null?s:{};class i extends E{constructor(){var l;let y=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{cause:(l=n.cause)!=null?l:t,meta:a,status:n.status,retryable:n.retryable,raw:y,path:n.path});d(this,"code",o)}}return new i}}},H=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){return t=>{var s;if(!this.predicate(t))return null;let n=r(t),o=(s=n.meta)!=null?s:{},a=this.errorCode;class i extends E{constructor(){let l=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{cause:n.cause,meta:o,status:n.status,retryable:n.retryable,raw:l,path:n.path});d(this,"code",a)}}return new i}}},R={when:e=>new U(e),instance:e=>new U(r=>r instanceof e)},x={typed:(e=>e instanceof E?e:null),abort:R.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:R.when(e=>e instanceof Error&&e.name==="TimeoutError").toCode("TIMEOUT").with(e=>({message:e.message||"Operation timed out",cause:e,raw:e})),network:R.when(e=>ge(e)).toCode("NETWORK").with(e=>({message:(e instanceof Error,e.message||"Network error"),cause:e,raw:e})),http:R.when(e=>{var n;if(!Ce(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:R.when(e=>e instanceof Error&&!(e instanceof E)).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=>R.when(e),instance:e=>R.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 y;i=!0,u(),a((y=n==null?void 0:n())!=null?y:new Error(`Operation timed out after ${r}ms`))},r),c=()=>{i||(i=!0,u(),a(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(s),t==null||t.removeEventListener("abort",c)};t==null||t.addEventListener("abort",c,{once:!0}),e.then(y=>{i||(i=!0,u(),o(y))},y=>{i||(i=!0,u(),a(y))})});var Oe=e=>{var i,s,c;if(e.toError)return e.toError;let r=(i=e.rulesMode)!=null?i:"extend",t=(s=e.rules)!=null?s:[],n=j,o=(c=e.fallback)!=null?c:(u=>X(B)(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}`)}}},$=e=>{let r=e;return r.timeout!==void 0&&(r={...r,timeout:Number(b(r.timeout))}),r.concurrency!==void 0&&(r={...r,concurrency:Number.isFinite(r.concurrency)?Number(q(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={}){d(this,"circuitBreaker");d(this,"config");d(this,"lastCircuitState");let{rules:t,rulesMode:n,fallback:o,toError:a,mapError:i,...s}=r,c=Oe(r),u={...s,errorHandling:{normalizer:c,mapError:i}};this.config=$(u),u.circuitBreaker&&(this.circuitBreaker=new _(u.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var a,i,s,c,u,y;let n=$({...this.config,...t});if(this.circuitBreaker){let l=(a=this.lastCircuitState)!=null?a:this.circuitBreaker.getState().state,m=await this.circuitBreaker.canExecute(),T=this.circuitBreaker.getState().state;if(l!==T)try{(s=(i=n.hooks)==null?void 0:i.onCircuitStateChange)==null||s.call(i,l,T)}catch{}if(this.lastCircuitState=T,!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 l=(c=this.lastCircuitState)!=null?c:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let m=this.circuitBreaker.getState().state;if(l!==m)try{(y=(u=n.hooks)==null?void 0:u.onCircuitStateChange)==null||y.call(u,l,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 y;let n=$({...this.config,...t}),o=(y=n.concurrency)!=null?y:Number.POSITIVE_INFINITY,a=Number.isFinite(o)?Number(q(o)):Number.POSITIVE_INFINITY;if(a===Number.POSITIVE_INFINITY)return Promise.all(r.map(l=>this.run(l,n)));let i=new Array(r.length),s=0,c=async()=>{var l;for(;s<r.length&&!((l=n.signal)!=null&&l.aborted);){let m=s++;if(m>=r.length)break;let T=r[m];T&&(i[m]=await this.run(T,n))}},u=Array.from({length:Math.min(a,r.length)},()=>c());await Promise.all(u);for(let l=0;l<r.length;l++)if(!(l in i)){let m=r[l];m&&(i[l]=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 c,u;let{errorHandling:t,...n}=this.config,{errorHandling:o,...a}=r,i=(c=o==null?void 0:o.normalizer)!=null?c: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:c}=r,u=(C,...p)=>{try{C==null||C(...p)}catch{}},y,l=0,m=0,T=[],k=Date.now(),{signal:A,cleanup:de}=se(t),M=C=>({totalAttempts:l,totalRetries:l>0?Number(l)-1:0,totalDuration:Date.now()-k,lastError:C,retryHistory:T});try{if(A.aborted){u(s==null?void 0:s.onAbort,A);let p=i.normalizer(new DOMException("Aborted","AbortError")),w=i.mapError?i.mapError(p):p;return{type:"aborted",ok:!1,data:null,error:w,metrics:{totalAttempts:l,totalRetries:m,totalDuration:Date.now()-k,lastError:w,retryHistory:T}}}let C=async()=>{var v,g,W;let p=1,w=h((v=a==null?void 0:a.maxRetries)!=null?v:0);for(;;){l=p;let K=new AbortController,{signal:L,cleanup:ye}=se(A,K.signal);try{let D=Promise.resolve(e({signal:L})),O=o?await oe(D,o,L,()=>(K.abort(),new P(b(o)))):await D;return u(s==null?void 0:s.onSuccess,O,M()),u(c==null?void 0:c.info,`Task succeeded on attempt ${p}`),O}catch(D){let O=i.normalizer(D),f=i.mapError?i.mapError(O):O;if(y=f,f.code==="ABORTED"&&u(s==null?void 0:s.onAbort,L),n&&f.code==="ABORTED"||(u(s==null?void 0:s.onError,f,M(f)),u(c==null?void 0:c.error,`Task failed on attempt ${p}`,f)),f.code==="ABORTED"||f.retryable===!1)throw f;if(p<=Number(w)){let J=a==null?void 0:a.shouldRetry,pe={totalAttempts:Number(l),elapsedTime:Date.now()-k,startTime:new Date(k),lastDelay:(g=T[T.length-1])!=null&&g.delay?Number((W=T[T.length-1])==null?void 0:W.delay):void 0};if(!J||J(p,f,pe)){let me=a?re(a.strategy,p,f):0,z=Me(me,a==null?void 0:a.jitter),Y=b(z);T.push({attempt:p,error:f,delay:Y,timestamp:new Date}),u(s==null?void 0:s.onRetry,p,f,z),u(c==null?void 0:c.info,`Retrying in ${z}ms (attempt ${p+1})`),p+=1,await ne(Y,A);continue}}throw f}finally{ye()}}};try{let p=await C(),w=M();return m=w.totalRetries,u(s==null?void 0:s.onFinally,w),{type:"success",ok:!0,data:p,error:null,metrics:w}}catch(p){let w=y!=null?y:i.normalizer(p),v=w.code==="TIMEOUT"?"timeout":w.code==="ABORTED"?"aborted":"failure",g=M(w);return m=g.totalRetries,u(s==null?void 0:s.onFinally,g),{type:v,ok:!1,data:null,error:w,metrics:g}}}finally{de()}}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,58 @@ 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>;
|
|
297
|
+
type RuleLike = (error: unknown) => AnyTypedError | null;
|
|
295
298
|
type NonNull<T> = T extends null ? never : T;
|
|
296
299
|
type RuleReturn<R> = R extends (err: unknown) => infer Out ? NonNull<Out> : never;
|
|
297
|
-
type InferErrorFromRules<TRules extends readonly
|
|
298
|
-
type TryoOptions<E extends
|
|
300
|
+
type InferErrorFromRules<TRules extends readonly RuleLike[]> = TRules extends readonly [] ? TypedError : RuleReturn<TRules[number]> | UnknownError;
|
|
301
|
+
type TryoOptions<E extends AnyTypedError = AnyTypedError> = Omit<Partial<TryoConfig<E>>, 'errorHandling' | 'signal'> & {
|
|
299
302
|
rules?: Array<ErrorRule<E>>;
|
|
300
303
|
rulesMode?: RulesMode;
|
|
301
304
|
fallback?: (err: unknown) => E;
|
|
302
305
|
toError?: (err: unknown) => E;
|
|
303
306
|
mapError?: (error: E) => E;
|
|
304
307
|
};
|
|
305
|
-
|
|
308
|
+
type ExtractCode<R> = R extends (...args: unknown[]) => unknown ? Exclude<ReturnType<R>, null> extends {
|
|
309
|
+
readonly code: infer C extends string;
|
|
310
|
+
} ? string extends C ? never : C : never : never;
|
|
311
|
+
type CheckUniqueCodes<T extends readonly unknown[], Seen = never> = T extends readonly [infer Head, ...infer Tail] ? ExtractCode<Head> extends infer C ? [C] extends [never] ? CheckUniqueCodes<Tail, Seen> : [C] extends [Seen] ? {
|
|
312
|
+
error: '❌ ERROR: Código duplicado detectado';
|
|
313
|
+
code: C;
|
|
314
|
+
} : CheckUniqueCodes<Tail, Seen | C> : never : true;
|
|
315
|
+
type UniqueRulesConstraint<TRules extends readonly RuleLike[]> = CheckUniqueCodes<TRules> extends true ? unknown : {
|
|
316
|
+
__duplicate_error_codes__: CheckUniqueCodes<TRules>;
|
|
317
|
+
};
|
|
318
|
+
type EnsureRuleTuple<TRules extends readonly unknown[]> = TRules extends readonly RuleLike[] ? TRules : never;
|
|
319
|
+
declare function tryo<const TRules extends readonly unknown[]>(options: Omit<TryoOptions<InferErrorFromRules<EnsureRuleTuple<TRules>>>, 'rules' | 'rulesMode'> & {
|
|
306
320
|
rules: TRules;
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
321
|
+
rulesMode: 'replace';
|
|
322
|
+
} & UniqueRulesConstraint<EnsureRuleTuple<TRules>>): Tryo<InferErrorFromRules<EnsureRuleTuple<TRules>>>;
|
|
323
|
+
declare function tryo<const TRules extends readonly unknown[]>(options: Omit<TryoOptions<InferErrorFromRules<EnsureRuleTuple<TRules>> | DefaultError>, 'rules'> & {
|
|
324
|
+
rules: TRules;
|
|
325
|
+
rulesMode?: 'extend';
|
|
326
|
+
} & UniqueRulesConstraint<EnsureRuleTuple<TRules>>): Tryo<InferErrorFromRules<EnsureRuleTuple<TRules>> | DefaultError>;
|
|
327
|
+
declare function tryo<E extends AnyTypedError = DefaultError>(options?: TryoOptions<E>): Tryo<E>;
|
|
328
|
+
type Tryo<E extends AnyTypedError = AnyTypedError> = {
|
|
310
329
|
run: <T>(task: (ctx: {
|
|
311
330
|
signal: AbortSignal;
|
|
312
|
-
}) =>
|
|
331
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<TryoResult<T, E>>;
|
|
313
332
|
runOrThrow: <T>(task: (ctx: {
|
|
314
333
|
signal: AbortSignal;
|
|
315
|
-
}) =>
|
|
334
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
|
|
316
335
|
orThrow: <T>(task: (ctx: {
|
|
317
336
|
signal: AbortSignal;
|
|
318
|
-
}) =>
|
|
337
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
|
|
319
338
|
all: <T>(tasks: Array<(ctx: {
|
|
320
339
|
signal: AbortSignal;
|
|
321
|
-
}) =>
|
|
322
|
-
concurrency?: number;
|
|
323
|
-
}>) => Promise<Array<TryoResult<T, E>>>;
|
|
340
|
+
}) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<Array<TryoResult<T, E>>>;
|
|
324
341
|
allOrThrow: <T>(tasks: Array<(ctx: {
|
|
325
342
|
signal: AbortSignal;
|
|
326
|
-
}) =>
|
|
327
|
-
concurrency?: number;
|
|
328
|
-
}>) => Promise<T[]>;
|
|
343
|
+
}) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<T[]>;
|
|
329
344
|
partitionAll: <T>(results: Array<TryoResult<T, E>>) => {
|
|
330
345
|
ok: Array<SuccessResult<T, E>>;
|
|
331
346
|
errors: Array<FailureResult<E> | AbortedResult<E> | TimeoutResult<E>>;
|
|
@@ -338,53 +353,103 @@ type Tryo<E extends TypedError = TypedError> = {
|
|
|
338
353
|
|
|
339
354
|
declare const run: <T>(task: (ctx: {
|
|
340
355
|
signal: AbortSignal;
|
|
341
|
-
}) => Promise<T>, options?:
|
|
356
|
+
}) => T | Promise<T>, options?: {
|
|
357
|
+
readonly timeout?: number | undefined;
|
|
358
|
+
readonly signal?: AbortSignal | undefined;
|
|
359
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
360
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
361
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
362
|
+
readonly concurrency?: number | undefined;
|
|
363
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
364
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
365
|
+
} | undefined) => Promise<TryoResult<T, DefaultError>>;
|
|
342
366
|
declare const runOrThrow: <T>(task: (ctx: {
|
|
343
367
|
signal: AbortSignal;
|
|
344
|
-
}) => Promise<T>, options?:
|
|
368
|
+
}) => T | Promise<T>, options?: {
|
|
369
|
+
readonly timeout?: number | undefined;
|
|
370
|
+
readonly signal?: AbortSignal | undefined;
|
|
371
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
372
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
373
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
374
|
+
readonly concurrency?: number | undefined;
|
|
375
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
376
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
377
|
+
} | undefined) => Promise<T>;
|
|
345
378
|
declare const orThrow: <T>(task: (ctx: {
|
|
346
379
|
signal: AbortSignal;
|
|
347
|
-
}) => Promise<T>, options?:
|
|
380
|
+
}) => T | Promise<T>, options?: {
|
|
381
|
+
readonly timeout?: number | undefined;
|
|
382
|
+
readonly signal?: AbortSignal | undefined;
|
|
383
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
384
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
385
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
386
|
+
readonly concurrency?: number | undefined;
|
|
387
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
388
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
389
|
+
} | undefined) => Promise<T>;
|
|
348
390
|
declare const all: <T>(tasks: ((ctx: {
|
|
349
391
|
signal: AbortSignal;
|
|
350
|
-
}) => Promise<T>)[], options?:
|
|
351
|
-
|
|
352
|
-
|
|
392
|
+
}) => T | Promise<T>)[], options?: {
|
|
393
|
+
readonly timeout?: number | undefined;
|
|
394
|
+
readonly signal?: AbortSignal | undefined;
|
|
395
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
396
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
397
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
398
|
+
concurrency?: number | undefined;
|
|
399
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
400
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
401
|
+
} | undefined) => Promise<TryoResult<T, DefaultError>[]>;
|
|
353
402
|
declare const allOrThrow: <T>(tasks: ((ctx: {
|
|
354
403
|
signal: AbortSignal;
|
|
355
|
-
}) => Promise<T>)[], options?:
|
|
356
|
-
|
|
357
|
-
|
|
404
|
+
}) => T | Promise<T>)[], options?: {
|
|
405
|
+
readonly timeout?: number | undefined;
|
|
406
|
+
readonly signal?: AbortSignal | undefined;
|
|
407
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
408
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
409
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
410
|
+
concurrency?: number | undefined;
|
|
411
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
412
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
413
|
+
} | undefined) => Promise<T[]>;
|
|
358
414
|
|
|
359
415
|
/**
|
|
360
416
|
* Modern fluent error rule builder
|
|
361
417
|
* Provides type-safe error rule creation with enhanced ergonomics
|
|
362
418
|
*/
|
|
363
419
|
|
|
420
|
+
/** Standard error response properties allowed in rules */
|
|
421
|
+
interface ErrorResponse {
|
|
422
|
+
code: string;
|
|
423
|
+
message: string;
|
|
424
|
+
meta?: Record<string, unknown>;
|
|
425
|
+
status?: number;
|
|
426
|
+
cause?: unknown;
|
|
427
|
+
retryable?: boolean;
|
|
428
|
+
raw?: unknown;
|
|
429
|
+
path?: string;
|
|
430
|
+
}
|
|
431
|
+
/** Standard mapper properties allowed in .with() */
|
|
432
|
+
interface MapperResponse {
|
|
433
|
+
message: string;
|
|
434
|
+
cause?: unknown;
|
|
435
|
+
meta?: Record<string, unknown>;
|
|
436
|
+
status?: number;
|
|
437
|
+
retryable?: boolean;
|
|
438
|
+
raw?: unknown;
|
|
439
|
+
path?: string;
|
|
440
|
+
}
|
|
441
|
+
type Strict<T, Shape> = T & Record<Exclude<keyof T, keyof Shape>, never>;
|
|
364
442
|
declare class ErrorRuleBuilder<T> {
|
|
365
443
|
private readonly predicate;
|
|
366
444
|
constructor(predicate: (err: unknown) => err is T);
|
|
367
445
|
toCode<const C extends string>(code: C): ErrorMapper<T, C>;
|
|
368
|
-
toError<const Out extends
|
|
369
|
-
code: string;
|
|
370
|
-
message: string;
|
|
371
|
-
meta?: unknown;
|
|
372
|
-
status?: number;
|
|
373
|
-
cause?: unknown;
|
|
374
|
-
retryable?: boolean;
|
|
375
|
-
}>(mapper: (err: T) => Out): ErrorRule<TypedError<Out['code'], Out['meta']>>;
|
|
446
|
+
toError<const Out extends ErrorResponse>(mapper: (err: T) => Strict<Out, ErrorResponse>): ErrorRule<TypedError<Out['code'], Out['meta'] extends Record<string, unknown> ? Out['meta'] : Record<string, unknown>, Out['raw'] extends undefined ? T : Out['raw']>>;
|
|
376
447
|
}
|
|
377
448
|
declare class ErrorMapper<T, C extends string> {
|
|
378
449
|
private readonly predicate;
|
|
379
450
|
private readonly errorCode;
|
|
380
451
|
constructor(predicate: (err: unknown) => err is T, errorCode: C);
|
|
381
|
-
with<const
|
|
382
|
-
message: string;
|
|
383
|
-
cause?: unknown;
|
|
384
|
-
meta?: M;
|
|
385
|
-
status?: number;
|
|
386
|
-
retryable?: boolean;
|
|
387
|
-
}): (err: unknown) => TypedError<C, M> | null;
|
|
452
|
+
with<const Out extends MapperResponse>(mapper: (err: T) => Strict<Out, ErrorResponse>): ErrorRule<TypedError<C, Out['meta'] extends Record<string, unknown> ? Out['meta'] : Record<string, unknown>, Out['raw'] extends undefined ? T : Out['raw']>>;
|
|
388
453
|
}
|
|
389
454
|
|
|
390
455
|
declare const errorRule: {
|
|
@@ -392,4 +457,4 @@ declare const errorRule: {
|
|
|
392
457
|
readonly instance: <T extends new (...args: unknown[]) => unknown>(ErrorClass: T) => ErrorRuleBuilder<InstanceType<T>>;
|
|
393
458
|
};
|
|
394
459
|
|
|
395
|
-
export { type AbortedResult, type
|
|
460
|
+
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,58 @@ 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>;
|
|
297
|
+
type RuleLike = (error: unknown) => AnyTypedError | null;
|
|
295
298
|
type NonNull<T> = T extends null ? never : T;
|
|
296
299
|
type RuleReturn<R> = R extends (err: unknown) => infer Out ? NonNull<Out> : never;
|
|
297
|
-
type InferErrorFromRules<TRules extends readonly
|
|
298
|
-
type TryoOptions<E extends
|
|
300
|
+
type InferErrorFromRules<TRules extends readonly RuleLike[]> = TRules extends readonly [] ? TypedError : RuleReturn<TRules[number]> | UnknownError;
|
|
301
|
+
type TryoOptions<E extends AnyTypedError = AnyTypedError> = Omit<Partial<TryoConfig<E>>, 'errorHandling' | 'signal'> & {
|
|
299
302
|
rules?: Array<ErrorRule<E>>;
|
|
300
303
|
rulesMode?: RulesMode;
|
|
301
304
|
fallback?: (err: unknown) => E;
|
|
302
305
|
toError?: (err: unknown) => E;
|
|
303
306
|
mapError?: (error: E) => E;
|
|
304
307
|
};
|
|
305
|
-
|
|
308
|
+
type ExtractCode<R> = R extends (...args: unknown[]) => unknown ? Exclude<ReturnType<R>, null> extends {
|
|
309
|
+
readonly code: infer C extends string;
|
|
310
|
+
} ? string extends C ? never : C : never : never;
|
|
311
|
+
type CheckUniqueCodes<T extends readonly unknown[], Seen = never> = T extends readonly [infer Head, ...infer Tail] ? ExtractCode<Head> extends infer C ? [C] extends [never] ? CheckUniqueCodes<Tail, Seen> : [C] extends [Seen] ? {
|
|
312
|
+
error: '❌ ERROR: Código duplicado detectado';
|
|
313
|
+
code: C;
|
|
314
|
+
} : CheckUniqueCodes<Tail, Seen | C> : never : true;
|
|
315
|
+
type UniqueRulesConstraint<TRules extends readonly RuleLike[]> = CheckUniqueCodes<TRules> extends true ? unknown : {
|
|
316
|
+
__duplicate_error_codes__: CheckUniqueCodes<TRules>;
|
|
317
|
+
};
|
|
318
|
+
type EnsureRuleTuple<TRules extends readonly unknown[]> = TRules extends readonly RuleLike[] ? TRules : never;
|
|
319
|
+
declare function tryo<const TRules extends readonly unknown[]>(options: Omit<TryoOptions<InferErrorFromRules<EnsureRuleTuple<TRules>>>, 'rules' | 'rulesMode'> & {
|
|
306
320
|
rules: TRules;
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
321
|
+
rulesMode: 'replace';
|
|
322
|
+
} & UniqueRulesConstraint<EnsureRuleTuple<TRules>>): Tryo<InferErrorFromRules<EnsureRuleTuple<TRules>>>;
|
|
323
|
+
declare function tryo<const TRules extends readonly unknown[]>(options: Omit<TryoOptions<InferErrorFromRules<EnsureRuleTuple<TRules>> | DefaultError>, 'rules'> & {
|
|
324
|
+
rules: TRules;
|
|
325
|
+
rulesMode?: 'extend';
|
|
326
|
+
} & UniqueRulesConstraint<EnsureRuleTuple<TRules>>): Tryo<InferErrorFromRules<EnsureRuleTuple<TRules>> | DefaultError>;
|
|
327
|
+
declare function tryo<E extends AnyTypedError = DefaultError>(options?: TryoOptions<E>): Tryo<E>;
|
|
328
|
+
type Tryo<E extends AnyTypedError = AnyTypedError> = {
|
|
310
329
|
run: <T>(task: (ctx: {
|
|
311
330
|
signal: AbortSignal;
|
|
312
|
-
}) =>
|
|
331
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<TryoResult<T, E>>;
|
|
313
332
|
runOrThrow: <T>(task: (ctx: {
|
|
314
333
|
signal: AbortSignal;
|
|
315
|
-
}) =>
|
|
334
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
|
|
316
335
|
orThrow: <T>(task: (ctx: {
|
|
317
336
|
signal: AbortSignal;
|
|
318
|
-
}) =>
|
|
337
|
+
}) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
|
|
319
338
|
all: <T>(tasks: Array<(ctx: {
|
|
320
339
|
signal: AbortSignal;
|
|
321
|
-
}) =>
|
|
322
|
-
concurrency?: number;
|
|
323
|
-
}>) => Promise<Array<TryoResult<T, E>>>;
|
|
340
|
+
}) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<Array<TryoResult<T, E>>>;
|
|
324
341
|
allOrThrow: <T>(tasks: Array<(ctx: {
|
|
325
342
|
signal: AbortSignal;
|
|
326
|
-
}) =>
|
|
327
|
-
concurrency?: number;
|
|
328
|
-
}>) => Promise<T[]>;
|
|
343
|
+
}) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<T[]>;
|
|
329
344
|
partitionAll: <T>(results: Array<TryoResult<T, E>>) => {
|
|
330
345
|
ok: Array<SuccessResult<T, E>>;
|
|
331
346
|
errors: Array<FailureResult<E> | AbortedResult<E> | TimeoutResult<E>>;
|
|
@@ -338,53 +353,103 @@ type Tryo<E extends TypedError = TypedError> = {
|
|
|
338
353
|
|
|
339
354
|
declare const run: <T>(task: (ctx: {
|
|
340
355
|
signal: AbortSignal;
|
|
341
|
-
}) => Promise<T>, options?:
|
|
356
|
+
}) => T | Promise<T>, options?: {
|
|
357
|
+
readonly timeout?: number | undefined;
|
|
358
|
+
readonly signal?: AbortSignal | undefined;
|
|
359
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
360
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
361
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
362
|
+
readonly concurrency?: number | undefined;
|
|
363
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
364
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
365
|
+
} | undefined) => Promise<TryoResult<T, DefaultError>>;
|
|
342
366
|
declare const runOrThrow: <T>(task: (ctx: {
|
|
343
367
|
signal: AbortSignal;
|
|
344
|
-
}) => Promise<T>, options?:
|
|
368
|
+
}) => T | Promise<T>, options?: {
|
|
369
|
+
readonly timeout?: number | undefined;
|
|
370
|
+
readonly signal?: AbortSignal | undefined;
|
|
371
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
372
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
373
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
374
|
+
readonly concurrency?: number | undefined;
|
|
375
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
376
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
377
|
+
} | undefined) => Promise<T>;
|
|
345
378
|
declare const orThrow: <T>(task: (ctx: {
|
|
346
379
|
signal: AbortSignal;
|
|
347
|
-
}) => Promise<T>, options?:
|
|
380
|
+
}) => T | Promise<T>, options?: {
|
|
381
|
+
readonly timeout?: number | undefined;
|
|
382
|
+
readonly signal?: AbortSignal | undefined;
|
|
383
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
384
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
385
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
386
|
+
readonly concurrency?: number | undefined;
|
|
387
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
388
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
389
|
+
} | undefined) => Promise<T>;
|
|
348
390
|
declare const all: <T>(tasks: ((ctx: {
|
|
349
391
|
signal: AbortSignal;
|
|
350
|
-
}) => Promise<T>)[], options?:
|
|
351
|
-
|
|
352
|
-
|
|
392
|
+
}) => T | Promise<T>)[], options?: {
|
|
393
|
+
readonly timeout?: number | undefined;
|
|
394
|
+
readonly signal?: AbortSignal | undefined;
|
|
395
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
396
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
397
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
398
|
+
concurrency?: number | undefined;
|
|
399
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
400
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
401
|
+
} | undefined) => Promise<TryoResult<T, DefaultError>[]>;
|
|
353
402
|
declare const allOrThrow: <T>(tasks: ((ctx: {
|
|
354
403
|
signal: AbortSignal;
|
|
355
|
-
}) => Promise<T>)[], options?:
|
|
356
|
-
|
|
357
|
-
|
|
404
|
+
}) => T | Promise<T>)[], options?: {
|
|
405
|
+
readonly timeout?: number | undefined;
|
|
406
|
+
readonly signal?: AbortSignal | undefined;
|
|
407
|
+
readonly ignoreAbort?: boolean | undefined;
|
|
408
|
+
readonly retry?: RetryConfig<DefaultError> | undefined;
|
|
409
|
+
readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
|
|
410
|
+
concurrency?: number | undefined;
|
|
411
|
+
readonly logger?: LoggerConfig<DefaultError> | undefined;
|
|
412
|
+
readonly hooks?: HookConfig<DefaultError> | undefined;
|
|
413
|
+
} | undefined) => Promise<T[]>;
|
|
358
414
|
|
|
359
415
|
/**
|
|
360
416
|
* Modern fluent error rule builder
|
|
361
417
|
* Provides type-safe error rule creation with enhanced ergonomics
|
|
362
418
|
*/
|
|
363
419
|
|
|
420
|
+
/** Standard error response properties allowed in rules */
|
|
421
|
+
interface ErrorResponse {
|
|
422
|
+
code: string;
|
|
423
|
+
message: string;
|
|
424
|
+
meta?: Record<string, unknown>;
|
|
425
|
+
status?: number;
|
|
426
|
+
cause?: unknown;
|
|
427
|
+
retryable?: boolean;
|
|
428
|
+
raw?: unknown;
|
|
429
|
+
path?: string;
|
|
430
|
+
}
|
|
431
|
+
/** Standard mapper properties allowed in .with() */
|
|
432
|
+
interface MapperResponse {
|
|
433
|
+
message: string;
|
|
434
|
+
cause?: unknown;
|
|
435
|
+
meta?: Record<string, unknown>;
|
|
436
|
+
status?: number;
|
|
437
|
+
retryable?: boolean;
|
|
438
|
+
raw?: unknown;
|
|
439
|
+
path?: string;
|
|
440
|
+
}
|
|
441
|
+
type Strict<T, Shape> = T & Record<Exclude<keyof T, keyof Shape>, never>;
|
|
364
442
|
declare class ErrorRuleBuilder<T> {
|
|
365
443
|
private readonly predicate;
|
|
366
444
|
constructor(predicate: (err: unknown) => err is T);
|
|
367
445
|
toCode<const C extends string>(code: C): ErrorMapper<T, C>;
|
|
368
|
-
toError<const Out extends
|
|
369
|
-
code: string;
|
|
370
|
-
message: string;
|
|
371
|
-
meta?: unknown;
|
|
372
|
-
status?: number;
|
|
373
|
-
cause?: unknown;
|
|
374
|
-
retryable?: boolean;
|
|
375
|
-
}>(mapper: (err: T) => Out): ErrorRule<TypedError<Out['code'], Out['meta']>>;
|
|
446
|
+
toError<const Out extends ErrorResponse>(mapper: (err: T) => Strict<Out, ErrorResponse>): ErrorRule<TypedError<Out['code'], Out['meta'] extends Record<string, unknown> ? Out['meta'] : Record<string, unknown>, Out['raw'] extends undefined ? T : Out['raw']>>;
|
|
376
447
|
}
|
|
377
448
|
declare class ErrorMapper<T, C extends string> {
|
|
378
449
|
private readonly predicate;
|
|
379
450
|
private readonly errorCode;
|
|
380
451
|
constructor(predicate: (err: unknown) => err is T, errorCode: C);
|
|
381
|
-
with<const
|
|
382
|
-
message: string;
|
|
383
|
-
cause?: unknown;
|
|
384
|
-
meta?: M;
|
|
385
|
-
status?: number;
|
|
386
|
-
retryable?: boolean;
|
|
387
|
-
}): (err: unknown) => TypedError<C, M> | null;
|
|
452
|
+
with<const Out extends MapperResponse>(mapper: (err: T) => Strict<Out, ErrorResponse>): ErrorRule<TypedError<C, Out['meta'] extends Record<string, unknown> ? Out['meta'] : Record<string, unknown>, Out['raw'] extends undefined ? T : Out['raw']>>;
|
|
388
453
|
}
|
|
389
454
|
|
|
390
455
|
declare const errorRule: {
|
|
@@ -392,4 +457,4 @@ declare const errorRule: {
|
|
|
392
457
|
readonly instance: <T extends new (...args: unknown[]) => unknown>(ErrorClass: T) => ErrorRuleBuilder<InstanceType<T>>;
|
|
393
458
|
};
|
|
394
459
|
|
|
395
|
-
export { type AbortedResult, type
|
|
460
|
+
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 d=(e,r,t)=>ue(e,typeof r!="symbol"?r+"":r,t);var w=class extends Error{constructor(t,n){var o,a;super(t);d(this,"cause");d(this,"meta");d(this,"status");d(this,"raw");d(this,"path");d(this,"timestamp");d(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 w{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0,raw:n});d(this,"code","TIMEOUT")}};var I=class extends w{constructor(t,n){super(`Circuit breaker is open, reset after ${t}ms`,{cause:n,retryable:!1,raw:n});d(this,"code","CIRCUIT_OPEN")}};var P=class extends w{constructor(t,n){super(t,{cause:n,raw:n});d(this,"code","UNKNOWN")}};var b=e=>{if(e<0||!Number.isFinite(e))throw new Error(`Invalid milliseconds: must be a non-negative finite number, got ${e}`);return e},C=e=>{if(e<0||!Number.isInteger(e))throw new Error(`Invalid retry count: must be a non-negative integer, got ${e}`);return e},z=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){d(this,"state");d(this,"config");this.config={failureThreshold:C(r.failureThreshold),resetTimeout:b(r.resetTimeout),halfOpenRequests:C(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?b(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 w?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},de=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},B=class{constructor(r){this.predicate=r}toCode(r){return new q(this.predicate,r)}toError(r){return t=>{var s;if(!this.predicate(t))return null;let n=r(t),o=n.code,a=(s=n.meta)!=null?s:{};class i extends w{constructor(){var l;let y=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{cause:(l=n.cause)!=null?l:t,meta:a,status:n.status,retryable:n.retryable,raw:y,path:n.path});d(this,"code",o)}}return new i}}},q=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){return t=>{var s;if(!this.predicate(t))return null;let n=r(t),o=(s=n.meta)!=null?s:{},a=this.errorCode;class i extends w{constructor(){let l=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{cause:n.cause,meta:o,status:n.status,retryable:n.retryable,raw:l,path:n.path});d(this,"code",a)}}return new i}}},h={when:e=>new B(e),instance:e=>new B(r=>r instanceof e)},x={typed:(e=>e instanceof w?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=>de(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 w)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e,raw:e}))};var ye=[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=ye;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 y;i=!0,u(),a((y=n==null?void 0:n())!=null?y:new Error(`Operation timed out after ${r}ms`))},r),c=()=>{i||(i=!0,u(),a(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(s),t==null||t.removeEventListener("abort",c)};t==null||t.addEventListener("abort",c,{once:!0}),e.then(y=>{i||(i=!0,u(),o(y))},y=>{i||(i=!0,u(),a(y))})});var Te=e=>{var i,s,c;if(e.toError)return e.toError;let r=(i=e.rulesMode)!=null?i:"extend",t=(s=e.rules)!=null?s:[],n=X,o=(c=e.fallback)!=null?c:(u=>Q(P)(u)),a=r==="replace"?t:[...t,...n];return G(a,o)},Ee=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}`)}}},H=e=>{let r=e;return r.timeout!==void 0&&(r={...r,timeout:Number(b(r.timeout))}),r.concurrency!==void 0&&(r={...r,concurrency:Number.isFinite(r.concurrency)?Number(z(r.concurrency)):r.concurrency}),r.retry&&(j(r.retry.strategy),Ee(r.retry.jitter),r={...r,retry:{...r.retry,maxRetries:Number(C(r.retry.maxRetries))}}),r},$=class e{constructor(r={}){d(this,"circuitBreaker");d(this,"config");d(this,"lastCircuitState");let{rules:t,rulesMode:n,fallback:o,toError:a,mapError:i,...s}=r,c=Te(r),u={...s,errorHandling:{normalizer:c,mapError:i}};this.config=H(u),u.circuitBreaker&&(this.circuitBreaker=new F(u.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var a,i,s,c,u,y;let n=H({...this.config,...t});if(this.circuitBreaker){let l=(a=this.lastCircuitState)!=null?a:this.circuitBreaker.getState().state,m=await this.circuitBreaker.canExecute(),T=this.circuitBreaker.getState().state;if(l!==T)try{(s=(i=n.hooks)==null?void 0:i.onCircuitStateChange)==null||s.call(i,l,T)}catch{}if(this.lastCircuitState=T,!m)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let o=await we(r,n);if(this.circuitBreaker){let l=(c=this.lastCircuitState)!=null?c:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let m=this.circuitBreaker.getState().state;if(l!==m)try{(y=(u=n.hooks)==null?void 0:u.onCircuitStateChange)==null||y.call(u,l,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 y;let n=H({...this.config,...t}),o=(y=n.concurrency)!=null?y:Number.POSITIVE_INFINITY,a=Number.isFinite(o)?Number(z(o)):Number.POSITIVE_INFINITY;if(a===Number.POSITIVE_INFINITY)return Promise.all(r.map(l=>this.run(l,n)));let i=new Array(r.length),s=0,c=async()=>{var l;for(;s<r.length&&!((l=n.signal)!=null&&l.aborted);){let m=s++;if(m>=r.length)break;let T=r[m];T&&(i[m]=await this.run(T,n))}},u=Array.from({length:Math.min(a,r.length)},()=>c());await Promise.all(u);for(let l=0;l<r.length;l++)if(!(l in i)){let m=r[l];m&&(i[l]=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 c,u;let{errorHandling:t,...n}=this.config,{errorHandling:o,...a}=r,i=(c=o==null?void 0:o.normalizer)!=null?c:t.normalizer,s=(u=o==null?void 0:o.mapError)!=null?u:t.mapError;return new e({...n,...a,toError:i,mapError:s})}};function _(e={}){let r=new $(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 we(e,r){let{signal:t,ignoreAbort:n=!0,timeout:o,retry:a,errorHandling:i,hooks:s,logger:c}=r,u=(R,...p)=>{try{R==null||R(...p)}catch{}},y,l=0,m=0,T=[],k=Date.now(),{signal:A,cleanup:ne}=te(t),N=R=>({totalAttempts:l,totalRetries:l>0?Number(l)-1:0,totalDuration:Date.now()-k,lastError:R,retryHistory:T});try{if(A.aborted){u(s==null?void 0:s.onAbort,A);let p=i.normalizer(new DOMException("Aborted","AbortError")),E=i.mapError?i.mapError(p):p;return{type:"aborted",ok:!1,data:null,error:E,metrics:{totalAttempts:l,totalRetries:m,totalDuration:Date.now()-k,lastError:E,retryHistory:T}}}let R=async()=>{var M,g,V;let p=1,E=C((M=a==null?void 0:a.maxRetries)!=null?M:0);for(;;){l=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(b(o)))):await v;return u(s==null?void 0:s.onSuccess,O,N()),u(c==null?void 0:c.info,`Task succeeded on attempt ${p}`),O}catch(v){let O=i.normalizer(v),f=i.mapError?i.mapError(O):O;if(y=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(c==null?void 0:c.error,`Task failed on attempt ${p}`,f)),f.code==="ABORTED"||f.retryable===!1)throw f;if(p<=Number(E)){let K=a==null?void 0:a.shouldRetry,se={totalAttempts:Number(l),elapsedTime:Date.now()-k,startTime:new Date(k),lastDelay:(g=T[T.length-1])!=null&&g.delay?Number((V=T[T.length-1])==null?void 0:V.delay):void 0};if(!K||K(p,f,se)){let ae=a?Z(a.strategy,p,f):0,L=be(ae,a==null?void 0:a.jitter),J=b(L);T.push({attempt:p,error:f,delay:J,timestamp:new Date}),u(s==null?void 0:s.onRetry,p,f,L),u(c==null?void 0:c.info,`Retrying in ${L}ms (attempt ${p+1})`),p+=1,await ee(J,A);continue}}throw f}finally{oe()}}};try{let p=await R(),E=N();return m=E.totalRetries,u(s==null?void 0:s.onFinally,E),{type:"success",ok:!0,data:p,error:null,metrics:E}}catch(p){let E=y!=null?y:i.normalizer(p),M=E.code==="TIMEOUT"?"timeout":E.code==="ABORTED"?"aborted":"failure",g=N(E);return m=g.totalRetries,u(s==null?void 0:s.onFinally,g),{type:M,ok:!1,data:null,error:E,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 be(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=_(),he=S.run,Re=S.runOrThrow,Ce=S.orThrow,xe=S.all,ge=S.allOrThrow;export{me as RetryStrategies,w as TypedError,xe as all,ge as allOrThrow,b as asMilliseconds,C as asRetryCount,_ as default,pe as errorRule,Ce as orThrow,he as run,Re as runOrThrow,_ 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.4",
|
|
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",
|
|
@@ -32,28 +50,9 @@
|
|
|
32
50
|
"lint": "biome lint",
|
|
33
51
|
"lint:fix": "biome lint --fix",
|
|
34
52
|
"check": "biome check",
|
|
35
|
-
"check:fix": "biome check --write"
|
|
36
|
-
"prepare": "husky"
|
|
37
|
-
},
|
|
38
|
-
"lint-staged": {
|
|
39
|
-
"*.{ts,tsx,js,jsx,json}": [
|
|
40
|
-
"biome check --write"
|
|
41
|
-
]
|
|
53
|
+
"check:fix": "biome check --write"
|
|
42
54
|
},
|
|
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
|
-
}
|
|
55
|
+
"sideEffects": false,
|
|
56
|
+
"type": "module",
|
|
57
|
+
"types": "./dist/index.d.ts"
|
|
59
58
|
}
|