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/dist/index.d.ts
CHANGED
|
@@ -1,321 +1,395 @@
|
|
|
1
|
-
import { MaybePromise as MaybePromise$1 } from 'bun';
|
|
2
|
-
|
|
3
|
-
type AppErrorCode = "ABORTED" | "NETWORK" | "TIMEOUT" | "VALIDATION" | "HTTP" | "UNKNOWN";
|
|
4
1
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* - `code`: A stable identifier you can switch on (e.g. "HTTP", "VALIDATION").
|
|
8
|
-
* - `message`: User-facing or log-friendly message.
|
|
9
|
-
* - `status`: Optional numeric status (commonly HTTP status).
|
|
10
|
-
* - `meta`: Optional payload with extra context (response body, validation fields, etc.).
|
|
11
|
-
* - `cause`: The original thrown value for debugging.
|
|
2
|
+
* Branded types for enhanced type safety and runtime validation
|
|
3
|
+
* These provide compile-time guarantees while maintaining runtime checks
|
|
12
4
|
*/
|
|
13
|
-
type
|
|
14
|
-
|
|
15
|
-
message: string;
|
|
16
|
-
status?: number;
|
|
17
|
-
meta?: Meta;
|
|
18
|
-
cause?: unknown;
|
|
5
|
+
type Milliseconds = number & {
|
|
6
|
+
readonly __brand: 'Milliseconds';
|
|
19
7
|
};
|
|
20
|
-
type
|
|
21
|
-
|
|
22
|
-
type InferErrorFromRules<TRules extends readonly Rule<any>[]> = TRules extends readonly [] ? AppError : RuleReturn<TRules[number]> | AppError<"UNKNOWN">;
|
|
23
|
-
type Rule<E extends AppError = AppError> = (err: unknown) => E | null;
|
|
24
|
-
|
|
25
|
-
type RetryDelayFn<E> = (attempt: number, err: E) => number;
|
|
26
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
27
|
-
type Jitter = boolean | number | {
|
|
28
|
-
ratio?: number;
|
|
29
|
-
mode?: "full" | "equal";
|
|
30
|
-
rng?: () => number;
|
|
8
|
+
type RetryCount = number & {
|
|
9
|
+
readonly __brand: 'RetryCount';
|
|
31
10
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
* - "linear": uses the base delay as is in each attempt.
|
|
35
|
-
* - "exponential": multiplies the delay by 2^(attempt-1).
|
|
36
|
-
* - "fibonacci": multiplies by F(attempt) (classic Fibonacci sequence).
|
|
37
|
-
* - function: custom function based on the attempt number.
|
|
38
|
-
*/
|
|
39
|
-
type BackoffStrategy = "linear" | "exponential" | "fibonacci" | ((attempt: number) => number);
|
|
40
|
-
/**
|
|
41
|
-
* Retry options for `run`, `runAll` and `runAllOrThrow`.
|
|
42
|
-
*/
|
|
43
|
-
type RetryOptions<E extends AppError = AppError> = {
|
|
44
|
-
/**
|
|
45
|
-
* Number of retries to perform (does not include the initial attempt).
|
|
46
|
-
* @default 0
|
|
47
|
-
*/
|
|
48
|
-
retries?: number;
|
|
49
|
-
/**
|
|
50
|
-
* Delay between attempts:
|
|
51
|
-
* - number: fixed delay (ms)
|
|
52
|
-
* - () => number: lazy delay (evaluated per attempt)
|
|
53
|
-
* - (attempt, err) => number: delay based on attempt and last error
|
|
54
|
-
* @default 0 or a default baseDelay if retries are present
|
|
55
|
-
*/
|
|
56
|
-
retryDelay?: number | (() => number) | RetryDelayFn<E>;
|
|
57
|
-
/**
|
|
58
|
-
* Decides whether to retry given a specific error.
|
|
59
|
-
* Can be synchronous or asynchronous.
|
|
60
|
-
* Receives the next attempt number and a context with accumulated metrics.
|
|
61
|
-
* @default () => true
|
|
62
|
-
*/
|
|
63
|
-
shouldRetry?: (attempt: number, error: E, context: RetryContext) => boolean | Promise<boolean>;
|
|
64
|
-
/**
|
|
65
|
-
* Random jitter to avoid thundering herd:
|
|
66
|
-
* - true: default ratio 0.5
|
|
67
|
-
* - false: no jitter
|
|
68
|
-
* - number: ratio 0..1
|
|
69
|
-
* - object: full control (ratio, mode, rng)
|
|
70
|
-
* @default 0.5
|
|
71
|
-
*/
|
|
72
|
-
jitter?: Jitter;
|
|
73
|
-
/**
|
|
74
|
-
* Backoff strategy to apply on the calculated delay.
|
|
75
|
-
* @default "linear"
|
|
76
|
-
*/
|
|
77
|
-
backoffStrategy?: BackoffStrategy;
|
|
78
|
-
/**
|
|
79
|
-
* Upper limit of the delay after backoff and before jitter.
|
|
80
|
-
* @default undefined (no limit)
|
|
81
|
-
*/
|
|
82
|
-
maxDelay?: number;
|
|
11
|
+
type ConcurrencyLimit = number & {
|
|
12
|
+
readonly __brand: 'ConcurrencyLimit';
|
|
83
13
|
};
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
*/
|
|
87
|
-
type RetryContext = {
|
|
88
|
-
/** Total attempts (including the next retry). */
|
|
89
|
-
totalAttempts: number;
|
|
90
|
-
/** Elapsed time in ms since the start of `run`. */
|
|
91
|
-
elapsedTime: number;
|
|
14
|
+
type Percentage = number & {
|
|
15
|
+
readonly __brand: 'Percentage';
|
|
92
16
|
};
|
|
17
|
+
type StatusCode = number & {
|
|
18
|
+
readonly __brand: 'StatusCode';
|
|
19
|
+
};
|
|
20
|
+
declare const asMilliseconds: (n: number) => Milliseconds;
|
|
21
|
+
declare const asRetryCount: (n: number) => RetryCount;
|
|
22
|
+
declare const asConcurrencyLimit: (n: number) => ConcurrencyLimit;
|
|
23
|
+
declare const asPercentage: (n: number) => Percentage;
|
|
24
|
+
declare const asStatusCode: (n: number) => StatusCode;
|
|
25
|
+
|
|
93
26
|
/**
|
|
94
|
-
*
|
|
27
|
+
* Modern typed error hierarchy with enhanced capabilities
|
|
28
|
+
* Provides type-safe error handling with fluent API and metadata support
|
|
95
29
|
*/
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
* Callback on success.
|
|
113
|
-
*/
|
|
114
|
-
onSuccess?: (data: T) => void;
|
|
115
|
-
/**
|
|
116
|
-
* Callback that always executes at the end (success or error).
|
|
117
|
-
*/
|
|
118
|
-
onFinally?: () => void;
|
|
119
|
-
/**
|
|
120
|
-
* If true, aborts (ABORTED) are not considered fatal errors:
|
|
121
|
-
* `onError` is not called and `{ ok: false, error }` is returned.
|
|
122
|
-
* @default true
|
|
123
|
-
*/
|
|
124
|
-
ignoreAbort?: boolean;
|
|
125
|
-
/**
|
|
126
|
-
* Signal for native work cancellation.
|
|
127
|
-
* If aborted, it cuts with `AbortError`.
|
|
128
|
-
*/
|
|
129
|
-
signal?: AbortSignal;
|
|
130
|
-
/**
|
|
131
|
-
* Maximum timeout in ms for the work; expires with `TimeoutError`.
|
|
132
|
-
*/
|
|
133
|
-
timeout?: number;
|
|
134
|
-
/**
|
|
135
|
-
* Retry observability: receives attempt, error, and next delay.
|
|
136
|
-
*/
|
|
137
|
-
onRetry?: (attempt: number, error: E, nextDelay: number) => void;
|
|
138
|
-
/**
|
|
139
|
-
* Optional structured logger for debug and errors.
|
|
140
|
-
*/
|
|
141
|
-
logger?: {
|
|
142
|
-
debug?: (msg: string, meta?: any) => void;
|
|
143
|
-
error?: (msg: string, error: E) => void;
|
|
30
|
+
|
|
31
|
+
declare abstract class TypedError<Code extends string = string, Meta = unknown> extends Error {
|
|
32
|
+
abstract readonly code: Code;
|
|
33
|
+
readonly cause?: unknown;
|
|
34
|
+
readonly meta?: Meta;
|
|
35
|
+
readonly status?: number;
|
|
36
|
+
readonly timestamp: number;
|
|
37
|
+
readonly retryable: boolean;
|
|
38
|
+
constructor(message: string, opts?: {
|
|
39
|
+
cause?: unknown;
|
|
40
|
+
meta?: Meta;
|
|
41
|
+
status?: number;
|
|
42
|
+
retryable?: boolean;
|
|
43
|
+
});
|
|
44
|
+
is<C extends string>(code: C): this is TypedError<C> & {
|
|
45
|
+
code: C;
|
|
144
46
|
};
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
47
|
+
withMeta<const M>(meta: M): this & {
|
|
48
|
+
meta: M;
|
|
49
|
+
};
|
|
50
|
+
withStatus(status: number): this & {
|
|
51
|
+
status: number;
|
|
52
|
+
};
|
|
53
|
+
withCause(cause: unknown): this & {
|
|
54
|
+
cause: unknown;
|
|
55
|
+
};
|
|
56
|
+
withRetryable(retryable: boolean): this;
|
|
57
|
+
toJSON(): Record<string, unknown>;
|
|
58
|
+
}
|
|
59
|
+
declare class TimeoutError extends TypedError<'TIMEOUT'> {
|
|
60
|
+
readonly code: "TIMEOUT";
|
|
61
|
+
constructor(timeout: Milliseconds, cause?: unknown);
|
|
62
|
+
}
|
|
63
|
+
declare class AbortedError extends TypedError<'ABORTED'> {
|
|
64
|
+
readonly code: "ABORTED";
|
|
65
|
+
constructor(reason?: string, cause?: unknown);
|
|
66
|
+
}
|
|
67
|
+
declare class CircuitOpenError extends TypedError<'CIRCUIT_OPEN'> {
|
|
68
|
+
readonly code: "CIRCUIT_OPEN";
|
|
69
|
+
constructor(resetAfter: Milliseconds, cause?: unknown);
|
|
70
|
+
}
|
|
71
|
+
type ValidationMeta = {
|
|
72
|
+
validationErrors: unknown[];
|
|
154
73
|
};
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
74
|
+
declare class ValidationError extends TypedError<'VALIDATION', ValidationMeta> {
|
|
75
|
+
readonly validationErrors: unknown[];
|
|
76
|
+
readonly code: "VALIDATION";
|
|
77
|
+
constructor(message: string, validationErrors: unknown[], cause?: unknown);
|
|
78
|
+
}
|
|
79
|
+
declare class NetworkError extends TypedError<'NETWORK'> {
|
|
80
|
+
readonly statusCode?: number | undefined;
|
|
81
|
+
readonly code: "NETWORK";
|
|
82
|
+
constructor(message: string, statusCode?: number | undefined, cause?: unknown);
|
|
83
|
+
}
|
|
84
|
+
type HttpMeta = {
|
|
85
|
+
response?: unknown;
|
|
165
86
|
};
|
|
87
|
+
declare class HttpError extends TypedError<'HTTP', HttpMeta> {
|
|
88
|
+
readonly status: number;
|
|
89
|
+
readonly response?: unknown | undefined;
|
|
90
|
+
readonly code: "HTTP";
|
|
91
|
+
constructor(message: string, status: number, response?: unknown | undefined, cause?: unknown);
|
|
92
|
+
}
|
|
93
|
+
declare class UnknownError extends TypedError<'UNKNOWN'> {
|
|
94
|
+
readonly code: "UNKNOWN";
|
|
95
|
+
constructor(message: string, cause?: unknown);
|
|
96
|
+
}
|
|
97
|
+
|
|
166
98
|
/**
|
|
167
|
-
*
|
|
99
|
+
* Modern circuit breaker implementation with enhanced state management
|
|
100
|
+
* Provides circuit breaker pattern with type safety and observability
|
|
168
101
|
*/
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
error:
|
|
179
|
-
|
|
180
|
-
} | {
|
|
181
|
-
ok: false;
|
|
182
|
-
data: null;
|
|
183
|
-
error: E;
|
|
184
|
-
metrics?: Metrics;
|
|
185
|
-
};
|
|
102
|
+
|
|
103
|
+
interface CircuitBreakerConfig<E extends TypedError = TypedError> {
|
|
104
|
+
/** Number of consecutive failures before opening circuit */
|
|
105
|
+
readonly failureThreshold: number;
|
|
106
|
+
/** How long to wait before attempting to close circuit */
|
|
107
|
+
readonly resetTimeout: number;
|
|
108
|
+
/** Number of requests allowed in half-open state */
|
|
109
|
+
readonly halfOpenRequests: number;
|
|
110
|
+
/** Optional function to determine if error should count as failure */
|
|
111
|
+
readonly shouldCountAsFailure?: (error: E) => boolean;
|
|
112
|
+
}
|
|
186
113
|
|
|
187
114
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* Errors are normalized into an `AppError` (or a custom error type `E`)
|
|
191
|
-
* using the provided `toError` function.
|
|
192
|
-
*
|
|
193
|
-
* This utility is framework-agnostic and works in browsers, Node.js,
|
|
194
|
-
* React effects, and any async context.
|
|
115
|
+
* Modern error normalization system
|
|
116
|
+
* Provides type-safe error transformation and normalization
|
|
195
117
|
*/
|
|
196
|
-
|
|
118
|
+
|
|
119
|
+
type ErrorNormalizer<E extends TypedError = TypedError> = (error: unknown) => E;
|
|
120
|
+
type ErrorRule<E extends TypedError = TypedError> = (error: unknown) => E | null;
|
|
197
121
|
|
|
198
122
|
/**
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
* - "error": failed task with `error`
|
|
202
|
-
* - "skipped": task not executed due to cancellation/fail-fast/concurrency
|
|
123
|
+
* Modern result types with enhanced discriminated unions
|
|
124
|
+
* Provides better type safety and more granular result categorization
|
|
203
125
|
*/
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
type
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
126
|
+
|
|
127
|
+
type TryoResult<T, E extends TypedError = TypedError> = SuccessResult<T, E> | FailureResult<E> | AbortedResult<E> | TimeoutResult<E>;
|
|
128
|
+
interface SuccessResult<T, E extends TypedError> {
|
|
129
|
+
readonly type: 'success';
|
|
130
|
+
readonly ok: true;
|
|
131
|
+
readonly data: T;
|
|
132
|
+
readonly error: null;
|
|
133
|
+
readonly metrics?: TryoMetrics$1<E>;
|
|
134
|
+
}
|
|
135
|
+
interface FailureResult<E extends TypedError> {
|
|
136
|
+
readonly type: 'failure';
|
|
137
|
+
readonly ok: false;
|
|
138
|
+
readonly data: null;
|
|
139
|
+
readonly error: E;
|
|
140
|
+
readonly metrics?: TryoMetrics$1<E>;
|
|
141
|
+
}
|
|
142
|
+
interface AbortedResult<E extends TypedError> {
|
|
143
|
+
readonly type: 'aborted';
|
|
144
|
+
readonly ok: false;
|
|
145
|
+
readonly data: null;
|
|
146
|
+
readonly error: E;
|
|
147
|
+
readonly metrics?: TryoMetrics$1<E>;
|
|
148
|
+
}
|
|
149
|
+
interface TimeoutResult<E extends TypedError> {
|
|
150
|
+
readonly type: 'timeout';
|
|
151
|
+
readonly ok: false;
|
|
152
|
+
readonly data: null;
|
|
153
|
+
readonly error: E;
|
|
154
|
+
readonly metrics?: TryoMetrics$1<E>;
|
|
155
|
+
}
|
|
156
|
+
interface TryoMetrics$1<E extends TypedError> {
|
|
157
|
+
readonly totalAttempts: RetryCount;
|
|
158
|
+
readonly totalRetries: RetryCount;
|
|
159
|
+
readonly totalDuration: Milliseconds;
|
|
160
|
+
readonly lastError?: E;
|
|
161
|
+
readonly retryHistory: Array<{
|
|
162
|
+
readonly attempt: RetryCount;
|
|
163
|
+
readonly error: E;
|
|
164
|
+
readonly delay: Milliseconds;
|
|
165
|
+
readonly timestamp: Date;
|
|
166
|
+
}>;
|
|
167
|
+
}
|
|
245
168
|
|
|
246
169
|
/**
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
* - `concurrency`: limit of simultaneous tasks; if one fails, it throws
|
|
170
|
+
* Modern retry strategies with enhanced capabilities
|
|
171
|
+
* Provides various retry patterns with type safety
|
|
250
172
|
*/
|
|
251
|
-
type RunAllOrThrowOptions<T, E extends AppError = AppError> = RunOptions<T, E> & {
|
|
252
|
-
/**
|
|
253
|
-
* Maximum number of concurrent tasks to run.
|
|
254
|
-
* @default Infinity
|
|
255
|
-
*/
|
|
256
|
-
concurrency?: number;
|
|
257
|
-
};
|
|
258
|
-
declare function runAll<T, E extends AppError = AppError>(tasks: Array<() => MaybePromise$1<T>>, options?: RunAllOrThrowOptions<T, E>): Promise<T[]>;
|
|
259
173
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
174
|
+
type RetryStrategy = FixedDelayStrategy | ExponentialBackoffStrategy | FibonacciBackoffStrategy | CustomDelayStrategy;
|
|
175
|
+
interface FixedDelayStrategy {
|
|
176
|
+
readonly type: 'fixed';
|
|
177
|
+
readonly delay: number;
|
|
178
|
+
}
|
|
179
|
+
interface ExponentialBackoffStrategy {
|
|
180
|
+
readonly type: 'exponential';
|
|
181
|
+
readonly base: number;
|
|
182
|
+
readonly factor: number;
|
|
183
|
+
readonly maxDelay?: number;
|
|
184
|
+
}
|
|
185
|
+
interface FibonacciBackoffStrategy {
|
|
186
|
+
readonly type: 'fibonacci';
|
|
187
|
+
readonly base: number;
|
|
188
|
+
readonly maxDelay?: number;
|
|
189
|
+
}
|
|
190
|
+
interface CustomDelayStrategy {
|
|
191
|
+
readonly type: 'custom';
|
|
192
|
+
readonly calculate: (attempt: RetryCount, error: unknown) => number;
|
|
193
|
+
}
|
|
194
|
+
declare const RetryStrategies: {
|
|
195
|
+
readonly fixed: (delay: number) => FixedDelayStrategy;
|
|
196
|
+
readonly exponential: (base: number, factor?: number, maxDelay?: number) => ExponentialBackoffStrategy;
|
|
197
|
+
readonly fibonacci: (base: number, maxDelay?: number) => FibonacciBackoffStrategy;
|
|
198
|
+
readonly custom: (calculate: (attempt: RetryCount, error: unknown) => number) => CustomDelayStrategy;
|
|
273
199
|
};
|
|
274
200
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
201
|
+
interface TryoConfig<E extends TypedError = TypedError> {
|
|
202
|
+
/** Abort signal passed to tasks */
|
|
203
|
+
readonly signal?: AbortSignal;
|
|
204
|
+
/** If true, aborts are treated as non-throwing failures */
|
|
205
|
+
readonly ignoreAbort?: boolean;
|
|
206
|
+
/** Timeout configuration */
|
|
207
|
+
readonly timeout?: number;
|
|
208
|
+
/** Retry configuration */
|
|
209
|
+
readonly retry?: RetryConfig<E>;
|
|
210
|
+
/** Circuit breaker configuration */
|
|
211
|
+
readonly circuitBreaker?: CircuitBreakerConfig<E>;
|
|
212
|
+
/** Error handling configuration */
|
|
213
|
+
readonly errorHandling: ErrorHandlingConfig<E>;
|
|
214
|
+
/** Concurrency configuration for batch operations */
|
|
215
|
+
readonly concurrency?: number;
|
|
216
|
+
/** Logging configuration */
|
|
217
|
+
readonly logger?: LoggerConfig<E>;
|
|
218
|
+
/** Callback hooks */
|
|
219
|
+
readonly hooks?: HookConfig<E>;
|
|
279
220
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
221
|
+
interface RetryConfig<E extends TypedError> {
|
|
222
|
+
/** Maximum number of retry attempts */
|
|
223
|
+
readonly maxRetries: number;
|
|
224
|
+
/** Base delay strategy */
|
|
225
|
+
readonly strategy: RetryStrategy;
|
|
226
|
+
/** Jitter configuration to prevent thundering herd */
|
|
227
|
+
readonly jitter?: JitterConfig;
|
|
228
|
+
/** Function to determine if retry should be attempted */
|
|
229
|
+
readonly shouldRetry?: ShouldRetryPredicate<E>;
|
|
230
|
+
}
|
|
231
|
+
type ShouldRetryPredicate<E extends TypedError> = (attempt: number, error: E, context: RetryContext) => boolean;
|
|
232
|
+
interface RetryContext {
|
|
233
|
+
/** Total attempts made so far */
|
|
234
|
+
readonly totalAttempts: number;
|
|
235
|
+
/** Elapsed time since start */
|
|
236
|
+
readonly elapsedTime: number;
|
|
237
|
+
/** Start timestamp */
|
|
238
|
+
readonly startTime: Date;
|
|
239
|
+
/** Last delay applied */
|
|
240
|
+
readonly lastDelay?: number;
|
|
241
|
+
}
|
|
242
|
+
interface ErrorHandlingConfig<E extends TypedError> {
|
|
243
|
+
/** Error normalizer function */
|
|
244
|
+
readonly normalizer: ErrorNormalizer<E>;
|
|
245
|
+
/** Optional error mapping/transformation */
|
|
246
|
+
readonly mapError?: (error: E) => E;
|
|
247
|
+
}
|
|
248
|
+
interface LoggerConfig<E extends TypedError> {
|
|
249
|
+
/** Debug logging function */
|
|
250
|
+
readonly debug?: (message: string, meta?: unknown) => void;
|
|
251
|
+
/** Error logging function */
|
|
252
|
+
readonly error?: (message: string, error: E) => void;
|
|
253
|
+
/** Info logging function */
|
|
254
|
+
readonly info?: (message: string, meta?: unknown) => void;
|
|
255
|
+
/** Warning logging function */
|
|
256
|
+
readonly warn?: (message: string, meta?: unknown) => void;
|
|
257
|
+
}
|
|
258
|
+
interface HookConfig<E extends TypedError> {
|
|
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 CircuitState = 'closed' | 'open' | 'half-open';
|
|
273
|
+
type TryoMetrics<E extends TypedError> = TryoMetrics$1<E>;
|
|
274
|
+
type JitterConfig = {
|
|
275
|
+
type: 'none';
|
|
276
|
+
} | {
|
|
277
|
+
type: 'full';
|
|
278
|
+
ratio: number;
|
|
279
|
+
} | {
|
|
280
|
+
type: 'equal';
|
|
281
|
+
ratio: number;
|
|
282
|
+
} | {
|
|
283
|
+
type: 'custom';
|
|
284
|
+
calculate: (delay: number) => number;
|
|
285
|
+
};
|
|
286
|
+
declare const JitterConfig: {
|
|
287
|
+
readonly none: () => JitterConfig;
|
|
288
|
+
readonly full: (ratio?: number) => JitterConfig;
|
|
289
|
+
readonly equal: (ratio?: number) => JitterConfig;
|
|
290
|
+
readonly custom: (calculate: (delay: number) => number) => JitterConfig;
|
|
283
291
|
};
|
|
284
292
|
|
|
285
|
-
type
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
* If not provided, the default fallback is used.
|
|
294
|
-
*/
|
|
293
|
+
type RulesMode = 'extend' | 'replace';
|
|
294
|
+
type DefaultError = AbortedError | TimeoutError | NetworkError | HttpError | CircuitOpenError | ValidationError | UnknownError;
|
|
295
|
+
type NonNull<T> = T extends null ? never : T;
|
|
296
|
+
type RuleReturn<R> = R extends (err: unknown) => infer Out ? NonNull<Out> : never;
|
|
297
|
+
type InferErrorFromRules<TRules extends readonly ErrorRule<TypedError>[]> = TRules extends readonly [] ? TypedError : RuleReturn<TRules[number]> | UnknownError;
|
|
298
|
+
type TryoOptions<E extends TypedError = TypedError> = Omit<Partial<TryoConfig<E>>, 'errorHandling' | 'signal'> & {
|
|
299
|
+
rules?: Array<ErrorRule<E>>;
|
|
300
|
+
rulesMode?: RulesMode;
|
|
295
301
|
fallback?: (err: unknown) => E;
|
|
296
|
-
/**
|
|
297
|
-
* If you want a completely custom normalizer, you can provide it directly.
|
|
298
|
-
* If set, `matchers` and `fallback` are ignored.
|
|
299
|
-
*/
|
|
300
302
|
toError?: (err: unknown) => E;
|
|
301
|
-
/** Default for run options */
|
|
302
|
-
ignoreAbort?: boolean;
|
|
303
|
-
/** Optional default mapper for all runs */
|
|
304
303
|
mapError?: (error: E) => E;
|
|
305
|
-
/**
|
|
306
|
-
* Default circuit breaker for all executions of the instance.
|
|
307
|
-
* Can be overridden by `options.circuitBreaker` in each `run`.
|
|
308
|
-
*/
|
|
309
|
-
circuitBreaker?: CircuitBreakerOptions;
|
|
310
304
|
};
|
|
311
|
-
|
|
312
|
-
run<T>(fn: () => Promise<T>, options?: RunOptions<T, E>): Promise<RunResult<T, E>>;
|
|
313
|
-
all<T>(fns: Array<() => MaybePromise<T>>, options?: RunAllOptions<T, E>): Promise<RunAllItemResult<T, E>[]>;
|
|
314
|
-
allOrThrow<T>(fns: Array<() => MaybePromise<T>>, options?: RunOptions<T, E>): Promise<T[]>;
|
|
315
|
-
}
|
|
316
|
-
declare function createRunner(opts?: Omit<CreateRunnerOptions<AppError>, "rules">): Runner<AppError>;
|
|
317
|
-
declare function createRunner<const TRules extends readonly Rule<any>[]>(opts: {
|
|
305
|
+
declare function tryo<const TRules extends readonly ErrorRule<TypedError>[]>(options: Omit<TryoOptions<InferErrorFromRules<TRules>>, 'rules'> & {
|
|
318
306
|
rules: TRules;
|
|
319
|
-
}
|
|
307
|
+
}): Tryo<InferErrorFromRules<TRules>>;
|
|
308
|
+
declare function tryo<E extends TypedError = DefaultError>(options?: TryoOptions<E>): Tryo<E>;
|
|
309
|
+
type Tryo<E extends TypedError = TypedError> = {
|
|
310
|
+
run: <T>(task: (ctx: {
|
|
311
|
+
signal: AbortSignal;
|
|
312
|
+
}) => Promise<T>, options?: Partial<TryoConfig<E>>) => Promise<TryoResult<T, E>>;
|
|
313
|
+
runOrThrow: <T>(task: (ctx: {
|
|
314
|
+
signal: AbortSignal;
|
|
315
|
+
}) => Promise<T>, options?: Partial<TryoConfig<E>>) => Promise<T>;
|
|
316
|
+
orThrow: <T>(task: (ctx: {
|
|
317
|
+
signal: AbortSignal;
|
|
318
|
+
}) => Promise<T>, options?: Partial<TryoConfig<E>>) => Promise<T>;
|
|
319
|
+
all: <T>(tasks: Array<(ctx: {
|
|
320
|
+
signal: AbortSignal;
|
|
321
|
+
}) => Promise<T>>, options?: Partial<TryoConfig<E> & {
|
|
322
|
+
concurrency?: number;
|
|
323
|
+
}>) => Promise<Array<TryoResult<T, E>>>;
|
|
324
|
+
allOrThrow: <T>(tasks: Array<(ctx: {
|
|
325
|
+
signal: AbortSignal;
|
|
326
|
+
}) => Promise<T>>, options?: Partial<TryoConfig<E> & {
|
|
327
|
+
concurrency?: number;
|
|
328
|
+
}>) => Promise<T[]>;
|
|
329
|
+
partitionAll: <T>(results: Array<TryoResult<T, E>>) => {
|
|
330
|
+
ok: Array<SuccessResult<T, E>>;
|
|
331
|
+
errors: Array<FailureResult<E> | AbortedResult<E> | TimeoutResult<E>>;
|
|
332
|
+
failure: Array<FailureResult<E>>;
|
|
333
|
+
aborted: Array<AbortedResult<E>>;
|
|
334
|
+
timeout: Array<TimeoutResult<E>>;
|
|
335
|
+
};
|
|
336
|
+
withConfig: (additionalConfig: Omit<Partial<TryoConfig<E>>, 'signal'>) => Tryo<E>;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
declare const run: <T>(task: (ctx: {
|
|
340
|
+
signal: AbortSignal;
|
|
341
|
+
}) => Promise<T>, options?: Partial<TryoConfig<DefaultError>> | undefined) => Promise<TryoResult<T, DefaultError>>;
|
|
342
|
+
declare const runOrThrow: <T>(task: (ctx: {
|
|
343
|
+
signal: AbortSignal;
|
|
344
|
+
}) => Promise<T>, options?: Partial<TryoConfig<DefaultError>> | undefined) => Promise<T>;
|
|
345
|
+
declare const orThrow: <T>(task: (ctx: {
|
|
346
|
+
signal: AbortSignal;
|
|
347
|
+
}) => Promise<T>, options?: Partial<TryoConfig<DefaultError>> | undefined) => Promise<T>;
|
|
348
|
+
declare const all: <T>(tasks: ((ctx: {
|
|
349
|
+
signal: AbortSignal;
|
|
350
|
+
}) => Promise<T>)[], options?: Partial<TryoConfig<DefaultError> & {
|
|
351
|
+
concurrency?: number;
|
|
352
|
+
}> | undefined) => Promise<TryoResult<T, DefaultError>[]>;
|
|
353
|
+
declare const allOrThrow: <T>(tasks: ((ctx: {
|
|
354
|
+
signal: AbortSignal;
|
|
355
|
+
}) => Promise<T>)[], options?: Partial<TryoConfig<DefaultError> & {
|
|
356
|
+
concurrency?: number;
|
|
357
|
+
}> | undefined) => Promise<T[]>;
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Modern fluent error rule builder
|
|
361
|
+
* Provides type-safe error rule creation with enhanced ergonomics
|
|
362
|
+
*/
|
|
363
|
+
|
|
364
|
+
declare class ErrorRuleBuilder<T> {
|
|
365
|
+
private readonly predicate;
|
|
366
|
+
constructor(predicate: (err: unknown) => err is T);
|
|
367
|
+
toCode<const C extends string>(code: C): ErrorMapper<T, C>;
|
|
368
|
+
toError<const Out extends {
|
|
369
|
+
code: string;
|
|
370
|
+
message: string;
|
|
371
|
+
meta?: unknown;
|
|
372
|
+
status?: number;
|
|
373
|
+
cause?: unknown;
|
|
374
|
+
retryable?: boolean;
|
|
375
|
+
}>(mapper: (err: T) => Out): ErrorRule<TypedError<Out['code'], Out['meta']>>;
|
|
376
|
+
}
|
|
377
|
+
declare class ErrorMapper<T, C extends string> {
|
|
378
|
+
private readonly predicate;
|
|
379
|
+
private readonly errorCode;
|
|
380
|
+
constructor(predicate: (err: unknown) => err is T, errorCode: C);
|
|
381
|
+
with<const M = unknown>(mapper: (err: T) => {
|
|
382
|
+
message: string;
|
|
383
|
+
cause?: unknown;
|
|
384
|
+
meta?: M;
|
|
385
|
+
status?: number;
|
|
386
|
+
retryable?: boolean;
|
|
387
|
+
}): (err: unknown) => TypedError<C, M> | null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
declare const errorRule: {
|
|
391
|
+
readonly when: <T>(predicate: (err: unknown) => err is T) => ErrorRuleBuilder<T>;
|
|
392
|
+
readonly instance: <T extends new (...args: unknown[]) => unknown>(ErrorClass: T) => ErrorRuleBuilder<InstanceType<T>>;
|
|
393
|
+
};
|
|
320
394
|
|
|
321
|
-
export { type
|
|
395
|
+
export { type AbortedResult, type ConcurrencyLimit, type FailureResult, type Milliseconds, type Percentage, type RetryCount, RetryStrategies, type RulesMode, type StatusCode, type SuccessResult, type TimeoutResult, type TryoConfig, type TryoMetrics$1 as TryoMetrics, type TryoOptions, type TryoResult, all, allOrThrow, asConcurrencyLimit, asMilliseconds, asPercentage, asRetryCount, asStatusCode, errorRule, orThrow, run, runOrThrow, tryo };
|