tryharder 0.1.1 → 0.2.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 +63 -55
- package/dist/errors.d.ts +12 -3
- package/dist/errors.js +14 -4
- package/dist/index.d.ts +90 -15
- package/dist/index.js +160 -232
- package/dist/shared/chunk-1bnw2q61.js +123 -0
- package/dist/types.d.ts +12 -3
- package/package.json +9 -11
- package/dist/shared/chunk-vavcyath.js +0 -104
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ const result = await try$
|
|
|
38
38
|
catch: () => new RequestFailedError("request failed"),
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
-
// result is OrderStatus | RequestFailedError |
|
|
41
|
+
// result is OrderStatus | RequestFailedError | TimeoutError
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
<details>
|
|
@@ -47,7 +47,6 @@ const result = await try$
|
|
|
47
47
|
- [Why not plain try/catch?](#why-not-plain-trycatch)
|
|
48
48
|
- [Features](#features)
|
|
49
49
|
- [Installation](#installation)
|
|
50
|
-
- [Migration from hardtry](#migration-from-hardtry)
|
|
51
50
|
- [Execution Model](#execution-model)
|
|
52
51
|
- [Type Semantics](#type-semantics)
|
|
53
52
|
- [Quick Start](#quick-start)
|
|
@@ -59,7 +58,7 @@ const result = await try$
|
|
|
59
58
|
- [all and allSettled](#all-and-allsettled)
|
|
60
59
|
- [flow and $exit](#flow-and-exit)
|
|
61
60
|
- [gen](#gen)
|
|
62
|
-
- [
|
|
61
|
+
- [disposer](#disposer)
|
|
63
62
|
- [API Reference](#api-reference)
|
|
64
63
|
- [Common Recipes](#common-recipes)
|
|
65
64
|
- [When not to use tryharder](#when-not-to-use-tryharder)
|
|
@@ -133,7 +132,7 @@ const result = await try$
|
|
|
133
132
|
})
|
|
134
133
|
|
|
135
134
|
// result is
|
|
136
|
-
// User | UserUnavailableError |
|
|
135
|
+
// User | UserUnavailableError | TimeoutError | CancellationError
|
|
137
136
|
```
|
|
138
137
|
|
|
139
138
|
That is the core shift:
|
|
@@ -142,7 +141,8 @@ That is the core shift:
|
|
|
142
141
|
- `tryharder` exposes execution policy in the builder chain and failure shape in the return type.
|
|
143
142
|
- `run(fn)` returns `T | UnhandledException`.
|
|
144
143
|
- `run({ try, catch })` returns `T | C`.
|
|
145
|
-
- Adding `
|
|
144
|
+
- Adding `timeout(...)` and `signal(...)` widens the union with `TimeoutError` and `CancellationError`.
|
|
145
|
+
- Adding `retry(...)` changes how persistent failure is reported: with `catch`, the last attempt's error is mapped by `catch`; without `catch`, it surfaces as `RetryExhaustedError` (last error as `cause`) instead of `UnhandledException`.
|
|
146
146
|
|
|
147
147
|
## Features
|
|
148
148
|
|
|
@@ -151,7 +151,7 @@ That is the core shift:
|
|
|
151
151
|
- **Sync and async parity** - Use the same mental model for `runSync(...)` and `run(...)`.
|
|
152
152
|
- **Named task orchestration** - Express concurrent and ordered workflows with object-shaped task graphs instead of positional arrays.
|
|
153
153
|
- **Observable execution hooks** - Add top-level instrumentation with `wrap(...)` without changing task behavior.
|
|
154
|
-
- **Resource cleanup** - Register teardown that survives async boundaries with `
|
|
154
|
+
- **Resource cleanup** - Register teardown that survives async boundaries with `disposer()` and task disposers.
|
|
155
155
|
- **No runtime dependencies** - The published package ships without runtime dependencies.
|
|
156
156
|
|
|
157
157
|
## Installation
|
|
@@ -170,39 +170,23 @@ yarn add tryharder
|
|
|
170
170
|
pnpm add tryharder
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
-
## Migration from hardtry
|
|
174
|
-
|
|
175
|
-
Replace import specifiers only:
|
|
176
|
-
|
|
177
|
-
- `hardtry` -> `tryharder`
|
|
178
|
-
- `hardtry/errors` -> `tryharder/errors`
|
|
179
|
-
- `hardtry/types` -> `tryharder/types`
|
|
180
|
-
|
|
181
|
-
You can keep the same namespace alias in your code:
|
|
182
|
-
|
|
183
|
-
```ts
|
|
184
|
-
import * as try$ from "tryharder"
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
No runtime API names changed.
|
|
188
|
-
|
|
189
173
|
## Execution Model
|
|
190
174
|
|
|
191
175
|
`tryharder` has three layers: terminal execution APIs, policy builders, and orchestration APIs.
|
|
192
176
|
|
|
193
|
-
Terminal execution APIs are `run(...)` and `runSync(...)`. They are the points where work is actually executed and a result union is produced. Function form is the minimal shape and returns `T | UnhandledException
|
|
177
|
+
Terminal execution APIs are `run(...)` and `runSync(...)`. They are the points where work is actually executed and a result union is produced. Function form is the minimal shape and returns `T | UnhandledException` by default, or `T | RetryExhaustedError` when retry is configured. Object form adds a `catch` mapper and returns `T | C`.
|
|
194
178
|
|
|
195
179
|
Policy builders decorate terminal execution. `retry(...)`, `timeout(...)`, and `signal(...)` do not run work by themselves; they configure the next terminal call and widen the resulting union with the policy-level failures they can introduce. `retry(limit)` counts the first attempt. `timeout(ms)` applies one total deadline across attempts, delays, and catch handling. `signal(abortSignal)` forwards external cancellation into execution.
|
|
196
180
|
|
|
197
181
|
Orchestration APIs scale the same model from one operation to a task graph. `all(...)` runs a fail-fast named task map. `allSettled(...)` preserves every settled task outcome. `flow(...)` runs an ordered workflow that must explicitly terminate through `this.$exit(...)`.
|
|
198
182
|
|
|
199
|
-
`wrap(...)` sits above those execution APIs as observational middleware. It can inspect readonly execution context and surround terminal calls, but it is not available after `retry(...)`, `timeout(...)`, or execution-scoped `signal(...)` chains. `gen(...)` offers a more linear way to compose returned unions. `
|
|
183
|
+
`wrap(...)` sits above those execution APIs as observational middleware. It can inspect readonly execution context and surround terminal calls, but it is not available after `retry(...)`, `timeout(...)`, or execution-scoped `signal(...)` chains. `gen(...)` offers a more linear way to compose returned unions. `disposer()` provides cleanup registration for work that spans async boundaries.
|
|
200
184
|
|
|
201
185
|
| Term | Meaning |
|
|
202
186
|
| --------------------- | ------------------------------------------------------------------------------ |
|
|
203
187
|
| `run` | Async terminal execution that returns a value, mapped failure, or policy error |
|
|
204
188
|
| `runSync` | Sync terminal execution for synchronous work only |
|
|
205
|
-
| `retry(limit)` | Retry policy
|
|
189
|
+
| `retry(limit)` | Retry policy; `limit` is a positive integer counting the first attempt |
|
|
206
190
|
| `timeout(ms)` | Total execution timeout across attempts, delays, and catch handling |
|
|
207
191
|
| `signal(abortSignal)` | External cancellation for `run(...)` and root-level orchestration |
|
|
208
192
|
| `wrap(fn)` | Top-level observational middleware around terminal APIs |
|
|
@@ -237,10 +221,20 @@ const result = await try$
|
|
|
237
221
|
})
|
|
238
222
|
|
|
239
223
|
// result is
|
|
240
|
-
// ValidationError |
|
|
224
|
+
// ValidationError | TimeoutError
|
|
241
225
|
```
|
|
242
226
|
|
|
243
|
-
That inferred union is the contract. A caller can see whether a function returns a domain error
|
|
227
|
+
That inferred union is the contract. A caller can see whether a function returns a domain error and whether a deadline may fire, without reading the implementation body.
|
|
228
|
+
|
|
229
|
+
The `catch` contract is strict: `catch` maps errors that originated inside `try` — thrown directly, or carried out of the retry loop as the last attempt's error once the retry policy gives up. Policy outcomes (`TimeoutError`, `CancellationError`) never pass through `catch`; they surface typed in the union so you can handle them at the call site:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
if (result instanceof TimeoutError) {
|
|
233
|
+
// deadline expired; map or handle it here
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Without `catch`, unmapped failures are wrapped: `RetryExhaustedError` when a retry policy gave up (for any reason — limit exhausted or `shouldRetry` declining), `UnhandledException` otherwise. The original error is always available as `cause`.
|
|
244
238
|
|
|
245
239
|
`Panic` is intentionally separate from that model. It signals programmer errors such as invalid builder usage or invalid task graphs, not expected business-domain failures.
|
|
246
240
|
|
|
@@ -407,6 +401,8 @@ const result = await try$
|
|
|
407
401
|
|
|
408
402
|
`timeout(ms)` measures total execution time, not just a single attempt.
|
|
409
403
|
|
|
404
|
+
`runSync(...)` stays available after the numeric retry shorthand (`retry(3)`), but not after the object form (`retry({ ... })`) or `timeout(...)` — those policies may need to wait or interrupt, which synchronous execution cannot do. This restriction is intentionally conservative: it is enforced at the type level even for object policies that would be sync-safe at runtime (constant backoff, no delay, no jitter). If you need retries with `runSync(...)`, use the numeric shorthand.
|
|
405
|
+
|
|
410
406
|
Apply `signal(...)` on the root builder when you want cancellation to cover `all(...)`, `allSettled(...)`, or `flow(...)`.
|
|
411
407
|
|
|
412
408
|
### wrap
|
|
@@ -495,21 +491,21 @@ const value = await try$.gen(function* (use) {
|
|
|
495
491
|
})
|
|
496
492
|
```
|
|
497
493
|
|
|
498
|
-
###
|
|
494
|
+
### disposer
|
|
499
495
|
|
|
500
|
-
Use `
|
|
496
|
+
Use `disposer()` when cleanup should stay colocated with the workflow that allocates the resource, even across async boundaries. The returned `AsyncDisposer` gives you three operations:
|
|
501
497
|
|
|
502
|
-
- `
|
|
498
|
+
- `defer(fn)` registers a cleanup callback.
|
|
503
499
|
- `use(resource)` tracks a disposable resource.
|
|
504
|
-
- `
|
|
500
|
+
- `dispose()` runs the registered teardown in reverse order (also triggered by leaving an `await using` scope).
|
|
505
501
|
|
|
506
502
|
```ts
|
|
507
|
-
await using disposer = try$.
|
|
503
|
+
await using disposer = try$.disposer()
|
|
508
504
|
|
|
509
505
|
{
|
|
510
506
|
const connection = await db.connect()
|
|
511
507
|
|
|
512
|
-
disposer.
|
|
508
|
+
disposer.defer(async () => {
|
|
513
509
|
await connection.close()
|
|
514
510
|
})
|
|
515
511
|
|
|
@@ -523,32 +519,44 @@ await using disposer = try$.dispose()
|
|
|
523
519
|
|
|
524
520
|
### Runtime
|
|
525
521
|
|
|
526
|
-
| Export | Description
|
|
527
|
-
| -------------- |
|
|
528
|
-
| `run` | Async terminal execution API
|
|
529
|
-
| `runSync` | Sync terminal execution API
|
|
530
|
-
| `retry` | Create an execution-scoped retry builder
|
|
531
|
-
| `retryOptions` | Normalize retry policy input
|
|
532
|
-
| `timeout` | Add a total execution timeout
|
|
533
|
-
| `signal` | Add external cancellation to execution or root-level orchestration
|
|
534
|
-
| `wrap` | Add top-level observational middleware
|
|
535
|
-
| `all` | Run a fail-fast parallel named task graph
|
|
536
|
-
| `allSettled` | Run a settled parallel named task graph
|
|
537
|
-
| `flow` | Run an ordered workflow with explicit early exit
|
|
538
|
-
| `gen` | Compose `run(...)` results through generators
|
|
539
|
-
| `
|
|
522
|
+
| Export | Description |
|
|
523
|
+
| -------------- | ------------------------------------------------------------------------- |
|
|
524
|
+
| `run` | Async terminal execution API |
|
|
525
|
+
| `runSync` | Sync terminal execution API |
|
|
526
|
+
| `retry` | Create an execution-scoped retry builder |
|
|
527
|
+
| `retryOptions` | Normalize retry policy input |
|
|
528
|
+
| `timeout` | Add a total execution timeout |
|
|
529
|
+
| `signal` | Add external cancellation to execution or root-level orchestration |
|
|
530
|
+
| `wrap` | Add top-level observational middleware |
|
|
531
|
+
| `all` | Run a fail-fast parallel named task graph |
|
|
532
|
+
| `allSettled` | Run a settled parallel named task graph |
|
|
533
|
+
| `flow` | Run an ordered workflow with explicit early exit |
|
|
534
|
+
| `gen` | Compose `run(...)` results through generators |
|
|
535
|
+
| `disposer` | Create an `AsyncDisposer` helper with `defer()`, `use()`, and `dispose()` |
|
|
540
536
|
|
|
541
537
|
### Errors
|
|
542
538
|
|
|
543
539
|
Exports from `tryharder/errors`:
|
|
544
540
|
|
|
545
|
-
| Export | Description
|
|
546
|
-
| --------------------- |
|
|
547
|
-
| `CancellationError` | Returned or thrown when execution is externally cancelled
|
|
548
|
-
| `TimeoutError` | Returned when timed execution expires
|
|
549
|
-
| `RetryExhaustedError` | Returned when retry
|
|
550
|
-
| `UnhandledException` | Returned when function-form execution throws
|
|
551
|
-
| `Panic` | Thrown for programmer errors and invalid API usage
|
|
541
|
+
| Export | Description |
|
|
542
|
+
| --------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
543
|
+
| `CancellationError` | Returned or thrown when execution is externally cancelled |
|
|
544
|
+
| `TimeoutError` | Returned when timed execution expires |
|
|
545
|
+
| `RetryExhaustedError` | Returned when a retry policy gives up and no `catch` is provided; the last attempt's error is the `cause` |
|
|
546
|
+
| `UnhandledException` | Returned when function-form execution throws |
|
|
547
|
+
| `Panic` | Thrown for programmer errors and invalid API usage |
|
|
548
|
+
|
|
549
|
+
Each error class has a matching type guard: `isCancellationError`, `isTimeoutError`, `isRetryExhaustedError`, `isUnhandledException`, and `isPanic`. Prefer the guards over `instanceof` — they also match by `error.name`, so they keep working when two copies of `tryharder` end up in one dependency graph or when errors cross realm boundaries, where `instanceof` silently fails.
|
|
550
|
+
|
|
551
|
+
```ts
|
|
552
|
+
import { isTimeoutError } from "tryharder/errors"
|
|
553
|
+
|
|
554
|
+
const result = await try$.timeout(1_000).run(fetchUser)
|
|
555
|
+
|
|
556
|
+
if (isTimeoutError(result)) {
|
|
557
|
+
// handle the deadline here
|
|
558
|
+
}
|
|
559
|
+
```
|
|
552
560
|
|
|
553
561
|
### Types
|
|
554
562
|
|
|
@@ -557,7 +565,7 @@ Exports from `tryharder/types`:
|
|
|
557
565
|
| Export | Description |
|
|
558
566
|
| ------------------ | ---------------------------------------------------- |
|
|
559
567
|
| `AllSettledResult` | Settled result map returned by `allSettled(...)` |
|
|
560
|
-
| `AsyncDisposer` | Async cleanup helper returned by `
|
|
568
|
+
| `AsyncDisposer` | Async cleanup helper returned by `disposer()` |
|
|
561
569
|
| `SettledFulfilled` | Fulfilled branch of a settled task result |
|
|
562
570
|
| `SettledRejected` | Rejected branch of a settled task result |
|
|
563
571
|
| `SettledResult` | Union of fulfilled and rejected settled task results |
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
type PanicCode = "ALL_CATCH_HANDLER_REJECT" | "ALL_CATCH_HANDLER_THROW" | "FLOW_NO_EXIT" | "ORCHESTRATION_UNSUPPORTED_POLICY" | "RETRY_INVALID_LIMIT" | "RUN_CATCH_HANDLER_REJECT" | "RUN_CATCH_HANDLER_THROW" | "RUN_SYNC_ASYNC_RETRY_POLICY" | "RUN_SYNC_CATCH_HANDLER_THROW" | "RUN_SYNC_CATCH_PROMISE" | "RUN_SYNC_TRY_PROMISE" | "RUN_SYNC_WRAPPED_RESULT_PROMISE" | "TASK_INVALID_HANDLER" | "TASK_SELF_REFERENCE" | "TIMEOUT_INVALID_MS" | "TASK_UNKNOWN_REFERENCE" | "UNREACHABLE_RETRY_POLICY_BACKOFF";
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
declare class ControlError extends Error {
|
|
6
|
+
constructor(message?: string, options?: ErrorOptions);
|
|
7
|
+
}
|
|
4
8
|
declare class CancellationError extends ControlError {
|
|
5
9
|
constructor(message?: string, options?: ErrorOptions);
|
|
6
10
|
}
|
|
@@ -19,4 +23,9 @@ declare class Panic extends Error {
|
|
|
19
23
|
message?: string;
|
|
20
24
|
});
|
|
21
25
|
}
|
|
22
|
-
|
|
26
|
+
declare function isCancellationError(error: unknown): error is CancellationError;
|
|
27
|
+
declare function isTimeoutError(error: unknown): error is TimeoutError;
|
|
28
|
+
declare function isRetryExhaustedError(error: unknown): error is RetryExhaustedError;
|
|
29
|
+
declare function isUnhandledException(error: unknown): error is UnhandledException;
|
|
30
|
+
declare function isPanic(error: unknown): error is Panic;
|
|
31
|
+
export { isUnhandledException, isTimeoutError, isRetryExhaustedError, isPanic, isCancellationError, UnhandledException, TimeoutError, RetryExhaustedError, PanicCode, Panic, CancellationError };
|
package/dist/errors.js
CHANGED
|
@@ -3,9 +3,19 @@ import {
|
|
|
3
3
|
Panic,
|
|
4
4
|
RetryExhaustedError,
|
|
5
5
|
TimeoutError,
|
|
6
|
-
UnhandledException
|
|
7
|
-
|
|
6
|
+
UnhandledException,
|
|
7
|
+
isCancellationError,
|
|
8
|
+
isPanic,
|
|
9
|
+
isRetryExhaustedError,
|
|
10
|
+
isTimeoutError,
|
|
11
|
+
isUnhandledException
|
|
12
|
+
} from "./shared/chunk-1bnw2q61.js";
|
|
8
13
|
export {
|
|
14
|
+
isUnhandledException,
|
|
15
|
+
isTimeoutError,
|
|
16
|
+
isRetryExhaustedError,
|
|
17
|
+
isPanic,
|
|
18
|
+
isCancellationError,
|
|
9
19
|
UnhandledException,
|
|
10
20
|
TimeoutError,
|
|
11
21
|
RetryExhaustedError,
|
|
@@ -13,5 +23,5 @@ export {
|
|
|
13
23
|
CancellationError
|
|
14
24
|
};
|
|
15
25
|
|
|
16
|
-
//# debugId=
|
|
17
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
26
|
+
//# debugId=796F554FE5FDACF264756E2164756E21
|
|
27
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFtdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICBdLAogICJtYXBwaW5ncyI6ICIiLAogICJkZWJ1Z0lkIjogIjc5NkY1NTRGRTVGREFDRjI2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @internal
|
|
3
|
+
*/
|
|
4
|
+
declare class ControlError extends Error {
|
|
5
|
+
constructor(message?: string, options?: ErrorOptions);
|
|
6
|
+
}
|
|
3
7
|
declare class CancellationError extends ControlError {
|
|
4
8
|
constructor(message?: string, options?: ErrorOptions);
|
|
5
9
|
}
|
|
@@ -13,11 +17,20 @@ declare class UnhandledException extends Error {
|
|
|
13
17
|
constructor(message?: string, options?: ErrorOptions);
|
|
14
18
|
}
|
|
15
19
|
interface AsyncDisposer extends AsyncDisposable {
|
|
16
|
-
|
|
17
|
-
cleanup()
|
|
20
|
+
/**
|
|
21
|
+
* Registers a cleanup callback. Callbacks run in reverse registration order (LIFO) when the
|
|
22
|
+
* disposer is disposed.
|
|
23
|
+
*/
|
|
18
24
|
defer(fn: () => void | PromiseLike<void>): void;
|
|
25
|
+
/**
|
|
26
|
+
* Tracks a disposable resource and returns it.
|
|
27
|
+
*/
|
|
19
28
|
use<T extends AsyncDisposable | Disposable | null | undefined>(value: T): T;
|
|
20
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Runs all registered teardown in reverse registration order. Equivalent to leaving an `await
|
|
31
|
+
* using` scope.
|
|
32
|
+
*/
|
|
33
|
+
dispose(): Promise<void>;
|
|
21
34
|
}
|
|
22
35
|
type TaskRecord = Record<string, any>;
|
|
23
36
|
type TaskValidation<T extends TaskRecord> = { [K in keyof T] : T[K] extends (...args: any[]) => any ? T[K] : never };
|
|
@@ -127,6 +140,27 @@ type RetryPolicy = LinearBackoffRetryPolicy | ExponentialBackoffRetryPolicy | Co
|
|
|
127
140
|
* - `RetryPolicy`: detailed retry settings
|
|
128
141
|
*/
|
|
129
142
|
type RetryOptions = number | RetryPolicy;
|
|
143
|
+
declare const INVALID_RETRY_LIMIT: unique symbol;
|
|
144
|
+
type InvalidRetryLimit = {
|
|
145
|
+
readonly [INVALID_RETRY_LIMIT]: "retry() requires a positive integer limit";
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Compile-time validation for literal retry limits. Rejects `0`, negative, and fractional literals;
|
|
149
|
+
* non-literal `number` values pass and are validated at runtime by {@link retryOptions} instead.
|
|
150
|
+
* (`Infinity`/`NaN` have no literal types, so they are runtime-only.)
|
|
151
|
+
*/
|
|
152
|
+
type ValidateRetryLimit<N extends number> = number extends N ? number : `${N}` extends `-${string}` | `${string}.${string}` ? InvalidRetryLimit : N extends 0 ? InvalidRetryLimit : number;
|
|
153
|
+
/**
|
|
154
|
+
* Compile-time validation for full {@link RetryOptions} values, including unions of number and
|
|
155
|
+
* policy forms. Distributes over union members so that mixed inputs like `flag ? 3 : { backoff:
|
|
156
|
+
* "constant", limit: 5 }` are accepted while any member carrying an invalid literal limit still
|
|
157
|
+
* fails to compile.
|
|
158
|
+
*/
|
|
159
|
+
type ValidateRetryOptions<P extends RetryOptions> = P extends number ? ValidateRetryLimit<P> : P extends {
|
|
160
|
+
limit: infer N extends number;
|
|
161
|
+
} ? {
|
|
162
|
+
limit: ValidateRetryLimit<N>;
|
|
163
|
+
} : never;
|
|
130
164
|
declare function retryOptions(policy: RetryOptions): RetryPolicy;
|
|
131
165
|
declare const FLOW_EXIT_BRAND: unique symbol;
|
|
132
166
|
type FlowExit<T> = {
|
|
@@ -149,6 +183,12 @@ interface RunAsyncOptions<
|
|
|
149
183
|
Ctx extends BaseTryCtx = BaseTryCtx
|
|
150
184
|
> {
|
|
151
185
|
try: RunTryFn<T, Ctx>;
|
|
186
|
+
/**
|
|
187
|
+
* Maps errors that originated inside `try` — thrown directly, or carried out of the retry loop as
|
|
188
|
+
* the last attempt's error once the retry policy gives up. Policy outcomes (timeout,
|
|
189
|
+
* cancellation) and defects (`Panic`) never pass through `catch`: they are reported directly,
|
|
190
|
+
* even when they fire while the handler is running.
|
|
191
|
+
*/
|
|
152
192
|
catch: RunCatchFn<E>;
|
|
153
193
|
}
|
|
154
194
|
type AsyncRunInput<
|
|
@@ -167,6 +207,12 @@ interface SyncRunOptions<
|
|
|
167
207
|
Ctx extends BaseTryCtx = BaseTryCtx
|
|
168
208
|
> {
|
|
169
209
|
try: SyncRunTryFn<T, Ctx>;
|
|
210
|
+
/**
|
|
211
|
+
* Maps errors that originated inside `try` — thrown directly, or carried out of the retry loop as
|
|
212
|
+
* the last attempt's error once the retry policy gives up. Policy outcomes (timeout,
|
|
213
|
+
* cancellation) and defects (`Panic`) never pass through `catch`: they are reported directly,
|
|
214
|
+
* even when they fire while the handler is running.
|
|
215
|
+
*/
|
|
170
216
|
catch: SyncRunCatchFn<E>;
|
|
171
217
|
}
|
|
172
218
|
type SyncRunInput<
|
|
@@ -175,8 +221,8 @@ type SyncRunInput<
|
|
|
175
221
|
Ctx extends BaseTryCtx = BaseTryCtx
|
|
176
222
|
> = SyncRunTryFn<T, Ctx> | SyncRunOptions<T, E, Ctx>;
|
|
177
223
|
/**
|
|
178
|
-
* Wraps are observational hooks: they can inspect execution context and
|
|
179
|
-
*
|
|
224
|
+
* Wraps are observational hooks: they can inspect execution context and surround execution, but
|
|
225
|
+
* they must not mutate context or replace it.
|
|
180
226
|
*/
|
|
181
227
|
type WrapCtx = Readonly<Omit<TryCtx, "retry">> & {
|
|
182
228
|
readonly retry: Readonly<TryCtx["retry"]>;
|
|
@@ -200,7 +246,13 @@ interface BuilderConfig {
|
|
|
200
246
|
*/
|
|
201
247
|
wraps?: WrapFn[];
|
|
202
248
|
}
|
|
203
|
-
type ConfigRunErrors =
|
|
249
|
+
type ConfigRunErrors = TimeoutError | CancellationError;
|
|
250
|
+
/**
|
|
251
|
+
* The failure type for try functions without a catch handler. With retry configured, any give-up
|
|
252
|
+
* (limit exhausted or `shouldRetry` declining) is reported as {@link RetryExhaustedError} carrying
|
|
253
|
+
* the last attempt's error as `cause`; otherwise failures are wrapped in {@link UnhandledException}.
|
|
254
|
+
*/
|
|
255
|
+
type UnmappedError<HasRetry extends boolean> = HasRetry extends true ? RetryExhaustedError : UnhandledException;
|
|
204
256
|
type OrchestrationMethods = "all" | "allSettled" | "flow";
|
|
205
257
|
type SignalBuilderHiddenMethods<SupportsOrchestration extends boolean> = "runSync" | "wrap" | (SupportsOrchestration extends true ? never : OrchestrationMethods);
|
|
206
258
|
type ExecutionBuilderSurface<
|
|
@@ -223,20 +275,39 @@ declare class RunBuilder<
|
|
|
223
275
|
> {
|
|
224
276
|
protected readonly config: BuilderConfig;
|
|
225
277
|
constructor(config?: BuilderConfig);
|
|
226
|
-
protected buildRetryConfig(policy: RetryOptions): BuilderConfig;
|
|
227
278
|
protected buildTimeoutConfig(ms: number): BuilderConfig;
|
|
228
279
|
protected buildSignalConfig(signal: AbortSignal): BuilderConfig;
|
|
229
|
-
retry(policy:
|
|
230
|
-
retry(policy:
|
|
280
|
+
retry<N extends number>(policy: N & ValidateRetryLimit<N>): ExecutionBuilderSurface<E, true>;
|
|
281
|
+
retry<N extends number>(policy: RetryPolicy & {
|
|
282
|
+
limit: N & ValidateRetryLimit<N>;
|
|
283
|
+
}): AsyncExecutionBuilderSurface<E, true>;
|
|
284
|
+
retry<const P extends RetryOptions>(policy: P & ValidateRetryOptions<P>): AsyncExecutionBuilderSurface<E, true>;
|
|
231
285
|
timeout(ms: number): AsyncExecutionBuilderSurface<E | TimeoutError, HasRetry>;
|
|
232
286
|
signal(signal: AbortSignal): SignalBuilderSurface<E | CancellationError, HasRetry, SupportsOrchestration>;
|
|
233
287
|
wrap(fn: WrapFn): RunBuilder<E, HasRetry, SupportsOrchestration>;
|
|
234
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Executes an async unit of work.
|
|
290
|
+
*
|
|
291
|
+
* `catch` maps errors that originated inside `try` — thrown directly, or carried out of the retry
|
|
292
|
+
* loop as the last attempt's error once the retry policy gives up. Policy outcomes ({@link
|
|
293
|
+
* TimeoutError}, {@link CancellationError}) and defects (`Panic`) never pass through `catch`; they
|
|
294
|
+
* surface typed in the return union (or are thrown, for `Panic`).
|
|
295
|
+
*
|
|
296
|
+
* Without `catch`, unmapped failures are wrapped: {@link RetryExhaustedError} when a retry policy
|
|
297
|
+
* gave up, {@link UnhandledException} otherwise. The original error is available as `cause`.
|
|
298
|
+
*/
|
|
299
|
+
run<T>(tryFn: RunTryFn<T, TryCtxFor<HasRetry>>): Promise<T | UnmappedError<HasRetry> | E>;
|
|
235
300
|
run<
|
|
236
301
|
T,
|
|
237
302
|
C
|
|
238
303
|
>(options: AsyncRunInput<T, C, TryCtxFor<HasRetry>>): Promise<T | C | E>;
|
|
239
|
-
|
|
304
|
+
/**
|
|
305
|
+
* Executes a sync unit of work.
|
|
306
|
+
*
|
|
307
|
+
* Follows the same `catch` contract as {@link RunBuilder.run}: `catch` maps try-originated errors
|
|
308
|
+
* (including retry give-up); policy outcomes and defects never pass through it.
|
|
309
|
+
*/
|
|
310
|
+
runSync<T>(tryFn: SyncRunTryFn<T, TryCtxFor<HasRetry>>): T | UnmappedError<HasRetry> | E;
|
|
240
311
|
runSync<
|
|
241
312
|
T,
|
|
242
313
|
C
|
|
@@ -248,7 +319,11 @@ declare class RunBuilder<
|
|
|
248
319
|
allSettled<T extends TaskRecord>(tasks: T & TaskValidation<NoInfer<T>> & ThisType<InferredTaskContext<T>>): Promise<AllSettledResult<T>>;
|
|
249
320
|
flow<T extends TaskRecord>(tasks: T & ThisType<InferredFlowTaskContext<T>>): Promise<FlowResult<T>>;
|
|
250
321
|
}
|
|
251
|
-
|
|
322
|
+
/**
|
|
323
|
+
* Creates an {@link AsyncDisposer}: register cleanup with `defer(fn)` or `use(resource)`, then run
|
|
324
|
+
* teardown with `await d.dispose()` or by declaring the disposer with `await using`.
|
|
325
|
+
*/
|
|
326
|
+
declare function disposer(): AsyncDisposer;
|
|
252
327
|
type GenUnwrap<T> = Exclude<Awaited<T>, Error>;
|
|
253
328
|
type GenUse = <T>(value: T) => Generator<T, GenUnwrap<T>, GenUnwrap<T>>;
|
|
254
329
|
type GenErrors<TYield> = Extract<Awaited<TYield>, Error>;
|
|
@@ -273,4 +348,4 @@ declare const runSync2: RunBuilder["runSync"];
|
|
|
273
348
|
declare const signal: RunBuilder["signal"];
|
|
274
349
|
declare const timeout: RunBuilder["timeout"];
|
|
275
350
|
declare const wrap: RunBuilder["wrap"];
|
|
276
|
-
export { wrap, timeout, signal, runSync2 as runSync, run, retryOptions, retry, driveGen as gen, flow,
|
|
351
|
+
export { wrap, timeout, signal, runSync2 as runSync, run, retryOptions, retry, driveGen as gen, flow, disposer, allSettled2 as allSettled, all };
|