trybox 0.1.2 → 0.10.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 +141 -20
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -46
- package/dist/index.d.ts +58 -46
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/metafile-cjs.json +1 -1
- package/dist/metafile-esm.json +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
type AppErrorCode = "ABORTED" | "NETWORK" | "TIMEOUT" | "VALIDATION" | "HTTP" | "UNKNOWN";
|
|
1
|
+
type ResultErrorCode = "ABORTED" | "NETWORK" | "TIMEOUT" | "VALIDATION" | "HTTP" | "CIRCUIT_OPEN" | "UNKNOWN";
|
|
4
2
|
/**
|
|
5
3
|
* A normalized error shape returned by `run()`.
|
|
6
4
|
*
|
|
@@ -10,14 +8,17 @@ type AppErrorCode = "ABORTED" | "NETWORK" | "TIMEOUT" | "VALIDATION" | "HTTP" |
|
|
|
10
8
|
* - `meta`: Optional payload with extra context (response body, validation fields, etc.).
|
|
11
9
|
* - `cause`: The original thrown value for debugging.
|
|
12
10
|
*/
|
|
13
|
-
type
|
|
11
|
+
type ResultError<Code extends string = ResultErrorCode | (string & {}), Meta = unknown> = {
|
|
14
12
|
code: Code;
|
|
15
13
|
message: string;
|
|
16
14
|
status?: number;
|
|
17
15
|
meta?: Meta;
|
|
18
16
|
cause?: unknown;
|
|
19
17
|
};
|
|
20
|
-
type
|
|
18
|
+
type NonNull<T> = T extends null ? never : T;
|
|
19
|
+
type RuleReturn<R> = R extends (err: unknown) => infer Out ? NonNull<Out> : never;
|
|
20
|
+
type InferErrorFromRules<TRules extends readonly Rule<any>[]> = TRules extends readonly [] ? ResultError : RuleReturn<TRules[number]> | ResultError<"UNKNOWN">;
|
|
21
|
+
type Rule<E extends ResultError = ResultError> = (err: unknown) => E | null;
|
|
21
22
|
|
|
22
23
|
type RetryDelayFn<E> = (attempt: number, err: E) => number;
|
|
23
24
|
type MaybePromise<T> = T | Promise<T>;
|
|
@@ -37,7 +38,7 @@ type BackoffStrategy = "linear" | "exponential" | "fibonacci" | ((attempt: numbe
|
|
|
37
38
|
/**
|
|
38
39
|
* Retry options for `run`, `runAll` and `runAllOrThrow`.
|
|
39
40
|
*/
|
|
40
|
-
type RetryOptions<E extends
|
|
41
|
+
type RetryOptions<E extends ResultError = ResultError> = {
|
|
41
42
|
/**
|
|
42
43
|
* Number of retries to perform (does not include the initial attempt).
|
|
43
44
|
* @default 0
|
|
@@ -86,11 +87,15 @@ type RetryContext = {
|
|
|
86
87
|
totalAttempts: number;
|
|
87
88
|
/** Elapsed time in ms since the start of `run`. */
|
|
88
89
|
elapsedTime: number;
|
|
90
|
+
/** Timestamp (ms) when the execution started. */
|
|
91
|
+
startTime: number;
|
|
92
|
+
/** The delay (ms) applied before the last attempt, if any. */
|
|
93
|
+
lastDelay?: number;
|
|
89
94
|
};
|
|
90
95
|
/**
|
|
91
96
|
* Main options for `run` and extended to `runAll`/`runAllOrThrow`.
|
|
92
97
|
*/
|
|
93
|
-
type RunOptions<T, E extends
|
|
98
|
+
type RunOptions<T, E extends ResultError = ResultError> = RetryOptions<E> & {
|
|
94
99
|
/**
|
|
95
100
|
* Normalizes an unknown error value to your type `E`.
|
|
96
101
|
* If not provided, a default normalizer is used.
|
|
@@ -136,18 +141,13 @@ type RunOptions<T, E extends AppError = AppError> = RetryOptions<E> & {
|
|
|
136
141
|
* Optional structured logger for debug and errors.
|
|
137
142
|
*/
|
|
138
143
|
logger?: {
|
|
139
|
-
debug?: (msg: string, meta?:
|
|
144
|
+
debug?: (msg: string, meta?: unknown) => void;
|
|
140
145
|
error?: (msg: string, error: E) => void;
|
|
141
146
|
};
|
|
142
147
|
/**
|
|
143
148
|
* Callback on abort, useful for reacting to `AbortSignal`.
|
|
144
149
|
*/
|
|
145
150
|
onAbort?: (signal: AbortSignal) => void;
|
|
146
|
-
/**
|
|
147
|
-
* Per-call circuit breaker configuration.
|
|
148
|
-
* If not defined, can use the default value from `Runner`.
|
|
149
|
-
*/
|
|
150
|
-
circuitBreaker?: CircuitBreakerOptions;
|
|
151
151
|
};
|
|
152
152
|
/**
|
|
153
153
|
* Circuit breaker configuration options:
|
|
@@ -163,42 +163,44 @@ type CircuitBreakerOptions = {
|
|
|
163
163
|
/**
|
|
164
164
|
* Execution metrics optionally returned in `RunResult`.
|
|
165
165
|
*/
|
|
166
|
-
type Metrics = {
|
|
166
|
+
type Metrics<E extends ResultError = ResultError> = {
|
|
167
167
|
totalAttempts: number;
|
|
168
168
|
totalRetries: number;
|
|
169
169
|
totalDuration: number;
|
|
170
|
-
lastError?:
|
|
170
|
+
lastError?: E;
|
|
171
171
|
};
|
|
172
|
-
type RunResult<T, E extends
|
|
172
|
+
type RunResult<T, E extends ResultError = ResultError> = {
|
|
173
173
|
ok: true;
|
|
174
174
|
data: T;
|
|
175
175
|
error: null;
|
|
176
|
-
metrics?: Metrics
|
|
176
|
+
metrics?: Metrics<E>;
|
|
177
177
|
} | {
|
|
178
178
|
ok: false;
|
|
179
179
|
data: null;
|
|
180
180
|
error: E;
|
|
181
|
-
metrics?: Metrics
|
|
181
|
+
metrics?: Metrics<E>;
|
|
182
182
|
};
|
|
183
183
|
|
|
184
184
|
/**
|
|
185
185
|
* Executes an async operation and returns a Result instead of throwing.
|
|
186
186
|
*
|
|
187
|
-
* Errors are normalized into an `
|
|
187
|
+
* Errors are normalized into an `ResultError` (or a custom error type `E`)
|
|
188
188
|
* using the provided `toError` function.
|
|
189
189
|
*
|
|
190
190
|
* This utility is framework-agnostic and works in browsers, Node.js,
|
|
191
191
|
* React effects, and any async context.
|
|
192
192
|
*/
|
|
193
|
-
declare function run<T, E extends
|
|
193
|
+
declare function run<T, E extends ResultError = ResultError>(fn: (ctx?: {
|
|
194
|
+
signal: AbortSignal;
|
|
195
|
+
}) => MaybePromise<T>, options?: RunOptions<T, E>): Promise<RunResult<T, E>>;
|
|
194
196
|
|
|
195
197
|
/**
|
|
196
|
-
* Discriminated result per item in `
|
|
198
|
+
* Discriminated result per item in `runAll`:
|
|
197
199
|
* - "ok": successful task with `data`
|
|
198
200
|
* - "error": failed task with `error`
|
|
199
201
|
* - "skipped": task not executed due to cancellation/fail-fast/concurrency
|
|
200
202
|
*/
|
|
201
|
-
type RunAllItemResult<T, E extends
|
|
203
|
+
type RunAllItemResult<T, E extends ResultError = ResultError> = {
|
|
202
204
|
status: "ok";
|
|
203
205
|
ok: true;
|
|
204
206
|
data: T;
|
|
@@ -219,12 +221,12 @@ type SuccessResult<T> = Extract<RunAllItemResult<T, any>, {
|
|
|
219
221
|
status: "ok";
|
|
220
222
|
}>;
|
|
221
223
|
/** Helper to discriminate error results. */
|
|
222
|
-
type ErrorResult<E> = Extract<RunAllItemResult<any, E extends
|
|
224
|
+
type ErrorResult<E> = Extract<RunAllItemResult<any, E extends ResultError ? E : ResultError>, {
|
|
223
225
|
status: "error";
|
|
224
226
|
}>;
|
|
225
227
|
/** Type guard that detects `status: "ok"` with `data` typing. */
|
|
226
|
-
declare const isSuccess: <T, E extends
|
|
227
|
-
type RunAllOptions<T, E extends
|
|
228
|
+
declare const isSuccess: <T, E extends ResultError = ResultError>(r: RunAllItemResult<T, E>) => r is SuccessResult<T>;
|
|
229
|
+
type RunAllOptions<T, E extends ResultError = ResultError> = RunOptions<T, E> & {
|
|
228
230
|
/**
|
|
229
231
|
* Maximum number of concurrent tasks to run.
|
|
230
232
|
* @default Infinity
|
|
@@ -238,53 +240,61 @@ type RunAllOptions<T, E extends AppError = AppError> = RunOptions<T, E> & {
|
|
|
238
240
|
*/
|
|
239
241
|
mode?: "settle" | "fail-fast";
|
|
240
242
|
};
|
|
241
|
-
declare function
|
|
243
|
+
declare function runAll<T, E extends ResultError = ResultError>(tasks: Array<() => MaybePromise<T>>, options?: RunAllOptions<T, E>): Promise<RunAllItemResult<T, E>[]>;
|
|
242
244
|
|
|
243
245
|
/**
|
|
244
|
-
* Options for `
|
|
246
|
+
* Options for `runAllOrThrow`:
|
|
245
247
|
* - Inherits all options from `RunOptions`
|
|
246
248
|
* - `concurrency`: limit of simultaneous tasks; if one fails, it throws
|
|
247
249
|
*/
|
|
248
|
-
type RunAllOrThrowOptions<T, E extends
|
|
250
|
+
type RunAllOrThrowOptions<T, E extends ResultError = ResultError> = RunOptions<T, E> & {
|
|
249
251
|
/**
|
|
250
252
|
* Maximum number of concurrent tasks to run.
|
|
251
253
|
* @default Infinity
|
|
252
254
|
*/
|
|
253
255
|
concurrency?: number;
|
|
254
256
|
};
|
|
255
|
-
declare function
|
|
257
|
+
declare function runAllOrThrow<T, E extends ResultError = ResultError>(tasks: Array<() => MaybePromise<T>>, options?: RunAllOrThrowOptions<T, E>): Promise<T[]>;
|
|
256
258
|
|
|
257
|
-
declare function createNormalizer<E extends
|
|
258
|
-
declare function defaultFallback(err: unknown):
|
|
259
|
-
declare function
|
|
259
|
+
declare function createNormalizer<E extends ResultError>(rules: Rule<E>[], fallback: (err: unknown) => E): (err: unknown) => E;
|
|
260
|
+
declare function defaultFallback(err: unknown): ResultError;
|
|
261
|
+
declare function toResultError(err: unknown): ResultError;
|
|
260
262
|
|
|
261
263
|
declare const rules: {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
264
|
+
circuitOpen: Rule<ResultError<"CIRCUIT_OPEN">>;
|
|
265
|
+
abort: Rule<ResultError<"ABORTED">>;
|
|
266
|
+
timeout: Rule<ResultError<"TIMEOUT">>;
|
|
267
|
+
httpStatus: Rule<ResultError<"HTTP">>;
|
|
268
|
+
aggregate: Rule<ResultError<"UNKNOWN", {
|
|
266
269
|
errors: unknown[];
|
|
267
270
|
}>>;
|
|
268
|
-
string: Rule<
|
|
269
|
-
message: Rule<
|
|
271
|
+
string: Rule<ResultError<"UNKNOWN">>;
|
|
272
|
+
message: Rule<ResultError<"UNKNOWN">>;
|
|
270
273
|
};
|
|
271
274
|
|
|
272
275
|
declare class ErrorRuleBuilder<E> {
|
|
273
276
|
private readonly matcher;
|
|
274
277
|
constructor(matcher: (err: unknown) => err is E);
|
|
275
|
-
toError<const Out extends
|
|
278
|
+
toError<const Out extends ResultError>(mapper: (err: E) => Out): Rule<Out>;
|
|
276
279
|
}
|
|
277
280
|
declare const errorRule: {
|
|
278
281
|
instance<E extends new (...args: any[]) => unknown>(ctor: E): ErrorRuleBuilder<InstanceType<E>>;
|
|
279
282
|
when<E = unknown>(predicate: (err: unknown) => err is E): ErrorRuleBuilder<E>;
|
|
280
283
|
};
|
|
281
284
|
|
|
282
|
-
type
|
|
285
|
+
type RulesMode = "extend" | "replace";
|
|
286
|
+
type CreateRunnerOptions<E extends ResultError = ResultError> = {
|
|
283
287
|
/**
|
|
284
288
|
* Custom matchers to use for normalizing errors.
|
|
285
289
|
* If not provided, the default matchers are used.
|
|
286
290
|
*/
|
|
287
291
|
rules?: Rule<E>[];
|
|
292
|
+
/**
|
|
293
|
+
* How to treat provided rules in relation to default rules.
|
|
294
|
+
* - "extend": Use default rules after custom rules (default).
|
|
295
|
+
* - "replace": Use only custom rules (and fallback).
|
|
296
|
+
*/
|
|
297
|
+
rulesMode?: RulesMode;
|
|
288
298
|
/**
|
|
289
299
|
* Custom fallback function to use for normalizing errors.
|
|
290
300
|
* If not provided, the default fallback is used.
|
|
@@ -300,17 +310,19 @@ type CreateRunnerOptions<E extends AppError = AppError> = {
|
|
|
300
310
|
/** Optional default mapper for all runs */
|
|
301
311
|
mapError?: (error: E) => E;
|
|
302
312
|
/**
|
|
303
|
-
*
|
|
304
|
-
* Can be overridden by `options.circuitBreaker` in each `run`.
|
|
313
|
+
* Circuit breaker configuration (applies to all executions of the Runner).
|
|
305
314
|
*/
|
|
306
315
|
circuitBreaker?: CircuitBreakerOptions;
|
|
307
316
|
};
|
|
308
|
-
interface Runner<E extends
|
|
317
|
+
interface Runner<E extends ResultError> {
|
|
309
318
|
run<T>(fn: () => Promise<T>, options?: RunOptions<T, E>): Promise<RunResult<T, E>>;
|
|
310
319
|
all<T>(fns: Array<() => MaybePromise<T>>, options?: RunAllOptions<T, E>): Promise<RunAllItemResult<T, E>[]>;
|
|
311
|
-
allOrThrow<T>(fns: Array<() => MaybePromise<T>>, options?:
|
|
320
|
+
allOrThrow<T>(fns: Array<() => MaybePromise<T>>, options?: RunAllOrThrowOptions<T, E>): Promise<T[]>;
|
|
312
321
|
}
|
|
313
322
|
|
|
314
|
-
declare function trybox(options?: CreateRunnerOptions): Runner<
|
|
323
|
+
declare function trybox(options?: Omit<CreateRunnerOptions<ResultError>, "rules">): Runner<ResultError>;
|
|
324
|
+
declare function trybox<const TRules extends readonly Rule<any>[]>(options: {
|
|
325
|
+
rules: TRules;
|
|
326
|
+
} & Omit<CreateRunnerOptions<InferErrorFromRules<TRules>>, "rules">): Runner<InferErrorFromRules<TRules>>;
|
|
315
327
|
|
|
316
|
-
export { type
|
|
328
|
+
export { type BackoffStrategy, type CircuitBreakerOptions, type ErrorResult, type Metrics, type ResultError, type ResultErrorCode, type RetryContext, type RetryOptions, type RunAllItemResult, type RunAllOptions, type RunOptions, type RunResult, type SuccessResult, createNormalizer, trybox as default, defaultFallback, errorRule, isSuccess, rules, run, runAll, runAllOrThrow, toResultError };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
type AppErrorCode = "ABORTED" | "NETWORK" | "TIMEOUT" | "VALIDATION" | "HTTP" | "UNKNOWN";
|
|
1
|
+
type ResultErrorCode = "ABORTED" | "NETWORK" | "TIMEOUT" | "VALIDATION" | "HTTP" | "CIRCUIT_OPEN" | "UNKNOWN";
|
|
4
2
|
/**
|
|
5
3
|
* A normalized error shape returned by `run()`.
|
|
6
4
|
*
|
|
@@ -10,14 +8,17 @@ type AppErrorCode = "ABORTED" | "NETWORK" | "TIMEOUT" | "VALIDATION" | "HTTP" |
|
|
|
10
8
|
* - `meta`: Optional payload with extra context (response body, validation fields, etc.).
|
|
11
9
|
* - `cause`: The original thrown value for debugging.
|
|
12
10
|
*/
|
|
13
|
-
type
|
|
11
|
+
type ResultError<Code extends string = ResultErrorCode | (string & {}), Meta = unknown> = {
|
|
14
12
|
code: Code;
|
|
15
13
|
message: string;
|
|
16
14
|
status?: number;
|
|
17
15
|
meta?: Meta;
|
|
18
16
|
cause?: unknown;
|
|
19
17
|
};
|
|
20
|
-
type
|
|
18
|
+
type NonNull<T> = T extends null ? never : T;
|
|
19
|
+
type RuleReturn<R> = R extends (err: unknown) => infer Out ? NonNull<Out> : never;
|
|
20
|
+
type InferErrorFromRules<TRules extends readonly Rule<any>[]> = TRules extends readonly [] ? ResultError : RuleReturn<TRules[number]> | ResultError<"UNKNOWN">;
|
|
21
|
+
type Rule<E extends ResultError = ResultError> = (err: unknown) => E | null;
|
|
21
22
|
|
|
22
23
|
type RetryDelayFn<E> = (attempt: number, err: E) => number;
|
|
23
24
|
type MaybePromise<T> = T | Promise<T>;
|
|
@@ -37,7 +38,7 @@ type BackoffStrategy = "linear" | "exponential" | "fibonacci" | ((attempt: numbe
|
|
|
37
38
|
/**
|
|
38
39
|
* Retry options for `run`, `runAll` and `runAllOrThrow`.
|
|
39
40
|
*/
|
|
40
|
-
type RetryOptions<E extends
|
|
41
|
+
type RetryOptions<E extends ResultError = ResultError> = {
|
|
41
42
|
/**
|
|
42
43
|
* Number of retries to perform (does not include the initial attempt).
|
|
43
44
|
* @default 0
|
|
@@ -86,11 +87,15 @@ type RetryContext = {
|
|
|
86
87
|
totalAttempts: number;
|
|
87
88
|
/** Elapsed time in ms since the start of `run`. */
|
|
88
89
|
elapsedTime: number;
|
|
90
|
+
/** Timestamp (ms) when the execution started. */
|
|
91
|
+
startTime: number;
|
|
92
|
+
/** The delay (ms) applied before the last attempt, if any. */
|
|
93
|
+
lastDelay?: number;
|
|
89
94
|
};
|
|
90
95
|
/**
|
|
91
96
|
* Main options for `run` and extended to `runAll`/`runAllOrThrow`.
|
|
92
97
|
*/
|
|
93
|
-
type RunOptions<T, E extends
|
|
98
|
+
type RunOptions<T, E extends ResultError = ResultError> = RetryOptions<E> & {
|
|
94
99
|
/**
|
|
95
100
|
* Normalizes an unknown error value to your type `E`.
|
|
96
101
|
* If not provided, a default normalizer is used.
|
|
@@ -136,18 +141,13 @@ type RunOptions<T, E extends AppError = AppError> = RetryOptions<E> & {
|
|
|
136
141
|
* Optional structured logger for debug and errors.
|
|
137
142
|
*/
|
|
138
143
|
logger?: {
|
|
139
|
-
debug?: (msg: string, meta?:
|
|
144
|
+
debug?: (msg: string, meta?: unknown) => void;
|
|
140
145
|
error?: (msg: string, error: E) => void;
|
|
141
146
|
};
|
|
142
147
|
/**
|
|
143
148
|
* Callback on abort, useful for reacting to `AbortSignal`.
|
|
144
149
|
*/
|
|
145
150
|
onAbort?: (signal: AbortSignal) => void;
|
|
146
|
-
/**
|
|
147
|
-
* Per-call circuit breaker configuration.
|
|
148
|
-
* If not defined, can use the default value from `Runner`.
|
|
149
|
-
*/
|
|
150
|
-
circuitBreaker?: CircuitBreakerOptions;
|
|
151
151
|
};
|
|
152
152
|
/**
|
|
153
153
|
* Circuit breaker configuration options:
|
|
@@ -163,42 +163,44 @@ type CircuitBreakerOptions = {
|
|
|
163
163
|
/**
|
|
164
164
|
* Execution metrics optionally returned in `RunResult`.
|
|
165
165
|
*/
|
|
166
|
-
type Metrics = {
|
|
166
|
+
type Metrics<E extends ResultError = ResultError> = {
|
|
167
167
|
totalAttempts: number;
|
|
168
168
|
totalRetries: number;
|
|
169
169
|
totalDuration: number;
|
|
170
|
-
lastError?:
|
|
170
|
+
lastError?: E;
|
|
171
171
|
};
|
|
172
|
-
type RunResult<T, E extends
|
|
172
|
+
type RunResult<T, E extends ResultError = ResultError> = {
|
|
173
173
|
ok: true;
|
|
174
174
|
data: T;
|
|
175
175
|
error: null;
|
|
176
|
-
metrics?: Metrics
|
|
176
|
+
metrics?: Metrics<E>;
|
|
177
177
|
} | {
|
|
178
178
|
ok: false;
|
|
179
179
|
data: null;
|
|
180
180
|
error: E;
|
|
181
|
-
metrics?: Metrics
|
|
181
|
+
metrics?: Metrics<E>;
|
|
182
182
|
};
|
|
183
183
|
|
|
184
184
|
/**
|
|
185
185
|
* Executes an async operation and returns a Result instead of throwing.
|
|
186
186
|
*
|
|
187
|
-
* Errors are normalized into an `
|
|
187
|
+
* Errors are normalized into an `ResultError` (or a custom error type `E`)
|
|
188
188
|
* using the provided `toError` function.
|
|
189
189
|
*
|
|
190
190
|
* This utility is framework-agnostic and works in browsers, Node.js,
|
|
191
191
|
* React effects, and any async context.
|
|
192
192
|
*/
|
|
193
|
-
declare function run<T, E extends
|
|
193
|
+
declare function run<T, E extends ResultError = ResultError>(fn: (ctx?: {
|
|
194
|
+
signal: AbortSignal;
|
|
195
|
+
}) => MaybePromise<T>, options?: RunOptions<T, E>): Promise<RunResult<T, E>>;
|
|
194
196
|
|
|
195
197
|
/**
|
|
196
|
-
* Discriminated result per item in `
|
|
198
|
+
* Discriminated result per item in `runAll`:
|
|
197
199
|
* - "ok": successful task with `data`
|
|
198
200
|
* - "error": failed task with `error`
|
|
199
201
|
* - "skipped": task not executed due to cancellation/fail-fast/concurrency
|
|
200
202
|
*/
|
|
201
|
-
type RunAllItemResult<T, E extends
|
|
203
|
+
type RunAllItemResult<T, E extends ResultError = ResultError> = {
|
|
202
204
|
status: "ok";
|
|
203
205
|
ok: true;
|
|
204
206
|
data: T;
|
|
@@ -219,12 +221,12 @@ type SuccessResult<T> = Extract<RunAllItemResult<T, any>, {
|
|
|
219
221
|
status: "ok";
|
|
220
222
|
}>;
|
|
221
223
|
/** Helper to discriminate error results. */
|
|
222
|
-
type ErrorResult<E> = Extract<RunAllItemResult<any, E extends
|
|
224
|
+
type ErrorResult<E> = Extract<RunAllItemResult<any, E extends ResultError ? E : ResultError>, {
|
|
223
225
|
status: "error";
|
|
224
226
|
}>;
|
|
225
227
|
/** Type guard that detects `status: "ok"` with `data` typing. */
|
|
226
|
-
declare const isSuccess: <T, E extends
|
|
227
|
-
type RunAllOptions<T, E extends
|
|
228
|
+
declare const isSuccess: <T, E extends ResultError = ResultError>(r: RunAllItemResult<T, E>) => r is SuccessResult<T>;
|
|
229
|
+
type RunAllOptions<T, E extends ResultError = ResultError> = RunOptions<T, E> & {
|
|
228
230
|
/**
|
|
229
231
|
* Maximum number of concurrent tasks to run.
|
|
230
232
|
* @default Infinity
|
|
@@ -238,53 +240,61 @@ type RunAllOptions<T, E extends AppError = AppError> = RunOptions<T, E> & {
|
|
|
238
240
|
*/
|
|
239
241
|
mode?: "settle" | "fail-fast";
|
|
240
242
|
};
|
|
241
|
-
declare function
|
|
243
|
+
declare function runAll<T, E extends ResultError = ResultError>(tasks: Array<() => MaybePromise<T>>, options?: RunAllOptions<T, E>): Promise<RunAllItemResult<T, E>[]>;
|
|
242
244
|
|
|
243
245
|
/**
|
|
244
|
-
* Options for `
|
|
246
|
+
* Options for `runAllOrThrow`:
|
|
245
247
|
* - Inherits all options from `RunOptions`
|
|
246
248
|
* - `concurrency`: limit of simultaneous tasks; if one fails, it throws
|
|
247
249
|
*/
|
|
248
|
-
type RunAllOrThrowOptions<T, E extends
|
|
250
|
+
type RunAllOrThrowOptions<T, E extends ResultError = ResultError> = RunOptions<T, E> & {
|
|
249
251
|
/**
|
|
250
252
|
* Maximum number of concurrent tasks to run.
|
|
251
253
|
* @default Infinity
|
|
252
254
|
*/
|
|
253
255
|
concurrency?: number;
|
|
254
256
|
};
|
|
255
|
-
declare function
|
|
257
|
+
declare function runAllOrThrow<T, E extends ResultError = ResultError>(tasks: Array<() => MaybePromise<T>>, options?: RunAllOrThrowOptions<T, E>): Promise<T[]>;
|
|
256
258
|
|
|
257
|
-
declare function createNormalizer<E extends
|
|
258
|
-
declare function defaultFallback(err: unknown):
|
|
259
|
-
declare function
|
|
259
|
+
declare function createNormalizer<E extends ResultError>(rules: Rule<E>[], fallback: (err: unknown) => E): (err: unknown) => E;
|
|
260
|
+
declare function defaultFallback(err: unknown): ResultError;
|
|
261
|
+
declare function toResultError(err: unknown): ResultError;
|
|
260
262
|
|
|
261
263
|
declare const rules: {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
264
|
+
circuitOpen: Rule<ResultError<"CIRCUIT_OPEN">>;
|
|
265
|
+
abort: Rule<ResultError<"ABORTED">>;
|
|
266
|
+
timeout: Rule<ResultError<"TIMEOUT">>;
|
|
267
|
+
httpStatus: Rule<ResultError<"HTTP">>;
|
|
268
|
+
aggregate: Rule<ResultError<"UNKNOWN", {
|
|
266
269
|
errors: unknown[];
|
|
267
270
|
}>>;
|
|
268
|
-
string: Rule<
|
|
269
|
-
message: Rule<
|
|
271
|
+
string: Rule<ResultError<"UNKNOWN">>;
|
|
272
|
+
message: Rule<ResultError<"UNKNOWN">>;
|
|
270
273
|
};
|
|
271
274
|
|
|
272
275
|
declare class ErrorRuleBuilder<E> {
|
|
273
276
|
private readonly matcher;
|
|
274
277
|
constructor(matcher: (err: unknown) => err is E);
|
|
275
|
-
toError<const Out extends
|
|
278
|
+
toError<const Out extends ResultError>(mapper: (err: E) => Out): Rule<Out>;
|
|
276
279
|
}
|
|
277
280
|
declare const errorRule: {
|
|
278
281
|
instance<E extends new (...args: any[]) => unknown>(ctor: E): ErrorRuleBuilder<InstanceType<E>>;
|
|
279
282
|
when<E = unknown>(predicate: (err: unknown) => err is E): ErrorRuleBuilder<E>;
|
|
280
283
|
};
|
|
281
284
|
|
|
282
|
-
type
|
|
285
|
+
type RulesMode = "extend" | "replace";
|
|
286
|
+
type CreateRunnerOptions<E extends ResultError = ResultError> = {
|
|
283
287
|
/**
|
|
284
288
|
* Custom matchers to use for normalizing errors.
|
|
285
289
|
* If not provided, the default matchers are used.
|
|
286
290
|
*/
|
|
287
291
|
rules?: Rule<E>[];
|
|
292
|
+
/**
|
|
293
|
+
* How to treat provided rules in relation to default rules.
|
|
294
|
+
* - "extend": Use default rules after custom rules (default).
|
|
295
|
+
* - "replace": Use only custom rules (and fallback).
|
|
296
|
+
*/
|
|
297
|
+
rulesMode?: RulesMode;
|
|
288
298
|
/**
|
|
289
299
|
* Custom fallback function to use for normalizing errors.
|
|
290
300
|
* If not provided, the default fallback is used.
|
|
@@ -300,17 +310,19 @@ type CreateRunnerOptions<E extends AppError = AppError> = {
|
|
|
300
310
|
/** Optional default mapper for all runs */
|
|
301
311
|
mapError?: (error: E) => E;
|
|
302
312
|
/**
|
|
303
|
-
*
|
|
304
|
-
* Can be overridden by `options.circuitBreaker` in each `run`.
|
|
313
|
+
* Circuit breaker configuration (applies to all executions of the Runner).
|
|
305
314
|
*/
|
|
306
315
|
circuitBreaker?: CircuitBreakerOptions;
|
|
307
316
|
};
|
|
308
|
-
interface Runner<E extends
|
|
317
|
+
interface Runner<E extends ResultError> {
|
|
309
318
|
run<T>(fn: () => Promise<T>, options?: RunOptions<T, E>): Promise<RunResult<T, E>>;
|
|
310
319
|
all<T>(fns: Array<() => MaybePromise<T>>, options?: RunAllOptions<T, E>): Promise<RunAllItemResult<T, E>[]>;
|
|
311
|
-
allOrThrow<T>(fns: Array<() => MaybePromise<T>>, options?:
|
|
320
|
+
allOrThrow<T>(fns: Array<() => MaybePromise<T>>, options?: RunAllOrThrowOptions<T, E>): Promise<T[]>;
|
|
312
321
|
}
|
|
313
322
|
|
|
314
|
-
declare function trybox(options?: CreateRunnerOptions): Runner<
|
|
323
|
+
declare function trybox(options?: Omit<CreateRunnerOptions<ResultError>, "rules">): Runner<ResultError>;
|
|
324
|
+
declare function trybox<const TRules extends readonly Rule<any>[]>(options: {
|
|
325
|
+
rules: TRules;
|
|
326
|
+
} & Omit<CreateRunnerOptions<InferErrorFromRules<TRules>>, "rules">): Runner<InferErrorFromRules<TRules>>;
|
|
315
327
|
|
|
316
|
-
export { type
|
|
328
|
+
export { type BackoffStrategy, type CircuitBreakerOptions, type ErrorResult, type Metrics, type ResultError, type ResultErrorCode, type RetryContext, type RetryOptions, type RunAllItemResult, type RunAllOptions, type RunOptions, type RunResult, type SuccessResult, createNormalizer, trybox as default, defaultFallback, errorRule, isSuccess, rules, run, runAll, runAllOrThrow, toResultError };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function
|
|
1
|
+
function K(r,e){return t=>{for(let n of r){let s=n(t);if(s)return s}return e(t)}}function v(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 N(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}:v(r)}function D(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")}var $=r=>new Promise(e=>setTimeout(e,r));function G(r,e,t){return Math.max(e,Math.min(t,r))}function Q(r,e,t,n,s,l){let u=typeof r=="function"?r(e,t):typeof r=="number"?r:n,E=ur(s??"linear",u,e),i=l!=null?G(E,0,l):E;return Number.isFinite(i)?i:0}function V(r,e){if(r<=0||!e)return r;let t=typeof e=="object"&&e.rng?e.rng:Math.random,n=typeof e=="number"?e:e===true?.5:typeof e=="object"&&e.ratio!=null?e.ratio:.5,s=G(n,0,1);return (typeof e=="object"&&e.mode?e.mode:"full")==="equal"?r*(1-s)+t()*r*s:r+t()*r*s}function ur(r,e,t){if(typeof r=="function"){let l=r(t);return l>=0?l:0}let n=e>=0?e:0,s=t>=1?t:1;if(r==="linear")return n;if(r==="exponential")return n*Math.pow(2,s-1);if(r==="fibonacci"){if(s<=2)return n;let l=1,u=1;for(let E=3;E<=s;E++){let i=l+u;l=u,u=i;}return n*u}return n}async function O(r,e={}){let{toError:t=N,mapError:n,onError:s,onSuccess:l,onFinally:u,ignoreAbort:E=true,retries:i=0,retryDelay:x,shouldRetry:R=()=>true,jitter:y={ratio:.5,mode:"full"},backoffStrategy:o,maxDelay:m,timeout:f,signal:a,onRetry:g,logger:d,onAbort:w}=e,M=i>0?300:0;if(D(e),a?.aborted)try{w?.(a);}finally{let T=t(new DOMException("Aborted","AbortError")),A=n?n(T):T;return E||s?.(A),u?.(),{ok:false,data:null,error:A,metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,lastError:A}}}let P=Date.now(),b=0,H,U=0;for(;;){let T=new AbortController;a&&(a.aborted?T.abort(a.reason):a.addEventListener("abort",()=>T.abort(a.reason),{once:true}));let A=null;f&&f>0&&(A=setTimeout(()=>{T.abort(new DOMException("Timeout","TimeoutError"));},f));try{let I=new Promise((k,F)=>{T.signal.aborted?F(T.signal.reason):T.signal.addEventListener("abort",()=>F(T.signal.reason),{once:!0});}),p=await Promise.race([r({signal:T.signal}),I]);A&&clearTimeout(A);try{l?.(p);}catch(k){d?.error?.("run:onSuccess failed",t(k));}try{u?.();}catch(k){d?.error?.("run:onFinally failed",t(k));}let L=Date.now()-P,h={totalAttempts:b+1,totalRetries:b,totalDuration:L,lastError:H};return d?.debug?.("run:success",{attempts:h.totalAttempts,duration:h.totalDuration}),{ok:!0,data:p,error:null,metrics:h}}catch(I){let p=t(I);if(n&&(p=n(p)),p?.code==="ABORTED"||I instanceof DOMException&&I.name==="AbortError"||a?.aborted)try{if(a)try{w?.(a);}catch(c){d?.error?.("run:onAbort failed",t(c));}}finally{if(!E)try{s?.(p);}catch(q){d?.error?.("run:onError failed",t(q));}try{u?.();}catch(q){d?.error?.("run:onFinally failed",t(q));}let c=Date.now()-P,_={totalAttempts:b+1,totalRetries:b,totalDuration:c,lastError:p};return d?.debug?.("run:aborted",{attempt:b,duration:_.totalDuration}),{ok:false,data:null,error:p,metrics:_}}H=p;let h=b+1,k={totalAttempts:h,elapsedTime:Date.now()-P,startTime:P,lastDelay:U>0?U:void 0},F=await Promise.resolve(R(h,p,k));if(b<i&&F){b=h;let c=Q(x,b,p,M,o,m);(!Number.isFinite(c)||c<0)&&(c=0),c=V(c,y),U=c,g?.(b,p,c),d?.debug?.("run:retry",{attempt:b,delay:c}),c>0&&await $(c);continue}try{s?.(p);}catch(c){d?.error?.("run:onError failed",t(c));}try{u?.();}catch(c){d?.error?.("run:onFinally failed",t(c));}let nr=Date.now()-P,sr={totalAttempts:b+1,totalRetries:b,totalDuration:nr,lastError:p};return d?.error?.("run:error",p),{ok:false,data:null,error:p,metrics:sr}}}}var lr=r=>r.status==="ok";async function W(r,e={}){let{concurrency:t=1/0,mode:n="settle",...s}=e;if(D(s),r.length===0)return [];let l=Number.isFinite(t)?Math.max(1,Math.floor(t)):1/0,u=new Array(r.length),E=0,i=false,x=(o,m)=>{u[o]=m.ok?{status:"ok",ok:true,data:m.data,error:null}:{status:"error",ok:false,data:null,error:m.error};},R=()=>{for(let o=0;o<r.length;o++)u[o]||(u[o]={status:"skipped",ok:false,data:null,error:null});};if(l>=r.length)return (await Promise.all(r.map(m=>O(m,s)))).forEach((m,f)=>{x(f,m);}),u;let y=async()=>{for(;;){if(i)return;let o=E++;if(o>=r.length)return;let m=r[o];if(!m)continue;let f=await O(m,s);if(x(o,f),!f.ok&&n==="fail-fast"){i=true;return}}};return await Promise.all(Array.from({length:Math.min(l,r.length)},()=>y())),R(),u}async function J(r,e={}){let{concurrency:t=1/0,...n}=e;if(D(n),r.length===0)return [];let s=Number.isFinite(t)?Math.max(1,Math.floor(t)):1/0,l=new Array(r.length);if(s>=r.length)return await Promise.all(r.map(async(R,y)=>{let o=await O(R,n);if(!o.ok)throw o.error;l[y]=o.data;})),l;let u=0,E=false,i=null,x=async()=>{for(;;){if(E)return;let R=u++;if(R>=r.length)return;let y=r[R];if(!y)continue;let o=await O(y,n);if(!o.ok){i??(i=o.error),E=true;return}l[R]=o.data;}};if(await Promise.all(Array.from({length:Math.min(s,r.length)},()=>x())),i)throw i;return l}var C=class extends Error{constructor(){super("Circuit open"),this.name="CircuitOpenError";}},X=r=>r instanceof C||r instanceof Error&&r.name==="CircuitOpenError"?{code:"CIRCUIT_OPEN",message:"Circuit open",cause:r}:null,Y=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,Z=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,j=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},rr=r=>typeof AggregateError<"u"&&r instanceof AggregateError?{code:"UNKNOWN",message:r.message||"Multiple errors occurred",cause:r,meta:{errors:r.errors}}:null,er=r=>typeof r=="string"?{code:"UNKNOWN",message:r,cause:r}:null,tr=r=>typeof r=="object"&&r!==null&&"message"in r&&typeof r.message=="string"?{code:"UNKNOWN",message:r.message,cause:r}:null,ar={circuitOpen:X,abort:Y,timeout:Z,httpStatus:j,aggregate:rr,string:er,message:tr};function z(){return [X,Y,Z,j,rr,er,tr]}var S=class{constructor(e){this.matcher=e;}toError(e){return t=>this.matcher(t)?e(t):null}},ir={instance(r){return new S(e=>e instanceof r)},when(r){return new S(r)}};var B=(r,e)=>t=>e?e(r?r(t):t):r?r(t):t;function or(r={}){let{rules:e=[],rulesMode:t="extend",fallback:n=f=>v(f),toError:s,ignoreAbort:l=true,mapError:u,circuitBreaker:E}=r,i=[],x=z()||[];e.length>0?t==="extend"?i=[...e,...x]:i=[...e]:t==="extend"?i=x:i=[];let R=s??(i.length>0?K(i,n):N),y=0,o=null,m=null;return {run(f,a={}){let g=E,d=Date.now();if(g&&o&&d<o){let w=R(new C),M=B(u,a.mapError)(w);return a.onError?.(M),a.onFinally?.(),Promise.resolve({ok:false,data:null,error:M,metrics:{totalAttempts:0,totalRetries:0,totalDuration:0,lastError:M}})}return O(f,{toError:R,ignoreAbort:l,...a,mapError:B(u,a.mapError)}).then(w=>(g&&(w.ok?o&&Date.now()>=o?(m&&m>0&&m--,(!m||m<=0)&&(o=null,y=0,m=null)):y=0:o&&Date.now()>=o?(o=Date.now()+g.resetTimeout,m=g.halfOpenRequests??1,y=0):o||(y++,y>=g.failureThreshold&&(o=Date.now()+g.resetTimeout,m=g.halfOpenRequests??1))),w))},all(f,a={}){return W(f,{toError:R,ignoreAbort:l,...a,mapError:B(u,a.mapError)})},allOrThrow(f,a={}){return J(f,{toError:R,ignoreAbort:l,...a,mapError:B(u,a.mapError)})}}}function mr(r={}){return or(r)}export{K as createNormalizer,mr as default,v as defaultFallback,ir as errorRule,lr as isSuccess,ar as rules,O as run,W as runAll,J as runAllOrThrow,N as toResultError};//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|