tryo 0.13.7 → 0.13.9

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 we=Object.prototype.hasOwnProperty;var Te=(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))!we.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)=>Te(e,typeof r!="symbol"?r+"":r,t);var De={};be(De,{RetryStrategies:()=>ee,TypedError:()=>f,all:()=>ce,allOrThrow:()=>le,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"},ge=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.code=="string"&&r.code.length>0},xe=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(ge(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 c;if(!this.predicate(t))return null;let n=r(t),o=n.code,i=Object.hasOwn(n,"cause")?n.cause:t,s=(c=n.meta)!=null?c:{};class a extends f{constructor(){let l=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{title:n.title,cause:i,meta:s,status:n.status,retryable:n.retryable,raw:l,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",c=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:c?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 c;if(!this.predicate(t))return null;let n=r(t),o=Object.hasOwn(n,"cause")?n.cause:t,i=(c=n.meta)!=null?c:{},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},g={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=>xe(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=[g.typed,g.abort,g.timeout,g.http,g.network,g.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),c=()=>{s||(s=!0,u(),i(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(a),t==null||t.removeEventListener("abort",c)};t==null||t.addEventListener("abort",c,{once:!0}),e.then(y=>{s||(s=!0,u(),o(y))},y=>{s||(s=!0,u(),i(y))})});var Ne=e=>{var s,a,c;if(e.toError)return e.toError;let r=(s=e.rulesMode)!=null?s:"extend",t=(a=e.rules)!=null?a:[],n=Z,o=(c=e.fallback)!=null?c:(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,c=Ne(r),u={...a,errorHandling:{normalizer:c,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,c,u,y;let n=$({...this.config,...t});if(this.circuitBreaker){let l=(i=this.lastCircuitState)!=null?i:this.circuitBreaker.getState().state,p=await this.circuitBreaker.canExecute(),w=this.circuitBreaker.getState().state;if(l!==w)try{(a=(s=n.hooks)==null?void 0:s.onCircuitStateChange)==null||a.call(s,l,w)}catch{}if(this.lastCircuitState=w,!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 l=(c=this.lastCircuitState)!=null?c:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let p=this.circuitBreaker.getState().state;if(l!==p)try{(y=(u=n.hooks)==null?void 0:u.onCircuitStateChange)==null||y.call(u,l,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(l=>this.run(l,n)));let s=new Array(r.length),a=0,c=async()=>{var l;for(;a<r.length&&!((l=n.signal)!=null&&l.aborted);){let p=a++;if(p>=r.length)break;let w=r[p];w&&(s[p]=await this.run(w,n))}},u=Array.from({length:Math.min(i,r.length)},()=>c());await Promise.all(u);for(let l=0;l<r.length;l++)if(!(l in s)){let p=r[l];p&&(s[l]=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 c,u;let{errorHandling:t,...n}=this.config,{errorHandling:o,...i}=r,s=(c=o==null?void 0:o.normalizer)!=null?c: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:c}=r,u=(C,...m)=>{try{C==null||C(...m)}catch{}},y,l=0,p=0,w=[],k=Date.now(),{signal:O,cleanup:de}=se(t),M=C=>({totalAttempts:l,totalRetries:l>0?Number(l)-1:0,totalDuration:Date.now()-k,lastError:C,retryHistory:w});try{if(O.aborted){u(a==null?void 0:a.onAbort,O);let m=s.normalizer(new DOMException("Aborted","AbortError")),T=s.mapError?s.mapError(m):m;return{type:"aborted",ok:!1,data:null,error:T,metrics:{totalAttempts:l,totalRetries:p,totalDuration:Date.now()-k,lastError:T,retryHistory:w}}}let C=async()=>{var I,x,K;let m=1,T=h((I=i==null?void 0:i.maxRetries)!=null?I:0);for(;;){l=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(c==null?void 0:c.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(c==null?void 0:c.error,`Task failed on attempt ${m}`,E)),E.code==="ABORTED"||E.retryable===!1)throw E;if(m<=Number(T)){let J=i==null?void 0:i.shouldRetry,pe={totalAttempts:Number(l),elapsedTime:Date.now()-k,startTime:new Date(k),lastDelay:(x=w[w.length-1])!=null&&x.delay?Number((K=w[w.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);w.push({attempt:m,error:E,delay:j,timestamp:new Date}),u(a==null?void 0:a.onRetry,m,E,z),u(c==null?void 0:c.info,`Retrying in ${z}ms (attempt ${m+1})`),m+=1,await ne(j,O);continue}}throw E}finally{ye()}}};try{let m=await C(),T=M();return p=T.totalRetries,u(a==null?void 0:a.onFinally,T),{type:"success",ok:!0,data:m,error:null,metrics:T}}catch(m){let T=y!=null?y:s.normalizer(m),I=T.code==="TIMEOUT"?"timeout":T.code==="ABORTED"?"aborted":"failure",x=M(T);return p=x.totalRetries,u(a==null?void 0:a.onFinally,x),{type:I,ok:!1,data:null,error:T,metrics:x}}}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,ce=N.all,le=N.allOrThrow;0&&(module.exports={RetryStrategies,TypedError,all,allOrThrow,asMilliseconds,asRetryCount,errorRule,orThrow,run,runOrThrow,tryo});
1
+ "use strict";var F=Object.defineProperty;var we=Object.getOwnPropertyDescriptor;var be=Object.getOwnPropertyNames;var he=Object.prototype.hasOwnProperty;var Re=(e,r,t)=>r in e?F(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var Ce=(e,r)=>{for(var t in r)F(e,t,{get:r[t],enumerable:!0})},xe=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of be(r))!he.call(e,o)&&o!==t&&F(e,o,{get:()=>r[o],enumerable:!(n=we(r,o))||n.enumerable});return e};var ge=e=>xe(F({},"__esModule",{value:!0}),e);var d=(e,r,t)=>Re(e,typeof r!="symbol"?r+"":r,t);var Ue={};Ce(Ue,{RetryStrategies:()=>ne,TypedError:()=>f,all:()=>ye,allOrThrow:()=>pe,asMilliseconds:()=>b,asRetryCount:()=>h,default:()=>N,errorRule:()=>re,orThrow:()=>de,run:()=>ce,runOrThrow:()=>le,tryo:()=>N});module.exports=ge(Ue);var f=class extends Error{constructor(t,n){var o,s;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=(s=n.meta)!=null?s:{},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}}},B=class extends f{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0});d(this,"code","TIMEOUT")}};var _=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 U=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},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 L=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,s,a;if(!((a=(s=(o=this.config).shouldCountAsFailure)==null?void 0:s.call(o,r))!=null?a:!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 _(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 ke=e=>{let t=e.toString().match(/\bcode\s*:\s*['"`]([^'"`]+)['"`]/);return t==null?void 0:t[1]},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,z=class{constructor(r){this.predicate=r}toCode(r){return new K(this.predicate,r)}toError(r){let t=o=>{var c;if(!this.predicate(o))return null;let s=r(o),a=s.code,i=Object.hasOwn(s,"cause")?s.cause:o,l=(c=s.meta)!=null?c:{};class u extends f{constructor(){let m=Object.hasOwn(s,"raw")?s.raw:o;super(s.message,{title:s.title,cause:i,meta:l,status:s.status,retryable:s.retryable,raw:m,path:s.path});d(this,"code",a)}}return new u},n=ke(r);return n&&(t.__tryoCode=n),t}};function Me(e,r){let t=new z(s=>s instanceof e);if(r)return t.toError(r);let o=s=>{if(!(s instanceof e))return null;if(s instanceof f)return s;let a=s,i=typeof a.code=="string"&&a.code.length>0?a.code:"UNKNOWN",l=Object.hasOwn(a,"cause"),u=Object.hasOwn(a,"raw");class c extends f{constructor(){super(a.message||i,{title:typeof a.title=="string"?a.title:void 0,cause:l?a.cause:s,meta:Ne(a.meta)?a.meta:{},status:typeof a.status=="number"?a.status:void 0,retryable:typeof a.retryable=="boolean"?a.retryable:void 0,raw:u?a.raw:s,path:typeof a.path=="string"?a.path:void 0});d(this,"code",i)}}return new c};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){let t=n=>{var u;if(!this.predicate(n))return null;let o=r(n),s=Object.hasOwn(o,"cause")?o.cause:n,a=(u=o.meta)!=null?u:{},i=this.errorCode;class l extends f{constructor(){let m=Object.hasOwn(o,"raw")?o.raw:n;super(o.message,{title:o.title,cause:s,meta:a,status:o.status,retryable:o.retryable,raw:m,path:o.path});d(this,"code",i)}}return new l};return t.__tryoCode=this.errorCode,t}},R={when:e=>new z(e),instance:Me},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=>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 f)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e,raw:e}))};var Ie=[x.typed,x.abort,x.timeout,x.http,x.network,x.unknown];var re={when:e=>R.when(e),instance:R.instance},te=Ie;var ne={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})},oe=(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},se=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 ae=(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)},s,a=()=>{s&&clearTimeout(s),o(a),n(new DOMException("Aborted","AbortError"))};s=setTimeout(()=>{o(a),t()},e),r==null||r.addEventListener("abort",a,{once:!0})}),ie=(e,r,t,n)=>new Promise((o,s)=>{if(t!=null&&t.aborted){s(new DOMException("Aborted","AbortError"));return}let a=!1,i=setTimeout(()=>{var c;a=!0,u(),s((c=n==null?void 0:n())!=null?c:new Error(`Operation timed out after ${r}ms`))},r),l=()=>{a||(a=!0,u(),s(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(i),t==null||t.removeEventListener("abort",l)};t==null||t.addEventListener("abort",l,{once:!0}),e.then(c=>{a||(a=!0,u(),o(c))},c=>{a||(a=!0,u(),s(c))})});var De=e=>{var a,i,l;if(e.toError)return e.toError;let r=(a=e.rulesMode)!=null?a:"extend",t=(i=e.rules)!=null?i:[];Pe(t);let n=te,o=(l=e.fallback)!=null?l:(u=>ee(U)(u)),s=r==="replace"?t:[...t,...n];return Z(s,o)},Pe=e=>{let r=new Set;for(let t of e){let n=t.__tryoCode;if(n){if(r.has(n))throw new Error(`Duplicate rule code detected: ${n}`);r.add(n)}}},Fe=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&&(se(r.retry.strategy),Fe(r.retry.jitter),r={...r,retry:{...r.retry,maxRetries:Number(h(r.retry.maxRetries))}}),r},J=class e{constructor(r={}){d(this,"circuitBreaker");d(this,"config");d(this,"lastCircuitState");let{rules:t,rulesMode:n,fallback:o,toError:s,mapError:a,...i}=r,l=De(r),u={...i,errorHandling:{normalizer:l,mapError:a}};this.config=V(u),u.circuitBreaker&&(this.circuitBreaker=new L(u.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var s,a,i,l;let n=V({...this.config,...t});if(this.circuitBreaker){let u=(s=this.lastCircuitState)!=null?s:this.circuitBreaker.getState().state,c=await this.circuitBreaker.canExecute(),y=this.circuitBreaker.getState().state;if(u!==y)try{(a=n.onCircuitStateChange)==null||a.call(n,u,y)}catch{}if(this.lastCircuitState=y,!c)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let o=await Be(r,n);if(this.circuitBreaker){let u=(i=this.lastCircuitState)!=null?i:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let c=this.circuitBreaker.getState().state;if(u!==c)try{(l=n.onCircuitStateChange)==null||l.call(n,u,c)}catch{}this.lastCircuitState=c}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 c;let n=V({...this.config,...t}),o=(c=n.concurrency)!=null?c:Number.POSITIVE_INFINITY,s=Number.isFinite(o)?Number(W(o)):Number.POSITIVE_INFINITY;if(s===Number.POSITIVE_INFINITY)return Promise.all(r.map(y=>this.run(y,n)));let a=new Array(r.length),i=0,l=async()=>{var y;for(;i<r.length&&!((y=n.signal)!=null&&y.aborted);){let T=i++;if(T>=r.length)break;let m=r[T];m&&(a[T]=await this.run(m,n))}},u=Array.from({length:Math.min(s,r.length)},()=>l());await Promise.all(u);for(let y=0;y<r.length;y++)if(!(y in a)){let T=r[y];T&&(a[y]=await this.run(T,n))}return a}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=[],s=[],a=[];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":s.push(i);break;case"timeout":a.push(i);break}}return{ok:t,errors:n,failure:o,aborted:s,timeout:a}}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,...s}=r,a=(l=o==null?void 0:o.normalizer)!=null?l:t.normalizer,i=(u=o==null?void 0:o.mapError)!=null?u:t.mapError;return new e({...n,...s,toError:a,mapError:i})}};function N(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 Be(e,r){let{signal:t,ignoreAbort:n=!0,timeout:o,retry:s,errorHandling:a,logger:i,onSuccess:l,onError:u,onFinally:c,onAbort:y,onRetry:T}=r,m=(C,...p)=>{try{C==null||C(...p)}catch{}},I,k=0,g=[],H=Date.now(),{signal:v,cleanup:me}=ue(t),O=C=>({totalAttempts:k,totalRetries:k>0?Number(k)-1:0,totalDuration:Date.now()-H,lastError:C,retryHistory:g});try{if(v.aborted){m(y,v);let p=a.normalizer(new DOMException("Aborted","AbortError")),w=a.mapError?a.mapError(p):p;return{type:"aborted",ok:!1,data:null,error:w,metrics:O(w)}}let C=async()=>{var D,A,j;let p=1,w=h((D=s==null?void 0:s.maxRetries)!=null?D:0);for(;;){k=p;let Y=new AbortController,{signal:$,cleanup:fe}=ue(v,Y.signal);try{let P=Promise.resolve(e({signal:$})),S=o?await ie(P,o,$,()=>(Y.abort(),new B(b(o)))):await P;return m(l,S,O()),m(i==null?void 0:i.info,`Task succeeded on attempt ${p}`),S}catch(P){let S=a.normalizer(P),E=a.mapError?a.mapError(S):S;if(I=E,E.code==="ABORTED"&&m(y,$),n&&E.code==="ABORTED"||(m(u,E,O(E)),m(i==null?void 0:i.error,`Task failed on attempt ${p}`,E)),E.code==="ABORTED"||E.retryable===!1)throw E;if(p<=Number(w)){let G=s==null?void 0:s.shouldRetry,Ee={totalAttempts:Number(k),elapsedTime:Date.now()-H,startTime:new Date(H),lastDelay:(A=g[g.length-1])!=null&&A.delay?Number((j=g[g.length-1])==null?void 0:j.delay):void 0};if(!G||G(p,E,Ee)){let Te=s?oe(s.strategy,p,E):0,q=_e(Te,s==null?void 0:s.jitter),Q=b(q);g.push({attempt:p,error:E,delay:Q,timestamp:new Date}),m(T,p,E,q),m(i==null?void 0:i.info,`Retrying in ${q}ms (attempt ${p+1})`),p+=1,await ae(Q,v);continue}}throw E}finally{fe()}}};try{let p=await C(),w=O();return m(c,w),{type:"success",ok:!0,data:p,error:null,metrics:w}}catch(p){let w=I!=null?I:a.normalizer(p),D=w.code==="TIMEOUT"?"timeout":w.code==="ABORTED"?"aborted":"failure",A=O(w);return m(c,A),{type:D,ok:!1,data:null,error:w,metrics:A}}}finally{me()}}function ue(...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 s=()=>r.abort();o.addEventListener("abort",s,{once:!0}),n.push({signal:o,abort:s})}return{signal:r.signal,cleanup:()=>{for(let o of n)o.signal.removeEventListener("abort",o.abort)}}}function _e(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 M=N(),ce=M.run,le=M.runOrThrow,de=M.orThrow,ye=M.all,pe=M.allOrThrow;0&&(module.exports={RetryStrategies,TypedError,all,allOrThrow,asMilliseconds,asRetryCount,errorRule,orThrow,run,runOrThrow,tryo});
package/dist/index.d.cts CHANGED
@@ -94,53 +94,6 @@ declare class UnknownError extends TypedError<'UNKNOWN', Record<string, unknown>
94
94
  constructor(message: string, cause?: unknown);
95
95
  }
96
96
 
97
- /**
98
- * Modern result types with enhanced discriminated unions
99
- * Provides better type safety and more granular result categorization
100
- */
101
-
102
- type TryoResult<T, E extends AnyTypedError = AnyTypedError> = SuccessResult<T, E> | FailureResult<E> | AbortedResult<E> | TimeoutResult<E>;
103
- interface SuccessResult<T, E extends AnyTypedError> {
104
- readonly type: 'success';
105
- readonly ok: true;
106
- readonly data: T;
107
- readonly error: null;
108
- readonly metrics: TryoMetrics$1<E>;
109
- }
110
- interface FailureResult<E extends AnyTypedError> {
111
- readonly type: 'failure';
112
- readonly ok: false;
113
- readonly data: null;
114
- readonly error: E;
115
- readonly metrics: TryoMetrics$1<E>;
116
- }
117
- interface AbortedResult<E extends AnyTypedError> {
118
- readonly type: 'aborted';
119
- readonly ok: false;
120
- readonly data: null;
121
- readonly error: E;
122
- readonly metrics: TryoMetrics$1<E>;
123
- }
124
- interface TimeoutResult<E extends AnyTypedError> {
125
- readonly type: 'timeout';
126
- readonly ok: false;
127
- readonly data: null;
128
- readonly error: E;
129
- readonly metrics: TryoMetrics$1<E>;
130
- }
131
- interface TryoMetrics$1<E extends AnyTypedError> {
132
- readonly totalAttempts: RetryCount;
133
- readonly totalRetries: RetryCount;
134
- readonly totalDuration: Milliseconds;
135
- readonly lastError?: E;
136
- readonly retryHistory: Array<{
137
- readonly attempt: RetryCount;
138
- readonly error: E;
139
- readonly delay: Milliseconds;
140
- readonly timestamp: Date;
141
- }>;
142
- }
143
-
144
97
  /**
145
98
  * Modern circuit breaker implementation with enhanced state management
146
99
  * Provides circuit breaker pattern with type safety and observability
@@ -198,6 +151,58 @@ declare const RetryStrategies: {
198
151
  readonly custom: (calculate: (attempt: RetryCount, error: unknown) => number) => CustomDelayStrategy;
199
152
  };
200
153
 
154
+ /**
155
+ * Modern result types with enhanced discriminated unions
156
+ * Provides better type safety and more granular result categorization
157
+ */
158
+
159
+ type TryoResult<T, E extends AnyTypedError = AnyTypedError> = SuccessResult<T, E> | FailureResult<E> | AbortedResult<E> | TimeoutResult<E>;
160
+ interface SuccessResult<T, E extends AnyTypedError> {
161
+ readonly type: 'success';
162
+ readonly ok: true;
163
+ readonly data: T;
164
+ readonly error: null;
165
+ readonly metrics: TryoMetrics<E>;
166
+ }
167
+ interface FailureResult<E extends AnyTypedError> {
168
+ readonly type: 'failure';
169
+ readonly ok: false;
170
+ readonly data: null;
171
+ readonly error: E;
172
+ readonly metrics: TryoMetrics<E>;
173
+ }
174
+ interface AbortedResult<E extends AnyTypedError> {
175
+ readonly type: 'aborted';
176
+ readonly ok: false;
177
+ readonly data: null;
178
+ readonly error: E;
179
+ readonly metrics: TryoMetrics<E>;
180
+ }
181
+ interface TimeoutResult<E extends AnyTypedError> {
182
+ readonly type: 'timeout';
183
+ readonly ok: false;
184
+ readonly data: null;
185
+ readonly error: E;
186
+ readonly metrics: TryoMetrics<E>;
187
+ }
188
+ interface TryoMetrics<E extends AnyTypedError> {
189
+ readonly totalAttempts: RetryCount;
190
+ readonly totalRetries: RetryCount;
191
+ readonly totalDuration: Milliseconds;
192
+ readonly lastError?: E;
193
+ readonly retryHistory: Array<{
194
+ readonly attempt: RetryCount;
195
+ readonly error: E;
196
+ readonly delay: Milliseconds;
197
+ readonly timestamp: Date;
198
+ }>;
199
+ }
200
+
201
+ /**
202
+ * Configuration types for modern execution engine
203
+ * Provides comprehensive configuration with type safety
204
+ */
205
+
201
206
  interface TryoConfig<E extends AnyTypedError = AnyTypedError> {
202
207
  /** Abort signal passed to tasks */
203
208
  readonly signal?: AbortSignal;
@@ -215,8 +220,18 @@ interface TryoConfig<E extends AnyTypedError = AnyTypedError> {
215
220
  readonly concurrency?: number;
216
221
  /** Logging configuration */
217
222
  readonly logger?: LoggerConfig<E>;
218
- /** Callback hooks */
219
- readonly hooks?: HookConfig<E>;
223
+ /** Called on successful execution */
224
+ readonly onSuccess?: <T>(data: T, metrics?: TryoMetrics<E>) => void;
225
+ /** Called on failed execution */
226
+ readonly onError?: (error: E, metrics?: TryoMetrics<E>) => void;
227
+ /** Called always, success or failure */
228
+ readonly onFinally?: (metrics?: TryoMetrics<E>) => void;
229
+ /** Called on abort */
230
+ readonly onAbort?: (signal: AbortSignal) => void;
231
+ /** Called before retry attempt */
232
+ readonly onRetry?: (attempt: number, error: E, delay: number) => void;
233
+ /** Called when circuit breaker state changes */
234
+ readonly onCircuitStateChange?: (from: CircuitState, to: CircuitState) => void;
220
235
  }
221
236
  interface RetryConfig<E extends AnyTypedError> {
222
237
  /** Maximum number of retry attempts */
@@ -255,21 +270,6 @@ interface LoggerConfig<E extends AnyTypedError> {
255
270
  /** Warning logging function */
256
271
  readonly warn?: (message: string, meta?: unknown) => void;
257
272
  }
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
- type TryoMetrics<E extends AnyTypedError> = TryoMetrics$1<E>;
273
273
  type JitterConfig = {
274
274
  type: 'none';
275
275
  } | {
@@ -291,10 +291,17 @@ declare const JitterConfig: {
291
291
 
292
292
  type RulesMode = 'extend' | 'replace';
293
293
  type DefaultError = AbortedError | TimeoutError | NetworkError | HttpError | CircuitOpenError | ValidationError | UnknownError;
294
- type RunOptions<E extends AnyTypedError> = Omit<Partial<TryoConfig<E>>, 'circuitBreaker'>;
295
- type AllOptions<E extends AnyTypedError> = Omit<Partial<TryoConfig<E> & {
296
- concurrency?: number;
297
- }>, 'circuitBreaker'>;
294
+ type RunOptions<E extends AnyTypedError, TData = unknown> = Omit<Partial<TryoConfig<E>>, 'circuitBreaker' | 'onSuccess'> & {
295
+ onSuccess?: (data: TData, metrics?: TryoMetrics<E>) => void;
296
+ };
297
+ type AllOptions<E extends AnyTypedError, TData = unknown> = RunOptions<E, TData>;
298
+ type PartitionedResults<T, E extends AnyTypedError> = {
299
+ ok: Array<SuccessResult<T, E>>;
300
+ errors: Array<FailureResult<E> | AbortedResult<E> | TimeoutResult<E>>;
301
+ failure: Array<FailureResult<E>>;
302
+ aborted: Array<AbortedResult<E>>;
303
+ timeout: Array<TimeoutResult<E>>;
304
+ };
298
305
  type MaybePromise<T> = T | Promise<T>;
299
306
  type RuleLike = (error: unknown) => AnyTypedError | null;
300
307
  type NonNull<T> = T extends null ? never : T;
@@ -329,89 +336,48 @@ declare function tryo<E extends AnyTypedError = DefaultError>(options?: TryoOpti
329
336
  type Tryo<E extends AnyTypedError = AnyTypedError> = {
330
337
  run: <T>(task: (ctx: {
331
338
  signal: AbortSignal;
332
- }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<TryoResult<T, E>>;
339
+ }) => MaybePromise<T>, options?: RunOptions<E, T>) => Promise<TryoResult<T, E>>;
333
340
  runOrThrow: <T>(task: (ctx: {
334
341
  signal: AbortSignal;
335
- }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
342
+ }) => MaybePromise<T>, options?: RunOptions<E, T>) => Promise<T>;
336
343
  orThrow: <T>(task: (ctx: {
337
344
  signal: AbortSignal;
338
- }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
345
+ }) => MaybePromise<T>, options?: RunOptions<E, T>) => Promise<T>;
339
346
  all: <T>(tasks: Array<(ctx: {
340
347
  signal: AbortSignal;
341
- }) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<Array<TryoResult<T, E>>>;
348
+ }) => MaybePromise<T>>, options?: AllOptions<E, T>) => Promise<Array<TryoResult<T, E>>>;
342
349
  allOrThrow: <T>(tasks: Array<(ctx: {
343
350
  signal: AbortSignal;
344
- }) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<T[]>;
345
- partitionAll: <T>(results: Array<TryoResult<T, E>>) => {
346
- ok: Array<SuccessResult<T, E>>;
347
- errors: Array<FailureResult<E> | AbortedResult<E> | TimeoutResult<E>>;
348
- failure: Array<FailureResult<E>>;
349
- aborted: Array<AbortedResult<E>>;
350
- timeout: Array<TimeoutResult<E>>;
351
- };
351
+ }) => MaybePromise<T>>, options?: AllOptions<E, T>) => Promise<T[]>;
352
+ partitionAll: <T>(results: Array<TryoResult<T, E>>) => PartitionedResults<T, E>;
352
353
  withConfig: (additionalConfig: Omit<Partial<TryoConfig<E>>, 'signal'>) => Tryo<E>;
353
354
  };
354
355
 
355
356
  declare const run: <T>(task: (ctx: {
356
357
  signal: AbortSignal;
357
- }) => T | Promise<T>, options?: {
358
- readonly timeout?: number | undefined;
359
- readonly signal?: AbortSignal | undefined;
360
- readonly ignoreAbort?: boolean | undefined;
361
- readonly retry?: RetryConfig<DefaultError> | undefined;
362
- readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
363
- readonly concurrency?: number | undefined;
364
- readonly logger?: LoggerConfig<DefaultError> | undefined;
365
- readonly hooks?: HookConfig<DefaultError> | undefined;
366
- } | undefined) => Promise<TryoResult<T, DefaultError>>;
358
+ }) => T | Promise<T>, options?: (Omit<Partial<TryoConfig<DefaultError>>, "circuitBreaker" | "onSuccess"> & {
359
+ onSuccess?: ((data: T, metrics?: TryoMetrics<DefaultError> | undefined) => void) | undefined;
360
+ }) | undefined) => Promise<TryoResult<T, DefaultError>>;
367
361
  declare const runOrThrow: <T>(task: (ctx: {
368
362
  signal: AbortSignal;
369
- }) => T | Promise<T>, options?: {
370
- readonly timeout?: number | undefined;
371
- readonly signal?: AbortSignal | undefined;
372
- readonly ignoreAbort?: boolean | undefined;
373
- readonly retry?: RetryConfig<DefaultError> | undefined;
374
- readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
375
- readonly concurrency?: number | undefined;
376
- readonly logger?: LoggerConfig<DefaultError> | undefined;
377
- readonly hooks?: HookConfig<DefaultError> | undefined;
378
- } | undefined) => Promise<T>;
363
+ }) => T | Promise<T>, options?: (Omit<Partial<TryoConfig<DefaultError>>, "circuitBreaker" | "onSuccess"> & {
364
+ onSuccess?: ((data: T, metrics?: TryoMetrics<DefaultError> | undefined) => void) | undefined;
365
+ }) | undefined) => Promise<T>;
379
366
  declare const orThrow: <T>(task: (ctx: {
380
367
  signal: AbortSignal;
381
- }) => T | Promise<T>, options?: {
382
- readonly timeout?: number | undefined;
383
- readonly signal?: AbortSignal | undefined;
384
- readonly ignoreAbort?: boolean | undefined;
385
- readonly retry?: RetryConfig<DefaultError> | undefined;
386
- readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
387
- readonly concurrency?: number | undefined;
388
- readonly logger?: LoggerConfig<DefaultError> | undefined;
389
- readonly hooks?: HookConfig<DefaultError> | undefined;
390
- } | undefined) => Promise<T>;
368
+ }) => T | Promise<T>, options?: (Omit<Partial<TryoConfig<DefaultError>>, "circuitBreaker" | "onSuccess"> & {
369
+ onSuccess?: ((data: T, metrics?: TryoMetrics<DefaultError> | undefined) => void) | undefined;
370
+ }) | undefined) => Promise<T>;
391
371
  declare const all: <T>(tasks: ((ctx: {
392
372
  signal: AbortSignal;
393
- }) => T | Promise<T>)[], options?: {
394
- readonly timeout?: number | undefined;
395
- readonly signal?: AbortSignal | undefined;
396
- readonly ignoreAbort?: boolean | undefined;
397
- readonly retry?: RetryConfig<DefaultError> | undefined;
398
- readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
399
- concurrency?: number | undefined;
400
- readonly logger?: LoggerConfig<DefaultError> | undefined;
401
- readonly hooks?: HookConfig<DefaultError> | undefined;
402
- } | undefined) => Promise<TryoResult<T, DefaultError>[]>;
373
+ }) => T | Promise<T>)[], options?: (Omit<Partial<TryoConfig<DefaultError>>, "circuitBreaker" | "onSuccess"> & {
374
+ onSuccess?: ((data: T, metrics?: TryoMetrics<DefaultError> | undefined) => void) | undefined;
375
+ }) | undefined) => Promise<TryoResult<T, DefaultError>[]>;
403
376
  declare const allOrThrow: <T>(tasks: ((ctx: {
404
377
  signal: AbortSignal;
405
- }) => T | Promise<T>)[], options?: {
406
- readonly timeout?: number | undefined;
407
- readonly signal?: AbortSignal | undefined;
408
- readonly ignoreAbort?: boolean | undefined;
409
- readonly retry?: RetryConfig<DefaultError> | undefined;
410
- readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
411
- concurrency?: number | undefined;
412
- readonly logger?: LoggerConfig<DefaultError> | undefined;
413
- readonly hooks?: HookConfig<DefaultError> | undefined;
414
- } | undefined) => Promise<T[]>;
378
+ }) => T | Promise<T>)[], options?: (Omit<Partial<TryoConfig<DefaultError>>, "circuitBreaker" | "onSuccess"> & {
379
+ onSuccess?: ((data: T, metrics?: TryoMetrics<DefaultError> | undefined) => void) | undefined;
380
+ }) | undefined) => Promise<T[]>;
415
381
 
416
382
  /**
417
383
  * Modern fluent error rule builder
@@ -476,4 +442,4 @@ declare const errorRule: {
476
442
  };
477
443
  };
478
444
 
479
- 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 };
445
+ export { type AbortedResult, type FailureResult, type Milliseconds, type RetryCount, RetryStrategies, type RulesMode, type SuccessResult, type TimeoutResult, type TryoConfig, type TryoMetrics, type TryoOptions, type TryoResult, TypedError, all, allOrThrow, asMilliseconds, asRetryCount, tryo as default, errorRule, orThrow, run, runOrThrow, tryo };
package/dist/index.d.ts CHANGED
@@ -94,53 +94,6 @@ declare class UnknownError extends TypedError<'UNKNOWN', Record<string, unknown>
94
94
  constructor(message: string, cause?: unknown);
95
95
  }
96
96
 
97
- /**
98
- * Modern result types with enhanced discriminated unions
99
- * Provides better type safety and more granular result categorization
100
- */
101
-
102
- type TryoResult<T, E extends AnyTypedError = AnyTypedError> = SuccessResult<T, E> | FailureResult<E> | AbortedResult<E> | TimeoutResult<E>;
103
- interface SuccessResult<T, E extends AnyTypedError> {
104
- readonly type: 'success';
105
- readonly ok: true;
106
- readonly data: T;
107
- readonly error: null;
108
- readonly metrics: TryoMetrics$1<E>;
109
- }
110
- interface FailureResult<E extends AnyTypedError> {
111
- readonly type: 'failure';
112
- readonly ok: false;
113
- readonly data: null;
114
- readonly error: E;
115
- readonly metrics: TryoMetrics$1<E>;
116
- }
117
- interface AbortedResult<E extends AnyTypedError> {
118
- readonly type: 'aborted';
119
- readonly ok: false;
120
- readonly data: null;
121
- readonly error: E;
122
- readonly metrics: TryoMetrics$1<E>;
123
- }
124
- interface TimeoutResult<E extends AnyTypedError> {
125
- readonly type: 'timeout';
126
- readonly ok: false;
127
- readonly data: null;
128
- readonly error: E;
129
- readonly metrics: TryoMetrics$1<E>;
130
- }
131
- interface TryoMetrics$1<E extends AnyTypedError> {
132
- readonly totalAttempts: RetryCount;
133
- readonly totalRetries: RetryCount;
134
- readonly totalDuration: Milliseconds;
135
- readonly lastError?: E;
136
- readonly retryHistory: Array<{
137
- readonly attempt: RetryCount;
138
- readonly error: E;
139
- readonly delay: Milliseconds;
140
- readonly timestamp: Date;
141
- }>;
142
- }
143
-
144
97
  /**
145
98
  * Modern circuit breaker implementation with enhanced state management
146
99
  * Provides circuit breaker pattern with type safety and observability
@@ -198,6 +151,58 @@ declare const RetryStrategies: {
198
151
  readonly custom: (calculate: (attempt: RetryCount, error: unknown) => number) => CustomDelayStrategy;
199
152
  };
200
153
 
154
+ /**
155
+ * Modern result types with enhanced discriminated unions
156
+ * Provides better type safety and more granular result categorization
157
+ */
158
+
159
+ type TryoResult<T, E extends AnyTypedError = AnyTypedError> = SuccessResult<T, E> | FailureResult<E> | AbortedResult<E> | TimeoutResult<E>;
160
+ interface SuccessResult<T, E extends AnyTypedError> {
161
+ readonly type: 'success';
162
+ readonly ok: true;
163
+ readonly data: T;
164
+ readonly error: null;
165
+ readonly metrics: TryoMetrics<E>;
166
+ }
167
+ interface FailureResult<E extends AnyTypedError> {
168
+ readonly type: 'failure';
169
+ readonly ok: false;
170
+ readonly data: null;
171
+ readonly error: E;
172
+ readonly metrics: TryoMetrics<E>;
173
+ }
174
+ interface AbortedResult<E extends AnyTypedError> {
175
+ readonly type: 'aborted';
176
+ readonly ok: false;
177
+ readonly data: null;
178
+ readonly error: E;
179
+ readonly metrics: TryoMetrics<E>;
180
+ }
181
+ interface TimeoutResult<E extends AnyTypedError> {
182
+ readonly type: 'timeout';
183
+ readonly ok: false;
184
+ readonly data: null;
185
+ readonly error: E;
186
+ readonly metrics: TryoMetrics<E>;
187
+ }
188
+ interface TryoMetrics<E extends AnyTypedError> {
189
+ readonly totalAttempts: RetryCount;
190
+ readonly totalRetries: RetryCount;
191
+ readonly totalDuration: Milliseconds;
192
+ readonly lastError?: E;
193
+ readonly retryHistory: Array<{
194
+ readonly attempt: RetryCount;
195
+ readonly error: E;
196
+ readonly delay: Milliseconds;
197
+ readonly timestamp: Date;
198
+ }>;
199
+ }
200
+
201
+ /**
202
+ * Configuration types for modern execution engine
203
+ * Provides comprehensive configuration with type safety
204
+ */
205
+
201
206
  interface TryoConfig<E extends AnyTypedError = AnyTypedError> {
202
207
  /** Abort signal passed to tasks */
203
208
  readonly signal?: AbortSignal;
@@ -215,8 +220,18 @@ interface TryoConfig<E extends AnyTypedError = AnyTypedError> {
215
220
  readonly concurrency?: number;
216
221
  /** Logging configuration */
217
222
  readonly logger?: LoggerConfig<E>;
218
- /** Callback hooks */
219
- readonly hooks?: HookConfig<E>;
223
+ /** Called on successful execution */
224
+ readonly onSuccess?: <T>(data: T, metrics?: TryoMetrics<E>) => void;
225
+ /** Called on failed execution */
226
+ readonly onError?: (error: E, metrics?: TryoMetrics<E>) => void;
227
+ /** Called always, success or failure */
228
+ readonly onFinally?: (metrics?: TryoMetrics<E>) => void;
229
+ /** Called on abort */
230
+ readonly onAbort?: (signal: AbortSignal) => void;
231
+ /** Called before retry attempt */
232
+ readonly onRetry?: (attempt: number, error: E, delay: number) => void;
233
+ /** Called when circuit breaker state changes */
234
+ readonly onCircuitStateChange?: (from: CircuitState, to: CircuitState) => void;
220
235
  }
221
236
  interface RetryConfig<E extends AnyTypedError> {
222
237
  /** Maximum number of retry attempts */
@@ -255,21 +270,6 @@ interface LoggerConfig<E extends AnyTypedError> {
255
270
  /** Warning logging function */
256
271
  readonly warn?: (message: string, meta?: unknown) => void;
257
272
  }
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
- type TryoMetrics<E extends AnyTypedError> = TryoMetrics$1<E>;
273
273
  type JitterConfig = {
274
274
  type: 'none';
275
275
  } | {
@@ -291,10 +291,17 @@ declare const JitterConfig: {
291
291
 
292
292
  type RulesMode = 'extend' | 'replace';
293
293
  type DefaultError = AbortedError | TimeoutError | NetworkError | HttpError | CircuitOpenError | ValidationError | UnknownError;
294
- type RunOptions<E extends AnyTypedError> = Omit<Partial<TryoConfig<E>>, 'circuitBreaker'>;
295
- type AllOptions<E extends AnyTypedError> = Omit<Partial<TryoConfig<E> & {
296
- concurrency?: number;
297
- }>, 'circuitBreaker'>;
294
+ type RunOptions<E extends AnyTypedError, TData = unknown> = Omit<Partial<TryoConfig<E>>, 'circuitBreaker' | 'onSuccess'> & {
295
+ onSuccess?: (data: TData, metrics?: TryoMetrics<E>) => void;
296
+ };
297
+ type AllOptions<E extends AnyTypedError, TData = unknown> = RunOptions<E, TData>;
298
+ type PartitionedResults<T, E extends AnyTypedError> = {
299
+ ok: Array<SuccessResult<T, E>>;
300
+ errors: Array<FailureResult<E> | AbortedResult<E> | TimeoutResult<E>>;
301
+ failure: Array<FailureResult<E>>;
302
+ aborted: Array<AbortedResult<E>>;
303
+ timeout: Array<TimeoutResult<E>>;
304
+ };
298
305
  type MaybePromise<T> = T | Promise<T>;
299
306
  type RuleLike = (error: unknown) => AnyTypedError | null;
300
307
  type NonNull<T> = T extends null ? never : T;
@@ -329,89 +336,48 @@ declare function tryo<E extends AnyTypedError = DefaultError>(options?: TryoOpti
329
336
  type Tryo<E extends AnyTypedError = AnyTypedError> = {
330
337
  run: <T>(task: (ctx: {
331
338
  signal: AbortSignal;
332
- }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<TryoResult<T, E>>;
339
+ }) => MaybePromise<T>, options?: RunOptions<E, T>) => Promise<TryoResult<T, E>>;
333
340
  runOrThrow: <T>(task: (ctx: {
334
341
  signal: AbortSignal;
335
- }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
342
+ }) => MaybePromise<T>, options?: RunOptions<E, T>) => Promise<T>;
336
343
  orThrow: <T>(task: (ctx: {
337
344
  signal: AbortSignal;
338
- }) => MaybePromise<T>, options?: RunOptions<E>) => Promise<T>;
345
+ }) => MaybePromise<T>, options?: RunOptions<E, T>) => Promise<T>;
339
346
  all: <T>(tasks: Array<(ctx: {
340
347
  signal: AbortSignal;
341
- }) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<Array<TryoResult<T, E>>>;
348
+ }) => MaybePromise<T>>, options?: AllOptions<E, T>) => Promise<Array<TryoResult<T, E>>>;
342
349
  allOrThrow: <T>(tasks: Array<(ctx: {
343
350
  signal: AbortSignal;
344
- }) => MaybePromise<T>>, options?: AllOptions<E>) => Promise<T[]>;
345
- partitionAll: <T>(results: Array<TryoResult<T, E>>) => {
346
- ok: Array<SuccessResult<T, E>>;
347
- errors: Array<FailureResult<E> | AbortedResult<E> | TimeoutResult<E>>;
348
- failure: Array<FailureResult<E>>;
349
- aborted: Array<AbortedResult<E>>;
350
- timeout: Array<TimeoutResult<E>>;
351
- };
351
+ }) => MaybePromise<T>>, options?: AllOptions<E, T>) => Promise<T[]>;
352
+ partitionAll: <T>(results: Array<TryoResult<T, E>>) => PartitionedResults<T, E>;
352
353
  withConfig: (additionalConfig: Omit<Partial<TryoConfig<E>>, 'signal'>) => Tryo<E>;
353
354
  };
354
355
 
355
356
  declare const run: <T>(task: (ctx: {
356
357
  signal: AbortSignal;
357
- }) => T | Promise<T>, options?: {
358
- readonly timeout?: number | undefined;
359
- readonly signal?: AbortSignal | undefined;
360
- readonly ignoreAbort?: boolean | undefined;
361
- readonly retry?: RetryConfig<DefaultError> | undefined;
362
- readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
363
- readonly concurrency?: number | undefined;
364
- readonly logger?: LoggerConfig<DefaultError> | undefined;
365
- readonly hooks?: HookConfig<DefaultError> | undefined;
366
- } | undefined) => Promise<TryoResult<T, DefaultError>>;
358
+ }) => T | Promise<T>, options?: (Omit<Partial<TryoConfig<DefaultError>>, "circuitBreaker" | "onSuccess"> & {
359
+ onSuccess?: ((data: T, metrics?: TryoMetrics<DefaultError> | undefined) => void) | undefined;
360
+ }) | undefined) => Promise<TryoResult<T, DefaultError>>;
367
361
  declare const runOrThrow: <T>(task: (ctx: {
368
362
  signal: AbortSignal;
369
- }) => T | Promise<T>, options?: {
370
- readonly timeout?: number | undefined;
371
- readonly signal?: AbortSignal | undefined;
372
- readonly ignoreAbort?: boolean | undefined;
373
- readonly retry?: RetryConfig<DefaultError> | undefined;
374
- readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
375
- readonly concurrency?: number | undefined;
376
- readonly logger?: LoggerConfig<DefaultError> | undefined;
377
- readonly hooks?: HookConfig<DefaultError> | undefined;
378
- } | undefined) => Promise<T>;
363
+ }) => T | Promise<T>, options?: (Omit<Partial<TryoConfig<DefaultError>>, "circuitBreaker" | "onSuccess"> & {
364
+ onSuccess?: ((data: T, metrics?: TryoMetrics<DefaultError> | undefined) => void) | undefined;
365
+ }) | undefined) => Promise<T>;
379
366
  declare const orThrow: <T>(task: (ctx: {
380
367
  signal: AbortSignal;
381
- }) => T | Promise<T>, options?: {
382
- readonly timeout?: number | undefined;
383
- readonly signal?: AbortSignal | undefined;
384
- readonly ignoreAbort?: boolean | undefined;
385
- readonly retry?: RetryConfig<DefaultError> | undefined;
386
- readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
387
- readonly concurrency?: number | undefined;
388
- readonly logger?: LoggerConfig<DefaultError> | undefined;
389
- readonly hooks?: HookConfig<DefaultError> | undefined;
390
- } | undefined) => Promise<T>;
368
+ }) => T | Promise<T>, options?: (Omit<Partial<TryoConfig<DefaultError>>, "circuitBreaker" | "onSuccess"> & {
369
+ onSuccess?: ((data: T, metrics?: TryoMetrics<DefaultError> | undefined) => void) | undefined;
370
+ }) | undefined) => Promise<T>;
391
371
  declare const all: <T>(tasks: ((ctx: {
392
372
  signal: AbortSignal;
393
- }) => T | Promise<T>)[], options?: {
394
- readonly timeout?: number | undefined;
395
- readonly signal?: AbortSignal | undefined;
396
- readonly ignoreAbort?: boolean | undefined;
397
- readonly retry?: RetryConfig<DefaultError> | undefined;
398
- readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
399
- concurrency?: number | undefined;
400
- readonly logger?: LoggerConfig<DefaultError> | undefined;
401
- readonly hooks?: HookConfig<DefaultError> | undefined;
402
- } | undefined) => Promise<TryoResult<T, DefaultError>[]>;
373
+ }) => T | Promise<T>)[], options?: (Omit<Partial<TryoConfig<DefaultError>>, "circuitBreaker" | "onSuccess"> & {
374
+ onSuccess?: ((data: T, metrics?: TryoMetrics<DefaultError> | undefined) => void) | undefined;
375
+ }) | undefined) => Promise<TryoResult<T, DefaultError>[]>;
403
376
  declare const allOrThrow: <T>(tasks: ((ctx: {
404
377
  signal: AbortSignal;
405
- }) => T | Promise<T>)[], options?: {
406
- readonly timeout?: number | undefined;
407
- readonly signal?: AbortSignal | undefined;
408
- readonly ignoreAbort?: boolean | undefined;
409
- readonly retry?: RetryConfig<DefaultError> | undefined;
410
- readonly errorHandling?: ErrorHandlingConfig<DefaultError> | undefined;
411
- concurrency?: number | undefined;
412
- readonly logger?: LoggerConfig<DefaultError> | undefined;
413
- readonly hooks?: HookConfig<DefaultError> | undefined;
414
- } | undefined) => Promise<T[]>;
378
+ }) => T | Promise<T>)[], options?: (Omit<Partial<TryoConfig<DefaultError>>, "circuitBreaker" | "onSuccess"> & {
379
+ onSuccess?: ((data: T, metrics?: TryoMetrics<DefaultError> | undefined) => void) | undefined;
380
+ }) | undefined) => Promise<T[]>;
415
381
 
416
382
  /**
417
383
  * Modern fluent error rule builder
@@ -476,4 +442,4 @@ declare const errorRule: {
476
442
  };
477
443
  };
478
444
 
479
- 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 };
445
+ export { type AbortedResult, type FailureResult, type Milliseconds, type RetryCount, RetryStrategies, type RulesMode, type SuccessResult, type TimeoutResult, type TryoConfig, type 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 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 ce=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.status=="number"||typeof r.statusCode=="number"},le=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(le(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 c;if(!this.predicate(t))return null;let n=r(t),s=n.code,i=Object.hasOwn(n,"cause")?n.cause:t,o=(c=n.meta)!=null?c:{};class a extends E{constructor(){let l=Object.hasOwn(n,"raw")?n.raw:t;super(n.message,{title:n.title,cause:i,meta:o,status:n.status,retryable:n.retryable,raw:l,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",c=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:c?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 c;if(!this.predicate(t))return null;let n=r(t),s=Object.hasOwn(n,"cause")?n.cause:t,i=(c=n.meta)!=null?c:{},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},g={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(!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: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=[g.typed,g.abort,g.timeout,g.http,g.network,g.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*we(Number(r));return e.maxDelay!==void 0?Math.min(n,e.maxDelay):n}case"custom":return e.calculate(r,t);default:return e}},we=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),c=()=>{o||(o=!0,u(),i(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(a),t==null||t.removeEventListener("abort",c)};t==null||t.addEventListener("abort",c,{once:!0}),e.then(y=>{o||(o=!0,u(),s(y))},y=>{o||(o=!0,u(),i(y))})});var Te=e=>{var o,a,c;if(e.toError)return e.toError;let r=(o=e.rulesMode)!=null?o:"extend",t=(a=e.rules)!=null?a:[],n=Q,s=(c=e.fallback)!=null?c:(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,c=Te(r),u={...a,errorHandling:{normalizer:c,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,c,u,y;let n=q({...this.config,...t});if(this.circuitBreaker){let l=(i=this.lastCircuitState)!=null?i:this.circuitBreaker.getState().state,p=await this.circuitBreaker.canExecute(),w=this.circuitBreaker.getState().state;if(l!==w)try{(a=(o=n.hooks)==null?void 0:o.onCircuitStateChange)==null||a.call(o,l,w)}catch{}if(this.lastCircuitState=w,!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 l=(c=this.lastCircuitState)!=null?c:this.circuitBreaker.getState().state;s.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(s.error);let p=this.circuitBreaker.getState().state;if(l!==p)try{(y=(u=n.hooks)==null?void 0:u.onCircuitStateChange)==null||y.call(u,l,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(l=>this.run(l,n)));let o=new Array(r.length),a=0,c=async()=>{var l;for(;a<r.length&&!((l=n.signal)!=null&&l.aborted);){let p=a++;if(p>=r.length)break;let w=r[p];w&&(o[p]=await this.run(w,n))}},u=Array.from({length:Math.min(i,r.length)},()=>c());await Promise.all(u);for(let l=0;l<r.length;l++)if(!(l in o)){let p=r[l];p&&(o[l]=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 c,u;let{errorHandling:t,...n}=this.config,{errorHandling:s,...i}=r,o=(c=s==null?void 0:s.normalizer)!=null?c: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:c}=r,u=(R,...m)=>{try{R==null||R(...m)}catch{}},y,l=0,p=0,w=[],k=Date.now(),{signal:O,cleanup:ne}=te(t),N=R=>({totalAttempts:l,totalRetries:l>0?Number(l)-1:0,totalDuration:Date.now()-k,lastError:R,retryHistory:w});try{if(O.aborted){u(a==null?void 0:a.onAbort,O);let m=o.normalizer(new DOMException("Aborted","AbortError")),T=o.mapError?o.mapError(m):m;return{type:"aborted",ok:!1,data:null,error:T,metrics:{totalAttempts:l,totalRetries:p,totalDuration:Date.now()-k,lastError:T,retryHistory:w}}}let R=async()=>{var M,x,W;let m=1,T=C((M=i==null?void 0:i.maxRetries)!=null?M:0);for(;;){l=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(c==null?void 0:c.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(c==null?void 0:c.error,`Task failed on attempt ${m}`,f)),f.code==="ABORTED"||f.retryable===!1)throw f;if(m<=Number(T)){let V=i==null?void 0:i.shouldRetry,se={totalAttempts:Number(l),elapsedTime:Date.now()-k,startTime:new Date(k),lastDelay:(x=w[w.length-1])!=null&&x.delay?Number((W=w[w.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);w.push({attempt:m,error:f,delay:J,timestamp:new Date}),u(a==null?void 0:a.onRetry,m,f,L),u(c==null?void 0:c.info,`Retrying in ${L}ms (attempt ${m+1})`),m+=1,await ee(J,O);continue}}throw f}finally{oe()}}};try{let m=await R(),T=N();return p=T.totalRetries,u(a==null?void 0:a.onFinally,T),{type:"success",ok:!0,data:m,error:null,metrics:T}}catch(m){let T=y!=null?y:o.normalizer(m),M=T.code==="TIMEOUT"?"timeout":T.code==="ABORTED"?"aborted":"failure",x=N(T);return p=x.totalRetries,u(a==null?void 0:a.onFinally,x),{type:M,ok:!1,data:null,error:T,metrics:x}}}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,ge=S.runOrThrow,xe=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,xe as orThrow,Ce as run,ge as runOrThrow,_ as tryo};
1
+ var le=Object.defineProperty;var de=(e,r,t)=>r in e?le(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var d=(e,r,t)=>de(e,typeof r!="symbol"?r+"":r,t);var E=class extends Error{constructor(t,n){var o,s;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=(s=n.meta)!=null?s:{},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 E{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0});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});d(this,"code","CIRCUIT_OPEN")}};var B=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},q=e=>{if(e<1||!Number.isInteger(e))throw new Error(`Invalid concurrency limit: must be a positive integer, got ${e}`);return e},Q=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: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,s,a;if(!((a=(s=(o=this.config).shouldCountAsFailure)==null?void 0:s.call(o,r))!=null?a:!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 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 X=(e,r)=>t=>{for(let n of e){let o=n(t);if(o!==null)return o}return r(t)},Z=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 ye=e=>{let t=e.toString().match(/\bcode\s*:\s*['"`]([^'"`]+)['"`]/);return t==null?void 0:t[1]},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,U=class{constructor(r){this.predicate=r}toCode(r){return new W(this.predicate,r)}toError(r){let t=o=>{var c;if(!this.predicate(o))return null;let s=r(o),a=s.code,i=Object.hasOwn(s,"cause")?s.cause:o,l=(c=s.meta)!=null?c:{};class u extends E{constructor(){let m=Object.hasOwn(s,"raw")?s.raw:o;super(s.message,{title:s.title,cause:i,meta:l,status:s.status,retryable:s.retryable,raw:m,path:s.path});d(this,"code",a)}}return new u},n=ye(r);return n&&(t.__tryoCode=n),t}};function Te(e,r){let t=new U(s=>s instanceof e);if(r)return t.toError(r);let o=s=>{if(!(s instanceof e))return null;if(s instanceof E)return s;let a=s,i=typeof a.code=="string"&&a.code.length>0?a.code:"UNKNOWN",l=Object.hasOwn(a,"cause"),u=Object.hasOwn(a,"raw");class c extends E{constructor(){super(a.message||i,{title:typeof a.title=="string"?a.title:void 0,cause:l?a.cause:s,meta:Ee(a.meta)?a.meta:{},status:typeof a.status=="number"?a.status:void 0,retryable:typeof a.retryable=="boolean"?a.retryable:void 0,raw:u?a.raw:s,path:typeof a.path=="string"?a.path:void 0});d(this,"code",i)}}return new c};return o.toCode=t.toCode.bind(t),o.toError=t.toError.bind(t),o}var W=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){let t=n=>{var u;if(!this.predicate(n))return null;let o=r(n),s=Object.hasOwn(o,"cause")?o.cause:n,a=(u=o.meta)!=null?u:{},i=this.errorCode;class l extends E{constructor(){let m=Object.hasOwn(o,"raw")?o.raw:n;super(o.message,{title:o.title,cause:s,meta:a,status:o.status,retryable:o.retryable,raw:m,path:o.path});d(this,"code",i)}}return new l};return t.__tryoCode=this.errorCode,t}},h={when:e=>new U(e),instance:Te},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=>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 E)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e,raw:e}))};var we=[x.typed,x.abort,x.timeout,x.http,x.network,x.unknown];var be={when:e=>h.when(e),instance:h.instance},ee=we;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})},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*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},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=i=>{r&&i&&r.removeEventListener("abort",i)},s,a=()=>{s&&clearTimeout(s),o(a),n(new DOMException("Aborted","AbortError"))};s=setTimeout(()=>{o(a),t()},e),r==null||r.addEventListener("abort",a,{once:!0})}),oe=(e,r,t,n)=>new Promise((o,s)=>{if(t!=null&&t.aborted){s(new DOMException("Aborted","AbortError"));return}let a=!1,i=setTimeout(()=>{var c;a=!0,u(),s((c=n==null?void 0:n())!=null?c:new Error(`Operation timed out after ${r}ms`))},r),l=()=>{a||(a=!0,u(),s(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(i),t==null||t.removeEventListener("abort",l)};t==null||t.addEventListener("abort",l,{once:!0}),e.then(c=>{a||(a=!0,u(),o(c))},c=>{a||(a=!0,u(),s(c))})});var Ce=e=>{var a,i,l;if(e.toError)return e.toError;let r=(a=e.rulesMode)!=null?a:"extend",t=(i=e.rules)!=null?i:[];xe(t);let n=ee,o=(l=e.fallback)!=null?l:(u=>Z(B)(u)),s=r==="replace"?t:[...t,...n];return X(s,o)},xe=e=>{let r=new Set;for(let t of e){let n=t.__tryoCode;if(n){if(r.has(n))throw new Error(`Duplicate rule code detected: ${n}`);r.add(n)}}},ge=e=>{if(e)switch(e.type){case"none":return;case"full":case"equal":Q(e.ratio);return;case"custom":return;default:{let r=e;throw new Error(`Unsupported jitter configuration: ${r}`)}}},K=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),ge(r.retry.jitter),r={...r,retry:{...r.retry,maxRetries:Number(C(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:s,mapError:a,...i}=r,l=Ce(r),u={...i,errorHandling:{normalizer:l,mapError:a}};this.config=K(u),u.circuitBreaker&&(this.circuitBreaker=new _(u.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var s,a,i,l;let n=K({...this.config,...t});if(this.circuitBreaker){let u=(s=this.lastCircuitState)!=null?s:this.circuitBreaker.getState().state,c=await this.circuitBreaker.canExecute(),y=this.circuitBreaker.getState().state;if(u!==y)try{(a=n.onCircuitStateChange)==null||a.call(n,u,y)}catch{}if(this.lastCircuitState=y,!c)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let o=await ke(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 c=this.circuitBreaker.getState().state;if(u!==c)try{(l=n.onCircuitStateChange)==null||l.call(n,u,c)}catch{}this.lastCircuitState=c}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 c;let n=K({...this.config,...t}),o=(c=n.concurrency)!=null?c:Number.POSITIVE_INFINITY,s=Number.isFinite(o)?Number(q(o)):Number.POSITIVE_INFINITY;if(s===Number.POSITIVE_INFINITY)return Promise.all(r.map(y=>this.run(y,n)));let a=new Array(r.length),i=0,l=async()=>{var y;for(;i<r.length&&!((y=n.signal)!=null&&y.aborted);){let T=i++;if(T>=r.length)break;let m=r[T];m&&(a[T]=await this.run(m,n))}},u=Array.from({length:Math.min(s,r.length)},()=>l());await Promise.all(u);for(let y=0;y<r.length;y++)if(!(y in a)){let T=r[y];T&&(a[y]=await this.run(T,n))}return a}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=[],s=[],a=[];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":s.push(i);break;case"timeout":a.push(i);break}}return{ok:t,errors:n,failure:o,aborted:s,timeout:a}}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,...s}=r,a=(l=o==null?void 0:o.normalizer)!=null?l:t.normalizer,i=(u=o==null?void 0:o.mapError)!=null?u:t.mapError;return new e({...n,...s,toError:a,mapError:i})}};function L(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 ke(e,r){let{signal:t,ignoreAbort:n=!0,timeout:o,retry:s,errorHandling:a,logger:i,onSuccess:l,onError:u,onFinally:c,onAbort:y,onRetry:T}=r,m=(R,...p)=>{try{R==null||R(...p)}catch{}},M,k=0,g=[],z=Date.now(),{signal:I,cleanup:ae}=se(t),O=R=>({totalAttempts:k,totalRetries:k>0?Number(k)-1:0,totalDuration:Date.now()-z,lastError:R,retryHistory:g});try{if(I.aborted){m(y,I);let p=a.normalizer(new DOMException("Aborted","AbortError")),w=a.mapError?a.mapError(p):p;return{type:"aborted",ok:!1,data:null,error:w,metrics:O(w)}}let R=async()=>{var v,A,J;let p=1,w=C((v=s==null?void 0:s.maxRetries)!=null?v:0);for(;;){k=p;let j=new AbortController,{signal:H,cleanup:ie}=se(I,j.signal);try{let D=Promise.resolve(e({signal:H})),S=o?await oe(D,o,H,()=>(j.abort(),new P(b(o)))):await D;return m(l,S,O()),m(i==null?void 0:i.info,`Task succeeded on attempt ${p}`),S}catch(D){let S=a.normalizer(D),f=a.mapError?a.mapError(S):S;if(M=f,f.code==="ABORTED"&&m(y,H),n&&f.code==="ABORTED"||(m(u,f,O(f)),m(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 Y=s==null?void 0:s.shouldRetry,ue={totalAttempts:Number(k),elapsedTime:Date.now()-z,startTime:new Date(z),lastDelay:(A=g[g.length-1])!=null&&A.delay?Number((J=g[g.length-1])==null?void 0:J.delay):void 0};if(!Y||Y(p,f,ue)){let ce=s?re(s.strategy,p,f):0,$=Oe(ce,s==null?void 0:s.jitter),G=b($);g.push({attempt:p,error:f,delay:G,timestamp:new Date}),m(T,p,f,$),m(i==null?void 0:i.info,`Retrying in ${$}ms (attempt ${p+1})`),p+=1,await ne(G,I);continue}}throw f}finally{ie()}}};try{let p=await R(),w=O();return m(c,w),{type:"success",ok:!0,data:p,error:null,metrics:w}}catch(p){let w=M!=null?M:a.normalizer(p),v=w.code==="TIMEOUT"?"timeout":w.code==="ABORTED"?"aborted":"failure",A=O(w);return m(c,A),{type:v,ok:!1,data:null,error:w,metrics:A}}}finally{ae()}}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 s=()=>r.abort();o.addEventListener("abort",s,{once:!0}),n.push({signal:o,abort:s})}return{signal:r.signal,cleanup:()=>{for(let o of n)o.signal.removeEventListener("abort",o.abort)}}}function Oe(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=L(),Ae=N.run,Se=N.runOrThrow,Ne=N.orThrow,Me=N.all,Ie=N.allOrThrow;export{he as RetryStrategies,E as TypedError,Me as all,Ie as allOrThrow,b as asMilliseconds,C as asRetryCount,L as default,be as errorRule,Ne as orThrow,Ae as run,Se as runOrThrow,L as tryo};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tryo",
3
- "version": "0.13.7",
3
+ "version": "0.13.9",
4
4
  "author": {
5
5
  "name": "sebasxsala",
6
6
  "url": "https://github.com/sebasxsala"