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 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?: 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
- declare class TimeoutError extends TypedError<'TIMEOUT'> {
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 TypedError = TypedError> = SuccessResult<T, E> | FailureResult<E> | AbortedResult<E> | TimeoutResult<E>;
128
- interface SuccessResult<T, E extends TypedError> {
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?: TryoMetrics$1<E>;
106
+ readonly metrics: TryoMetrics$1<E>;
134
107
  }
135
- interface FailureResult<E extends TypedError> {
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?: TryoMetrics$1<E>;
113
+ readonly metrics: TryoMetrics$1<E>;
141
114
  }
142
- interface AbortedResult<E extends TypedError> {
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?: TryoMetrics$1<E>;
120
+ readonly metrics: TryoMetrics$1<E>;
148
121
  }
149
- interface TimeoutResult<E extends TypedError> {
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?: TryoMetrics$1<E>;
127
+ readonly metrics: TryoMetrics$1<E>;
155
128
  }
156
- interface TryoMetrics$1<E extends TypedError> {
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 TypedError = TypedError> {
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 TypedError> {
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 TypedError> = (attempt: number, error: E, context: RetryContext) => boolean;
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 TypedError> {
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 TypedError> {
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 TypedError> {
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 CircuitState = 'closed' | 'open' | 'half-open';
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 ErrorRule<TypedError>[]> = TRules extends readonly [] ? TypedError : RuleReturn<TRules[number]> | UnknownError;
298
- type TryoOptions<E extends TypedError = TypedError> = Omit<Partial<TryoConfig<E>>, 'errorHandling' | 'signal'> & {
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
- declare function tryo<const TRules extends readonly ErrorRule<TypedError>[]>(options: Omit<TryoOptions<InferErrorFromRules<TRules>>, 'rules'> & {
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
- }): Tryo<InferErrorFromRules<TRules>>;
308
- declare function tryo<E extends TypedError = DefaultError>(options?: TryoOptions<E>): Tryo<E>;
309
- type Tryo<E extends TypedError = TypedError> = {
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
- }) => Promise<T>, options?: Partial<TryoConfig<E>>) => Promise<TryoResult<T, E>>;
331
+ }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<TryoResult<T, E>>;
313
332
  runOrThrow: <T>(task: (ctx: {
314
333
  signal: AbortSignal;
315
- }) => Promise<T>, options?: Partial<TryoConfig<E>>) => Promise<T>;
334
+ }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
316
335
  orThrow: <T>(task: (ctx: {
317
336
  signal: AbortSignal;
318
- }) => Promise<T>, options?: Partial<TryoConfig<E>>) => Promise<T>;
337
+ }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
319
338
  all: <T>(tasks: Array<(ctx: {
320
339
  signal: AbortSignal;
321
- }) => Promise<T>>, options?: Partial<TryoConfig<E> & {
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
- }) => Promise<T>>, options?: Partial<TryoConfig<E> & {
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?: Partial<TryoConfig<DefaultError>> | undefined) => Promise<TryoResult<T, DefaultError>>;
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?: Partial<TryoConfig<DefaultError>> | undefined) => Promise<T>;
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?: Partial<TryoConfig<DefaultError>> | undefined) => Promise<T>;
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?: Partial<TryoConfig<DefaultError> & {
351
- concurrency?: number;
352
- }> | undefined) => Promise<TryoResult<T, DefaultError>[]>;
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?: Partial<TryoConfig<DefaultError> & {
356
- concurrency?: number;
357
- }> | undefined) => Promise<T[]>;
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 M = unknown>(mapper: (err: T) => {
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 ConcurrencyLimit, type FailureResult, type Milliseconds, type Percentage, type RetryCount, RetryStrategies, type RulesMode, type StatusCode, type SuccessResult, type TimeoutResult, type TryoConfig, type TryoMetrics$1 as TryoMetrics, type TryoOptions, type TryoResult, all, allOrThrow, asConcurrencyLimit, asMilliseconds, asPercentage, asRetryCount, asStatusCode, errorRule, orThrow, run, runOrThrow, tryo };
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?: 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
- declare class TimeoutError extends TypedError<'TIMEOUT'> {
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 TypedError = TypedError> = SuccessResult<T, E> | FailureResult<E> | AbortedResult<E> | TimeoutResult<E>;
128
- interface SuccessResult<T, E extends TypedError> {
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?: TryoMetrics$1<E>;
106
+ readonly metrics: TryoMetrics$1<E>;
134
107
  }
135
- interface FailureResult<E extends TypedError> {
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?: TryoMetrics$1<E>;
113
+ readonly metrics: TryoMetrics$1<E>;
141
114
  }
142
- interface AbortedResult<E extends TypedError> {
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?: TryoMetrics$1<E>;
120
+ readonly metrics: TryoMetrics$1<E>;
148
121
  }
149
- interface TimeoutResult<E extends TypedError> {
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?: TryoMetrics$1<E>;
127
+ readonly metrics: TryoMetrics$1<E>;
155
128
  }
156
- interface TryoMetrics$1<E extends TypedError> {
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 TypedError = TypedError> {
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 TypedError> {
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 TypedError> = (attempt: number, error: E, context: RetryContext) => boolean;
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 TypedError> {
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 TypedError> {
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 TypedError> {
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 CircuitState = 'closed' | 'open' | 'half-open';
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 ErrorRule<TypedError>[]> = TRules extends readonly [] ? TypedError : RuleReturn<TRules[number]> | UnknownError;
298
- type TryoOptions<E extends TypedError = TypedError> = Omit<Partial<TryoConfig<E>>, 'errorHandling' | 'signal'> & {
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
- declare function tryo<const TRules extends readonly ErrorRule<TypedError>[]>(options: Omit<TryoOptions<InferErrorFromRules<TRules>>, 'rules'> & {
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
- }): Tryo<InferErrorFromRules<TRules>>;
308
- declare function tryo<E extends TypedError = DefaultError>(options?: TryoOptions<E>): Tryo<E>;
309
- type Tryo<E extends TypedError = TypedError> = {
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
- }) => Promise<T>, options?: Partial<TryoConfig<E>>) => Promise<TryoResult<T, E>>;
331
+ }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<TryoResult<T, E>>;
313
332
  runOrThrow: <T>(task: (ctx: {
314
333
  signal: AbortSignal;
315
- }) => Promise<T>, options?: Partial<TryoConfig<E>>) => Promise<T>;
334
+ }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
316
335
  orThrow: <T>(task: (ctx: {
317
336
  signal: AbortSignal;
318
- }) => Promise<T>, options?: Partial<TryoConfig<E>>) => Promise<T>;
337
+ }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
319
338
  all: <T>(tasks: Array<(ctx: {
320
339
  signal: AbortSignal;
321
- }) => Promise<T>>, options?: Partial<TryoConfig<E> & {
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
- }) => Promise<T>>, options?: Partial<TryoConfig<E> & {
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?: Partial<TryoConfig<DefaultError>> | undefined) => Promise<TryoResult<T, DefaultError>>;
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?: Partial<TryoConfig<DefaultError>> | undefined) => Promise<T>;
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?: Partial<TryoConfig<DefaultError>> | undefined) => Promise<T>;
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?: Partial<TryoConfig<DefaultError> & {
351
- concurrency?: number;
352
- }> | undefined) => Promise<TryoResult<T, DefaultError>[]>;
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?: Partial<TryoConfig<DefaultError> & {
356
- concurrency?: number;
357
- }> | undefined) => Promise<T[]>;
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 M = unknown>(mapper: (err: T) => {
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 ConcurrencyLimit, type FailureResult, type Milliseconds, type Percentage, type RetryCount, RetryStrategies, type RulesMode, type StatusCode, type SuccessResult, type TimeoutResult, type TryoConfig, type TryoMetrics$1 as TryoMetrics, type TryoOptions, type TryoResult, all, allOrThrow, asConcurrencyLimit, asMilliseconds, asPercentage, asRetryCount, asStatusCode, errorRule, orThrow, run, runOrThrow, tryo };
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.0",
4
- "description": "Run sync/async functions and return a typed Result instead of throwing.",
5
- "type": "module",
6
- "sideEffects": false,
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
- "types": "./dist/index.d.ts",
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
- "devDependencies": {
44
- "@biomejs/biome": "2.3.13",
45
- "@types/bun": "latest",
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
  }