tryo 0.13.6 → 0.13.8

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/README.md CHANGED
@@ -169,11 +169,9 @@ Add hooks for logging or monitoring:
169
169
 
170
170
  ```typescript
171
171
  const ex = tryo({
172
- hooks: {
173
- onRetry: (attempt, error, delay) => console.log(`Retry ${attempt}...`),
174
- onCircuitStateChange: (from, to) =>
175
- console.log(`Breaker moved: ${from} -> ${to}`),
176
- },
172
+ onRetry: (attempt, error, delay) => console.log(`Retry ${attempt}...`),
173
+ onCircuitStateChange: (from, to) =>
174
+ console.log(`Breaker moved: ${from} -> ${to}`),
177
175
  })
178
176
  ```
179
177
 
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var D=Object.defineProperty;var fe=Object.getOwnPropertyDescriptor;var Ee=Object.getOwnPropertyNames;var Te=Object.prototype.hasOwnProperty;var we=(e,r,t)=>r in e?D(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var be=(e,r)=>{for(var t in r)D(e,t,{get:r[t],enumerable:!0})},he=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of Ee(r))!Te.call(e,o)&&o!==t&&D(e,o,{get:()=>r[o],enumerable:!(n=fe(r,o))||n.enumerable});return e};var Re=e=>he(D({},"__esModule",{value:!0}),e);var d=(e,r,t)=>we(e,typeof r!="symbol"?r+"":r,t);var De={};be(De,{RetryStrategies:()=>ee,TypedError:()=>f,all:()=>le,allOrThrow:()=>ce,asMilliseconds:()=>b,asRetryCount:()=>h,default:()=>S,errorRule:()=>X,orThrow:()=>ue,run:()=>ae,runOrThrow:()=>ie,tryo:()=>S});module.exports=Re(De);var f=class extends Error{constructor(t,n){var o,i;super(t);d(this,"cause");d(this,"title");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.title=n.title,this.meta=(i=n.meta)!=null?i:{},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,title:this.title,message:this.message,timestamp:this.timestamp,retryable:this.retryable,cause:this.cause,raw:this.raw,path:this.path,stack:this.stack}}},P=class extends f{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0});d(this,"code","TIMEOUT")}};var F=class extends f{constructor(t,n){super(`Circuit breaker is open, reset after ${t}ms`,{cause:n,retryable:!1});d(this,"code","CIRCUIT_OPEN")}};var B=class extends f{constructor(t,n){super(t,{cause: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},H=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 _=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,i,s;if(!((s=(i=(o=this.config).shouldCountAsFailure)==null?void 0:i.call(o,r))!=null?s:!0))return;let n=new Date;switch(this.state.state){case"closed":{let a=this.state.failureCount+1;a>=this.config.failureThreshold?this.state={state:"open",failureCount:a,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)}:this.state={...this.state,failureCount:a,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 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 f?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},ke=e=>typeof e=="object"&&e!==null,U=class{constructor(r){this.predicate=r}toCode(r){return new q(this.predicate,r)}toError(r){return t=>{var l;if(!this.predicate(t))return null;let n=r(t),o=n.code,i=Object.hasOwn(n,"cause")?n.cause:t,s=(l=n.meta)!=null?l:{};class a extends f{constructor(){let c=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{title:n.title,cause:i,meta:s,status:n.status,retryable:n.retryable,raw:c,path:n.path});d(this,"code",o)}}return new a}}};function Oe(e,r){let t=new U(i=>i instanceof e);if(r)return t.toError(r);let o=i=>{if(!(i instanceof e))return null;if(i instanceof f)return i;let s=i,a=typeof s.code=="string"&&s.code.length>0?s.code:"UNKNOWN",l=Object.hasOwn(s,"cause"),u=Object.hasOwn(s,"raw");class y extends f{constructor(){super(s.message||a,{title:typeof s.title=="string"?s.title:void 0,cause:l?s.cause:i,meta:ke(s.meta)?s.meta:{},status:typeof s.status=="number"?s.status:void 0,retryable:typeof s.retryable=="boolean"?s.retryable:void 0,raw:u?s.raw:i,path:typeof s.path=="string"?s.path:void 0});d(this,"code",a)}}return new y};return o.toCode=t.toCode.bind(t),o.toError=t.toError.bind(t),o}var q=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){return t=>{var l;if(!this.predicate(t))return null;let n=r(t),o=Object.hasOwn(n,"cause")?n.cause:t,i=(l=n.meta)!=null?l:{},s=this.errorCode;class a extends f{constructor(){let p=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{title:n.title,cause:o,meta:i,status:n.status,retryable:n.retryable,raw:p,path:n.path});d(this,"code",s)}}return new a}}},R={when:e=>new U(e),instance:Oe},x={typed:(e=>e instanceof f?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 f)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e,raw:e}))};var Ae=[x.typed,x.abort,x.timeout,x.http,x.network,x.unknown];var X={when:e=>R.when(e),instance:R.instance},Z=Ae;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*Se(Number(r));return e.maxDelay!==void 0?Math.min(n,e.maxDelay):n}case"custom":return e.calculate(r,t);default:return e}},Se=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=a=>{r&&a&&r.removeEventListener("abort",a)},i,s=()=>{i&&clearTimeout(i),o(s),n(new DOMException("Aborted","AbortError"))};i=setTimeout(()=>{o(s),t()},e),r==null||r.addEventListener("abort",s,{once:!0})}),oe=(e,r,t,n)=>new Promise((o,i)=>{if(t!=null&&t.aborted){i(new DOMException("Aborted","AbortError"));return}let s=!1,a=setTimeout(()=>{var y;s=!0,u(),i((y=n==null?void 0:n())!=null?y:new Error(`Operation timed out after ${r}ms`))},r),l=()=>{s||(s=!0,u(),i(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(a),t==null||t.removeEventListener("abort",l)};t==null||t.addEventListener("abort",l,{once:!0}),e.then(y=>{s||(s=!0,u(),o(y))},y=>{s||(s=!0,u(),i(y))})});var Ne=e=>{var s,a,l;if(e.toError)return e.toError;let r=(s=e.rulesMode)!=null?s:"extend",t=(a=e.rules)!=null?a:[],n=Z,o=(l=e.fallback)!=null?l:(u=>Q(B)(u)),i=r==="replace"?t:[...t,...n];return G(i,o)},Me=e=>{if(e)switch(e.type){case"none":return;case"full":case"equal":Y(e.ratio);return;case"custom":return;default:{let r=e;throw new Error(`Unsupported jitter configuration: ${r}`)}}},$=e=>{let r=e;return r.timeout!==void 0&&(r={...r,timeout:Number(b(r.timeout))}),r.concurrency!==void 0&&(r={...r,concurrency:Number.isFinite(r.concurrency)?Number(H(r.concurrency)):r.concurrency}),r.retry&&(te(r.retry.strategy),Me(r.retry.jitter),r={...r,retry:{...r.retry,maxRetries:Number(h(r.retry.maxRetries))}}),r},W=class e{constructor(r={}){d(this,"circuitBreaker");d(this,"config");d(this,"lastCircuitState");let{rules:t,rulesMode:n,fallback:o,toError:i,mapError:s,...a}=r,l=Ne(r),u={...a,errorHandling:{normalizer:l,mapError:s}};this.config=$(u),u.circuitBreaker&&(this.circuitBreaker=new _(u.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var i,s,a,l,u,y;let n=$({...this.config,...t});if(this.circuitBreaker){let c=(i=this.lastCircuitState)!=null?i:this.circuitBreaker.getState().state,p=await this.circuitBreaker.canExecute(),T=this.circuitBreaker.getState().state;if(c!==T)try{(a=(s=n.hooks)==null?void 0:s.onCircuitStateChange)==null||a.call(s,c,T)}catch{}if(this.lastCircuitState=T,!p)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let o=await Ie(r,n);if(this.circuitBreaker){let c=(l=this.lastCircuitState)!=null?l:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let p=this.circuitBreaker.getState().state;if(c!==p)try{(y=(u=n.hooks)==null?void 0:u.onCircuitStateChange)==null||y.call(u,c,p)}catch{}this.lastCircuitState=p}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,i=Number.isFinite(o)?Number(H(o)):Number.POSITIVE_INFINITY;if(i===Number.POSITIVE_INFINITY)return Promise.all(r.map(c=>this.run(c,n)));let s=new Array(r.length),a=0,l=async()=>{var c;for(;a<r.length&&!((c=n.signal)!=null&&c.aborted);){let p=a++;if(p>=r.length)break;let T=r[p];T&&(s[p]=await this.run(T,n))}},u=Array.from({length:Math.min(i,r.length)},()=>l());await Promise.all(u);for(let c=0;c<r.length;c++)if(!(c in s)){let p=r[c];p&&(s[c]=await this.run(p,n))}return s}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=[],i=[],s=[];for(let a of r){if(a.type==="success"){t.push(a);continue}switch(n.push(a),a.type){case"failure":o.push(a);break;case"aborted":i.push(a);break;case"timeout":s.push(a);break}}return{ok:t,errors:n,failure:o,aborted:i,timeout:s}}getCircuitBreakerState(){var r;return(r=this.circuitBreaker)==null?void 0:r.getState()}resetCircuitBreaker(){var r;(r=this.circuitBreaker)==null||r.reset()}getConfig(){return{...this.config}}withConfig(r){var l,u;let{errorHandling:t,...n}=this.config,{errorHandling:o,...i}=r,s=(l=o==null?void 0:o.normalizer)!=null?l:t.normalizer,a=(u=o==null?void 0:o.mapError)!=null?u:t.mapError;return new e({...n,...i,toError:s,mapError:a})}};function S(e={}){let r=new W(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 Ie(e,r){let{signal:t,ignoreAbort:n=!0,timeout:o,retry:i,errorHandling:s,hooks:a,logger:l}=r,u=(C,...m)=>{try{C==null||C(...m)}catch{}},y,c=0,p=0,T=[],k=Date.now(),{signal:O,cleanup:de}=se(t),M=C=>({totalAttempts:c,totalRetries:c>0?Number(c)-1:0,totalDuration:Date.now()-k,lastError:C,retryHistory:T});try{if(O.aborted){u(a==null?void 0:a.onAbort,O);let m=s.normalizer(new DOMException("Aborted","AbortError")),w=s.mapError?s.mapError(m):m;return{type:"aborted",ok:!1,data:null,error:w,metrics:{totalAttempts:c,totalRetries:p,totalDuration:Date.now()-k,lastError:w,retryHistory:T}}}let C=async()=>{var I,g,K;let m=1,w=h((I=i==null?void 0:i.maxRetries)!=null?I:0);for(;;){c=m;let V=new AbortController,{signal:L,cleanup:ye}=se(O,V.signal);try{let v=Promise.resolve(e({signal:L})),A=o?await oe(v,o,L,()=>(V.abort(),new P(b(o)))):await v;return u(a==null?void 0:a.onSuccess,A,M()),u(l==null?void 0:l.info,`Task succeeded on attempt ${m}`),A}catch(v){let A=s.normalizer(v),E=s.mapError?s.mapError(A):A;if(y=E,E.code==="ABORTED"&&u(a==null?void 0:a.onAbort,L),n&&E.code==="ABORTED"||(u(a==null?void 0:a.onError,E,M(E)),u(l==null?void 0:l.error,`Task failed on attempt ${m}`,E)),E.code==="ABORTED"||E.retryable===!1)throw E;if(m<=Number(w)){let J=i==null?void 0:i.shouldRetry,pe={totalAttempts:Number(c),elapsedTime:Date.now()-k,startTime:new Date(k),lastDelay:(g=T[T.length-1])!=null&&g.delay?Number((K=T[T.length-1])==null?void 0:K.delay):void 0};if(!J||J(m,E,pe)){let me=i?re(i.strategy,m,E):0,z=ve(me,i==null?void 0:i.jitter),j=b(z);T.push({attempt:m,error:E,delay:j,timestamp:new Date}),u(a==null?void 0:a.onRetry,m,E,z),u(l==null?void 0:l.info,`Retrying in ${z}ms (attempt ${m+1})`),m+=1,await ne(j,O);continue}}throw E}finally{ye()}}};try{let m=await C(),w=M();return p=w.totalRetries,u(a==null?void 0:a.onFinally,w),{type:"success",ok:!0,data:m,error:null,metrics:w}}catch(m){let w=y!=null?y:s.normalizer(m),I=w.code==="TIMEOUT"?"timeout":w.code==="ABORTED"?"aborted":"failure",g=M(w);return p=g.totalRetries,u(a==null?void 0:a.onFinally,g),{type:I,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 i=()=>r.abort();o.addEventListener("abort",i,{once:!0}),n.push({signal:o,abort:i})}return{signal:r.signal,cleanup:()=>{for(let o of n)o.signal.removeEventListener("abort",o.abort)}}}function ve(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});
1
+ "use strict";var B=Object.defineProperty;var be=Object.getOwnPropertyDescriptor;var he=Object.getOwnPropertyNames;var Re=Object.prototype.hasOwnProperty;var Ce=(e,r,t)=>r in e?B(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var xe=(e,r)=>{for(var t in r)B(e,t,{get:r[t],enumerable:!0})},ge=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of he(r))!Re.call(e,o)&&o!==t&&B(e,o,{get:()=>r[o],enumerable:!(n=be(r,o))||n.enumerable});return e};var ke=e=>ge(B({},"__esModule",{value:!0}),e);var l=(e,r,t)=>Ce(e,typeof r!="symbol"?r+"":r,t);var _e={};xe(_e,{RetryStrategies:()=>oe,TypedError:()=>m,all:()=>pe,allOrThrow:()=>me,asMilliseconds:()=>b,asRetryCount:()=>h,default:()=>S,errorRule:()=>te,orThrow:()=>ye,run:()=>le,runOrThrow:()=>de,tryo:()=>S});module.exports=ke(_e);var m=class extends Error{constructor(t,n){var o,a;super(t);l(this,"cause");l(this,"title");l(this,"meta");l(this,"status");l(this,"raw");l(this,"path");l(this,"timestamp");l(this,"retryable");this.timestamp=Date.now(),this.retryable=(o=n.retryable)!=null?o:!0,this.name=this.constructor.name,this.cause=n.cause,this.title=n.title,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,title:this.title,message:this.message,timestamp:this.timestamp,retryable:this.retryable,cause:this.cause,raw:this.raw,path:this.path,stack:this.stack}}},_=class extends m{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0});l(this,"code","TIMEOUT")}};var U=class extends m{constructor(t,n){super(`Circuit breaker is open, reset after ${t}ms`,{cause:n,retryable:!1});l(this,"code","CIRCUIT_OPEN")}};var L=class extends m{constructor(t,n){super(t,{cause:n});l(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},K=e=>{if(e<1||!Number.isInteger(e))throw new Error(`Invalid concurrency limit: must be a positive integer, got ${e}`);return e},Z=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 z=class{constructor(r){l(this,"state");l(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,s;if(!((s=(a=(o=this.config).shouldCountAsFailure)==null?void 0:a.call(o,r))!=null?s:!0))return;let n=new Date;switch(this.state.state){case"closed":{let i=this.state.failureCount+1;i>=this.config.failureThreshold?this.state={state:"open",failureCount:i,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)}:this.state={...this.state,failureCount:i,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 U(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 ee=(e,r)=>t=>{for(let n of e){let o=n(t);if(o!==null)return o}return r(t)},re=e=>r=>r instanceof m?r:r instanceof Error?new e(r.message,r):typeof r=="string"?new e(r):new e("Unknown error occurred",r);var Oe=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.status=="number"||typeof r.statusCode=="number"},Ae=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){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(Ae(e)){let t=((r=e.code)!=null?r:"").toUpperCase();return t==="ECONNRESET"||t==="ECONNREFUSED"||t==="ETIMEDOUT"||t==="ENOTFOUND"||t==="EAI_AGAIN"}return!1},Ne=e=>typeof e=="object"&&e!==null,H=class{constructor(r){this.predicate=r}toCode(r){return new V(this.predicate,r)}toError(r){return t=>{var c;if(!this.predicate(t))return null;let n=r(t),o=n.code,a=Object.hasOwn(n,"cause")?n.cause:t,s=(c=n.meta)!=null?c:{};class i extends m{constructor(){let y=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{title:n.title,cause:a,meta:s,status:n.status,retryable:n.retryable,raw:y,path:n.path});l(this,"code",o)}}return new i}}};function Me(e,r){let t=new H(a=>a instanceof e);if(r)return t.toError(r);let o=a=>{if(!(a instanceof e))return null;if(a instanceof m)return a;let s=a,i=typeof s.code=="string"&&s.code.length>0?s.code:"UNKNOWN",c=Object.hasOwn(s,"cause"),u=Object.hasOwn(s,"raw");class d extends m{constructor(){super(s.message||i,{title:typeof s.title=="string"?s.title:void 0,cause:c?s.cause:a,meta:Ne(s.meta)?s.meta:{},status:typeof s.status=="number"?s.status:void 0,retryable:typeof s.retryable=="boolean"?s.retryable:void 0,raw:u?s.raw:a,path:typeof s.path=="string"?s.path:void 0});l(this,"code",i)}}return new d};return o.toCode=t.toCode.bind(t),o.toError=t.toError.bind(t),o}var V=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){return t=>{var c;if(!this.predicate(t))return null;let n=r(t),o=Object.hasOwn(n,"cause")?n.cause:t,a=(c=n.meta)!=null?c:{},s=this.errorCode;class i extends m{constructor(){let T=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{title:n.title,cause:o,meta:a,status:n.status,retryable:n.retryable,raw:T,path:n.path});l(this,"code",s)}}return new i}}},R={when:e=>new H(e),instance:Me},g={typed:(e=>e instanceof m?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=>Se(e)).toCode("NETWORK").with(e=>({message:(e instanceof Error,e.message||"Network error"),cause:e,raw:e})),http:R.when(e=>{var n;if(!Oe(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 m)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e,raw:e}))};var Ie=[g.typed,g.abort,g.timeout,g.http,g.network,g.unknown];var te={when:e=>R.when(e),instance:R.instance},ne=Ie;var oe={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})},se=(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*ve(Number(r));return e.maxDelay!==void 0?Math.min(n,e.maxDelay):n}case"custom":return e.calculate(r,t);default:return e}},ve=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},ae=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 ie=(e,r)=>new Promise((t,n)=>{if(r!=null&&r.aborted){n(new DOMException("Aborted","AbortError"));return}let o=i=>{r&&i&&r.removeEventListener("abort",i)},a,s=()=>{a&&clearTimeout(a),o(s),n(new DOMException("Aborted","AbortError"))};a=setTimeout(()=>{o(s),t()},e),r==null||r.addEventListener("abort",s,{once:!0})}),ue=(e,r,t,n)=>new Promise((o,a)=>{if(t!=null&&t.aborted){a(new DOMException("Aborted","AbortError"));return}let s=!1,i=setTimeout(()=>{var d;s=!0,u(),a((d=n==null?void 0:n())!=null?d:new Error(`Operation timed out after ${r}ms`))},r),c=()=>{s||(s=!0,u(),a(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(i),t==null||t.removeEventListener("abort",c)};t==null||t.addEventListener("abort",c,{once:!0}),e.then(d=>{s||(s=!0,u(),o(d))},d=>{s||(s=!0,u(),a(d))})});var De=e=>{var s,i,c;if(e.toError)return e.toError;let r=(s=e.rulesMode)!=null?s:"extend",t=(i=e.rules)!=null?i:[],n=ne,o=(c=e.fallback)!=null?c:(u=>re(L)(u)),a=r==="replace"?t:[...t,...n];return ee(a,o)},Pe=e=>{if(e)switch(e.type){case"none":return;case"full":case"equal":Z(e.ratio);return;case"custom":return;default:{let r=e;throw new Error(`Unsupported jitter configuration: ${r}`)}}},J=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(K(r.concurrency)):r.concurrency}),r.retry&&(ae(r.retry.strategy),Pe(r.retry.jitter),r={...r,retry:{...r.retry,maxRetries:Number(h(r.retry.maxRetries))}}),r},j=class e{constructor(r={}){l(this,"circuitBreaker");l(this,"config");l(this,"lastCircuitState");let{rules:t,rulesMode:n,fallback:o,toError:a,mapError:s,...i}=r,c=De(r),u={...i,errorHandling:{normalizer:c,mapError:s}};this.config=J(u),u.circuitBreaker&&(this.circuitBreaker=new z(u.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var a,s,i,c;let n=J({...this.config,...t});if(this.circuitBreaker){let u=(a=this.lastCircuitState)!=null?a:this.circuitBreaker.getState().state,d=await this.circuitBreaker.canExecute(),y=this.circuitBreaker.getState().state;if(u!==y)try{(s=n.onCircuitStateChange)==null||s.call(n,u,y)}catch{}if(this.lastCircuitState=y,!d)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let o=await Fe(r,n);if(this.circuitBreaker){let u=(i=this.lastCircuitState)!=null?i:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let d=this.circuitBreaker.getState().state;if(u!==d)try{(c=n.onCircuitStateChange)==null||c.call(n,u,d)}catch{}this.lastCircuitState=d}return o}async runOrThrow(r,t={}){return this.orThrow(r,t)}async orThrow(r,t={}){let n=await this.run(r,t);if(n.ok)return n.data;throw n.error}async all(r,t={}){var d;let n=J({...this.config,...t}),o=(d=n.concurrency)!=null?d:Number.POSITIVE_INFINITY,a=Number.isFinite(o)?Number(K(o)):Number.POSITIVE_INFINITY;if(a===Number.POSITIVE_INFINITY)return Promise.all(r.map(y=>this.run(y,n)));let s=new Array(r.length),i=0,c=async()=>{var y;for(;i<r.length&&!((y=n.signal)!=null&&y.aborted);){let T=i++;if(T>=r.length)break;let E=r[T];E&&(s[T]=await this.run(E,n))}},u=Array.from({length:Math.min(a,r.length)},()=>c());await Promise.all(u);for(let y=0;y<r.length;y++)if(!(y in s)){let T=r[y];T&&(s[y]=await this.run(T,n))}return s}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=[],s=[];for(let i of r){if(i.type==="success"){t.push(i);continue}switch(n.push(i),i.type){case"failure":o.push(i);break;case"aborted":a.push(i);break;case"timeout":s.push(i);break}}return{ok:t,errors:n,failure:o,aborted:a,timeout:s}}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,s=(c=o==null?void 0:o.normalizer)!=null?c:t.normalizer,i=(u=o==null?void 0:o.mapError)!=null?u:t.mapError;return new e({...n,...a,toError:s,mapError:i})}};function S(e={}){let r=new j(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 Fe(e,r){let{signal:t,ignoreAbort:n=!0,timeout:o,retry:a,errorHandling:s,logger:i,onSuccess:c,onError:u,onFinally:d,onAbort:y,onRetry:T}=r,E=(x,...p)=>{try{x==null||x(...p)}catch{}},M,k=0,q=0,C=[],I=Date.now(),{signal:v,cleanup:fe}=ce(t),D=x=>({totalAttempts:k,totalRetries:k>0?Number(k)-1:0,totalDuration:Date.now()-I,lastError:x,retryHistory:C});try{if(v.aborted){E(y,v);let p=s.normalizer(new DOMException("Aborted","AbortError")),w=s.mapError?s.mapError(p):p;return{type:"aborted",ok:!1,data:null,error:w,metrics:{totalAttempts:k,totalRetries:q,totalDuration:Date.now()-I,lastError:w,retryHistory:C}}}let x=async()=>{var P,O,Y;let p=1,w=h((P=a==null?void 0:a.maxRetries)!=null?P:0);for(;;){k=p;let G=new AbortController,{signal:$,cleanup:Ee}=ce(v,G.signal);try{let F=Promise.resolve(e({signal:$})),A=o?await ue(F,o,$,()=>(G.abort(),new _(b(o)))):await F;return E(c,A,D()),E(i==null?void 0:i.info,`Task succeeded on attempt ${p}`),A}catch(F){let A=s.normalizer(F),f=s.mapError?s.mapError(A):A;if(M=f,f.code==="ABORTED"&&E(y,$),n&&f.code==="ABORTED"||(E(u,f,D(f)),E(i==null?void 0:i.error,`Task failed on attempt ${p}`,f)),f.code==="ABORTED"||f.retryable===!1)throw f;if(p<=Number(w)){let Q=a==null?void 0:a.shouldRetry,we={totalAttempts:Number(k),elapsedTime:Date.now()-I,startTime:new Date(I),lastDelay:(O=C[C.length-1])!=null&&O.delay?Number((Y=C[C.length-1])==null?void 0:Y.delay):void 0};if(!Q||Q(p,f,we)){let Te=a?se(a.strategy,p,f):0,W=Be(Te,a==null?void 0:a.jitter),X=b(W);C.push({attempt:p,error:f,delay:X,timestamp:new Date}),E(T,p,f,W),E(i==null?void 0:i.info,`Retrying in ${W}ms (attempt ${p+1})`),p+=1,await ie(X,v);continue}}throw f}finally{Ee()}}};try{let p=await x(),w=D();return q=w.totalRetries,E(d,w),{type:"success",ok:!0,data:p,error:null,metrics:w}}catch(p){let w=M!=null?M:s.normalizer(p),P=w.code==="TIMEOUT"?"timeout":w.code==="ABORTED"?"aborted":"failure",O=D(w);return q=O.totalRetries,E(d,O),{type:P,ok:!1,data:null,error:w,metrics:O}}}finally{fe()}}function ce(...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 N=S(),le=N.run,de=N.runOrThrow,ye=N.orThrow,pe=N.all,me=N.allOrThrow;0&&(module.exports={RetryStrategies,TypedError,all,allOrThrow,asMilliseconds,asRetryCount,errorRule,orThrow,run,runOrThrow,tryo});
package/dist/index.d.cts CHANGED
@@ -94,6 +94,23 @@ declare class UnknownError extends TypedError<'UNKNOWN', Record<string, unknown>
94
94
  constructor(message: string, cause?: unknown);
95
95
  }
96
96
 
97
+ /**
98
+ * Modern circuit breaker implementation with enhanced state management
99
+ * Provides circuit breaker pattern with type safety and observability
100
+ */
101
+
102
+ interface CircuitBreakerConfig<E extends AnyTypedError = AnyTypedError> {
103
+ /** Number of consecutive failures before opening circuit */
104
+ readonly failureThreshold: number;
105
+ /** How long to wait before attempting to close circuit */
106
+ readonly resetTimeout: number;
107
+ /** Number of requests allowed in half-open state */
108
+ readonly halfOpenRequests: number;
109
+ /** Optional function to determine if error should count as failure */
110
+ readonly shouldCountAsFailure?: (error: E) => boolean;
111
+ }
112
+ type CircuitState = 'closed' | 'open' | 'half-open';
113
+
97
114
  /**
98
115
  * Modern result types with enhanced discriminated unions
99
116
  * Provides better type safety and more granular result categorization
@@ -141,23 +158,6 @@ interface TryoMetrics$1<E extends AnyTypedError> {
141
158
  }>;
142
159
  }
143
160
 
144
- /**
145
- * Modern circuit breaker implementation with enhanced state management
146
- * Provides circuit breaker pattern with type safety and observability
147
- */
148
-
149
- interface CircuitBreakerConfig<E extends AnyTypedError = AnyTypedError> {
150
- /** Number of consecutive failures before opening circuit */
151
- readonly failureThreshold: number;
152
- /** How long to wait before attempting to close circuit */
153
- readonly resetTimeout: number;
154
- /** Number of requests allowed in half-open state */
155
- readonly halfOpenRequests: number;
156
- /** Optional function to determine if error should count as failure */
157
- readonly shouldCountAsFailure?: (error: E) => boolean;
158
- }
159
- type CircuitState = 'closed' | 'open' | 'half-open';
160
-
161
161
  /**
162
162
  * Modern error normalization system
163
163
  * Provides type-safe error transformation and normalization
@@ -215,8 +215,18 @@ interface TryoConfig<E extends AnyTypedError = AnyTypedError> {
215
215
  readonly concurrency?: number;
216
216
  /** Logging configuration */
217
217
  readonly logger?: LoggerConfig<E>;
218
- /** Callback hooks */
219
- readonly hooks?: HookConfig<E>;
218
+ /** Called on successful execution */
219
+ readonly onSuccess?: <T>(data: T, metrics?: TryoMetrics<E>) => void;
220
+ /** Called on failed execution */
221
+ readonly onError?: (error: E, metrics?: TryoMetrics<E>) => void;
222
+ /** Called always, success or failure */
223
+ readonly onFinally?: (metrics?: TryoMetrics<E>) => void;
224
+ /** Called on abort */
225
+ readonly onAbort?: (signal: AbortSignal) => void;
226
+ /** Called before retry attempt */
227
+ readonly onRetry?: (attempt: number, error: E, delay: number) => void;
228
+ /** Called when circuit breaker state changes */
229
+ readonly onCircuitStateChange?: (from: CircuitState, to: CircuitState) => void;
220
230
  }
221
231
  interface RetryConfig<E extends AnyTypedError> {
222
232
  /** Maximum number of retry attempts */
@@ -255,20 +265,6 @@ interface LoggerConfig<E extends AnyTypedError> {
255
265
  /** Warning logging function */
256
266
  readonly warn?: (message: string, meta?: unknown) => void;
257
267
  }
258
- interface HookConfig<E extends AnyTypedError> {
259
- /** Called on successful execution */
260
- readonly onSuccess?: <T>(data: T, metrics?: TryoMetrics<E>) => void;
261
- /** Called on failed execution */
262
- readonly onError?: (error: E, metrics?: TryoMetrics<E>) => void;
263
- /** Called always, success or failure */
264
- readonly onFinally?: (metrics?: TryoMetrics<E>) => void;
265
- /** Called on abort */
266
- readonly onAbort?: (signal: AbortSignal) => void;
267
- /** Called before retry attempt */
268
- readonly onRetry?: (attempt: number, error: E, delay: number) => void;
269
- /** Called when circuit breaker state changes */
270
- readonly onCircuitStateChange?: (from: CircuitState, to: CircuitState) => void;
271
- }
272
268
  type TryoMetrics<E extends AnyTypedError> = TryoMetrics$1<E>;
273
269
  type JitterConfig = {
274
270
  type: 'none';
@@ -317,15 +313,14 @@ type CheckUniqueCodes<T extends readonly unknown[], Seen = never> = T extends re
317
313
  type UniqueRulesConstraint<TRules extends readonly RuleLike[]> = CheckUniqueCodes<TRules> extends true ? unknown : {
318
314
  __duplicate_error_codes__: CheckUniqueCodes<TRules>;
319
315
  };
320
- type EnsureRuleTuple<TRules extends readonly unknown[]> = TRules extends readonly RuleLike[] ? TRules : never;
321
- declare function tryo<const TRules extends readonly unknown[]>(options: Omit<TryoOptions<InferErrorFromRules<EnsureRuleTuple<TRules>>>, 'rules' | 'rulesMode'> & {
316
+ declare function tryo<const TRules extends readonly RuleLike[]>(options: Omit<TryoOptions<InferErrorFromRules<TRules>>, 'rules' | 'rulesMode'> & {
322
317
  rules: TRules;
323
318
  rulesMode: 'replace';
324
- } & UniqueRulesConstraint<EnsureRuleTuple<TRules>>): Tryo<InferErrorFromRules<EnsureRuleTuple<TRules>>>;
325
- declare function tryo<const TRules extends readonly unknown[]>(options: Omit<TryoOptions<InferErrorFromRules<EnsureRuleTuple<TRules>> | DefaultError>, 'rules'> & {
319
+ } & UniqueRulesConstraint<TRules>): Tryo<InferErrorFromRules<TRules>>;
320
+ declare function tryo<const TRules extends readonly RuleLike[]>(options: Omit<TryoOptions<InferErrorFromRules<TRules> | DefaultError>, 'rules'> & {
326
321
  rules: TRules;
327
322
  rulesMode?: 'extend';
328
- } & UniqueRulesConstraint<EnsureRuleTuple<TRules>>): Tryo<InferErrorFromRules<EnsureRuleTuple<TRules>> | DefaultError>;
323
+ } & UniqueRulesConstraint<TRules>): Tryo<InferErrorFromRules<TRules> | DefaultError>;
329
324
  declare function tryo<E extends AnyTypedError = DefaultError>(options?: TryoOptions<E>): Tryo<E>;
330
325
  type Tryo<E extends AnyTypedError = AnyTypedError> = {
331
326
  run: <T>(task: (ctx: {
@@ -363,7 +358,12 @@ declare const run: <T>(task: (ctx: {
363
358
  readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
364
359
  readonly concurrency?: number | undefined;
365
360
  readonly logger?: LoggerConfig<DefaultError> | undefined;
366
- readonly hooks?: HookConfig<DefaultError> | undefined;
361
+ readonly onSuccess?: (<T_1>(data: T_1, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
362
+ readonly onError?: ((error: DefaultError, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
363
+ readonly onFinally?: ((metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
364
+ readonly onAbort?: ((signal: AbortSignal) => void) | undefined;
365
+ readonly onRetry?: ((attempt: number, error: DefaultError, delay: number) => void) | undefined;
366
+ readonly onCircuitStateChange?: ((from: CircuitState, to: CircuitState) => void) | undefined;
367
367
  } | undefined) => Promise<TryoResult<T, DefaultError>>;
368
368
  declare const runOrThrow: <T>(task: (ctx: {
369
369
  signal: AbortSignal;
@@ -375,7 +375,12 @@ declare const runOrThrow: <T>(task: (ctx: {
375
375
  readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
376
376
  readonly concurrency?: number | undefined;
377
377
  readonly logger?: LoggerConfig<DefaultError> | undefined;
378
- readonly hooks?: HookConfig<DefaultError> | undefined;
378
+ readonly onSuccess?: (<T_1>(data: T_1, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
379
+ readonly onError?: ((error: DefaultError, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
380
+ readonly onFinally?: ((metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
381
+ readonly onAbort?: ((signal: AbortSignal) => void) | undefined;
382
+ readonly onRetry?: ((attempt: number, error: DefaultError, delay: number) => void) | undefined;
383
+ readonly onCircuitStateChange?: ((from: CircuitState, to: CircuitState) => void) | undefined;
379
384
  } | undefined) => Promise<T>;
380
385
  declare const orThrow: <T>(task: (ctx: {
381
386
  signal: AbortSignal;
@@ -387,7 +392,12 @@ declare const orThrow: <T>(task: (ctx: {
387
392
  readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
388
393
  readonly concurrency?: number | undefined;
389
394
  readonly logger?: LoggerConfig<DefaultError> | undefined;
390
- readonly hooks?: HookConfig<DefaultError> | undefined;
395
+ readonly onSuccess?: (<T_1>(data: T_1, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
396
+ readonly onError?: ((error: DefaultError, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
397
+ readonly onFinally?: ((metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
398
+ readonly onAbort?: ((signal: AbortSignal) => void) | undefined;
399
+ readonly onRetry?: ((attempt: number, error: DefaultError, delay: number) => void) | undefined;
400
+ readonly onCircuitStateChange?: ((from: CircuitState, to: CircuitState) => void) | undefined;
391
401
  } | undefined) => Promise<T>;
392
402
  declare const all: <T>(tasks: ((ctx: {
393
403
  signal: AbortSignal;
@@ -399,7 +409,12 @@ declare const all: <T>(tasks: ((ctx: {
399
409
  readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
400
410
  concurrency?: number | undefined;
401
411
  readonly logger?: LoggerConfig<DefaultError> | undefined;
402
- readonly hooks?: HookConfig<DefaultError> | undefined;
412
+ readonly onSuccess?: (<T_1>(data: T_1, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
413
+ readonly onError?: ((error: DefaultError, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
414
+ readonly onFinally?: ((metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
415
+ readonly onAbort?: ((signal: AbortSignal) => void) | undefined;
416
+ readonly onRetry?: ((attempt: number, error: DefaultError, delay: number) => void) | undefined;
417
+ readonly onCircuitStateChange?: ((from: CircuitState, to: CircuitState) => void) | undefined;
403
418
  } | undefined) => Promise<TryoResult<T, DefaultError>[]>;
404
419
  declare const allOrThrow: <T>(tasks: ((ctx: {
405
420
  signal: AbortSignal;
@@ -411,7 +426,12 @@ declare const allOrThrow: <T>(tasks: ((ctx: {
411
426
  readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
412
427
  concurrency?: number | undefined;
413
428
  readonly logger?: LoggerConfig<DefaultError> | undefined;
414
- readonly hooks?: HookConfig<DefaultError> | undefined;
429
+ readonly onSuccess?: (<T_1>(data: T_1, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
430
+ readonly onError?: ((error: DefaultError, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
431
+ readonly onFinally?: ((metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
432
+ readonly onAbort?: ((signal: AbortSignal) => void) | undefined;
433
+ readonly onRetry?: ((attempt: number, error: DefaultError, delay: number) => void) | undefined;
434
+ readonly onCircuitStateChange?: ((from: CircuitState, to: CircuitState) => void) | undefined;
415
435
  } | undefined) => Promise<T[]>;
416
436
 
417
437
  /**
package/dist/index.d.ts CHANGED
@@ -94,6 +94,23 @@ declare class UnknownError extends TypedError<'UNKNOWN', Record<string, unknown>
94
94
  constructor(message: string, cause?: unknown);
95
95
  }
96
96
 
97
+ /**
98
+ * Modern circuit breaker implementation with enhanced state management
99
+ * Provides circuit breaker pattern with type safety and observability
100
+ */
101
+
102
+ interface CircuitBreakerConfig<E extends AnyTypedError = AnyTypedError> {
103
+ /** Number of consecutive failures before opening circuit */
104
+ readonly failureThreshold: number;
105
+ /** How long to wait before attempting to close circuit */
106
+ readonly resetTimeout: number;
107
+ /** Number of requests allowed in half-open state */
108
+ readonly halfOpenRequests: number;
109
+ /** Optional function to determine if error should count as failure */
110
+ readonly shouldCountAsFailure?: (error: E) => boolean;
111
+ }
112
+ type CircuitState = 'closed' | 'open' | 'half-open';
113
+
97
114
  /**
98
115
  * Modern result types with enhanced discriminated unions
99
116
  * Provides better type safety and more granular result categorization
@@ -141,23 +158,6 @@ interface TryoMetrics$1<E extends AnyTypedError> {
141
158
  }>;
142
159
  }
143
160
 
144
- /**
145
- * Modern circuit breaker implementation with enhanced state management
146
- * Provides circuit breaker pattern with type safety and observability
147
- */
148
-
149
- interface CircuitBreakerConfig<E extends AnyTypedError = AnyTypedError> {
150
- /** Number of consecutive failures before opening circuit */
151
- readonly failureThreshold: number;
152
- /** How long to wait before attempting to close circuit */
153
- readonly resetTimeout: number;
154
- /** Number of requests allowed in half-open state */
155
- readonly halfOpenRequests: number;
156
- /** Optional function to determine if error should count as failure */
157
- readonly shouldCountAsFailure?: (error: E) => boolean;
158
- }
159
- type CircuitState = 'closed' | 'open' | 'half-open';
160
-
161
161
  /**
162
162
  * Modern error normalization system
163
163
  * Provides type-safe error transformation and normalization
@@ -215,8 +215,18 @@ interface TryoConfig<E extends AnyTypedError = AnyTypedError> {
215
215
  readonly concurrency?: number;
216
216
  /** Logging configuration */
217
217
  readonly logger?: LoggerConfig<E>;
218
- /** Callback hooks */
219
- readonly hooks?: HookConfig<E>;
218
+ /** Called on successful execution */
219
+ readonly onSuccess?: <T>(data: T, metrics?: TryoMetrics<E>) => void;
220
+ /** Called on failed execution */
221
+ readonly onError?: (error: E, metrics?: TryoMetrics<E>) => void;
222
+ /** Called always, success or failure */
223
+ readonly onFinally?: (metrics?: TryoMetrics<E>) => void;
224
+ /** Called on abort */
225
+ readonly onAbort?: (signal: AbortSignal) => void;
226
+ /** Called before retry attempt */
227
+ readonly onRetry?: (attempt: number, error: E, delay: number) => void;
228
+ /** Called when circuit breaker state changes */
229
+ readonly onCircuitStateChange?: (from: CircuitState, to: CircuitState) => void;
220
230
  }
221
231
  interface RetryConfig<E extends AnyTypedError> {
222
232
  /** Maximum number of retry attempts */
@@ -255,20 +265,6 @@ interface LoggerConfig<E extends AnyTypedError> {
255
265
  /** Warning logging function */
256
266
  readonly warn?: (message: string, meta?: unknown) => void;
257
267
  }
258
- interface HookConfig<E extends AnyTypedError> {
259
- /** Called on successful execution */
260
- readonly onSuccess?: <T>(data: T, metrics?: TryoMetrics<E>) => void;
261
- /** Called on failed execution */
262
- readonly onError?: (error: E, metrics?: TryoMetrics<E>) => void;
263
- /** Called always, success or failure */
264
- readonly onFinally?: (metrics?: TryoMetrics<E>) => void;
265
- /** Called on abort */
266
- readonly onAbort?: (signal: AbortSignal) => void;
267
- /** Called before retry attempt */
268
- readonly onRetry?: (attempt: number, error: E, delay: number) => void;
269
- /** Called when circuit breaker state changes */
270
- readonly onCircuitStateChange?: (from: CircuitState, to: CircuitState) => void;
271
- }
272
268
  type TryoMetrics<E extends AnyTypedError> = TryoMetrics$1<E>;
273
269
  type JitterConfig = {
274
270
  type: 'none';
@@ -317,15 +313,14 @@ type CheckUniqueCodes<T extends readonly unknown[], Seen = never> = T extends re
317
313
  type UniqueRulesConstraint<TRules extends readonly RuleLike[]> = CheckUniqueCodes<TRules> extends true ? unknown : {
318
314
  __duplicate_error_codes__: CheckUniqueCodes<TRules>;
319
315
  };
320
- type EnsureRuleTuple<TRules extends readonly unknown[]> = TRules extends readonly RuleLike[] ? TRules : never;
321
- declare function tryo<const TRules extends readonly unknown[]>(options: Omit<TryoOptions<InferErrorFromRules<EnsureRuleTuple<TRules>>>, 'rules' | 'rulesMode'> & {
316
+ declare function tryo<const TRules extends readonly RuleLike[]>(options: Omit<TryoOptions<InferErrorFromRules<TRules>>, 'rules' | 'rulesMode'> & {
322
317
  rules: TRules;
323
318
  rulesMode: 'replace';
324
- } & UniqueRulesConstraint<EnsureRuleTuple<TRules>>): Tryo<InferErrorFromRules<EnsureRuleTuple<TRules>>>;
325
- declare function tryo<const TRules extends readonly unknown[]>(options: Omit<TryoOptions<InferErrorFromRules<EnsureRuleTuple<TRules>> | DefaultError>, 'rules'> & {
319
+ } & UniqueRulesConstraint<TRules>): Tryo<InferErrorFromRules<TRules>>;
320
+ declare function tryo<const TRules extends readonly RuleLike[]>(options: Omit<TryoOptions<InferErrorFromRules<TRules> | DefaultError>, 'rules'> & {
326
321
  rules: TRules;
327
322
  rulesMode?: 'extend';
328
- } & UniqueRulesConstraint<EnsureRuleTuple<TRules>>): Tryo<InferErrorFromRules<EnsureRuleTuple<TRules>> | DefaultError>;
323
+ } & UniqueRulesConstraint<TRules>): Tryo<InferErrorFromRules<TRules> | DefaultError>;
329
324
  declare function tryo<E extends AnyTypedError = DefaultError>(options?: TryoOptions<E>): Tryo<E>;
330
325
  type Tryo<E extends AnyTypedError = AnyTypedError> = {
331
326
  run: <T>(task: (ctx: {
@@ -363,7 +358,12 @@ declare const run: <T>(task: (ctx: {
363
358
  readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
364
359
  readonly concurrency?: number | undefined;
365
360
  readonly logger?: LoggerConfig<DefaultError> | undefined;
366
- readonly hooks?: HookConfig<DefaultError> | undefined;
361
+ readonly onSuccess?: (<T_1>(data: T_1, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
362
+ readonly onError?: ((error: DefaultError, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
363
+ readonly onFinally?: ((metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
364
+ readonly onAbort?: ((signal: AbortSignal) => void) | undefined;
365
+ readonly onRetry?: ((attempt: number, error: DefaultError, delay: number) => void) | undefined;
366
+ readonly onCircuitStateChange?: ((from: CircuitState, to: CircuitState) => void) | undefined;
367
367
  } | undefined) => Promise<TryoResult<T, DefaultError>>;
368
368
  declare const runOrThrow: <T>(task: (ctx: {
369
369
  signal: AbortSignal;
@@ -375,7 +375,12 @@ declare const runOrThrow: <T>(task: (ctx: {
375
375
  readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
376
376
  readonly concurrency?: number | undefined;
377
377
  readonly logger?: LoggerConfig<DefaultError> | undefined;
378
- readonly hooks?: HookConfig<DefaultError> | undefined;
378
+ readonly onSuccess?: (<T_1>(data: T_1, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
379
+ readonly onError?: ((error: DefaultError, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
380
+ readonly onFinally?: ((metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
381
+ readonly onAbort?: ((signal: AbortSignal) => void) | undefined;
382
+ readonly onRetry?: ((attempt: number, error: DefaultError, delay: number) => void) | undefined;
383
+ readonly onCircuitStateChange?: ((from: CircuitState, to: CircuitState) => void) | undefined;
379
384
  } | undefined) => Promise<T>;
380
385
  declare const orThrow: <T>(task: (ctx: {
381
386
  signal: AbortSignal;
@@ -387,7 +392,12 @@ declare const orThrow: <T>(task: (ctx: {
387
392
  readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
388
393
  readonly concurrency?: number | undefined;
389
394
  readonly logger?: LoggerConfig<DefaultError> | undefined;
390
- readonly hooks?: HookConfig<DefaultError> | undefined;
395
+ readonly onSuccess?: (<T_1>(data: T_1, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
396
+ readonly onError?: ((error: DefaultError, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
397
+ readonly onFinally?: ((metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
398
+ readonly onAbort?: ((signal: AbortSignal) => void) | undefined;
399
+ readonly onRetry?: ((attempt: number, error: DefaultError, delay: number) => void) | undefined;
400
+ readonly onCircuitStateChange?: ((from: CircuitState, to: CircuitState) => void) | undefined;
391
401
  } | undefined) => Promise<T>;
392
402
  declare const all: <T>(tasks: ((ctx: {
393
403
  signal: AbortSignal;
@@ -399,7 +409,12 @@ declare const all: <T>(tasks: ((ctx: {
399
409
  readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
400
410
  concurrency?: number | undefined;
401
411
  readonly logger?: LoggerConfig<DefaultError> | undefined;
402
- readonly hooks?: HookConfig<DefaultError> | undefined;
412
+ readonly onSuccess?: (<T_1>(data: T_1, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
413
+ readonly onError?: ((error: DefaultError, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
414
+ readonly onFinally?: ((metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
415
+ readonly onAbort?: ((signal: AbortSignal) => void) | undefined;
416
+ readonly onRetry?: ((attempt: number, error: DefaultError, delay: number) => void) | undefined;
417
+ readonly onCircuitStateChange?: ((from: CircuitState, to: CircuitState) => void) | undefined;
403
418
  } | undefined) => Promise<TryoResult<T, DefaultError>[]>;
404
419
  declare const allOrThrow: <T>(tasks: ((ctx: {
405
420
  signal: AbortSignal;
@@ -411,7 +426,12 @@ declare const allOrThrow: <T>(tasks: ((ctx: {
411
426
  readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
412
427
  concurrency?: number | undefined;
413
428
  readonly logger?: LoggerConfig<DefaultError> | undefined;
414
- readonly hooks?: HookConfig<DefaultError> | undefined;
429
+ readonly onSuccess?: (<T_1>(data: T_1, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
430
+ readonly onError?: ((error: DefaultError, metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
431
+ readonly onFinally?: ((metrics?: TryoMetrics$1<DefaultError> | undefined) => void) | undefined;
432
+ readonly onAbort?: ((signal: AbortSignal) => void) | undefined;
433
+ readonly onRetry?: ((attempt: number, error: DefaultError, delay: number) => void) | undefined;
434
+ readonly onCircuitStateChange?: ((from: CircuitState, to: CircuitState) => void) | undefined;
415
435
  } | undefined) => Promise<T[]>;
416
436
 
417
437
  /**
package/dist/index.js CHANGED
@@ -1 +1 @@
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 E=class extends Error{constructor(t,n){var s,i;super(t);d(this,"cause");d(this,"title");d(this,"meta");d(this,"status");d(this,"raw");d(this,"path");d(this,"timestamp");d(this,"retryable");this.timestamp=Date.now(),this.retryable=(s=n.retryable)!=null?s:!0,this.name=this.constructor.name,this.cause=n.cause,this.title=n.title,this.meta=(i=n.meta)!=null?i:{},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,title:this.title,message:this.message,timestamp:this.timestamp,retryable:this.retryable,cause:this.cause,raw:this.raw,path:this.path,stack:this.stack}}},v=class extends E{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0});d(this,"code","TIMEOUT")}};var D=class extends E{constructor(t,n){super(`Circuit breaker is open, reset after ${t}ms`,{cause:n,retryable:!1});d(this,"code","CIRCUIT_OPEN")}};var P=class extends E{constructor(t,n){super(t,{cause: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},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};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 s,i,o;if(!((o=(i=(s=this.config).shouldCountAsFailure)==null?void 0:i.call(s,r))!=null?o:!0))return;let n=new Date;switch(this.state.state){case"closed":{let a=this.state.failureCount+1;a>=this.config.failureThreshold?this.state={state:"open",failureCount:a,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)}:this.state={...this.state,failureCount:a,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 D(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 Y=(e,r)=>t=>{for(let n of e){let s=n(t);if(s!==null)return s}return r(t)},G=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 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},ye=e=>typeof e=="object"&&e!==null,B=class{constructor(r){this.predicate=r}toCode(r){return new H(this.predicate,r)}toError(r){return t=>{var l;if(!this.predicate(t))return null;let n=r(t),s=n.code,i=Object.hasOwn(n,"cause")?n.cause:t,o=(l=n.meta)!=null?l:{};class a extends E{constructor(){let c=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{title:n.title,cause:i,meta:o,status:n.status,retryable:n.retryable,raw:c,path:n.path});d(this,"code",s)}}return new a}}};function pe(e,r){let t=new B(i=>i instanceof e);if(r)return t.toError(r);let s=i=>{if(!(i instanceof e))return null;if(i instanceof E)return i;let o=i,a=typeof o.code=="string"&&o.code.length>0?o.code:"UNKNOWN",l=Object.hasOwn(o,"cause"),u=Object.hasOwn(o,"raw");class y extends E{constructor(){super(o.message||a,{title:typeof o.title=="string"?o.title:void 0,cause:l?o.cause:i,meta:ye(o.meta)?o.meta:{},status:typeof o.status=="number"?o.status:void 0,retryable:typeof o.retryable=="boolean"?o.retryable:void 0,raw:u?o.raw:i,path:typeof o.path=="string"?o.path:void 0});d(this,"code",a)}}return new y};return s.toCode=t.toCode.bind(t),s.toError=t.toError.bind(t),s}var H=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){return t=>{var l;if(!this.predicate(t))return null;let n=r(t),s=Object.hasOwn(n,"cause")?n.cause:t,i=(l=n.meta)!=null?l:{},o=this.errorCode;class a extends E{constructor(){let p=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{title:n.title,cause:s,meta:i,status:n.status,retryable:n.retryable,raw:p,path:n.path});d(this,"code",o)}}return new a}}},h={when:e=>new B(e),instance:pe},x={typed:(e=>e instanceof E?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 E)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e,raw:e}))};var me=[x.typed,x.abort,x.timeout,x.http,x.network,x.unknown];var fe={when:e=>h.when(e),instance:h.instance},Q=me;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})},X=(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*Te(Number(r));return e.maxDelay!==void 0?Math.min(n,e.maxDelay):n}case"custom":return e.calculate(r,t);default:return e}},Te=e=>{if(e<=1)return 1;let r=1,t=1;for(let n=2;n<=e;n++){let s=r+t;r=t,t=s}return t},Z=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 s=a=>{r&&a&&r.removeEventListener("abort",a)},i,o=()=>{i&&clearTimeout(i),s(o),n(new DOMException("Aborted","AbortError"))};i=setTimeout(()=>{s(o),t()},e),r==null||r.addEventListener("abort",o,{once:!0})}),re=(e,r,t,n)=>new Promise((s,i)=>{if(t!=null&&t.aborted){i(new DOMException("Aborted","AbortError"));return}let o=!1,a=setTimeout(()=>{var y;o=!0,u(),i((y=n==null?void 0:n())!=null?y:new Error(`Operation timed out after ${r}ms`))},r),l=()=>{o||(o=!0,u(),i(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(a),t==null||t.removeEventListener("abort",l)};t==null||t.addEventListener("abort",l,{once:!0}),e.then(y=>{o||(o=!0,u(),s(y))},y=>{o||(o=!0,u(),i(y))})});var we=e=>{var o,a,l;if(e.toError)return e.toError;let r=(o=e.rulesMode)!=null?o:"extend",t=(a=e.rules)!=null?a:[],n=Q,s=(l=e.fallback)!=null?l:(u=>G(P)(u)),i=r==="replace"?t:[...t,...n];return Y(i,s)},be=e=>{if(e)switch(e.type){case"none":return;case"full":case"equal":j(e.ratio);return;case"custom":return;default:{let r=e;throw new Error(`Unsupported jitter configuration: ${r}`)}}},q=e=>{let r=e;return r.timeout!==void 0&&(r={...r,timeout:Number(b(r.timeout))}),r.concurrency!==void 0&&(r={...r,concurrency:Number.isFinite(r.concurrency)?Number(z(r.concurrency)):r.concurrency}),r.retry&&(Z(r.retry.strategy),be(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:s,toError:i,mapError:o,...a}=r,l=we(r),u={...a,errorHandling:{normalizer:l,mapError:o}};this.config=q(u),u.circuitBreaker&&(this.circuitBreaker=new F(u.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var i,o,a,l,u,y;let n=q({...this.config,...t});if(this.circuitBreaker){let c=(i=this.lastCircuitState)!=null?i:this.circuitBreaker.getState().state,p=await this.circuitBreaker.canExecute(),T=this.circuitBreaker.getState().state;if(c!==T)try{(a=(o=n.hooks)==null?void 0:o.onCircuitStateChange)==null||a.call(o,c,T)}catch{}if(this.lastCircuitState=T,!p)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let s=await he(r,n);if(this.circuitBreaker){let c=(l=this.lastCircuitState)!=null?l:this.circuitBreaker.getState().state;s.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(s.error);let p=this.circuitBreaker.getState().state;if(c!==p)try{(y=(u=n.hooks)==null?void 0:u.onCircuitStateChange)==null||y.call(u,c,p)}catch{}this.lastCircuitState=p}return s}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=q({...this.config,...t}),s=(y=n.concurrency)!=null?y:Number.POSITIVE_INFINITY,i=Number.isFinite(s)?Number(z(s)):Number.POSITIVE_INFINITY;if(i===Number.POSITIVE_INFINITY)return Promise.all(r.map(c=>this.run(c,n)));let o=new Array(r.length),a=0,l=async()=>{var c;for(;a<r.length&&!((c=n.signal)!=null&&c.aborted);){let p=a++;if(p>=r.length)break;let T=r[p];T&&(o[p]=await this.run(T,n))}},u=Array.from({length:Math.min(i,r.length)},()=>l());await Promise.all(u);for(let c=0;c<r.length;c++)if(!(c in o)){let p=r[c];p&&(o[c]=await this.run(p,n))}return o}async allOrThrow(r,t={}){return(await this.all(r,t)).map(s=>{if(!s.ok)throw s.error;return s.data})}partitionAll(r){let t=[],n=[],s=[],i=[],o=[];for(let a of r){if(a.type==="success"){t.push(a);continue}switch(n.push(a),a.type){case"failure":s.push(a);break;case"aborted":i.push(a);break;case"timeout":o.push(a);break}}return{ok:t,errors:n,failure:s,aborted:i,timeout:o}}getCircuitBreakerState(){var r;return(r=this.circuitBreaker)==null?void 0:r.getState()}resetCircuitBreaker(){var r;(r=this.circuitBreaker)==null||r.reset()}getConfig(){return{...this.config}}withConfig(r){var l,u;let{errorHandling:t,...n}=this.config,{errorHandling:s,...i}=r,o=(l=s==null?void 0:s.normalizer)!=null?l:t.normalizer,a=(u=s==null?void 0:s.mapError)!=null?u:t.mapError;return new e({...n,...i,toError:o,mapError:a})}};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 he(e,r){let{signal:t,ignoreAbort:n=!0,timeout:s,retry:i,errorHandling:o,hooks:a,logger:l}=r,u=(R,...m)=>{try{R==null||R(...m)}catch{}},y,c=0,p=0,T=[],k=Date.now(),{signal:O,cleanup:ne}=te(t),N=R=>({totalAttempts:c,totalRetries:c>0?Number(c)-1:0,totalDuration:Date.now()-k,lastError:R,retryHistory:T});try{if(O.aborted){u(a==null?void 0:a.onAbort,O);let m=o.normalizer(new DOMException("Aborted","AbortError")),w=o.mapError?o.mapError(m):m;return{type:"aborted",ok:!1,data:null,error:w,metrics:{totalAttempts:c,totalRetries:p,totalDuration:Date.now()-k,lastError:w,retryHistory:T}}}let R=async()=>{var M,g,W;let m=1,w=C((M=i==null?void 0:i.maxRetries)!=null?M:0);for(;;){c=m;let K=new AbortController,{signal:U,cleanup:oe}=te(O,K.signal);try{let I=Promise.resolve(e({signal:U})),A=s?await re(I,s,U,()=>(K.abort(),new v(b(s)))):await I;return u(a==null?void 0:a.onSuccess,A,N()),u(l==null?void 0:l.info,`Task succeeded on attempt ${m}`),A}catch(I){let A=o.normalizer(I),f=o.mapError?o.mapError(A):A;if(y=f,f.code==="ABORTED"&&u(a==null?void 0:a.onAbort,U),n&&f.code==="ABORTED"||(u(a==null?void 0:a.onError,f,N(f)),u(l==null?void 0:l.error,`Task failed on attempt ${m}`,f)),f.code==="ABORTED"||f.retryable===!1)throw f;if(m<=Number(w)){let V=i==null?void 0:i.shouldRetry,se={totalAttempts:Number(c),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(!V||V(m,f,se)){let ae=i?X(i.strategy,m,f):0,L=Re(ae,i==null?void 0:i.jitter),J=b(L);T.push({attempt:m,error:f,delay:J,timestamp:new Date}),u(a==null?void 0:a.onRetry,m,f,L),u(l==null?void 0:l.info,`Retrying in ${L}ms (attempt ${m+1})`),m+=1,await ee(J,O);continue}}throw f}finally{oe()}}};try{let m=await R(),w=N();return p=w.totalRetries,u(a==null?void 0:a.onFinally,w),{type:"success",ok:!0,data:m,error:null,metrics:w}}catch(m){let w=y!=null?y:o.normalizer(m),M=w.code==="TIMEOUT"?"timeout":w.code==="ABORTED"?"aborted":"failure",g=N(w);return p=g.totalRetries,u(a==null?void 0:a.onFinally,g),{type:M,ok:!1,data:null,error:w,metrics:g}}}finally{ne()}}function te(...e){let r=new AbortController,t=e.filter(s=>s!==void 0);if(t.length===0)return{signal:r.signal,cleanup:()=>{}};if(t.some(s=>s.aborted))return r.abort(),{signal:r.signal,cleanup:()=>{}};let n=[];for(let s of t){let i=()=>r.abort();s.addEventListener("abort",i,{once:!0}),n.push({signal:s,abort:i})}return{signal:r.signal,cleanup:()=>{for(let s of n)s.signal.removeEventListener("abort",s.abort)}}}function Re(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)),s=Number(e);return n+Math.random()*(s-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=_(),Ce=S.run,xe=S.runOrThrow,ge=S.orThrow,ke=S.all,Oe=S.allOrThrow;export{Ee as RetryStrategies,E as TypedError,ke as all,Oe as allOrThrow,b as asMilliseconds,C as asRetryCount,_ as default,fe as errorRule,ge as orThrow,Ce as run,xe as runOrThrow,_ as tryo};
1
+ var de=Object.defineProperty;var ye=(e,r,t)=>r in e?de(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var l=(e,r,t)=>ye(e,typeof r!="symbol"?r+"":r,t);var f=class extends Error{constructor(t,n){var o,a;super(t);l(this,"cause");l(this,"title");l(this,"meta");l(this,"status");l(this,"raw");l(this,"path");l(this,"timestamp");l(this,"retryable");this.timestamp=Date.now(),this.retryable=(o=n.retryable)!=null?o:!0,this.name=this.constructor.name,this.cause=n.cause,this.title=n.title,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,title:this.title,message:this.message,timestamp:this.timestamp,retryable:this.retryable,cause:this.cause,raw:this.raw,path:this.path,stack:this.stack}}},F=class extends f{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0});l(this,"code","TIMEOUT")}};var B=class extends f{constructor(t,n){super(`Circuit breaker is open, reset after ${t}ms`,{cause:n,retryable:!1});l(this,"code","CIRCUIT_OPEN")}};var _=class extends f{constructor(t,n){super(t,{cause:n});l(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},x=e=>{if(e<0||!Number.isInteger(e))throw new Error(`Invalid retry count: must be a non-negative integer, got ${e}`);return e},W=e=>{if(e<1||!Number.isInteger(e))throw new Error(`Invalid concurrency limit: must be a positive integer, got ${e}`);return e},X=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 U=class{constructor(r){l(this,"state");l(this,"config");this.config={failureThreshold:x(r.failureThreshold),resetTimeout:b(r.resetTimeout),halfOpenRequests:x(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,s;if(!((s=(a=(o=this.config).shouldCountAsFailure)==null?void 0:a.call(o,r))!=null?s:!0))return;let n=new Date;switch(this.state.state){case"closed":{let i=this.state.failureCount+1;i>=this.config.failureThreshold?this.state={state:"open",failureCount:i,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)}:this.state={...this.state,failureCount:i,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 B(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 Z=(e,r)=>t=>{for(let n of e){let o=n(t);if(o!==null)return o}return r(t)},ee=e=>r=>r instanceof f?r:r instanceof Error?new e(r.message,r):typeof r=="string"?new e(r):new e("Unknown error occurred",r);var pe=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.status=="number"||typeof r.statusCode=="number"},me=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.code=="string"&&r.code.length>0},fe=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(me(e)){let t=((r=e.code)!=null?r:"").toUpperCase();return t==="ECONNRESET"||t==="ECONNREFUSED"||t==="ETIMEDOUT"||t==="ENOTFOUND"||t==="EAI_AGAIN"}return!1},Ee=e=>typeof e=="object"&&e!==null,L=class{constructor(r){this.predicate=r}toCode(r){return new K(this.predicate,r)}toError(r){return t=>{var c;if(!this.predicate(t))return null;let n=r(t),o=n.code,a=Object.hasOwn(n,"cause")?n.cause:t,s=(c=n.meta)!=null?c:{};class i extends f{constructor(){let y=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{title:n.title,cause:a,meta:s,status:n.status,retryable:n.retryable,raw:y,path:n.path});l(this,"code",o)}}return new i}}};function we(e,r){let t=new L(a=>a instanceof e);if(r)return t.toError(r);let o=a=>{if(!(a instanceof e))return null;if(a instanceof f)return a;let s=a,i=typeof s.code=="string"&&s.code.length>0?s.code:"UNKNOWN",c=Object.hasOwn(s,"cause"),u=Object.hasOwn(s,"raw");class d extends f{constructor(){super(s.message||i,{title:typeof s.title=="string"?s.title:void 0,cause:c?s.cause:a,meta:Ee(s.meta)?s.meta:{},status:typeof s.status=="number"?s.status:void 0,retryable:typeof s.retryable=="boolean"?s.retryable:void 0,raw:u?s.raw:a,path:typeof s.path=="string"?s.path:void 0});l(this,"code",i)}}return new d};return o.toCode=t.toCode.bind(t),o.toError=t.toError.bind(t),o}var K=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){return t=>{var c;if(!this.predicate(t))return null;let n=r(t),o=Object.hasOwn(n,"cause")?n.cause:t,a=(c=n.meta)!=null?c:{},s=this.errorCode;class i extends f{constructor(){let T=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{title:n.title,cause:o,meta:a,status:n.status,retryable:n.retryable,raw:T,path:n.path});l(this,"code",s)}}return new i}}},h={when:e=>new L(e),instance:we},g={typed:(e=>e instanceof f?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=>fe(e)).toCode("NETWORK").with(e=>({message:(e instanceof Error,e.message||"Network error"),cause:e,raw:e})),http:h.when(e=>{var n;if(!pe(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 f)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e,raw:e}))};var Te=[g.typed,g.abort,g.timeout,g.http,g.network,g.unknown];var be={when:e=>h.when(e),instance:h.instance},re=Te;var he={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})},te=(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*Re(Number(r));return e.maxDelay!==void 0?Math.min(n,e.maxDelay):n}case"custom":return e.calculate(r,t);default:return e}},Re=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},ne=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 oe=(e,r)=>new Promise((t,n)=>{if(r!=null&&r.aborted){n(new DOMException("Aborted","AbortError"));return}let o=i=>{r&&i&&r.removeEventListener("abort",i)},a,s=()=>{a&&clearTimeout(a),o(s),n(new DOMException("Aborted","AbortError"))};a=setTimeout(()=>{o(s),t()},e),r==null||r.addEventListener("abort",s,{once:!0})}),se=(e,r,t,n)=>new Promise((o,a)=>{if(t!=null&&t.aborted){a(new DOMException("Aborted","AbortError"));return}let s=!1,i=setTimeout(()=>{var d;s=!0,u(),a((d=n==null?void 0:n())!=null?d:new Error(`Operation timed out after ${r}ms`))},r),c=()=>{s||(s=!0,u(),a(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(i),t==null||t.removeEventListener("abort",c)};t==null||t.addEventListener("abort",c,{once:!0}),e.then(d=>{s||(s=!0,u(),o(d))},d=>{s||(s=!0,u(),a(d))})});var Ce=e=>{var s,i,c;if(e.toError)return e.toError;let r=(s=e.rulesMode)!=null?s:"extend",t=(i=e.rules)!=null?i:[],n=re,o=(c=e.fallback)!=null?c:(u=>ee(_)(u)),a=r==="replace"?t:[...t,...n];return Z(a,o)},xe=e=>{if(e)switch(e.type){case"none":return;case"full":case"equal":X(e.ratio);return;case"custom":return;default:{let r=e;throw new Error(`Unsupported jitter configuration: ${r}`)}}},V=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(W(r.concurrency)):r.concurrency}),r.retry&&(ne(r.retry.strategy),xe(r.retry.jitter),r={...r,retry:{...r.retry,maxRetries:Number(x(r.retry.maxRetries))}}),r},J=class e{constructor(r={}){l(this,"circuitBreaker");l(this,"config");l(this,"lastCircuitState");let{rules:t,rulesMode:n,fallback:o,toError:a,mapError:s,...i}=r,c=Ce(r),u={...i,errorHandling:{normalizer:c,mapError:s}};this.config=V(u),u.circuitBreaker&&(this.circuitBreaker=new U(u.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var a,s,i,c;let n=V({...this.config,...t});if(this.circuitBreaker){let u=(a=this.lastCircuitState)!=null?a:this.circuitBreaker.getState().state,d=await this.circuitBreaker.canExecute(),y=this.circuitBreaker.getState().state;if(u!==y)try{(s=n.onCircuitStateChange)==null||s.call(n,u,y)}catch{}if(this.lastCircuitState=y,!d)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let o=await ge(r,n);if(this.circuitBreaker){let u=(i=this.lastCircuitState)!=null?i:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let d=this.circuitBreaker.getState().state;if(u!==d)try{(c=n.onCircuitStateChange)==null||c.call(n,u,d)}catch{}this.lastCircuitState=d}return o}async runOrThrow(r,t={}){return this.orThrow(r,t)}async orThrow(r,t={}){let n=await this.run(r,t);if(n.ok)return n.data;throw n.error}async all(r,t={}){var d;let n=V({...this.config,...t}),o=(d=n.concurrency)!=null?d:Number.POSITIVE_INFINITY,a=Number.isFinite(o)?Number(W(o)):Number.POSITIVE_INFINITY;if(a===Number.POSITIVE_INFINITY)return Promise.all(r.map(y=>this.run(y,n)));let s=new Array(r.length),i=0,c=async()=>{var y;for(;i<r.length&&!((y=n.signal)!=null&&y.aborted);){let T=i++;if(T>=r.length)break;let E=r[T];E&&(s[T]=await this.run(E,n))}},u=Array.from({length:Math.min(a,r.length)},()=>c());await Promise.all(u);for(let y=0;y<r.length;y++)if(!(y in s)){let T=r[y];T&&(s[y]=await this.run(T,n))}return s}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=[],s=[];for(let i of r){if(i.type==="success"){t.push(i);continue}switch(n.push(i),i.type){case"failure":o.push(i);break;case"aborted":a.push(i);break;case"timeout":s.push(i);break}}return{ok:t,errors:n,failure:o,aborted:a,timeout:s}}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,s=(c=o==null?void 0:o.normalizer)!=null?c:t.normalizer,i=(u=o==null?void 0:o.mapError)!=null?u:t.mapError;return new e({...n,...a,toError:s,mapError:i})}};function z(e={}){let r=new J(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 ge(e,r){let{signal:t,ignoreAbort:n=!0,timeout:o,retry:a,errorHandling:s,logger:i,onSuccess:c,onError:u,onFinally:d,onAbort:y,onRetry:T}=r,E=(C,...p)=>{try{C==null||C(...p)}catch{}},N,k=0,H=0,R=[],M=Date.now(),{signal:I,cleanup:ie}=ae(t),v=C=>({totalAttempts:k,totalRetries:k>0?Number(k)-1:0,totalDuration:Date.now()-M,lastError:C,retryHistory:R});try{if(I.aborted){E(y,I);let p=s.normalizer(new DOMException("Aborted","AbortError")),w=s.mapError?s.mapError(p):p;return{type:"aborted",ok:!1,data:null,error:w,metrics:{totalAttempts:k,totalRetries:H,totalDuration:Date.now()-M,lastError:w,retryHistory:R}}}let C=async()=>{var D,O,j;let p=1,w=x((D=a==null?void 0:a.maxRetries)!=null?D:0);for(;;){k=p;let Y=new AbortController,{signal:q,cleanup:ue}=ae(I,Y.signal);try{let P=Promise.resolve(e({signal:q})),A=o?await se(P,o,q,()=>(Y.abort(),new F(b(o)))):await P;return E(c,A,v()),E(i==null?void 0:i.info,`Task succeeded on attempt ${p}`),A}catch(P){let A=s.normalizer(P),m=s.mapError?s.mapError(A):A;if(N=m,m.code==="ABORTED"&&E(y,q),n&&m.code==="ABORTED"||(E(u,m,v(m)),E(i==null?void 0:i.error,`Task failed on attempt ${p}`,m)),m.code==="ABORTED"||m.retryable===!1)throw m;if(p<=Number(w)){let G=a==null?void 0:a.shouldRetry,ce={totalAttempts:Number(k),elapsedTime:Date.now()-M,startTime:new Date(M),lastDelay:(O=R[R.length-1])!=null&&O.delay?Number((j=R[R.length-1])==null?void 0:j.delay):void 0};if(!G||G(p,m,ce)){let le=a?te(a.strategy,p,m):0,$=ke(le,a==null?void 0:a.jitter),Q=b($);R.push({attempt:p,error:m,delay:Q,timestamp:new Date}),E(T,p,m,$),E(i==null?void 0:i.info,`Retrying in ${$}ms (attempt ${p+1})`),p+=1,await oe(Q,I);continue}}throw m}finally{ue()}}};try{let p=await C(),w=v();return H=w.totalRetries,E(d,w),{type:"success",ok:!0,data:p,error:null,metrics:w}}catch(p){let w=N!=null?N:s.normalizer(p),D=w.code==="TIMEOUT"?"timeout":w.code==="ABORTED"?"aborted":"failure",O=v(w);return H=O.totalRetries,E(d,O),{type:D,ok:!1,data:null,error:w,metrics:O}}}finally{ie()}}function ae(...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 ke(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=z(),Oe=S.run,Ae=S.runOrThrow,Se=S.orThrow,Ne=S.all,Me=S.allOrThrow;export{he as RetryStrategies,f as TypedError,Ne as all,Me as allOrThrow,b as asMilliseconds,x as asRetryCount,z as default,be as errorRule,Se as orThrow,Oe as run,Ae as runOrThrow,z as tryo};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tryo",
3
- "version": "0.13.6",
3
+ "version": "0.13.8",
4
4
  "author": {
5
5
  "name": "sebasxsala",
6
6
  "url": "https://github.com/sebasxsala"