tryo 0.9.4 → 0.13.0
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 +172 -55
- package/dist/index.cjs +1 -2
- package/dist/index.d.cts +361 -287
- package/dist/index.d.ts +361 -287
- package/dist/index.js +1 -2
- package/package.json +57 -43
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/metafile-cjs.json +0 -1
- package/dist/metafile-esm.json +0 -1
package/README.md
CHANGED
|
@@ -1,73 +1,190 @@
|
|
|
1
|
-
|
|
1
|
+
# tryo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run sync/async functions and return a typed Result instead of throwing. `tryo` provides powerful error normalization, retry logic, concurrency control, and circuit breakers with a premium developer experience.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install tryo
|
|
9
|
+
# or
|
|
10
|
+
bun add tryo
|
|
11
|
+
```
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
## Basic Usage
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
await runner.run(fn, {
|
|
13
|
-
retries: 3,
|
|
14
|
-
retryDelay: (attempt) => attempt * 1000,
|
|
15
|
-
onError: (err) => toast.error(err.message),
|
|
16
|
-
onSuccess: (data) => console.log(data),
|
|
17
|
-
});
|
|
18
|
-
````
|
|
15
|
+
You can use the top-level shortcuts for simple cases, or create a configured instance for complex scenarios.
|
|
19
16
|
|
|
20
|
-
###
|
|
17
|
+
### Using Shortcuts
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
```typescript
|
|
20
|
+
import { run } from 'tryo'
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
()
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
];
|
|
22
|
+
const result = await run(async () => {
|
|
23
|
+
const res = await fetch('/api/data')
|
|
24
|
+
if (!res.ok) throw new Error('Failed')
|
|
25
|
+
return res.json()
|
|
26
|
+
})
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
28
|
+
if (result.ok) {
|
|
29
|
+
console.log(result.data) // result.data is typed
|
|
30
|
+
} else {
|
|
31
|
+
console.error(result.error.code) // "HTTP", "UNKNOWN", etc.
|
|
32
|
+
}
|
|
36
33
|
```
|
|
37
34
|
|
|
38
|
-
###
|
|
35
|
+
### Using the Factory (Best for Apps)
|
|
36
|
+
|
|
37
|
+
Creating an instance allows you to shared configuration like retries, circuit breakers, and custom error rules across your app.
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
```typescript
|
|
40
|
+
import tryo from 'tryo'
|
|
41
|
+
|
|
42
|
+
const ex = tryo({
|
|
43
|
+
retry: {
|
|
44
|
+
maxRetries: 3,
|
|
45
|
+
strategy: RetryStrategies.exponential(100),
|
|
46
|
+
},
|
|
47
|
+
circuitBreaker: {
|
|
48
|
+
failureThreshold: 5,
|
|
49
|
+
resetTimeout: 10000,
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const result = await ex.run(fetchData)
|
|
54
|
+
```
|
|
41
55
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
## Error Handling
|
|
57
|
+
|
|
58
|
+
`tryo` normalizes all errors into a `TypedError` instance with a stable `code`.
|
|
59
|
+
|
|
60
|
+
### The `TypedError` Shape
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
type TypedError<Code extends string = string, Meta = unknown> = {
|
|
64
|
+
code: Code // Stable error code (e.g. "TIMEOUT", "HTTP")
|
|
65
|
+
message: string // Human-readable message
|
|
66
|
+
cause?: unknown // Original error
|
|
67
|
+
meta?: Meta // Extra metadata (optional)
|
|
68
|
+
status?: number // Optional HTTP status (if applicable)
|
|
69
|
+
retryable: boolean // Whether the error is safe to retry
|
|
70
|
+
timestamp: number // When the error occurred (ms)
|
|
71
|
+
stack?: string // Stack trace for debugging
|
|
48
72
|
}
|
|
49
73
|
```
|
|
50
74
|
|
|
51
|
-
|
|
75
|
+
### Default Rules
|
|
76
|
+
|
|
77
|
+
By default, `tryo` detects:
|
|
78
|
+
|
|
79
|
+
- **ABORTED**: Detects `AbortError`.
|
|
80
|
+
- **TIMEOUT**: Detects `TimeoutError`.
|
|
81
|
+
- **HTTP**: Detects status codes in error objects.
|
|
82
|
+
- **UNKNOWN**: Fallback for everything else.
|
|
83
|
+
|
|
84
|
+
### Custom Rules
|
|
85
|
+
|
|
86
|
+
Map specific exceptions to typed error codes using the `rules` option.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import tryo, { errorRule } from 'tryo'
|
|
90
|
+
|
|
91
|
+
const ex = tryo({
|
|
92
|
+
rules: [
|
|
93
|
+
errorRule
|
|
94
|
+
.when(e => e === 'unauthorized')
|
|
95
|
+
.toError(() => ({
|
|
96
|
+
code: 'AUTH_ERROR',
|
|
97
|
+
message: 'Please login',
|
|
98
|
+
})),
|
|
99
|
+
] as const,
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## API Reference
|
|
104
|
+
|
|
105
|
+
### `.run(task, options?)`
|
|
106
|
+
|
|
107
|
+
Executes a single task. The `options` can override the instance defaults (except `signal`, which must be passed per call).
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const result = await ex.run(task, {
|
|
111
|
+
timeout: 5000,
|
|
112
|
+
signal: abortController.signal,
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### `.all(tasks, options?)`
|
|
117
|
+
|
|
118
|
+
Executes multiple tasks with **concurrency control**. Like `Promise.allSettled` but with retries, timeouts, and a worker pool.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const tasks = [() => job(1), () => job(2), () => job(3)]
|
|
52
122
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
```ts
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
let cancelled = false;
|
|
58
|
-
|
|
59
|
-
runner.run(fetchData, {
|
|
60
|
-
onSuccess: (data) => {
|
|
61
|
-
if (!cancelled) setData(data);
|
|
62
|
-
},
|
|
63
|
-
onError: (err) => {
|
|
64
|
-
if (err.code === "ABORTED") return;
|
|
65
|
-
toast.error(err.message);
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
return () => {
|
|
70
|
-
cancelled = true;
|
|
71
|
-
};
|
|
72
|
-
}, []);
|
|
123
|
+
// Execute 5-at-a-time
|
|
124
|
+
const results = await ex.all(tasks, { concurrency: 5 })
|
|
73
125
|
```
|
|
126
|
+
|
|
127
|
+
### `.partitionAll(results)`
|
|
128
|
+
|
|
129
|
+
Utility to separate successes from failures after an `all()` call.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const { ok, failure, aborted, timeout } = ex.partitionAll(results)
|
|
133
|
+
|
|
134
|
+
console.log(`Successes: ${ok.length}`)
|
|
135
|
+
console.log(`Errors: ${failure.length}`)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### `.runOrThrow(task, options?)`
|
|
139
|
+
|
|
140
|
+
Utility if you prefer exceptions but want the power of `tryo` (retries, breaker, normalization).
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const data = await ex.runOrThrow(task) // Returns data or throws TypedError
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Advanced Features
|
|
147
|
+
|
|
148
|
+
### Concurrency
|
|
149
|
+
|
|
150
|
+
The `all()` method includes a worker pool that respects your `concurrency` limit and stops launching new tasks if the `signal` is aborted.
|
|
151
|
+
|
|
152
|
+
### Circuit Breaker
|
|
153
|
+
|
|
154
|
+
If your tasks fail repeatedly, the circuit breaker opens and prevents further calls to protect your downstream services.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
const ex = tryo({
|
|
158
|
+
circuitBreaker: {
|
|
159
|
+
failureThreshold: 5,
|
|
160
|
+
resetTimeout: 30000,
|
|
161
|
+
halfOpenRequests: 2,
|
|
162
|
+
},
|
|
163
|
+
})
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Observability Hooks
|
|
167
|
+
|
|
168
|
+
Add hooks for logging or monitoring:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
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
|
+
},
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Why tryo?
|
|
181
|
+
|
|
182
|
+
1. **No More Try/Catch**: Handle results as data.
|
|
183
|
+
2. **Concurrency Control**: Built-in worker pool for batch operations.
|
|
184
|
+
3. **Normalized Errors**: Stable codes instead of unreliable error messages.
|
|
185
|
+
4. **Resiliency**: Sophisticated retry strategies and circuit breakers out of the box.
|
|
186
|
+
5. **Type Safety**: Full TypeScript support with inference for custom error rules.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
License: MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
'use strict';function B(r,e){return t=>{for(let u of r){let n=u(t);if(n)return n}return e(t)}}function N(r){return r instanceof Error?{code:r.name==="TypeError"?"NETWORK":"UNKNOWN",message:r.message||"Something went wrong",cause:r}:{code:"UNKNOWN",message:"Something went wrong",cause:r}}function D(r){return r instanceof DOMException&&r.name==="AbortError"?{code:"ABORTED",message:"Request cancelled",cause:r}:r instanceof DOMException&&r.name==="TimeoutError"?{code:"TIMEOUT",message:"Request timed out",cause:r}:N(r)}function O(r){if(r.retries!=null&&r.retries<0)throw new Error("retries must be >= 0");if(r.timeout!=null&&r.timeout<=0)throw new Error("timeout must be > 0");if(r.maxDelay!=null&&r.maxDelay<0)throw new Error("maxDelay must be >= 0");let e=r.circuitBreaker;if(e){if(e.failureThreshold<1)throw new Error("failureThreshold must be >= 1");if(e.resetTimeout<=0)throw new Error("resetTimeout must be > 0");if(e.halfOpenRequests!=null&&e.halfOpenRequests<1)throw new Error("halfOpenRequests must be >= 1")}}var J=r=>new Promise(e=>setTimeout(e,r));function z(r,e,t){return Math.max(e,Math.min(t,r))}function H(r,e,t,u,n,s){let a=typeof r=="function"?r(e,t):typeof r=="number"?r:u,i=Q(n??"linear",a,e),p=s!=null?z(i,0,s):i;return Number.isFinite(p)?p:0}function _(r,e){if(r<=0||!e)return r;let t=typeof e=="object"&&e.rng?e.rng:Math.random,u=typeof e=="number"?e:e===true?.5:typeof e=="object"&&e.ratio!=null?e.ratio:.5,n=z(u,0,1);return (typeof e=="object"&&e.mode?e.mode:"full")==="equal"?r*(1-n)+t()*r*n:r+t()*r*n}function Q(r,e,t){if(typeof r=="function"){let s=r(t);return s>=0?s:0}let u=e>=0?e:0,n=t>=1?t:1;if(r==="linear")return u;if(r==="exponential")return u*Math.pow(2,n-1);if(r==="fibonacci"){if(n<=2)return u;let s=1,a=1;for(let i=3;i<=n;i++){let p=s+a;s=a,a=p;}return u*a}return u}async function g(r,e={}){let{toError:t=D,mapError:u,onError:n,onSuccess:s,onFinally:a,ignoreAbort:i=true,retries:p=0,retryDelay:y,shouldRetry:f=()=>true,jitter:E={ratio:.5,mode:"full"},backoffStrategy:o,maxDelay:m,timeout:A,signal:R,onRetry:x,logger:b,onAbort:q}=e,$=p>0?300:0;if(O(e),R?.aborted)try{q?.(R);}finally{let T=t(new DOMException("Aborted","AbortError")),l=u?u(T):T;return i||n?.(l),a?.(),{ok:false,data:null,error:l,metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,lastError:l}}}let M=Date.now(),d=0,U;for(;;)try{let T=null,l=A&&A>0?new Promise((w,K)=>{T=setTimeout(()=>K(new DOMException("Timeout","TimeoutError")),A);}):null,I=await(l?Promise.race([r(),l]):r());T&&clearTimeout(T);try{s?.(I);}catch(w){b?.error?.("run:onSuccess failed",t(w));}try{a?.();}catch(w){b?.error?.("run:onFinally failed",t(w));}let h=Date.now()-M,k={totalAttempts:d+1,totalRetries:d,totalDuration:h,lastError:U};return b?.debug?.("run:success",{attempts:k.totalAttempts,duration:k.totalDuration}),{ok:!0,data:I,error:null,metrics:k}}catch(T){let l=t(T);if(u&&(l=u(l)),l?.code==="ABORTED"||T instanceof DOMException&&T.name==="AbortError"||R?.aborted)try{if(R)try{q?.(R);}catch(c){b?.error?.("run:onAbort failed",t(c));}}finally{if(!i)try{n?.(l);}catch(F){b?.error?.("run:onError failed",t(F));}try{a?.();}catch(F){b?.error?.("run:onFinally failed",t(F));}let c=Date.now()-M,W={totalAttempts:d,totalRetries:d,totalDuration:c,lastError:l};return b?.debug?.("run:aborted",{attempt:d,duration:W.totalDuration}),{ok:false,data:null,error:l,metrics:W}}U=l;let h=d+1,k={totalAttempts:h,elapsedTime:Date.now()-M},w=await Promise.resolve(f(h,l,k));if(d<p&&w){d=h;let c=H(y,d,l,$,o,m);(!Number.isFinite(c)||c<0)&&(c=0),c=_(c,E),x?.(d,l,c),b?.debug?.("run:retry",{attempt:d,delay:c}),c>0&&await J(c);continue}try{n?.(l);}catch(c){b?.error?.("run:onError failed",t(c));}try{a?.();}catch(c){b?.error?.("run:onFinally failed",t(c));}let G=Date.now()-M,L={totalAttempts:d+1,totalRetries:d,totalDuration:G,lastError:l};return b?.error?.("run:error",l),{ok:false,data:null,error:l,metrics:L}}}var V=r=>r.status==="ok";async function v(r,e={}){let{concurrency:t=1/0,mode:u="settle",...n}=e;if(O(n),r.length===0)return [];let s=Number.isFinite(t)?Math.max(1,Math.floor(t)):1/0,a=new Array(r.length),i=0,p=false,y=(o,m)=>{a[o]=m.ok?{status:"ok",ok:true,data:m.data,error:null}:{status:"error",ok:false,data:null,error:m.error};},f=()=>{for(let o=0;o<r.length;o++)a[o]||(a[o]={status:"skipped",ok:false,data:null,error:null});};if(s>=r.length)return (await Promise.all(r.map(m=>g(m,n)))).forEach((m,A)=>{y(A,m);}),a;let E=async()=>{for(;;){if(p)return;let o=i++;if(o>=r.length)return;let m=r[o];if(!m)continue;let A=await g(m,n);if(y(o,A),!A.ok&&u==="fail-fast"){p=true;return}}};return await Promise.all(Array.from({length:Math.min(s,r.length)},()=>E())),f(),a}async function C(r,e={}){let{concurrency:t=1/0,...u}=e;if(O(u),r.length===0)return [];let n=Number.isFinite(t)?Math.max(1,Math.floor(t)):1/0,s=new Array(r.length);if(n>=r.length)return await Promise.all(r.map(async(f,E)=>{let o=await g(f,u);if(!o.ok)throw o.error;s[E]=o.data;})),s;let a=0,i=false,p=null,y=async()=>{for(;;){if(i)return;let f=a++;if(f>=r.length)return;let E=r[f];if(!E)continue;let o=await g(E,u);if(!o.ok){p??(p=o.error),i=true;return}s[f]=o.data;}};if(await Promise.all(Array.from({length:Math.min(n,r.length)},()=>y())),p)throw p;return s}var X=r=>r instanceof DOMException&&r.name==="AbortError"?{code:"ABORTED",message:"Request cancelled",cause:r}:r instanceof Error&&r.name==="AbortError"?{code:"ABORTED",message:r.message||"Request cancelled",cause:r}:null,Y=r=>r instanceof DOMException&&r.name==="TimeoutError"?{code:"TIMEOUT",message:"Request timed out",cause:r}:r instanceof Error&&r.name==="TimeoutError"?{code:"TIMEOUT",message:r.message||"Request timed out",cause:r}:null,Z=r=>{if(typeof r=="object"&&r!==null){let e=r,t=e.status??e.statusCode;if(typeof t=="number")return {code:"HTTP",message:typeof e.message=="string"?e.message:`HTTP Error ${t}`,status:t,cause:r}}return null},j=r=>typeof AggregateError<"u"&&r instanceof AggregateError?{code:"UNKNOWN",message:r.message||"Multiple errors occurred",cause:r,meta:{errors:r.errors}}:null,rr=r=>typeof r=="string"?{code:"UNKNOWN",message:r,cause:r}:null,er=r=>typeof r=="object"&&r!==null&&"message"in r&&typeof r.message=="string"?{code:"UNKNOWN",message:r.message,cause:r}:null,tr={abort:X,timeout:Y,httpStatus:Z,aggregate:j,string:rr,message:er};var S=class{constructor(e){this.matcher=e;}toError(e){return t=>this.matcher(t)?e(t):null}},or={instance(r){return new S(e=>e instanceof r)},when(r){return new S(r)}};var P=(r,e)=>t=>e?e(r?r(t):t):r?r(t):t;function nr(r={}){let{rules:e=[],fallback:t=E=>N(E),toError:u,ignoreAbort:n=true,mapError:s,circuitBreaker:a}=r,i=u??(e.length>0?B(e,t):D),p=0,y=null,f=null;return {run(E,o={}){let m=o.circuitBreaker??a,A=Date.now();if(m){if(y&&A<y){let R=i(new Error("Circuit open")),x=P(s,o.mapError)(R);return o.onError?.(x),o.onFinally?.(),Promise.resolve({ok:false,data:null,error:x,metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,lastError:x}})}if(y&&A>=y&&(y=null,p=0,f=m.halfOpenRequests??1),f!=null)if(f<=0){let R=i(new Error("Circuit half-open limit")),x=P(s,o.mapError)(R);return o.onError?.(x),o.onFinally?.(),Promise.resolve({ok:false,data:null,error:x,metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,lastError:x}})}else f--;}return g(E,{toError:i,ignoreAbort:n,...o,mapError:P(s,o.mapError)}).then(R=>(m&&(R.ok?(p=0,y=null,f=null):(p++,p>=m.failureThreshold&&(y=Date.now()+m.resetTimeout,f=null))),R))},allSettled(E,o={}){return v(E,{toError:i,ignoreAbort:n,...o,mapError:P(s,o.mapError)})},all(E,o={}){return C(E,{toError:i,ignoreAbort:n,...o,mapError:P(s,o.mapError)})}}}exports.createNormalizer=B;exports.createRunner=nr;exports.defaultFallback=N;exports.errorRule=or;exports.isSuccess=V;exports.rules=tr;exports.run=g;exports.runAll=C;exports.runAllSettled=v;exports.toAppError=D;//# sourceMappingURL=index.cjs.map
|
|
2
|
-
//# sourceMappingURL=index.cjs.map
|
|
1
|
+
"use strict";var D=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var me=Object.prototype.hasOwnProperty;var de=(e,r,t)=>r in e?D(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var ye=(e,r)=>{for(var t in r)D(e,t,{get:r[t],enumerable:!0})},pe=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of le(r))!me.call(e,o)&&o!==t&&D(e,o,{get:()=>r[o],enumerable:!(n=ce(r,o))||n.enumerable});return e};var fe=e=>pe(D({},"__esModule",{value:!0}),e);var m=(e,r,t)=>de(e,typeof r!="symbol"?r+"":r,t);var Ae={};ye(Ae,{RetryStrategies:()=>X,all:()=>ne,allOrThrow:()=>oe,asConcurrencyLimit:()=>B,asMilliseconds:()=>C,asPercentage:()=>J,asRetryCount:()=>R,asStatusCode:()=>K,errorRule:()=>G,orThrow:()=>te,run:()=>ee,runOrThrow:()=>re,tryo:()=>z});module.exports=fe(Ae);var T=class extends Error{constructor(t,n){var o;super(t);m(this,"cause");m(this,"meta");m(this,"status");m(this,"timestamp");m(this,"retryable");this.timestamp=Date.now(),this.retryable=(o=n==null?void 0:n.retryable)!=null?o:!0,this.name=this.constructor.name,this.cause=n==null?void 0:n.cause,this.meta=n==null?void 0:n.meta,this.status=n==null?void 0:n.status,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}is(t){return this.code===t}withMeta(t){return Object.assign(this,{meta:t})}withStatus(t){return Object.assign(this,{status:t})}withCause(t){return Object.assign(this,{cause:t})}withRetryable(t){return this.retryable=t,this}toJSON(){return{name:this.name,code:this.code,message:this.message,timestamp:this.timestamp,retryable:this.retryable,cause:this.cause,stack:this.stack}}},v=class extends T{constructor(t,n){super(`Operation timed out after ${t}ms`,{cause:n,retryable:!0});m(this,"code","TIMEOUT")}};var A=class extends T{constructor(t,n){super(`Circuit breaker is open, reset after ${t}ms`,{cause:n,retryable:!1});m(this,"code","CIRCUIT_OPEN")}},I=class extends T{constructor(t,n,o){super(t,{cause:o,meta:{validationErrors:n},retryable:!1});this.validationErrors=n;m(this,"code","VALIDATION")}};var F=class extends T{constructor(t,n){super(t,{cause:n});m(this,"code","UNKNOWN")}};var C=e=>{if(e<0||!Number.isFinite(e))throw new Error(`Invalid milliseconds: must be a non-negative finite number, got ${e}`);return e},R=e=>{if(e<0||!Number.isInteger(e))throw new Error(`Invalid retry count: must be a non-negative integer, got ${e}`);return e},B=e=>{if(e<1||!Number.isInteger(e))throw new Error(`Invalid concurrency limit: must be a positive integer, got ${e}`);return e},J=e=>{if(e<0||e>100||!Number.isFinite(e))throw new Error(`Invalid percentage: must be between 0 and 100, got ${e}`);return e},K=e=>{if(!Number.isInteger(e)||e<100||e>599)throw new Error(`Invalid status code: must be an integer between 100-599, got ${e}`);return e};var L=class{constructor(r){m(this,"state");m(this,"config");this.config={failureThreshold:R(r.failureThreshold),resetTimeout:C(r.resetTimeout),halfOpenRequests:R(r.halfOpenRequests),shouldCountAsFailure:r.shouldCountAsFailure},this.state={state:"closed",failureCount:0,halfOpenCount:0}}async canExecute(){if(this.updateStateIfNeeded(),this.state.state==="open")return!1;if(this.state.state==="half-open"){if(this.state.halfOpenCount>=this.config.halfOpenRequests)return!1;this.state={...this.state,halfOpenCount:this.state.halfOpenCount+1}}return!0}async recordSuccess(){switch(this.state.state){case"closed":this.state={...this.state,failureCount:0};break;case"half-open":this.state={state:"closed",failureCount:0,halfOpenCount:0};break;case"open":break}}async recordFailure(r){var o,a,i;if(!((i=(a=(o=this.config).shouldCountAsFailure)==null?void 0:a.call(o,r))!=null?i:!0))return;let n=new Date;switch(this.state.state){case"closed":{let s=this.state.failureCount+1;s>=this.config.failureThreshold?this.state={state:"open",failureCount:s,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)}:this.state={...this.state,failureCount:s,lastFailureTime:n};break}case"half-open":this.state={state:"open",failureCount:this.state.failureCount+1,halfOpenCount:0,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)};break;case"open":this.state={...this.state,lastFailureTime:n,nextAttemptTime:new Date(n.getTime()+this.config.resetTimeout)};break}}getState(){return this.updateStateIfNeeded(),{...this.state,canExecute:this.state.state!=="open"}}createOpenError(){let r=this.state.nextAttemptTime?C(Math.max(0,this.state.nextAttemptTime.getTime()-Date.now())):this.config.resetTimeout;return new A(r)}forceState(r){this.state={state:r,failureCount:0,halfOpenCount:0}}reset(){this.state={state:"closed",failureCount:0,halfOpenCount:0}}updateStateIfNeeded(){this.state.state==="open"&&this.state.nextAttemptTime&&new Date>=this.state.nextAttemptTime&&(this.state={...this.state,state:"half-open",halfOpenCount:0})}};var q=(e,r)=>t=>{for(let n of e){let o=n(t);if(o!==null)return o}return r(t)},Y=e=>r=>r instanceof T?r:r instanceof Error?new e(r.message,r):typeof r=="string"?new e(r):new e("Unknown error occurred",r);var Te=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.status=="number"||typeof r.statusCode=="number"},Ee=e=>{if(typeof e!="object"||e===null)return!1;let r=e;return typeof r.code=="string"&&r.code.length>0},we=e=>{var r;if(e instanceof Error){if(e.name==="TypeError")return!0;let t=e.message.toLowerCase();if(t.includes("fetch")&&t.includes("failed")||t.includes("network"))return!0}if(Ee(e)){let t=((r=e.code)!=null?r:"").toUpperCase();return t==="ECONNRESET"||t==="ECONNREFUSED"||t==="ETIMEDOUT"||t==="ENOTFOUND"||t==="EAI_AGAIN"}return!1},U=class{constructor(r){this.predicate=r}toCode(r){return new _(this.predicate,r)}toError(r){return t=>{if(!this.predicate(t))return null;let n=r(t),o=n.code;class a extends T{constructor(){var u;super(n.message,{cause:(u=n.cause)!=null?u:t,meta:n.meta,status:n.status,retryable:n.retryable});m(this,"code",o)}}return new a}}},_=class{constructor(r,t){this.predicate=r;this.errorCode=t}with(r){return t=>{if(!this.predicate(t))return null;let n=r(t),o=this.errorCode;class a extends T{constructor(){super(n.message,{cause:n.cause,meta:n.meta,status:n.status,retryable:n.retryable});m(this,"code",o)}}return new a}}},E={when:e=>new U(e),instance:e=>new U(r=>r instanceof e),code:e=>({for:r=>new _(r,e)}),string:(e,r)=>({when:t=>E.when(t).toCode(r).with(n=>({message:n===e?e:`${r}: ${n}`,cause:n}))}),httpStatus:(e,r)=>({for:t=>E.when(t).toCode(r!=null?r:`HTTP_${e}`).with(n=>({message:`HTTP ${e} error`,cause:n}))})};var w={typed:(e=>e instanceof T?e:null),abort:E.when(e=>e instanceof DOMException&&e.name==="AbortError").toCode("ABORTED").with(e=>({message:e.message||"Operation was aborted",cause:e,retryable:!1})),timeout:E.when(e=>e instanceof Error&&e.name==="TimeoutError").toCode("TIMEOUT").with(e=>({message:e.message||"Operation timed out",cause:e})),network:E.when(e=>we(e)).toCode("NETWORK").with(e=>({message:(e instanceof Error,e.message||"Network error"),cause:e})),http:E.when(e=>{var n;if(!Te(e))return!1;let r=e,t=(n=r.status)!=null?n:r.statusCode;return typeof t=="number"&&t>=400}).toCode("HTTP").with(e=>{var n;let r=(n=e.status)!=null?n:e.statusCode,t=typeof r=="number"&&(r>=500||r===429);return{message:e.message||`HTTP ${r!=null?r:"error"} error`,cause:e,status:typeof r=="number"?r:void 0,retryable:t}}),unknown:E.when(e=>e instanceof Error&&!(e instanceof T)).toCode("UNKNOWN").with(e=>({message:e.message||"Unknown error occurred",cause:e}))};var be=[w.typed,w.abort,w.timeout,w.http,w.network,w.unknown];var G={when:e=>E.when(e),instance:e=>E.instance(e)},Q=be,He=w.timeout,Ve=w.abort,We=w.network,Je=w.http,Ke=E.when(e=>e instanceof A).toCode("CIRCUIT_OPEN").with(e=>({message:e.message,cause:e})),qe=E.when(e=>e instanceof I).toCode("VALIDATION").with(e=>({message:e.message,cause:e}));var X={fixed:e=>({type:"fixed",delay:e}),exponential:(e,r=2,t)=>({type:"exponential",base:e,factor:r,maxDelay:t}),fibonacci:(e,r)=>({type:"fibonacci",base:e,maxDelay:r}),custom:e=>({type:"custom",calculate:e})},Z=(e,r,t)=>{switch(e.type){case"fixed":return e.delay;case"exponential":{let n=e.base*e.factor**(Number(r)-1);return e.maxDelay?Math.min(n,e.maxDelay):n}case"fibonacci":{let n=e.base*he(Number(r));return e.maxDelay?Math.min(n,e.maxDelay):n}case"custom":return e.calculate(r,t);default:return e}},he=e=>{if(e<=1)return 1;let r=1,t=1;for(let n=2;n<=e;n++){let o=r+t;r=t,t=o}return t};var j=(e,r)=>new Promise((t,n)=>{if(r!=null&&r.aborted){n(new DOMException("Aborted","AbortError"));return}let o=s=>{r&&s&&r.removeEventListener("abort",s)},a,i=()=>{a&&clearTimeout(a),o(i),n(new DOMException("Aborted","AbortError"))};a=setTimeout(()=>{o(i),t()},e),r==null||r.addEventListener("abort",i,{once:!0})});var Ce=e=>{var i,s,u;if(e.toError)return e.toError;let r=(i=e.rulesMode)!=null?i:"extend",t=(s=e.rules)!=null?s:[],n=Q,o=(u=e.fallback)!=null?u:(c=>Y(F)(c)),a=r==="replace"?t:[...t,...n];return q(a,o)},H=class e{constructor(r={}){m(this,"circuitBreaker");m(this,"config");m(this,"lastCircuitState");let{rules:t,rulesMode:n,fallback:o,toError:a,mapError:i,...s}=r,u=Ce(r),c={...s,errorHandling:{normalizer:u,mapError:i}};this.config=c,c.circuitBreaker&&(this.circuitBreaker=new L(c.circuitBreaker),this.lastCircuitState=this.circuitBreaker.getState().state)}async run(r,t={}){var a,i,s,u,c,b;let n={...this.config,...t};if(this.circuitBreaker){let l=(a=this.lastCircuitState)!=null?a:this.circuitBreaker.getState().state,y=await this.circuitBreaker.canExecute(),p=this.circuitBreaker.getState().state;if(l!==p)try{(s=(i=n.hooks)==null?void 0:i.onCircuitStateChange)==null||s.call(i,l,p)}catch{}if(this.lastCircuitState=p,!y)return{type:"failure",ok:!1,data:null,error:this.circuitBreaker.createOpenError(),metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,retryHistory:[]}}}let o=await xe(r,n);if(this.circuitBreaker){let l=(u=this.lastCircuitState)!=null?u:this.circuitBreaker.getState().state;o.ok?await this.circuitBreaker.recordSuccess():await this.circuitBreaker.recordFailure(o.error);let y=this.circuitBreaker.getState().state;if(l!==y)try{(b=(c=n.hooks)==null?void 0:c.onCircuitStateChange)==null||b.call(c,l,y)}catch{}this.lastCircuitState=y}return o}async runOrThrow(r,t={}){let n=await this.run(r,t);if(n.ok)return n.data;throw n.error}async orThrow(r,t={}){let n=await this.run(r,t);if(n.ok)return n.data;throw n.error}async all(r,t={}){var b;let n={...this.config,...t},o=(b=n.concurrency)!=null?b:Number.POSITIVE_INFINITY,a=Number.isFinite(o)?Number(B(o)):Number.POSITIVE_INFINITY,i=new Array(r.length),s=0,u=async()=>{var l;for(;s<r.length&&!((l=n.signal)!=null&&l.aborted);){let y=s++;if(y>=r.length)break;let p=r[y];p&&(i[y]=await this.run(p,n))}},c=Array.from({length:Math.min(a,r.length)},()=>u());await Promise.all(c);for(let l=0;l<r.length;l++)if(!(l in i)){let y=r[l];y&&(i[l]=await this.run(y,n))}return i}async allOrThrow(r,t={}){let n=await this.all(r,t);for(let o of n)if(!o.ok)throw o.error;return n.map(o=>{if(!o.ok)throw o.error;return o.data})}partitionAll(r){let t=[],n=[],o=[],a=[],i=[];for(let s of r){if(s.type==="success"){t.push(s);continue}switch(n.push(s),s.type){case"failure":o.push(s);break;case"aborted":a.push(s);break;case"timeout":i.push(s);break}}return{ok:t,errors:n,failure:o,aborted:a,timeout:i}}getCircuitBreakerState(){var r;return(r=this.circuitBreaker)==null?void 0:r.getState()}resetCircuitBreaker(){var r;(r=this.circuitBreaker)==null||r.reset()}getConfig(){return{...this.config}}withConfig(r){var u,c;let{errorHandling:t,...n}=this.config,{errorHandling:o,...a}=r,i=(u=o==null?void 0:o.normalizer)!=null?u:t.normalizer,s=(c=o==null?void 0:o.mapError)!=null?c:t.mapError;return new e({...n,...a,toError:i,mapError:s})}withErrorType(r={}){return new e(r)}};function z(e={}){let r=new H(e);return{run:(t,n)=>r.run(t,n),orThrow:(t,n)=>r.orThrow(t,n),runOrThrow:(t,n)=>r.runOrThrow(t,n),all:(t,n)=>r.all(t,n),allOrThrow:(t,n)=>r.allOrThrow(t,n),partitionAll:t=>r.partitionAll(t),withConfig:t=>r.withConfig(t)}}async function xe(e,r){let{signal:t,ignoreAbort:n=!0,timeout:o,retry:a,errorHandling:i,hooks:s,logger:u}=r,c=(g,...d)=>{try{g==null||g(...d)}catch{}},b,l=0,y=0,p=[],k=Date.now(),{signal:x,cleanup:se}=Re(t);try{if(x.aborted){c(s==null?void 0:s.onAbort,x);let d=i.normalizer(new DOMException("Aborted","AbortError")),f=i.mapError?i.mapError(d):d;return{type:"aborted",ok:!1,data:null,error:f,metrics:{totalAttempts:l,totalRetries:y,totalDuration:Date.now()-k,lastError:f,retryHistory:p}}}let g=async d=>{var f,P,O;l=d;try{let M=e({signal:x}),S=o?await ke(M,o,x):await M;return c(s==null?void 0:s.onSuccess,S),c(u==null?void 0:u.info,`Task succeeded on attempt ${d}`),S}catch(M){let S=i.normalizer(M),h=i.mapError?i.mapError(S):S;b=h,h.code==="ABORTED"&&c(s==null?void 0:s.onAbort,x),n&&h.code==="ABORTED"||(c(s==null?void 0:s.onError,h),c(u==null?void 0:u.error,`Task failed on attempt ${d}`,h));let ae=R((f=a==null?void 0:a.maxRetries)!=null?f:0);if(d<=Number(ae)){let V=a==null?void 0:a.shouldRetry,ie={totalAttempts:Number(l),elapsedTime:Date.now()-k,startTime:new Date(k),lastDelay:(P=p[p.length-1])!=null&&P.delay?Number((O=p[p.length-1])==null?void 0:O.delay):void 0};if(!V||V(d,h,ie)){let ue=a?Z(a.strategy,d,h):0,$=ge(ue,a==null?void 0:a.jitter),W=C($);return p.push({attempt:d,error:h,delay:W,timestamp:new Date}),c(s==null?void 0:s.onRetry,d,h,$),c(u==null?void 0:u.info,`Retrying in ${$}ms (attempt ${d+1})`),await j(W,x),g(d+1)}}throw h}};try{let d=await g(1);y=l>0?Number(l)-1:0;let f={totalAttempts:l,totalRetries:y,totalDuration:Date.now()-k,retryHistory:p};return c(s==null?void 0:s.onFinally,f),{type:"success",ok:!0,data:d,error:null,metrics:f}}catch(d){let f=b!=null?b:i.normalizer(d),P=f.code==="TIMEOUT"?"timeout":f.code==="ABORTED"?"aborted":"failure";y=l>0?Number(l)-1:0;let O={totalAttempts:l,totalRetries:y,totalDuration:Date.now()-k,lastError:f,retryHistory:p};return c(s==null?void 0:s.onFinally,O),{type:P,ok:!1,data:null,error:f,metrics:O}}}finally{se()}}function Re(e){let r=new AbortController;if(!e)return{signal:r.signal,cleanup:()=>{}};let t=()=>r.abort();return e.aborted?(t(),{signal:r.signal,cleanup:()=>{}}):(e.addEventListener("abort",t,{once:!0}),{signal:r.signal,cleanup:()=>e.removeEventListener("abort",t)})}async function ke(e,r,t){return new Promise((n,o)=>{if(t!=null&&t.aborted){o(new DOMException("Aborted","AbortError"));return}let a=!1,i=setTimeout(()=>{a=!0,u(),o(new v(C(r)))},r),s=()=>{a||(a=!0,u(),o(new DOMException("Aborted","AbortError")))},u=()=>{clearTimeout(i),t==null||t.removeEventListener("abort",s)};t==null||t.addEventListener("abort",s,{once:!0}),e.then(c=>{a||(a=!0,u(),n(c))},c=>{a||(a=!0,u(),o(c))})})}function ge(e,r){if(!r||r.type==="none"||e<=0)return e;switch(r.type){case"full":{let t=Number(r.ratio)/100,n=Math.max(0,Number(e)*(1-t)),o=Number(e);return n+Math.random()*(o-n)}case"equal":{let t=Number(r.ratio)/100,n=Number(e)*t/2;return Number(e)-n+Math.random()*n}case"custom":return r.calculate(e);default:return r}}var N=z(),ee=N.run,re=N.runOrThrow,te=N.orThrow,ne=N.all,oe=N.allOrThrow;0&&(module.exports={RetryStrategies,all,allOrThrow,asConcurrencyLimit,asMilliseconds,asPercentage,asRetryCount,asStatusCode,errorRule,orThrow,run,runOrThrow,tryo});
|