awaitly 1.35.0 → 2.0.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/dist/{duration.d.ts → di-BDlT7InM.d.cts} +15 -1
- package/dist/{duration.d.cts → di-BbFFfO8y.d.ts} +15 -1
- package/dist/errors-DtXvrCiO.d.cts +708 -0
- package/dist/errors-DtXvrCiO.d.ts +708 -0
- package/dist/index.cjs +4594 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1970 -141
- package/dist/index.d.ts +1970 -141
- package/dist/index.js +4398 -1
- package/dist/index.js.map +1 -1
- package/dist/result.cjs +641 -1
- package/dist/result.cjs.map +1 -1
- package/dist/result.d.cts +29 -4
- package/dist/result.d.ts +29 -4
- package/dist/result.js +561 -1
- package/dist/result.js.map +1 -1
- package/dist/testing.cjs +4202 -8
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -6
- package/dist/testing.d.ts +2 -6
- package/dist/testing.js +4154 -8
- package/dist/testing.js.map +1 -1
- package/dist/{run-entry-DGs0tySr.d.cts → types-B8NfNRGX.d.ts} +1078 -1502
- package/dist/{run-entry-BOuNyVoO.d.ts → types-BZ2f4MRR.d.cts} +1078 -1502
- package/dist/workflow.cjs +7096 -6
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +3346 -22
- package/dist/workflow.d.ts +3346 -22
- package/dist/workflow.js +6929 -6
- package/dist/workflow.js.map +1 -1
- package/package.json +3 -168
- package/dist/adapters.cjs +0 -7
- package/dist/adapters.cjs.map +0 -1
- package/dist/adapters.d.cts +0 -179
- package/dist/adapters.d.ts +0 -179
- package/dist/adapters.js +0 -7
- package/dist/adapters.js.map +0 -1
- package/dist/batch.cjs +0 -7
- package/dist/batch.cjs.map +0 -1
- package/dist/batch.d.cts +0 -200
- package/dist/batch.d.ts +0 -200
- package/dist/batch.js +0 -7
- package/dist/batch.js.map +0 -1
- package/dist/bind-deps.cjs +0 -2
- package/dist/bind-deps.cjs.map +0 -1
- package/dist/bind-deps.d.cts +0 -28
- package/dist/bind-deps.d.ts +0 -28
- package/dist/bind-deps.js +0 -2
- package/dist/bind-deps.js.map +0 -1
- package/dist/cache.cjs +0 -2
- package/dist/cache.cjs.map +0 -1
- package/dist/cache.d.cts +0 -269
- package/dist/cache.d.ts +0 -269
- package/dist/cache.js +0 -2
- package/dist/cache.js.map +0 -1
- package/dist/circuit-breaker.cjs +0 -7
- package/dist/circuit-breaker.cjs.map +0 -1
- package/dist/circuit-breaker.d.cts +0 -211
- package/dist/circuit-breaker.d.ts +0 -211
- package/dist/circuit-breaker.js +0 -7
- package/dist/circuit-breaker.js.map +0 -1
- package/dist/conditional.cjs +0 -2
- package/dist/conditional.cjs.map +0 -1
- package/dist/conditional.d.cts +0 -252
- package/dist/conditional.d.ts +0 -252
- package/dist/conditional.js +0 -2
- package/dist/conditional.js.map +0 -1
- package/dist/core.cjs +0 -7
- package/dist/core.cjs.map +0 -1
- package/dist/core.d.cts +0 -5
- package/dist/core.d.ts +0 -5
- package/dist/core.js +0 -7
- package/dist/core.js.map +0 -1
- package/dist/di-By77n4Fa.d.ts +0 -15
- package/dist/di-OJfsohf-.d.cts +0 -15
- package/dist/diagnostics.cjs +0 -8
- package/dist/diagnostics.cjs.map +0 -1
- package/dist/diagnostics.d.cts +0 -68
- package/dist/diagnostics.d.ts +0 -68
- package/dist/diagnostics.js +0 -8
- package/dist/diagnostics.js.map +0 -1
- package/dist/durable.cjs +0 -11
- package/dist/durable.cjs.map +0 -1
- package/dist/durable.d.cts +0 -9
- package/dist/durable.d.ts +0 -9
- package/dist/durable.js +0 -11
- package/dist/durable.js.map +0 -1
- package/dist/duration.cjs +0 -2
- package/dist/duration.cjs.map +0 -1
- package/dist/duration.js +0 -2
- package/dist/duration.js.map +0 -1
- package/dist/engine.cjs +0 -11
- package/dist/engine.cjs.map +0 -1
- package/dist/engine.d.cts +0 -115
- package/dist/engine.d.ts +0 -115
- package/dist/engine.js +0 -11
- package/dist/engine.js.map +0 -1
- package/dist/errors.cjs +0 -2
- package/dist/errors.cjs.map +0 -1
- package/dist/errors.d.cts +0 -361
- package/dist/errors.d.ts +0 -361
- package/dist/errors.js +0 -2
- package/dist/errors.js.map +0 -1
- package/dist/fetch.cjs +0 -7
- package/dist/fetch.cjs.map +0 -1
- package/dist/fetch.d.cts +0 -86
- package/dist/fetch.d.ts +0 -86
- package/dist/fetch.js +0 -7
- package/dist/fetch.js.map +0 -1
- package/dist/flow.cjs +0 -7
- package/dist/flow.cjs.map +0 -1
- package/dist/flow.d.cts +0 -163
- package/dist/flow.d.ts +0 -163
- package/dist/flow.js +0 -7
- package/dist/flow.js.map +0 -1
- package/dist/functional.cjs +0 -2
- package/dist/functional.cjs.map +0 -1
- package/dist/functional.d.cts +0 -444
- package/dist/functional.d.ts +0 -444
- package/dist/functional.js +0 -2
- package/dist/functional.js.map +0 -1
- package/dist/guards-4sV7mTqj.d.cts +0 -72
- package/dist/guards-BIX05ALH.d.ts +0 -72
- package/dist/hitl-DFn4Xa_l.d.cts +0 -468
- package/dist/hitl-DU5VpKq7.d.ts +0 -468
- package/dist/hitl.cjs +0 -7
- package/dist/hitl.cjs.map +0 -1
- package/dist/hitl.d.cts +0 -442
- package/dist/hitl.d.ts +0 -442
- package/dist/hitl.js +0 -7
- package/dist/hitl.js.map +0 -1
- package/dist/index-CnvBryQB.d.ts +0 -417
- package/dist/index-DEZEf8Fs.d.cts +0 -417
- package/dist/match-entry-DjI2bLpD.d.cts +0 -209
- package/dist/match-entry-DjI2bLpD.d.ts +0 -209
- package/dist/match.cjs +0 -2
- package/dist/match.cjs.map +0 -1
- package/dist/match.d.cts +0 -1
- package/dist/match.d.ts +0 -1
- package/dist/match.js +0 -2
- package/dist/match.js.map +0 -1
- package/dist/otel.cjs +0 -2
- package/dist/otel.cjs.map +0 -1
- package/dist/otel.d.cts +0 -188
- package/dist/otel.d.ts +0 -188
- package/dist/otel.js +0 -2
- package/dist/otel.js.map +0 -1
- package/dist/persistence-entry-B-8PjnSR.d.cts +0 -831
- package/dist/persistence-entry-D8zRkLiT.d.ts +0 -831
- package/dist/persistence.cjs +0 -2
- package/dist/persistence.cjs.map +0 -1
- package/dist/persistence.d.cts +0 -7
- package/dist/persistence.d.ts +0 -7
- package/dist/persistence.js +0 -2
- package/dist/persistence.js.map +0 -1
- package/dist/policies.cjs +0 -2
- package/dist/policies.cjs.map +0 -1
- package/dist/policies.d.cts +0 -379
- package/dist/policies.d.ts +0 -379
- package/dist/policies.js +0 -2
- package/dist/policies.js.map +0 -1
- package/dist/ratelimit.cjs +0 -7
- package/dist/ratelimit.cjs.map +0 -1
- package/dist/ratelimit.d.cts +0 -458
- package/dist/ratelimit.d.ts +0 -458
- package/dist/ratelimit.js +0 -7
- package/dist/ratelimit.js.map +0 -1
- package/dist/reliability.cjs +0 -11
- package/dist/reliability.cjs.map +0 -1
- package/dist/reliability.d.cts +0 -11
- package/dist/reliability.d.ts +0 -11
- package/dist/reliability.js +0 -11
- package/dist/reliability.js.map +0 -1
- package/dist/resolver.cjs +0 -7
- package/dist/resolver.cjs.map +0 -1
- package/dist/resolver.d.cts +0 -68
- package/dist/resolver.d.ts +0 -68
- package/dist/resolver.js +0 -7
- package/dist/resolver.js.map +0 -1
- package/dist/resource.cjs +0 -7
- package/dist/resource.cjs.map +0 -1
- package/dist/resource.d.cts +0 -174
- package/dist/resource.d.ts +0 -174
- package/dist/resource.js +0 -7
- package/dist/resource.js.map +0 -1
- package/dist/result/retry.cjs +0 -2
- package/dist/result/retry.cjs.map +0 -1
- package/dist/result/retry.d.cts +0 -70
- package/dist/result/retry.d.ts +0 -70
- package/dist/result/retry.js +0 -2
- package/dist/result/retry.js.map +0 -1
- package/dist/retry.cjs +0 -2
- package/dist/retry.cjs.map +0 -1
- package/dist/retry.d.cts +0 -388
- package/dist/retry.d.ts +0 -388
- package/dist/retry.js +0 -2
- package/dist/retry.js.map +0 -1
- package/dist/run.cjs +0 -7
- package/dist/run.cjs.map +0 -1
- package/dist/run.d.cts +0 -4
- package/dist/run.d.ts +0 -4
- package/dist/run.js +0 -7
- package/dist/run.js.map +0 -1
- package/dist/saga.cjs +0 -11
- package/dist/saga.cjs.map +0 -1
- package/dist/saga.d.cts +0 -164
- package/dist/saga.d.ts +0 -164
- package/dist/saga.js +0 -11
- package/dist/saga.js.map +0 -1
- package/dist/singleflight.cjs +0 -2
- package/dist/singleflight.cjs.map +0 -1
- package/dist/singleflight.d.cts +0 -145
- package/dist/singleflight.d.ts +0 -145
- package/dist/singleflight.js +0 -2
- package/dist/singleflight.js.map +0 -1
- package/dist/slugs.cjs +0 -2
- package/dist/slugs.cjs.map +0 -1
- package/dist/slugs.d.cts +0 -67
- package/dist/slugs.d.ts +0 -67
- package/dist/slugs.js +0 -2
- package/dist/slugs.js.map +0 -1
- package/dist/streaming.cjs +0 -9
- package/dist/streaming.cjs.map +0 -1
- package/dist/streaming.d.cts +0 -596
- package/dist/streaming.d.ts +0 -596
- package/dist/streaming.js +0 -9
- package/dist/streaming.js.map +0 -1
- package/dist/tagged-error.cjs +0 -2
- package/dist/tagged-error.cjs.map +0 -1
- package/dist/tagged-error.d.cts +0 -275
- package/dist/tagged-error.d.ts +0 -275
- package/dist/tagged-error.js +0 -2
- package/dist/tagged-error.js.map +0 -1
- package/dist/types-BziYHFkD.d.ts +0 -323
- package/dist/types-C5jLEUqY.d.cts +0 -323
- package/dist/webhook.cjs +0 -7
- package/dist/webhook.cjs.map +0 -1
- package/dist/webhook.d.cts +0 -499
- package/dist/webhook.d.ts +0 -499
- package/dist/webhook.js +0 -7
- package/dist/webhook.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,154 +1,1983 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { AWAITLY_CANCELLED, AWAITLY_TIMEOUT, AWAITLY_UNEXPECTED,
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
5
|
-
import {
|
|
6
|
-
export { R,
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
export { AwaitlySlug, AwaitlySlugCategory } from './slugs.cjs';
|
|
10
|
-
import { UnexpectedError } from './errors.cjs';
|
|
11
|
-
import './persistence-entry-B-8PjnSR.cjs';
|
|
12
|
-
import './types-C5jLEUqY.cjs';
|
|
1
|
+
import { AsyncResult, ErrorOf } from './result.cjs';
|
|
2
|
+
export { AWAITLY_CANCELLED, AWAITLY_TIMEOUT, AWAITLY_UNEXPECTED, CauseOf, DESERIALIZATION_ERROR, DeserializationError, EmptyInputError, Err, Errors, ErrorsOf, ExtractCause, ExtractError, ExtractValue, MatchErrorHandlers, MaybeAsyncResult, Ok, PROMISE_REJECTED, PromiseRejectedError, PromiseRejectionCause, Result, SerializedResult, SettledError, UnwrapError, all, allAsync, allSettled, allSettledAsync, andThen, any, anyAsync, bimap, deserialize, err, flatten, from, fromNullable, fromPromise, hydrate, isErr, isOk, isPromiseRejectedError, isSerializedResult, isUnexpectedError, map, mapError, mapErrorTry, mapTry, match, matchError, matchErrorPartial, ok, orElse, orElseAsync, partition, recover, recoverAsync, runOrNull, runOrThrow, runOrThrowAsync, runOrUndefined, serialize, tags, tap, tapError, tryAsync, unwrap, unwrapOr, unwrapOrElse, zip, zipAsync } from './result.cjs';
|
|
3
|
+
import { D as Duration } from './di-BDlT7InM.cjs';
|
|
4
|
+
export { a as add, c as clamp, d as days, b as divide, e as equals, f as format, g as greaterThan, h as greaterThanOrEqual, i as hours, j as infinity, k as isDuration, l as isFinite, m as isInfinite, n as isZero, o as lessThan, p as lessThanOrEqual, q as max, r as millis, s as min, t as minutes, u as multiply, v as parse, w as seconds, x as subtract, y as toDays, z as toHours, A as toMillis, B as toMinutes, C as toSeconds, E as withDeps, F as zero } from './di-BDlT7InM.cjs';
|
|
5
|
+
import { U as UnexpectedError, T as TimeoutError } from './errors-DtXvrCiO.cjs';
|
|
6
|
+
export { A as ALL_SLUGS, a as AWAITLY_SLUGS, b as AWAITLY_SYSTEM_ERROR_CLASSES, c as AwaitlyError, d as AwaitlySlug, e as AwaitlySlugCategory, f as AwaitlySystemError, C as CircuitBreakerOpenError, g as CompensationError, E as ErrorByTag, N as NetworkError, h as NotFoundError, P as PropsOf, R as RateLimitError, i as RetryExhaustedError, j as TagOf, k as TaggedError, l as TaggedErrorBase, m as TaggedErrorConstructor, n as TaggedErrorCreateOptions, o as TaggedErrorOptions, p as UnauthorizedError, V as ValidationError, q as isAwaitlyError, r as isAwaitlySlug, s as isCircuitBreakerOpenError, t as isCompensationError, u as isNetworkError, v as isNotFoundError, w as isRateLimitError, x as isRetryExhaustedError, y as isTimeoutError, z as isUnauthorizedError, B as isValidationError, D as makeError, F as slugCategory, G as slugDocsUrl } from './errors-DtXvrCiO.cjs';
|
|
7
|
+
import { D as DepValueOfReturn, R as Result, A as AsyncResult$1, S as StepOptions, a as RetryOptions, T as TimeoutOptions, W as WorkflowEvent } from './types-BZ2f4MRR.cjs';
|
|
8
|
+
export { B as BackoffStrategy, b as BoundSteps, E as EARLY_EXIT_SYMBOL, c as EarlyExit, d as RunOptions, e as RunOptionsWithCatch, f as RunOptionsWithoutCatch, g as RunStep, h as STEP_TIMEOUT_MARKER, i as ScopeType, j as StepFailureMeta, k as StepTimeoutError, l as StepTimeoutMarkerMeta, m as createEarlyExit, n as getStepTimeoutMeta, o as isEarlyExit, p as isStepTimeoutError, r as run } from './types-BZ2f4MRR.cjs';
|
|
13
9
|
import '@standard-schema/spec';
|
|
14
10
|
|
|
15
11
|
/**
|
|
16
|
-
*
|
|
12
|
+
* Per-dependency policies: retry, timeout, fallback.
|
|
17
13
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* Policies are value-level function wrappers declared where dependencies
|
|
15
|
+
* are declared — in the deps object — so call sites stay pristine and the
|
|
16
|
+
* policy is statically visible in the deps literal (the analyzer reads it
|
|
17
|
+
* as fact, not inference):
|
|
20
18
|
*
|
|
21
|
-
*
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const result = await run(
|
|
21
|
+
* {
|
|
22
|
+
* getUser,
|
|
23
|
+
* charge: retry(timeout(charge, 5000), { attempts: 3 }),
|
|
24
|
+
* sendEmail: fallback(sendEmail, () => ({ queued: true })),
|
|
25
|
+
* },
|
|
26
|
+
* async (s) => {
|
|
27
|
+
* const user = await s.getUser(userId); // call sites unchanged
|
|
28
|
+
* const payment = await s.charge(user.id);
|
|
29
|
+
* return s.sendEmail(user.id);
|
|
30
|
+
* }
|
|
31
|
+
* );
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Every policy returns a Result-returning function with exact error-union
|
|
35
|
+
* math:
|
|
36
|
+
* - `retry(fn, opts)` — same errors as `fn` (the last failure propagates)
|
|
37
|
+
* - `timeout(fn, ms)` — errors of `fn` plus `TimeoutError`
|
|
38
|
+
* - `fallback(fn, fb)` — errors of `fn` are consumed; only `fb`'s errors remain
|
|
39
|
+
*
|
|
40
|
+
* Plain (non-Result) functions are valid inputs: their values are
|
|
41
|
+
* normalized to `ok()`, and their throws keep throwing (so they surface as
|
|
42
|
+
* `UnexpectedError` at the run/workflow layer, same as unwrapped deps).
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
type AnyFunction = (...args: never[]) => unknown;
|
|
46
|
+
/** Milliseconds or a Duration value. */
|
|
47
|
+
type PolicyDelay = number | Duration;
|
|
48
|
+
/** The unwrapped success value a policy resolves to for a given function. */
|
|
49
|
+
type PolicyValue<F extends AnyFunction> = DepValueOfReturn<ReturnType<F>>;
|
|
50
|
+
/** A function wrapped by a policy: same arguments, Result-returning. */
|
|
51
|
+
type PolicyFn<F extends AnyFunction, E> = (...args: Parameters<F>) => AsyncResult<PolicyValue<F>, E>;
|
|
52
|
+
interface RetryPolicyOptions {
|
|
53
|
+
/** Total attempts including the first call (minimum 1). */
|
|
54
|
+
attempts: number;
|
|
55
|
+
/** Base delay between attempts. Default: no delay. */
|
|
56
|
+
delay?: PolicyDelay;
|
|
57
|
+
/** How the delay grows per attempt. Default: "fixed". */
|
|
58
|
+
backoff?: "fixed" | "linear" | "exponential";
|
|
59
|
+
/** Upper bound for the computed delay. */
|
|
60
|
+
maxDelay?: PolicyDelay;
|
|
61
|
+
/**
|
|
62
|
+
* Decide whether a failure is retryable. Receives the Result error, or
|
|
63
|
+
* the thrown value for plain functions. Default: retry everything.
|
|
64
|
+
*/
|
|
65
|
+
retryIf?: (failure: unknown) => boolean;
|
|
66
|
+
/** Observer invoked before each re-attempt. */
|
|
67
|
+
onRetry?: (info: {
|
|
68
|
+
attempt: number;
|
|
69
|
+
failure: unknown;
|
|
70
|
+
}) => void;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Retry a dependency. The error union is unchanged: if all attempts fail,
|
|
74
|
+
* the last failure propagates exactly as it would have without the policy
|
|
75
|
+
* (typed err for Result functions, throw for plain functions).
|
|
76
|
+
*/
|
|
77
|
+
declare function retry<F extends AnyFunction>(fn: F, options: RetryPolicyOptions): PolicyFn<F, ErrorOf<F>>;
|
|
78
|
+
/**
|
|
79
|
+
* Bound a dependency's execution time. On timeout, resolves to
|
|
80
|
+
* `err(TimeoutError)` — adding `TimeoutError` to the error union. The
|
|
81
|
+
* underlying operation is not cancelled (no AbortSignal is threaded);
|
|
82
|
+
* its eventual result is discarded.
|
|
83
|
+
*/
|
|
84
|
+
declare function timeout<F extends AnyFunction>(fn: F, after: PolicyDelay): PolicyFn<F, ErrorOf<F> | TimeoutError>;
|
|
85
|
+
/**
|
|
86
|
+
* Recover from a dependency's failure. The handler receives the failure
|
|
87
|
+
* (the typed Result error, or `UnexpectedError` wrapping a throw) plus the
|
|
88
|
+
* original arguments, and its result becomes the outcome. The base
|
|
89
|
+
* function's errors are consumed; only the handler's errors remain in the
|
|
90
|
+
* union — `fallback(fn, () => defaultValue)` has no typed errors at all.
|
|
91
|
+
*/
|
|
92
|
+
declare function fallback<F extends AnyFunction, FB extends (failure: ErrorOf<F> | UnexpectedError, ...args: Parameters<F>) => unknown>(fn: F, onFailure: FB): (...args: Parameters<F>) => AsyncResult<PolicyValue<F> | DepValueOfReturn<ReturnType<FB>>, ErrorOf<FB>>;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* awaitly/match
|
|
96
|
+
*
|
|
97
|
+
* Exhaustive pattern matching for discriminated unions.
|
|
98
|
+
* Extends the TaggedError.match pattern to work with any tagged union.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* type Event =
|
|
103
|
+
* | { _tag: 'UserCreated'; user: User }
|
|
104
|
+
* | { _tag: 'UserUpdated'; userId: string }
|
|
105
|
+
* | { _tag: 'UserDeleted'; userId: string }
|
|
106
|
+
*
|
|
107
|
+
* const message = Match.value(event)
|
|
108
|
+
* .pipe(Match.tag("UserCreated", e => `Created: ${e.user.name}`))
|
|
109
|
+
* .pipe(Match.tag("UserUpdated", e => `Updated: ${e.userId}`))
|
|
110
|
+
* .pipe(Match.tag("UserDeleted", e => `Deleted: ${e.userId}`))
|
|
111
|
+
* .pipe(Match.exhaustive)
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
/**
|
|
115
|
+
* Any object with a _tag discriminator.
|
|
116
|
+
*/
|
|
117
|
+
type Tagged<Tag extends string = string> = {
|
|
118
|
+
readonly _tag: Tag;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Extract the tag from a tagged union member.
|
|
122
|
+
*/
|
|
123
|
+
type TagOf<T extends Tagged> = T["_tag"];
|
|
124
|
+
/**
|
|
125
|
+
* Extract union members that match a specific tag.
|
|
126
|
+
*/
|
|
127
|
+
type MatchTag<T extends Tagged, Tag extends string> = Extract<T, {
|
|
128
|
+
_tag: Tag;
|
|
129
|
+
}>;
|
|
130
|
+
/**
|
|
131
|
+
* A matcher that is accumulating handlers for a tagged union.
|
|
132
|
+
*
|
|
133
|
+
* @typeParam Input - The full union type being matched
|
|
134
|
+
* @typeParam Remaining - Union members that haven't been handled yet
|
|
135
|
+
* @typeParam Output - The output type (union of all handler return types)
|
|
136
|
+
*/
|
|
137
|
+
interface Matcher<Input extends Tagged, Remaining extends Tagged, Output> {
|
|
138
|
+
readonly _tag: "Matcher";
|
|
139
|
+
readonly value: Input;
|
|
140
|
+
readonly handlers: Map<string, (value: Tagged) => unknown>;
|
|
141
|
+
readonly _remaining: Remaining;
|
|
142
|
+
readonly _output: Output;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Add a handler for a specific tag.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* Match.value(event)
|
|
150
|
+
* .pipe(Match.tag("UserCreated", e => e.user.name))
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
declare function tag<Input extends Tagged, Remaining extends Tagged, Output, Tag extends TagOf<Remaining>, NewOutput>(tagValue: Tag, handler: (value: MatchTag<Remaining, Tag>) => NewOutput): (matcher: Matcher<Input, Remaining, Output>) => Matcher<Input, Exclude<Remaining, {
|
|
154
|
+
_tag: Tag;
|
|
155
|
+
}>, Output | NewOutput>;
|
|
156
|
+
/**
|
|
157
|
+
* Add handlers for multiple tags at once.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```typescript
|
|
161
|
+
* Match.value(event)
|
|
162
|
+
* .pipe(Match.tags({
|
|
163
|
+
* UserCreated: e => e.user.name,
|
|
164
|
+
* UserUpdated: e => e.userId,
|
|
165
|
+
* }))
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
declare function tags<Input extends Tagged, Remaining extends Tagged, Output, Handlers extends {
|
|
169
|
+
[K in TagOf<Remaining>]?: (value: MatchTag<Remaining, K>) => unknown;
|
|
170
|
+
}>(handlers: Handlers): (matcher: Matcher<Input, Remaining, Output>) => Matcher<Input, Exclude<Remaining, {
|
|
171
|
+
_tag: keyof Handlers;
|
|
172
|
+
}>, Output | ReturnType<NonNullable<Handlers[keyof Handlers]>>>;
|
|
173
|
+
/**
|
|
174
|
+
* Complete the match, requiring all cases to be handled.
|
|
175
|
+
* This is a compile-time check - if any cases are missing, TypeScript will error.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* // TypeScript error if any tag is not handled
|
|
180
|
+
* const result = Match.value(event)
|
|
181
|
+
* .pipe(Match.tag("UserCreated", e => e.user))
|
|
182
|
+
* .pipe(Match.tag("UserUpdated", e => e.userId))
|
|
183
|
+
* .pipe(Match.tag("UserDeleted", e => e.userId))
|
|
184
|
+
* .pipe(Match.exhaustive)
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
declare function exhaustive<Input extends Tagged, Output>(matcher: Matcher<Input, never, Output>): Output;
|
|
188
|
+
/**
|
|
189
|
+
* Complete the match with a default handler for any remaining cases.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```typescript
|
|
193
|
+
* const result = Match.value(event)
|
|
194
|
+
* .pipe(Match.tag("UserCreated", e => `Created: ${e.user.name}`))
|
|
195
|
+
* .pipe(Match.orElse(e => `Other event: ${e._tag}`))
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
declare function orElse<Input extends Tagged, Remaining extends Tagged, Output, DefaultOutput>(handler: (value: Remaining) => DefaultOutput): (matcher: Matcher<Input, Remaining, Output>) => Output | DefaultOutput;
|
|
199
|
+
/**
|
|
200
|
+
* Complete the match with a default value for any remaining cases.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* const result = Match.value(event)
|
|
205
|
+
* .pipe(Match.tag("UserCreated", e => e.user.name))
|
|
206
|
+
* .pipe(Match.orElseValue("Unknown event"))
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
declare function orElseValue<Input extends Tagged, Remaining extends Tagged, Output, DefaultOutput>(defaultValue: DefaultOutput): (matcher: Matcher<Input, Remaining, Output>) => Output | DefaultOutput;
|
|
210
|
+
/**
|
|
211
|
+
* Add a handler with an additional predicate.
|
|
212
|
+
* The handler only runs if both the tag matches AND the predicate returns true.
|
|
22
213
|
*
|
|
214
|
+
* @example
|
|
23
215
|
* ```typescript
|
|
24
|
-
*
|
|
25
|
-
*
|
|
216
|
+
* Match.value(event)
|
|
217
|
+
* .pipe(Match.when(
|
|
218
|
+
* "UserCreated",
|
|
219
|
+
* e => e.user.isAdmin,
|
|
220
|
+
* e => `Admin created: ${e.user.name}`
|
|
221
|
+
* ))
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
declare function when$1<Input extends Tagged, Remaining extends Tagged, Output, Tag extends TagOf<Input>, NewOutput>(tagValue: Tag, predicate: (value: MatchTag<Input, Tag>) => boolean, handler: (value: MatchTag<Input, Tag>) => NewOutput): (matcher: Matcher<Input, Remaining, Output>) => Matcher<Input, Remaining, Output | NewOutput>;
|
|
225
|
+
/**
|
|
226
|
+
* Check if a tagged value has a specific tag.
|
|
227
|
+
* Type guard that narrows the type.
|
|
26
228
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* if (Match.is("UserCreated")(event)) {
|
|
232
|
+
* // event is narrowed to { _tag: 'UserCreated'; user: User }
|
|
233
|
+
* console.log(event.user.name);
|
|
31
234
|
* }
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
declare function is<T extends Tagged, Tag extends string>(tagValue: Tag): (value: T) => value is Extract<T, {
|
|
238
|
+
_tag: Tag;
|
|
239
|
+
}>;
|
|
240
|
+
/**
|
|
241
|
+
* Check if a tagged value has one of several tags.
|
|
32
242
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
};
|
|
87
|
-
readonly recoverWith: typeof recoverWith;
|
|
88
|
-
readonly getOrElse: typeof getOrElse;
|
|
89
|
-
readonly getOrElseLazy: typeof getOrElseLazy;
|
|
90
|
-
readonly mapAsync: typeof mapAsync;
|
|
91
|
-
readonly flatMapAsync: typeof flatMapAsync;
|
|
92
|
-
readonly tapAsync: typeof tapAsync;
|
|
93
|
-
readonly tapErrorAsync: typeof tapErrorAsync;
|
|
94
|
-
readonly race: typeof race;
|
|
95
|
-
readonly traverse: typeof traverse;
|
|
96
|
-
readonly traverseAsync: typeof traverseAsync;
|
|
97
|
-
readonly traverseParallel: typeof traverseParallel;
|
|
98
|
-
readonly ok: typeof ok;
|
|
99
|
-
readonly err: typeof err;
|
|
100
|
-
readonly matchError: typeof matchError;
|
|
101
|
-
readonly from: typeof from;
|
|
102
|
-
readonly fromPromise: typeof fromPromise;
|
|
103
|
-
readonly tryAsync: typeof tryAsync;
|
|
104
|
-
readonly fromNullable: typeof fromNullable;
|
|
105
|
-
readonly map: typeof map;
|
|
106
|
-
readonly mapError: typeof mapError;
|
|
107
|
-
readonly match: typeof match;
|
|
108
|
-
readonly andThen: typeof andThen;
|
|
109
|
-
readonly tap: typeof tap;
|
|
110
|
-
readonly tapError: typeof tapError;
|
|
111
|
-
readonly mapTry: typeof mapTry;
|
|
112
|
-
readonly mapErrorTry: typeof mapErrorTry;
|
|
113
|
-
readonly bimap: typeof bimap;
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* if (Match.isOneOf("UserCreated", "UserUpdated")(event)) {
|
|
246
|
+
* // event is narrowed to UserCreated | UserUpdated
|
|
247
|
+
* }
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
declare function isOneOf<T extends Tagged, Tags extends TagOf<T>[]>(...tags: Tags): (value: T) => value is Extract<T, {
|
|
251
|
+
_tag: Tags[number];
|
|
252
|
+
}>;
|
|
253
|
+
type PipedMatcher<Input extends Tagged, Remaining extends Tagged, Output> = Matcher<Input, Remaining, Output> & {
|
|
254
|
+
pipe: <NewRemaining extends Tagged, NewOutput>(fn: (self: Matcher<Input, Remaining, Output>) => Matcher<Input, NewRemaining, NewOutput>) => PipedMatcher<Input, NewRemaining, NewOutput>;
|
|
255
|
+
} & {
|
|
256
|
+
pipe: <R>(fn: (self: Matcher<Input, Remaining, Output>) => R) => R;
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* Start matching on a value (with pipe support).
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```typescript
|
|
263
|
+
* const result = Match.value(event)
|
|
264
|
+
* .pipe(Match.tag("Created", e => e.id))
|
|
265
|
+
* .pipe(Match.exhaustive)
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
declare function matchValue<T extends Tagged>(input: T): PipedMatcher<T, T, never>;
|
|
269
|
+
/**
|
|
270
|
+
* Match namespace for exhaustive pattern matching.
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```typescript
|
|
274
|
+
* import { Match } from "awaitly";
|
|
275
|
+
*
|
|
276
|
+
* type Event =
|
|
277
|
+
* | { _tag: 'Created'; id: string }
|
|
278
|
+
* | { _tag: 'Updated'; id: string; data: unknown }
|
|
279
|
+
* | { _tag: 'Deleted'; id: string }
|
|
280
|
+
*
|
|
281
|
+
* function handle(event: Event): string {
|
|
282
|
+
* return Match.value(event)
|
|
283
|
+
* .pipe(Match.tag("Created", e => `Created: ${e.id}`))
|
|
284
|
+
* .pipe(Match.tag("Updated", e => `Updated: ${e.id}`))
|
|
285
|
+
* .pipe(Match.tag("Deleted", e => `Deleted: ${e.id}`))
|
|
286
|
+
* .pipe(Match.exhaustive)
|
|
287
|
+
* }
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
declare const Match: {
|
|
291
|
+
readonly value: typeof matchValue;
|
|
292
|
+
readonly tag: typeof tag;
|
|
293
|
+
readonly tags: typeof tags;
|
|
294
|
+
readonly when: typeof when$1;
|
|
295
|
+
readonly exhaustive: typeof exhaustive;
|
|
114
296
|
readonly orElse: typeof orElse;
|
|
115
|
-
readonly
|
|
116
|
-
readonly
|
|
117
|
-
readonly
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
297
|
+
readonly orElseValue: typeof orElseValue;
|
|
298
|
+
readonly is: typeof is;
|
|
299
|
+
readonly isOneOf: typeof isOneOf;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Circuit Breaker for Steps
|
|
304
|
+
*
|
|
305
|
+
* Prevents cascading failures by tracking step failure rates and
|
|
306
|
+
* short-circuiting calls when a threshold is exceeded.
|
|
307
|
+
*
|
|
308
|
+
* Uses the circuit breaker pattern with three states:
|
|
309
|
+
* - CLOSED: Normal operation (steps executing)
|
|
310
|
+
* - OPEN: Fast-fail mode (steps blocked)
|
|
311
|
+
* - HALF_OPEN: Testing if service recovered
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```typescript
|
|
315
|
+
* import { createCircuitBreaker } from 'awaitly';
|
|
316
|
+
*
|
|
317
|
+
* const breaker = createCircuitBreaker({
|
|
318
|
+
* failureThreshold: 5,
|
|
319
|
+
* resetTimeout: 30000,
|
|
320
|
+
* halfOpenMax: 3,
|
|
321
|
+
* });
|
|
322
|
+
*
|
|
323
|
+
* const result = await workflow(async ({ step }) => {
|
|
324
|
+
* const data = await breaker.execute(
|
|
325
|
+
* () => step(() => callExternalApi()),
|
|
326
|
+
* { name: 'external-api' }
|
|
327
|
+
* );
|
|
328
|
+
* return data;
|
|
329
|
+
* });
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Circuit breaker state.
|
|
335
|
+
*/
|
|
336
|
+
type CircuitState = "CLOSED" | "OPEN" | "HALF_OPEN";
|
|
337
|
+
/**
|
|
338
|
+
* Configuration for circuit breaker behavior.
|
|
339
|
+
*/
|
|
340
|
+
interface CircuitBreakerConfig {
|
|
341
|
+
/**
|
|
342
|
+
* Number of failures within the window before opening the circuit.
|
|
343
|
+
* @default 5
|
|
344
|
+
*/
|
|
345
|
+
failureThreshold: number;
|
|
346
|
+
/**
|
|
347
|
+
* Time in ms to wait before transitioning from OPEN to HALF_OPEN.
|
|
348
|
+
* @default 30000 (30 seconds)
|
|
349
|
+
*/
|
|
350
|
+
resetTimeout: number;
|
|
351
|
+
/**
|
|
352
|
+
* Time window in ms for counting failures.
|
|
353
|
+
* Failures older than this are discarded.
|
|
354
|
+
* @default 60000 (1 minute)
|
|
355
|
+
*/
|
|
356
|
+
windowSize: number;
|
|
357
|
+
/**
|
|
358
|
+
* Maximum number of test requests allowed in HALF_OPEN state.
|
|
359
|
+
* If all succeed, circuit closes. If any fail, circuit reopens.
|
|
360
|
+
* @default 3
|
|
361
|
+
*/
|
|
362
|
+
halfOpenMax: number;
|
|
363
|
+
/**
|
|
364
|
+
* Optional callback when circuit state changes.
|
|
365
|
+
*/
|
|
366
|
+
onStateChange?: (from: CircuitState, to: CircuitState, name?: string) => void;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Error thrown when the circuit is open and calls are blocked.
|
|
370
|
+
*/
|
|
371
|
+
declare class CircuitOpenError extends Error {
|
|
372
|
+
readonly type: "CIRCUIT_OPEN";
|
|
373
|
+
readonly circuitName: string;
|
|
374
|
+
readonly state: CircuitState;
|
|
375
|
+
readonly retryAfterMs: number;
|
|
376
|
+
constructor(options: {
|
|
377
|
+
circuitName: string;
|
|
378
|
+
state: CircuitState;
|
|
379
|
+
retryAfterMs: number;
|
|
380
|
+
message?: string;
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Type guard for CircuitOpenError.
|
|
385
|
+
*/
|
|
386
|
+
declare function isCircuitOpenError(error: unknown): error is CircuitOpenError;
|
|
387
|
+
/**
|
|
388
|
+
* Circuit breaker statistics.
|
|
389
|
+
*/
|
|
390
|
+
interface CircuitBreakerStats {
|
|
391
|
+
state: CircuitState;
|
|
392
|
+
failureCount: number;
|
|
393
|
+
successCount: number;
|
|
394
|
+
lastFailureTime: number | null;
|
|
395
|
+
lastSuccessTime: number | null;
|
|
396
|
+
halfOpenSuccesses: number;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Circuit breaker instance for protecting external calls.
|
|
400
|
+
*/
|
|
401
|
+
interface CircuitBreaker {
|
|
402
|
+
/**
|
|
403
|
+
* Execute an operation with circuit breaker protection.
|
|
404
|
+
* Throws CircuitOpenError if the circuit is open.
|
|
405
|
+
*
|
|
406
|
+
* @param operation - The operation to execute
|
|
407
|
+
* @param options - Optional name for logging/metrics
|
|
408
|
+
* @returns The operation result
|
|
409
|
+
* @throws CircuitOpenError if circuit is open
|
|
410
|
+
*/
|
|
411
|
+
execute<T>(operation: () => T | Promise<T>, options?: {
|
|
412
|
+
name?: string;
|
|
413
|
+
}): Promise<T>;
|
|
414
|
+
/**
|
|
415
|
+
* Execute a Result-returning operation with circuit breaker protection.
|
|
416
|
+
* Returns a CircuitOpenError result instead of throwing.
|
|
417
|
+
*
|
|
418
|
+
* @param operation - The operation returning a Result
|
|
419
|
+
* @param options - Optional name for logging/metrics
|
|
420
|
+
* @returns Result with the value or CircuitOpenError
|
|
421
|
+
*/
|
|
422
|
+
executeResult<T, E>(operation: () => Result<T, E> | AsyncResult$1<T, E>, options?: {
|
|
423
|
+
name?: string;
|
|
424
|
+
}): AsyncResult$1<T, E | CircuitOpenError>;
|
|
425
|
+
/**
|
|
426
|
+
* Get current circuit state.
|
|
427
|
+
*/
|
|
428
|
+
getState(): CircuitState;
|
|
429
|
+
/**
|
|
430
|
+
* Get circuit breaker statistics.
|
|
431
|
+
*/
|
|
432
|
+
getStats(): CircuitBreakerStats;
|
|
433
|
+
/**
|
|
434
|
+
* Manually reset the circuit breaker to CLOSED state.
|
|
435
|
+
*/
|
|
436
|
+
reset(): void;
|
|
437
|
+
/**
|
|
438
|
+
* Manually open the circuit (for testing or manual intervention).
|
|
439
|
+
*/
|
|
440
|
+
forceOpen(): void;
|
|
441
|
+
/**
|
|
442
|
+
* Record a manual success (useful for health checks).
|
|
443
|
+
*/
|
|
444
|
+
recordSuccess(): void;
|
|
445
|
+
/**
|
|
446
|
+
* Record a manual failure (useful for health checks).
|
|
447
|
+
*/
|
|
448
|
+
recordFailure(error?: unknown): void;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Create a circuit breaker instance.
|
|
452
|
+
*
|
|
453
|
+
* @param name - Name for this circuit breaker (used in errors and logging)
|
|
454
|
+
* @param config - Configuration options
|
|
455
|
+
* @returns A CircuitBreaker instance
|
|
456
|
+
*
|
|
457
|
+
* @example
|
|
458
|
+
* ```typescript
|
|
459
|
+
* const apiBreaker = createCircuitBreaker('external-api', {
|
|
460
|
+
* failureThreshold: 5,
|
|
461
|
+
* resetTimeout: 30000,
|
|
462
|
+
* });
|
|
463
|
+
*
|
|
464
|
+
* // In workflow
|
|
465
|
+
* const data = await apiBreaker.execute(() =>
|
|
466
|
+
* step(() => fetchFromApi(id))
|
|
467
|
+
* );
|
|
468
|
+
* ```
|
|
469
|
+
*/
|
|
470
|
+
declare function createCircuitBreaker(name: string, config?: Partial<CircuitBreakerConfig>): CircuitBreaker;
|
|
471
|
+
/**
|
|
472
|
+
* Preset configurations for common use cases.
|
|
473
|
+
*/
|
|
474
|
+
declare const circuitBreakerPresets: {
|
|
475
|
+
/**
|
|
476
|
+
* Aggressive circuit breaker for critical paths.
|
|
477
|
+
* Opens quickly (3 failures) and recovers slowly (60s).
|
|
478
|
+
*/
|
|
479
|
+
readonly critical: {
|
|
480
|
+
failureThreshold: number;
|
|
481
|
+
resetTimeout: number;
|
|
482
|
+
windowSize: number;
|
|
483
|
+
halfOpenMax: number;
|
|
484
|
+
};
|
|
485
|
+
/**
|
|
486
|
+
* Standard circuit breaker for typical API calls.
|
|
487
|
+
* Balanced between stability and availability.
|
|
488
|
+
*/
|
|
489
|
+
readonly standard: {
|
|
490
|
+
failureThreshold: number;
|
|
491
|
+
resetTimeout: number;
|
|
492
|
+
windowSize: number;
|
|
493
|
+
halfOpenMax: number;
|
|
494
|
+
};
|
|
495
|
+
/**
|
|
496
|
+
* Lenient circuit breaker for non-critical operations.
|
|
497
|
+
* Opens slowly (10 failures) and recovers quickly (15s).
|
|
498
|
+
*/
|
|
499
|
+
readonly lenient: {
|
|
500
|
+
failureThreshold: number;
|
|
501
|
+
resetTimeout: number;
|
|
502
|
+
windowSize: number;
|
|
503
|
+
halfOpenMax: number;
|
|
504
|
+
};
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Rate Limiting / Concurrency Control
|
|
509
|
+
*
|
|
510
|
+
* Control throughput for steps that hit rate-limited APIs or shared resources.
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* ```typescript
|
|
514
|
+
* import { createRateLimiter, createConcurrencyLimiter } from 'awaitly';
|
|
515
|
+
*
|
|
516
|
+
* // Rate limiting (requests per second)
|
|
517
|
+
* const rateLimiter = createRateLimiter({ maxPerSecond: 10 });
|
|
518
|
+
*
|
|
519
|
+
* // Concurrency limiting (max concurrent)
|
|
520
|
+
* const concurrencyLimiter = createConcurrencyLimiter({ maxConcurrent: 5 });
|
|
521
|
+
*
|
|
522
|
+
* const result = await workflow(async ({ step }) => {
|
|
523
|
+
* // Wrap operations with rate limiting
|
|
524
|
+
* const data = await rateLimiter.execute(() =>
|
|
525
|
+
* step(() => callRateLimitedApi())
|
|
526
|
+
* );
|
|
527
|
+
*
|
|
528
|
+
* // Wrap batch operations with concurrency control
|
|
529
|
+
* const results = await concurrencyLimiter.executeAll(
|
|
530
|
+
* ids.map(id => () => step(() => fetchItem(id)))
|
|
531
|
+
* );
|
|
532
|
+
*
|
|
533
|
+
* return { data, results };
|
|
534
|
+
* });
|
|
535
|
+
* ```
|
|
536
|
+
*/
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Configuration for rate limiter.
|
|
540
|
+
*/
|
|
541
|
+
interface RateLimiterConfig {
|
|
542
|
+
/**
|
|
543
|
+
* Maximum operations per second.
|
|
544
|
+
*/
|
|
545
|
+
maxPerSecond: number;
|
|
546
|
+
/**
|
|
547
|
+
* Burst capacity - allows brief spikes above the rate.
|
|
548
|
+
* @default maxPerSecond * 2
|
|
549
|
+
*/
|
|
550
|
+
burstCapacity?: number;
|
|
551
|
+
/**
|
|
552
|
+
* Strategy when rate limit is exceeded.
|
|
553
|
+
* - 'wait': Wait until a slot is available (default)
|
|
554
|
+
* - 'reject': Reject immediately with error
|
|
555
|
+
* @default 'wait'
|
|
556
|
+
*/
|
|
557
|
+
strategy?: "wait" | "reject";
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Configuration for concurrency limiter.
|
|
561
|
+
*/
|
|
562
|
+
interface ConcurrencyLimiterConfig {
|
|
563
|
+
/**
|
|
564
|
+
* Maximum concurrent operations.
|
|
565
|
+
*/
|
|
566
|
+
maxConcurrent: number;
|
|
567
|
+
/**
|
|
568
|
+
* Strategy when limit is reached.
|
|
569
|
+
* - 'queue': Queue and wait (default)
|
|
570
|
+
* - 'reject': Reject immediately
|
|
571
|
+
* @default 'queue'
|
|
572
|
+
*/
|
|
573
|
+
strategy?: "queue" | "reject";
|
|
574
|
+
/**
|
|
575
|
+
* Maximum queue size (only for 'queue' strategy).
|
|
576
|
+
* @default Infinity
|
|
577
|
+
*/
|
|
578
|
+
maxQueueSize?: number;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Error when rate/concurrency limit is exceeded and strategy is 'reject'.
|
|
582
|
+
*/
|
|
583
|
+
interface RateLimitExceededError {
|
|
584
|
+
type: "RATE_LIMIT_EXCEEDED";
|
|
585
|
+
limiterName: string;
|
|
586
|
+
retryAfterMs?: number;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Error when concurrency limit queue is full.
|
|
590
|
+
*/
|
|
591
|
+
interface QueueFullError {
|
|
592
|
+
type: "QUEUE_FULL";
|
|
593
|
+
limiterName: string;
|
|
594
|
+
queueSize: number;
|
|
595
|
+
maxQueueSize: number;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Type guard for RateLimitExceededError.
|
|
599
|
+
*/
|
|
600
|
+
declare function isRateLimitExceededError(error: unknown): error is RateLimitExceededError;
|
|
601
|
+
/**
|
|
602
|
+
* Type guard for QueueFullError.
|
|
603
|
+
*/
|
|
604
|
+
declare function isQueueFullError(error: unknown): error is QueueFullError;
|
|
605
|
+
/**
|
|
606
|
+
* Statistics for rate limiter.
|
|
607
|
+
*/
|
|
608
|
+
interface RateLimiterStats {
|
|
609
|
+
availableTokens: number;
|
|
610
|
+
maxTokens: number;
|
|
611
|
+
tokensPerSecond: number;
|
|
612
|
+
waitingCount: number;
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Statistics for concurrency limiter.
|
|
616
|
+
*/
|
|
617
|
+
interface ConcurrencyLimiterStats {
|
|
618
|
+
activeCount: number;
|
|
619
|
+
maxConcurrent: number;
|
|
620
|
+
queueSize: number;
|
|
621
|
+
maxQueueSize: number;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Rate limiter interface.
|
|
625
|
+
*/
|
|
626
|
+
interface RateLimiter {
|
|
627
|
+
/**
|
|
628
|
+
* Execute an operation with rate limiting.
|
|
629
|
+
* @param operation - The operation to execute
|
|
630
|
+
* @returns The operation result
|
|
631
|
+
*/
|
|
632
|
+
execute<T>(operation: () => T | Promise<T>): Promise<T>;
|
|
633
|
+
/**
|
|
634
|
+
* Execute a Result-returning operation with rate limiting.
|
|
635
|
+
*/
|
|
636
|
+
executeResult<T, E>(operation: () => Result<T, E> | AsyncResult$1<T, E>): AsyncResult$1<T, E | RateLimitExceededError>;
|
|
637
|
+
/**
|
|
638
|
+
* Get current statistics.
|
|
639
|
+
*/
|
|
640
|
+
getStats(): RateLimiterStats;
|
|
641
|
+
/**
|
|
642
|
+
* Reset the rate limiter.
|
|
643
|
+
*/
|
|
644
|
+
reset(): void;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Create a token bucket rate limiter.
|
|
648
|
+
*
|
|
649
|
+
* @param name - Name for the limiter (used in errors)
|
|
650
|
+
* @param config - Rate limiter configuration
|
|
651
|
+
* @returns A RateLimiter instance
|
|
652
|
+
*
|
|
653
|
+
* @example
|
|
654
|
+
* ```typescript
|
|
655
|
+
* const limiter = createRateLimiter('api-calls', {
|
|
656
|
+
* maxPerSecond: 10,
|
|
657
|
+
* burstCapacity: 20,
|
|
658
|
+
* });
|
|
659
|
+
*
|
|
660
|
+
* // In workflow
|
|
661
|
+
* const data = await limiter.execute(() =>
|
|
662
|
+
* step(() => callApi())
|
|
663
|
+
* );
|
|
664
|
+
* ```
|
|
665
|
+
*/
|
|
666
|
+
declare function createRateLimiter(name: string, config: RateLimiterConfig): RateLimiter;
|
|
667
|
+
/**
|
|
668
|
+
* Concurrency limiter interface.
|
|
669
|
+
*/
|
|
670
|
+
interface ConcurrencyLimiter {
|
|
671
|
+
/**
|
|
672
|
+
* Execute an operation with concurrency limiting.
|
|
673
|
+
* @param operation - The operation to execute
|
|
674
|
+
* @returns The operation result
|
|
675
|
+
*/
|
|
676
|
+
execute<T>(operation: () => T | Promise<T>): Promise<T>;
|
|
677
|
+
/**
|
|
678
|
+
* Execute multiple operations with concurrency control.
|
|
679
|
+
* @param operations - Array of operation factories
|
|
680
|
+
* @returns Array of results (in order)
|
|
681
|
+
*/
|
|
682
|
+
executeAll<T>(operations: Array<() => T | Promise<T>>): Promise<T[]>;
|
|
683
|
+
/**
|
|
684
|
+
* Execute a Result-returning operation with concurrency limiting.
|
|
685
|
+
*/
|
|
686
|
+
executeResult<T, E>(operation: () => Result<T, E> | AsyncResult$1<T, E>): AsyncResult$1<T, E | QueueFullError>;
|
|
687
|
+
/**
|
|
688
|
+
* Get current statistics.
|
|
689
|
+
*/
|
|
690
|
+
getStats(): ConcurrencyLimiterStats;
|
|
691
|
+
/**
|
|
692
|
+
* Reset the concurrency limiter.
|
|
693
|
+
*/
|
|
694
|
+
reset(): void;
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Create a concurrency limiter.
|
|
698
|
+
*
|
|
699
|
+
* @param name - Name for the limiter (used in errors)
|
|
700
|
+
* @param config - Concurrency limiter configuration
|
|
701
|
+
* @returns A ConcurrencyLimiter instance
|
|
702
|
+
*
|
|
703
|
+
* @example
|
|
704
|
+
* ```typescript
|
|
705
|
+
* const limiter = createConcurrencyLimiter('db-pool', {
|
|
706
|
+
* maxConcurrent: 10,
|
|
707
|
+
* });
|
|
708
|
+
*
|
|
709
|
+
* // Execute with concurrency control
|
|
710
|
+
* const results = await limiter.executeAll(
|
|
711
|
+
* ids.map(id => () => fetchItem(id))
|
|
712
|
+
* );
|
|
713
|
+
* ```
|
|
714
|
+
*/
|
|
715
|
+
declare function createConcurrencyLimiter(name: string, config: ConcurrencyLimiterConfig): ConcurrencyLimiter;
|
|
716
|
+
/**
|
|
717
|
+
* Configuration for combined rate + concurrency limiter.
|
|
718
|
+
*/
|
|
719
|
+
interface CombinedLimiterConfig {
|
|
720
|
+
/**
|
|
721
|
+
* Rate limiting configuration.
|
|
722
|
+
*/
|
|
723
|
+
rate?: RateLimiterConfig;
|
|
724
|
+
/**
|
|
725
|
+
* Concurrency limiting configuration.
|
|
726
|
+
*/
|
|
727
|
+
concurrency?: ConcurrencyLimiterConfig;
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Create a combined rate + concurrency limiter.
|
|
731
|
+
*
|
|
732
|
+
* Operations are first rate-limited, then concurrency-limited.
|
|
733
|
+
*
|
|
734
|
+
* @param name - Name for the limiter
|
|
735
|
+
* @param config - Combined limiter configuration
|
|
736
|
+
* @returns An object with both limiters and a combined execute function
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```typescript
|
|
740
|
+
* const limiter = createCombinedLimiter('api', {
|
|
741
|
+
* rate: { maxPerSecond: 10 },
|
|
742
|
+
* concurrency: { maxConcurrent: 5 },
|
|
743
|
+
* });
|
|
744
|
+
*
|
|
745
|
+
* const result = await limiter.execute(() => callApi());
|
|
746
|
+
* ```
|
|
747
|
+
*/
|
|
748
|
+
declare function createCombinedLimiter(name: string, config: CombinedLimiterConfig): {
|
|
749
|
+
rate?: RateLimiter;
|
|
750
|
+
concurrency?: ConcurrencyLimiter;
|
|
751
|
+
execute: <T>(operation: () => T | Promise<T>) => Promise<T>;
|
|
752
|
+
};
|
|
753
|
+
/**
|
|
754
|
+
* Configuration for fixed window rate limiter.
|
|
755
|
+
*/
|
|
756
|
+
interface FixedWindowLimiterConfig {
|
|
757
|
+
/**
|
|
758
|
+
* Maximum requests allowed per window.
|
|
759
|
+
*/
|
|
760
|
+
limit: number;
|
|
761
|
+
/**
|
|
762
|
+
* Window duration in milliseconds.
|
|
763
|
+
* @default 1000 (1 second)
|
|
764
|
+
*/
|
|
765
|
+
windowMs?: number;
|
|
766
|
+
/**
|
|
767
|
+
* Strategy when rate limit is exceeded.
|
|
768
|
+
* - 'wait': Wait until window resets (default)
|
|
769
|
+
* - 'reject': Reject immediately with error
|
|
770
|
+
* @default 'wait'
|
|
771
|
+
*/
|
|
772
|
+
strategy?: "wait" | "reject";
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Statistics for fixed window rate limiter.
|
|
776
|
+
*/
|
|
777
|
+
interface FixedWindowLimiterStats {
|
|
778
|
+
/** Requests made in current window */
|
|
779
|
+
requestCount: number;
|
|
780
|
+
/** Maximum requests allowed per window */
|
|
781
|
+
limit: number;
|
|
782
|
+
/** Window duration in milliseconds */
|
|
783
|
+
windowMs: number;
|
|
784
|
+
/** Time remaining until window reset (ms) */
|
|
785
|
+
remainingMs: number;
|
|
786
|
+
/** Number of requests waiting for next window */
|
|
787
|
+
waitingCount: number;
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Fixed window rate limiter interface.
|
|
791
|
+
*/
|
|
792
|
+
interface FixedWindowLimiter {
|
|
793
|
+
/**
|
|
794
|
+
* Execute an operation with rate limiting.
|
|
795
|
+
* @param operation - The operation to execute
|
|
796
|
+
* @param cost - Optional cost for this operation (default: 1)
|
|
797
|
+
* @returns The operation result
|
|
798
|
+
*/
|
|
799
|
+
execute<T>(operation: () => T | Promise<T>, cost?: number): Promise<T>;
|
|
800
|
+
/**
|
|
801
|
+
* Execute a Result-returning operation with rate limiting.
|
|
802
|
+
* @param operation - The operation to execute
|
|
803
|
+
* @param cost - Optional cost for this operation (default: 1)
|
|
804
|
+
*/
|
|
805
|
+
executeResult<T, E>(operation: () => Result<T, E> | AsyncResult$1<T, E>, cost?: number): AsyncResult$1<T, E | RateLimitExceededError>;
|
|
806
|
+
/**
|
|
807
|
+
* Get current statistics.
|
|
808
|
+
*/
|
|
809
|
+
getStats(): FixedWindowLimiterStats;
|
|
810
|
+
/**
|
|
811
|
+
* Reset the rate limiter.
|
|
812
|
+
*/
|
|
813
|
+
reset(): void;
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Create a fixed window rate limiter.
|
|
817
|
+
*
|
|
818
|
+
* Unlike token bucket, fixed window resets at fixed intervals.
|
|
819
|
+
* Simpler to reason about but can allow bursts at window boundaries.
|
|
820
|
+
*
|
|
821
|
+
* @param name - Name for the limiter (used in errors)
|
|
822
|
+
* @param config - Rate limiter configuration
|
|
823
|
+
* @returns A FixedWindowLimiter instance
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* ```typescript
|
|
827
|
+
* const limiter = createFixedWindowLimiter('api-calls', {
|
|
828
|
+
* limit: 100, // 100 requests
|
|
829
|
+
* windowMs: 60000, // per minute
|
|
830
|
+
* });
|
|
831
|
+
*
|
|
832
|
+
* // In workflow
|
|
833
|
+
* const data = await limiter.execute(() => callApi());
|
|
834
|
+
*
|
|
835
|
+
* // Cost-based limiting (e.g., batch operations cost more)
|
|
836
|
+
* const batchData = await limiter.execute(() => callBatchApi(), 10);
|
|
837
|
+
* ```
|
|
838
|
+
*/
|
|
839
|
+
declare function createFixedWindowLimiter(name: string, config: FixedWindowLimiterConfig): FixedWindowLimiter;
|
|
840
|
+
/**
|
|
841
|
+
* Configuration for cost-based rate limiter.
|
|
842
|
+
*/
|
|
843
|
+
interface CostBasedRateLimiterConfig {
|
|
844
|
+
/**
|
|
845
|
+
* Maximum tokens (credits) per second refill rate.
|
|
846
|
+
*/
|
|
847
|
+
tokensPerSecond: number;
|
|
848
|
+
/**
|
|
849
|
+
* Maximum token capacity (burst capacity).
|
|
850
|
+
* @default tokensPerSecond * 2
|
|
851
|
+
*/
|
|
852
|
+
maxTokens?: number;
|
|
853
|
+
/**
|
|
854
|
+
* Strategy when rate limit is exceeded.
|
|
855
|
+
* - 'wait': Wait until tokens are available (default)
|
|
856
|
+
* - 'reject': Reject immediately with error
|
|
857
|
+
* @default 'wait'
|
|
858
|
+
*/
|
|
859
|
+
strategy?: "wait" | "reject";
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Statistics for cost-based rate limiter.
|
|
863
|
+
*/
|
|
864
|
+
interface CostBasedRateLimiterStats {
|
|
865
|
+
/** Available tokens (can be fractional) */
|
|
866
|
+
availableTokens: number;
|
|
867
|
+
/** Maximum token capacity */
|
|
868
|
+
maxTokens: number;
|
|
869
|
+
/** Token refill rate per second */
|
|
870
|
+
tokensPerSecond: number;
|
|
871
|
+
/** Number of operations waiting */
|
|
872
|
+
waitingCount: number;
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Cost-based rate limiter interface.
|
|
876
|
+
*/
|
|
877
|
+
interface CostBasedRateLimiter {
|
|
878
|
+
/**
|
|
879
|
+
* Execute an operation with cost-based rate limiting.
|
|
880
|
+
* @param operation - The operation to execute
|
|
881
|
+
* @param cost - Token cost for this operation (default: 1)
|
|
882
|
+
* @returns The operation result
|
|
883
|
+
*/
|
|
884
|
+
execute<T>(operation: () => T | Promise<T>, cost?: number): Promise<T>;
|
|
885
|
+
/**
|
|
886
|
+
* Execute a Result-returning operation with cost-based rate limiting.
|
|
887
|
+
* @param operation - The operation to execute
|
|
888
|
+
* @param cost - Token cost for this operation (default: 1)
|
|
889
|
+
*/
|
|
890
|
+
executeResult<T, E>(operation: () => Result<T, E> | AsyncResult$1<T, E>, cost?: number): AsyncResult$1<T, E | RateLimitExceededError>;
|
|
891
|
+
/**
|
|
892
|
+
* Get current statistics.
|
|
893
|
+
*/
|
|
894
|
+
getStats(): CostBasedRateLimiterStats;
|
|
895
|
+
/**
|
|
896
|
+
* Reset the rate limiter.
|
|
897
|
+
*/
|
|
898
|
+
reset(): void;
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Create a cost-based token bucket rate limiter.
|
|
902
|
+
*
|
|
903
|
+
* Different operations can have different costs, allowing fine-grained
|
|
904
|
+
* control over resource usage. For example, a batch API call might cost
|
|
905
|
+
* 10 tokens while a simple query costs 1.
|
|
906
|
+
*
|
|
907
|
+
* @param name - Name for the limiter (used in errors)
|
|
908
|
+
* @param config - Rate limiter configuration
|
|
909
|
+
* @returns A CostBasedRateLimiter instance
|
|
910
|
+
*
|
|
911
|
+
* @example
|
|
912
|
+
* ```typescript
|
|
913
|
+
* const limiter = createCostBasedRateLimiter('api', {
|
|
914
|
+
* tokensPerSecond: 100, // 100 tokens/second refill
|
|
915
|
+
* maxTokens: 200, // Can burst up to 200 tokens
|
|
916
|
+
* });
|
|
917
|
+
*
|
|
918
|
+
* // Simple query costs 1 token
|
|
919
|
+
* await limiter.execute(() => simpleQuery());
|
|
920
|
+
*
|
|
921
|
+
* // Batch operation costs 10 tokens
|
|
922
|
+
* await limiter.execute(() => batchOperation(), 10);
|
|
923
|
+
*
|
|
924
|
+
* // Heavy export costs 50 tokens
|
|
925
|
+
* await limiter.execute(() => exportData(), 50);
|
|
926
|
+
* ```
|
|
927
|
+
*/
|
|
928
|
+
declare function createCostBasedRateLimiter(name: string, config: CostBasedRateLimiterConfig): CostBasedRateLimiter;
|
|
929
|
+
/**
|
|
930
|
+
* Preset configurations for common use cases.
|
|
931
|
+
*/
|
|
932
|
+
declare const rateLimiterPresets: {
|
|
933
|
+
/**
|
|
934
|
+
* Typical API rate limit (10 req/s).
|
|
935
|
+
*/
|
|
936
|
+
readonly api: {
|
|
937
|
+
maxPerSecond: number;
|
|
938
|
+
burstCapacity: number;
|
|
939
|
+
strategy: "wait";
|
|
940
|
+
};
|
|
941
|
+
/**
|
|
942
|
+
* Database pool limit (concurrent connections).
|
|
943
|
+
*/
|
|
944
|
+
readonly database: {
|
|
945
|
+
maxConcurrent: number;
|
|
946
|
+
strategy: "queue";
|
|
947
|
+
maxQueueSize: number;
|
|
948
|
+
};
|
|
949
|
+
/**
|
|
950
|
+
* Aggressive rate limit for external APIs (5 req/s).
|
|
951
|
+
*/
|
|
952
|
+
readonly external: {
|
|
953
|
+
maxPerSecond: number;
|
|
954
|
+
burstCapacity: number;
|
|
955
|
+
strategy: "wait";
|
|
956
|
+
};
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* awaitly/cache
|
|
961
|
+
*
|
|
962
|
+
* Caching utilities for memoization and deduplication.
|
|
963
|
+
* Inspired by Effect.js caching patterns.
|
|
964
|
+
*
|
|
965
|
+
* @example
|
|
966
|
+
* ```typescript
|
|
967
|
+
* import { cached, cachedWithTTL, cachedFunction, once } from 'awaitly';
|
|
968
|
+
*
|
|
969
|
+
* // Compute once, reuse forever
|
|
970
|
+
* const getConfig = cached(() => loadConfig());
|
|
971
|
+
*
|
|
972
|
+
* // Expire after duration
|
|
973
|
+
* const getUser = cachedWithTTL(() => fetchUser(id), { ttl: '5m' });
|
|
974
|
+
*
|
|
975
|
+
* // Memoize by arguments
|
|
976
|
+
* const fetchUserMemo = cachedFunction((id: string) => fetchUser(id));
|
|
977
|
+
*
|
|
978
|
+
* // Execute exactly once (for initialization)
|
|
979
|
+
* const initDb = once(() => connectToDatabase());
|
|
980
|
+
* ```
|
|
981
|
+
*/
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Duration input type - supports Duration objects or string shorthand.
|
|
985
|
+
*/
|
|
986
|
+
type DurationInput = Duration | string;
|
|
987
|
+
/**
|
|
988
|
+
* Cache entry with metadata.
|
|
989
|
+
*/
|
|
990
|
+
interface CacheEntry<T> {
|
|
991
|
+
value: T;
|
|
992
|
+
timestamp: number;
|
|
993
|
+
expiresAt?: number;
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Cache options.
|
|
997
|
+
*/
|
|
998
|
+
interface CacheOptions {
|
|
999
|
+
/**
|
|
1000
|
+
* Time-to-live for cached values.
|
|
1001
|
+
* Accepts Duration or string shorthand like "5m", "1h", "30s".
|
|
1002
|
+
*/
|
|
1003
|
+
ttl?: DurationInput;
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Cached function options.
|
|
1007
|
+
*/
|
|
1008
|
+
interface CachedFunctionOptions<Args extends unknown[]> {
|
|
1009
|
+
/**
|
|
1010
|
+
* Custom key generator for arguments.
|
|
1011
|
+
* Default: JSON.stringify(args)
|
|
1012
|
+
*/
|
|
1013
|
+
keyFn?: (...args: Args) => string;
|
|
1014
|
+
/**
|
|
1015
|
+
* Time-to-live for cached values.
|
|
1016
|
+
*/
|
|
1017
|
+
ttl?: DurationInput;
|
|
1018
|
+
/**
|
|
1019
|
+
* Maximum cache size. When exceeded, oldest entries are evicted.
|
|
1020
|
+
* @default Infinity
|
|
1021
|
+
*/
|
|
1022
|
+
maxSize?: number;
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Cache statistics.
|
|
1026
|
+
*/
|
|
1027
|
+
interface CacheStats {
|
|
1028
|
+
hits: number;
|
|
1029
|
+
misses: number;
|
|
1030
|
+
size: number;
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Create a cached computation that executes once and reuses the result.
|
|
1034
|
+
*
|
|
1035
|
+
* The function is called at most once, even with concurrent calls.
|
|
1036
|
+
* Subsequent calls return the cached value immediately.
|
|
1037
|
+
*
|
|
1038
|
+
* @param fn - Function to compute the cached value
|
|
1039
|
+
* @returns Function that returns the cached value
|
|
1040
|
+
*
|
|
1041
|
+
* @example
|
|
1042
|
+
* ```typescript
|
|
1043
|
+
* const getConfig = cached(async () => {
|
|
1044
|
+
* console.log('Loading config...');
|
|
1045
|
+
* return await loadConfigFromFile();
|
|
1046
|
+
* });
|
|
1047
|
+
*
|
|
1048
|
+
* // First call executes the function
|
|
1049
|
+
* const config1 = await getConfig(); // "Loading config..."
|
|
1050
|
+
*
|
|
1051
|
+
* // Subsequent calls return cached value
|
|
1052
|
+
* const config2 = await getConfig(); // No log, instant return
|
|
1053
|
+
* const config3 = await getConfig(); // No log, instant return
|
|
1054
|
+
* ```
|
|
1055
|
+
*/
|
|
1056
|
+
declare function cached<T>(fn: () => T | Promise<T>): () => Promise<T>;
|
|
1057
|
+
/**
|
|
1058
|
+
* Create a cached computation that expires after a duration.
|
|
1059
|
+
*
|
|
1060
|
+
* The function is re-executed when the TTL expires.
|
|
1061
|
+
* Concurrent calls while computing share the same promise.
|
|
1062
|
+
*
|
|
1063
|
+
* @param fn - Function to compute the cached value
|
|
1064
|
+
* @param options - Cache options including TTL
|
|
1065
|
+
* @returns Function that returns the cached value
|
|
1066
|
+
*
|
|
1067
|
+
* @example
|
|
1068
|
+
* ```typescript
|
|
1069
|
+
* const getUser = cachedWithTTL(
|
|
1070
|
+
* async () => await fetchUser(userId),
|
|
1071
|
+
* { ttl: '5m' } // Cache for 5 minutes
|
|
1072
|
+
* );
|
|
1073
|
+
*
|
|
1074
|
+
* const user1 = await getUser(); // Fetches from API
|
|
1075
|
+
* const user2 = await getUser(); // Returns cached (within 5 min)
|
|
1076
|
+
*
|
|
1077
|
+
* // After 5 minutes...
|
|
1078
|
+
* const user3 = await getUser(); // Fetches again
|
|
1079
|
+
* ```
|
|
1080
|
+
*/
|
|
1081
|
+
declare function cachedWithTTL<T>(fn: () => T | Promise<T>, options: {
|
|
1082
|
+
ttl: DurationInput;
|
|
1083
|
+
}): () => Promise<T>;
|
|
1084
|
+
/**
|
|
1085
|
+
* Memoized function interface.
|
|
1086
|
+
*/
|
|
1087
|
+
interface MemoizedFunction<Args extends unknown[], T> {
|
|
1088
|
+
(...args: Args): Promise<T>;
|
|
1089
|
+
/** Clear the entire cache */
|
|
1090
|
+
clear(): void;
|
|
1091
|
+
/** Clear a specific cache entry */
|
|
1092
|
+
delete(...args: Args): boolean;
|
|
1093
|
+
/** Check if an entry exists */
|
|
1094
|
+
has(...args: Args): boolean;
|
|
1095
|
+
/** Get cache statistics */
|
|
1096
|
+
getStats(): CacheStats;
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Create a memoized function that caches results by arguments.
|
|
1100
|
+
*
|
|
1101
|
+
* Each unique set of arguments produces a cached result.
|
|
1102
|
+
* Supports TTL and max size limits.
|
|
1103
|
+
*
|
|
1104
|
+
* @param fn - Function to memoize
|
|
1105
|
+
* @param options - Memoization options
|
|
1106
|
+
* @returns Memoized function with cache control methods
|
|
1107
|
+
*
|
|
1108
|
+
* @example
|
|
1109
|
+
* ```typescript
|
|
1110
|
+
* const fetchUserMemo = cachedFunction(
|
|
1111
|
+
* async (id: string) => await fetchUser(id),
|
|
1112
|
+
* { ttl: '5m', maxSize: 100 }
|
|
1113
|
+
* );
|
|
1114
|
+
*
|
|
1115
|
+
* const user1 = await fetchUserMemo('user-1'); // Fetches
|
|
1116
|
+
* const user2 = await fetchUserMemo('user-2'); // Fetches
|
|
1117
|
+
* const user1Again = await fetchUserMemo('user-1'); // Cached!
|
|
1118
|
+
*
|
|
1119
|
+
* // Cache control
|
|
1120
|
+
* fetchUserMemo.delete('user-1'); // Remove specific entry
|
|
1121
|
+
* fetchUserMemo.clear(); // Clear all
|
|
1122
|
+
* console.log(fetchUserMemo.getStats()); // { hits: 1, misses: 2, size: 0 }
|
|
1123
|
+
* ```
|
|
1124
|
+
*/
|
|
1125
|
+
declare function cachedFunction<Args extends unknown[], T>(fn: (...args: Args) => T | Promise<T>, options?: CachedFunctionOptions<Args>): MemoizedFunction<Args, T>;
|
|
1126
|
+
/**
|
|
1127
|
+
* Once-executed function interface.
|
|
1128
|
+
*/
|
|
1129
|
+
interface OnceFunction<T> {
|
|
1130
|
+
(): Promise<T>;
|
|
1131
|
+
/** Check if the function has been called */
|
|
1132
|
+
called: boolean;
|
|
1133
|
+
/** Check if execution completed successfully */
|
|
1134
|
+
completed: boolean;
|
|
1135
|
+
/** Check if execution failed */
|
|
1136
|
+
failed: boolean;
|
|
1137
|
+
/** Reset to allow re-execution */
|
|
1138
|
+
reset(): void;
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Create a function that executes exactly once.
|
|
1142
|
+
*
|
|
1143
|
+
* Useful for initialization code that should only run once.
|
|
1144
|
+
* Subsequent calls return the same result or re-throw the same error.
|
|
1145
|
+
*
|
|
1146
|
+
* @param fn - Function to execute once
|
|
1147
|
+
* @returns Function that executes once and returns the result
|
|
1148
|
+
*
|
|
1149
|
+
* @example
|
|
1150
|
+
* ```typescript
|
|
1151
|
+
* const initDb = once(async () => {
|
|
1152
|
+
* console.log('Connecting to database...');
|
|
1153
|
+
* const conn = await createConnection();
|
|
1154
|
+
* return conn;
|
|
1155
|
+
* });
|
|
1156
|
+
*
|
|
1157
|
+
* // First call executes
|
|
1158
|
+
* const db1 = await initDb(); // "Connecting to database..."
|
|
1159
|
+
*
|
|
1160
|
+
* // Subsequent calls return cached result
|
|
1161
|
+
* const db2 = await initDb(); // Instant, same connection
|
|
1162
|
+
* const db3 = await initDb(); // Instant, same connection
|
|
1163
|
+
*
|
|
1164
|
+
* console.log(initDb.called); // true
|
|
1165
|
+
* console.log(initDb.completed); // true
|
|
1166
|
+
* ```
|
|
1167
|
+
*/
|
|
1168
|
+
declare function once<T>(fn: () => T | Promise<T>): OnceFunction<T>;
|
|
1169
|
+
/**
|
|
1170
|
+
* General purpose cache interface.
|
|
1171
|
+
*/
|
|
1172
|
+
interface Cache<K, V> {
|
|
1173
|
+
/** Get a value from the cache */
|
|
1174
|
+
get(key: K): V | undefined;
|
|
1175
|
+
/** Set a value in the cache */
|
|
1176
|
+
set(key: K, value: V, options?: {
|
|
1177
|
+
ttl?: DurationInput;
|
|
1178
|
+
}): void;
|
|
1179
|
+
/** Check if a key exists */
|
|
1180
|
+
has(key: K): boolean;
|
|
1181
|
+
/** Delete a key from the cache */
|
|
1182
|
+
delete(key: K): boolean;
|
|
1183
|
+
/** Clear the entire cache */
|
|
1184
|
+
clear(): void;
|
|
1185
|
+
/** Get the cache size */
|
|
1186
|
+
size: number;
|
|
1187
|
+
/** Get cache statistics */
|
|
1188
|
+
getStats(): CacheStats;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Cache configuration.
|
|
1192
|
+
*/
|
|
1193
|
+
interface CacheConfig {
|
|
1194
|
+
/**
|
|
1195
|
+
* Default TTL for all entries.
|
|
1196
|
+
*/
|
|
1197
|
+
defaultTTL?: DurationInput;
|
|
1198
|
+
/**
|
|
1199
|
+
* Maximum cache size.
|
|
1200
|
+
* @default Infinity
|
|
1201
|
+
*/
|
|
1202
|
+
maxSize?: number;
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* Create a general-purpose cache with TTL and size limits.
|
|
1206
|
+
*
|
|
1207
|
+
* @param config - Cache configuration
|
|
1208
|
+
* @returns A Cache instance
|
|
1209
|
+
*
|
|
1210
|
+
* @example
|
|
1211
|
+
* ```typescript
|
|
1212
|
+
* const cache = createCache<string, User>({
|
|
1213
|
+
* defaultTTL: '5m',
|
|
1214
|
+
* maxSize: 1000,
|
|
1215
|
+
* });
|
|
1216
|
+
*
|
|
1217
|
+
* cache.set('user:1', user);
|
|
1218
|
+
* cache.set('user:2', user2, { ttl: '1h' }); // Override TTL
|
|
1219
|
+
*
|
|
1220
|
+
* const user = cache.get('user:1');
|
|
1221
|
+
* ```
|
|
1222
|
+
*/
|
|
1223
|
+
declare function createCache<K, V>(config?: CacheConfig): Cache<K, V>;
|
|
1224
|
+
|
|
1225
|
+
/**
|
|
1226
|
+
* awaitly/singleflight
|
|
1227
|
+
*
|
|
1228
|
+
* Request coalescing - dedupe concurrent identical requests.
|
|
1229
|
+
* Multiple concurrent calls with the same key share one in-flight request.
|
|
1230
|
+
*
|
|
1231
|
+
* @example
|
|
1232
|
+
* ```typescript
|
|
1233
|
+
* import { singleflight } from 'awaitly';
|
|
1234
|
+
*
|
|
1235
|
+
* const fetchUserOnce = singleflight(
|
|
1236
|
+
* (id: string) => fetchUser(id),
|
|
1237
|
+
* { key: (id) => `user:${id}` }
|
|
1238
|
+
* );
|
|
1239
|
+
*
|
|
1240
|
+
* // All concurrent calls share one request
|
|
1241
|
+
* const [user1, user2] = await Promise.all([
|
|
1242
|
+
* fetchUserOnce('1'),
|
|
1243
|
+
* fetchUserOnce('1'), // Same key - shares request
|
|
1244
|
+
* ]);
|
|
1245
|
+
* ```
|
|
1246
|
+
*/
|
|
1247
|
+
|
|
1248
|
+
/**
|
|
1249
|
+
* Options for the singleflight wrapper.
|
|
1250
|
+
*/
|
|
1251
|
+
type SingleflightOptions<Args extends unknown[]> = {
|
|
1252
|
+
/**
|
|
1253
|
+
* Extract cache key from arguments.
|
|
1254
|
+
* Calls with the same key will share one in-flight request.
|
|
1255
|
+
*/
|
|
1256
|
+
key: (...args: Args) => string;
|
|
1257
|
+
/**
|
|
1258
|
+
* Optional TTL in milliseconds to cache successful results.
|
|
1259
|
+
* After TTL expires, next call will trigger a fresh request.
|
|
1260
|
+
* @default 0 (no caching after completion - only dedupes in-flight requests)
|
|
1261
|
+
*/
|
|
1262
|
+
ttl?: number;
|
|
1263
|
+
};
|
|
1264
|
+
/**
|
|
1265
|
+
* Create a singleflight-wrapped function.
|
|
1266
|
+
* Concurrent calls with the same key share one in-flight request.
|
|
1267
|
+
*
|
|
1268
|
+
* ## How It Works
|
|
1269
|
+
*
|
|
1270
|
+
* 1. First caller with a key starts the operation
|
|
1271
|
+
* 2. Subsequent callers with the same key get the same Promise
|
|
1272
|
+
* 3. When operation completes, all callers receive the same Result
|
|
1273
|
+
* 4. Key is removed from in-flight tracking (unless TTL is set)
|
|
1274
|
+
*
|
|
1275
|
+
* ## Use Cases
|
|
1276
|
+
*
|
|
1277
|
+
* - **Prevent thundering herd**: Multiple requests for the same user
|
|
1278
|
+
* - **API deduplication**: Avoid duplicate network calls
|
|
1279
|
+
* - **Expensive operations**: Share computation across callers
|
|
1280
|
+
*
|
|
1281
|
+
* @param operation - The async operation that returns an AsyncResult
|
|
1282
|
+
* @param options - Configuration with key extraction function
|
|
1283
|
+
* @returns A wrapped function that deduplicates concurrent calls
|
|
1284
|
+
*
|
|
1285
|
+
* @example
|
|
1286
|
+
* ```typescript
|
|
1287
|
+
* import { singleflight } from 'awaitly';
|
|
1288
|
+
* import { ok, err, type AsyncResult } from 'awaitly';
|
|
1289
|
+
*
|
|
1290
|
+
* const fetchUser = async (id: string): AsyncResult<User, 'NOT_FOUND'> =>
|
|
1291
|
+
* id !== '0' ? ok({ id, name: `User ${id}` }) : err('NOT_FOUND');
|
|
1292
|
+
*
|
|
1293
|
+
* const fetchUserOnce = singleflight(fetchUser, {
|
|
1294
|
+
* key: (id) => `user:${id}`,
|
|
1295
|
+
* });
|
|
1296
|
+
*
|
|
1297
|
+
* // Concurrent calls share one request
|
|
1298
|
+
* const [a, b, c] = await Promise.all([
|
|
1299
|
+
* fetchUserOnce('1'), // Triggers fetch
|
|
1300
|
+
* fetchUserOnce('1'), // Joins existing fetch
|
|
1301
|
+
* fetchUserOnce('2'), // Different key - new fetch
|
|
1302
|
+
* ]);
|
|
1303
|
+
* ```
|
|
1304
|
+
*
|
|
1305
|
+
* @example
|
|
1306
|
+
* ```typescript
|
|
1307
|
+
* // With TTL for result caching
|
|
1308
|
+
* const fetchUserCached = singleflight(fetchUser, {
|
|
1309
|
+
* key: (id) => `user:${id}`,
|
|
1310
|
+
* ttl: 5000, // Cache successful results for 5 seconds
|
|
1311
|
+
* });
|
|
1312
|
+
*
|
|
1313
|
+
* const user1 = await fetchUserCached('1'); // Fetches
|
|
1314
|
+
* const user2 = await fetchUserCached('1'); // Returns cached (within TTL)
|
|
1315
|
+
* // After 5 seconds...
|
|
1316
|
+
* const user3 = await fetchUserCached('1'); // Fetches again
|
|
1317
|
+
* ```
|
|
1318
|
+
*/
|
|
1319
|
+
declare function singleflight<Args extends unknown[], T, E, C = unknown>(operation: (...args: Args) => AsyncResult$1<T, E, C>, options: SingleflightOptions<Args>): (...args: Args) => AsyncResult$1<T, E, C>;
|
|
1320
|
+
/**
|
|
1321
|
+
* Create a singleflight group with manual key management.
|
|
1322
|
+
* More flexible but lower-level API than the `singleflight` wrapper.
|
|
1323
|
+
*
|
|
1324
|
+
* @returns A group object with execute, isInflight, and clear methods
|
|
1325
|
+
*
|
|
1326
|
+
* @example
|
|
1327
|
+
* ```typescript
|
|
1328
|
+
* import { createSingleflightGroup } from 'awaitly';
|
|
1329
|
+
*
|
|
1330
|
+
* const group = createSingleflightGroup<User, 'NOT_FOUND'>();
|
|
1331
|
+
*
|
|
1332
|
+
* // Execute with manual key
|
|
1333
|
+
* const user1 = await group.execute('user:1', () => fetchUser('1'));
|
|
1334
|
+
* const user2 = await group.execute('user:1', () => fetchUser('1')); // Shares request
|
|
1335
|
+
*
|
|
1336
|
+
* // Check if request is in-flight
|
|
1337
|
+
* if (group.isInflight('user:1')) {
|
|
1338
|
+
* console.log('Request pending');
|
|
1339
|
+
* }
|
|
1340
|
+
*
|
|
1341
|
+
* // Clear all in-flight requests
|
|
1342
|
+
* group.clear();
|
|
1343
|
+
* ```
|
|
1344
|
+
*/
|
|
1345
|
+
declare function createSingleflightGroup<T, E, C = unknown>(): {
|
|
1346
|
+
/**
|
|
1347
|
+
* Execute or join an in-flight request for the given key.
|
|
1348
|
+
*/
|
|
1349
|
+
execute: (key: string, operation: () => AsyncResult$1<T, E, C>) => AsyncResult$1<T, E, C>;
|
|
1350
|
+
/**
|
|
1351
|
+
* Check if a request is currently in-flight for the key.
|
|
1352
|
+
*/
|
|
1353
|
+
isInflight: (key: string) => boolean;
|
|
1354
|
+
/**
|
|
1355
|
+
* Get the number of in-flight requests.
|
|
1356
|
+
*/
|
|
1357
|
+
size: () => number;
|
|
1358
|
+
/**
|
|
1359
|
+
* Clear all in-flight tracking (does not cancel operations).
|
|
1360
|
+
*/
|
|
1361
|
+
clear: () => void;
|
|
1362
|
+
};
|
|
1363
|
+
|
|
1364
|
+
/**
|
|
1365
|
+
* awaitly/policies
|
|
1366
|
+
*
|
|
1367
|
+
* Policy-Driven Step Middleware - Reusable bundles of StepOptions
|
|
1368
|
+
* that can be composed and applied per-workflow or per-step.
|
|
1369
|
+
*/
|
|
1370
|
+
|
|
1371
|
+
/**
|
|
1372
|
+
* A policy is a partial StepOptions that can be merged with other policies.
|
|
1373
|
+
*/
|
|
1374
|
+
type Policy = Partial<StepOptions>;
|
|
1375
|
+
/**
|
|
1376
|
+
* A policy factory that creates policies based on context.
|
|
1377
|
+
*/
|
|
1378
|
+
type PolicyFactory<T = void> = T extends void ? () => Policy : (context: T) => Policy;
|
|
1379
|
+
/**
|
|
1380
|
+
* Named policy with metadata.
|
|
1381
|
+
*/
|
|
1382
|
+
interface NamedPolicy {
|
|
1383
|
+
name: string;
|
|
1384
|
+
policy: Policy;
|
|
1385
|
+
description?: string;
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Merge multiple policies into a single StepOptions object.
|
|
1389
|
+
* Later policies override earlier ones for conflicting properties.
|
|
1390
|
+
* Retry and timeout options are deep-merged.
|
|
1391
|
+
*
|
|
1392
|
+
* @param policies - Policies to merge (in order of precedence)
|
|
1393
|
+
* @returns Merged StepOptions
|
|
1394
|
+
*
|
|
1395
|
+
* @example
|
|
1396
|
+
* ```typescript
|
|
1397
|
+
* const merged = mergePolicies(
|
|
1398
|
+
* timeoutPolicies.api, // timeout: 5000ms
|
|
1399
|
+
* retryPolicies.transient, // retry: 3 attempts
|
|
1400
|
+
* { name: 'fetch-user' } // name override
|
|
1401
|
+
* );
|
|
1402
|
+
* ```
|
|
1403
|
+
*/
|
|
1404
|
+
declare function mergePolicies(...policies: Policy[]): StepOptions;
|
|
1405
|
+
/**
|
|
1406
|
+
* Create a policy applier that merges base policies with step-specific options.
|
|
1407
|
+
*
|
|
1408
|
+
* @param basePolicies - Base policies to apply to all steps
|
|
1409
|
+
* @returns A function that applies policies to step options
|
|
1410
|
+
*
|
|
1411
|
+
* @example
|
|
1412
|
+
* ```typescript
|
|
1413
|
+
* const applyPolicy = createPolicyApplier(
|
|
1414
|
+
* timeoutPolicies.api,
|
|
1415
|
+
* retryPolicies.transient
|
|
1416
|
+
* );
|
|
1417
|
+
*
|
|
1418
|
+
* // In workflow
|
|
1419
|
+
* const user = await step(
|
|
1420
|
+
* 'fetch-user',
|
|
1421
|
+
* () => fetchUser(id),
|
|
1422
|
+
* applyPolicy({ key: 'user:' + id })
|
|
1423
|
+
* );
|
|
1424
|
+
* ```
|
|
1425
|
+
*/
|
|
1426
|
+
declare function createPolicyApplier(...basePolicies: Policy[]): (stepOptions?: StepOptions) => StepOptions;
|
|
1427
|
+
/**
|
|
1428
|
+
* Create a named policy bundle for reuse across workflows.
|
|
1429
|
+
*
|
|
1430
|
+
* @param name - Policy bundle name
|
|
1431
|
+
* @param policies - Policies to include in the bundle
|
|
1432
|
+
* @returns Named policy object
|
|
1433
|
+
*/
|
|
1434
|
+
declare function createPolicyBundle(name: string, ...policies: Policy[]): NamedPolicy;
|
|
1435
|
+
/**
|
|
1436
|
+
* Create a retry policy with the given options.
|
|
1437
|
+
*/
|
|
1438
|
+
declare function retryPolicy(options: RetryOptions): Policy;
|
|
1439
|
+
/**
|
|
1440
|
+
* Pre-built retry policies for common scenarios.
|
|
1441
|
+
*/
|
|
1442
|
+
declare const retryPolicies: {
|
|
1443
|
+
/**
|
|
1444
|
+
* No retry - fail immediately on error.
|
|
1445
|
+
*/
|
|
1446
|
+
readonly none: Partial<StepOptions>;
|
|
1447
|
+
/**
|
|
1448
|
+
* Quick retry for transient errors (3 attempts, fast backoff).
|
|
1449
|
+
*/
|
|
1450
|
+
readonly transient: Partial<StepOptions>;
|
|
1451
|
+
/**
|
|
1452
|
+
* Standard retry for API calls (3 attempts, moderate backoff).
|
|
1453
|
+
*/
|
|
1454
|
+
readonly standard: Partial<StepOptions>;
|
|
1455
|
+
/**
|
|
1456
|
+
* Aggressive retry for critical operations (5 attempts, longer backoff).
|
|
1457
|
+
*/
|
|
1458
|
+
readonly aggressive: Partial<StepOptions>;
|
|
1459
|
+
/**
|
|
1460
|
+
* Fixed interval retry (useful for polling).
|
|
1461
|
+
*/
|
|
1462
|
+
readonly fixed: (attempts: number, delayMs: number) => Policy;
|
|
1463
|
+
/**
|
|
1464
|
+
* Linear backoff retry.
|
|
1465
|
+
*/
|
|
1466
|
+
readonly linear: (attempts: number, initialDelay: number) => Policy;
|
|
1467
|
+
/**
|
|
1468
|
+
* Custom retry policy builder.
|
|
1469
|
+
*/
|
|
1470
|
+
readonly custom: (options: Partial<RetryOptions> & {
|
|
1471
|
+
attempts: number;
|
|
1472
|
+
}) => Policy;
|
|
1473
|
+
};
|
|
1474
|
+
/**
|
|
1475
|
+
* Create a timeout policy with the given options.
|
|
1476
|
+
*/
|
|
1477
|
+
declare function timeoutPolicy(options: TimeoutOptions): Policy;
|
|
1478
|
+
/**
|
|
1479
|
+
* Pre-built timeout policies for common scenarios.
|
|
1480
|
+
*/
|
|
1481
|
+
declare const timeoutPolicies: {
|
|
1482
|
+
/**
|
|
1483
|
+
* No timeout.
|
|
1484
|
+
*/
|
|
1485
|
+
readonly none: Policy;
|
|
1486
|
+
/**
|
|
1487
|
+
* Fast timeout for quick operations (1 second).
|
|
1488
|
+
*/
|
|
1489
|
+
readonly fast: Partial<StepOptions>;
|
|
1490
|
+
/**
|
|
1491
|
+
* Standard API timeout (5 seconds).
|
|
1492
|
+
*/
|
|
1493
|
+
readonly api: Partial<StepOptions>;
|
|
1494
|
+
/**
|
|
1495
|
+
* Extended timeout for slower operations (30 seconds).
|
|
1496
|
+
*/
|
|
1497
|
+
readonly extended: Partial<StepOptions>;
|
|
1498
|
+
/**
|
|
1499
|
+
* Long timeout for batch operations (2 minutes).
|
|
1500
|
+
*/
|
|
1501
|
+
readonly long: Partial<StepOptions>;
|
|
1502
|
+
/**
|
|
1503
|
+
* Custom timeout in milliseconds.
|
|
1504
|
+
*/
|
|
1505
|
+
readonly ms: (ms: number) => Policy;
|
|
1506
|
+
/**
|
|
1507
|
+
* Custom timeout in seconds.
|
|
1508
|
+
*/
|
|
1509
|
+
readonly seconds: (seconds: number) => Policy;
|
|
1510
|
+
/**
|
|
1511
|
+
* Timeout with custom error.
|
|
1512
|
+
*/
|
|
1513
|
+
readonly withError: <E>(ms: number, error: E) => Policy;
|
|
1514
|
+
/**
|
|
1515
|
+
* Timeout with AbortSignal support.
|
|
1516
|
+
*/
|
|
1517
|
+
readonly withSignal: (ms: number) => Policy;
|
|
1518
|
+
};
|
|
1519
|
+
/**
|
|
1520
|
+
* Pre-built combined policies for common service patterns.
|
|
1521
|
+
*/
|
|
1522
|
+
declare const servicePolicies: {
|
|
1523
|
+
/**
|
|
1524
|
+
* Policy for external HTTP APIs.
|
|
1525
|
+
* - 5 second timeout
|
|
1526
|
+
* - 3 retries with exponential backoff
|
|
1527
|
+
*/
|
|
1528
|
+
readonly httpApi: StepOptions;
|
|
1529
|
+
/**
|
|
1530
|
+
* Policy for database operations.
|
|
1531
|
+
* - 30 second timeout
|
|
1532
|
+
* - 2 retries for transient errors
|
|
1533
|
+
*/
|
|
1534
|
+
readonly database: StepOptions;
|
|
1535
|
+
/**
|
|
1536
|
+
* Policy for cache operations.
|
|
1537
|
+
* - 1 second timeout
|
|
1538
|
+
* - No retry (cache misses are not errors)
|
|
1539
|
+
*/
|
|
1540
|
+
readonly cache: StepOptions;
|
|
1541
|
+
/**
|
|
1542
|
+
* Policy for message queue operations.
|
|
1543
|
+
* - 30 second timeout
|
|
1544
|
+
* - 5 retries with longer backoff
|
|
1545
|
+
*/
|
|
1546
|
+
readonly messageQueue: StepOptions;
|
|
1547
|
+
/**
|
|
1548
|
+
* Policy for file operations.
|
|
1549
|
+
* - 2 minute timeout
|
|
1550
|
+
* - 3 retries
|
|
1551
|
+
*/
|
|
1552
|
+
readonly fileSystem: StepOptions;
|
|
1553
|
+
/**
|
|
1554
|
+
* Policy for third-party services with rate limits.
|
|
1555
|
+
* - 10 second timeout
|
|
1556
|
+
* - 5 retries with linear backoff
|
|
1557
|
+
*/
|
|
1558
|
+
readonly rateLimited: StepOptions;
|
|
1559
|
+
};
|
|
1560
|
+
/**
|
|
1561
|
+
* Options for withPolicies workflow wrapper.
|
|
1562
|
+
*/
|
|
1563
|
+
interface WithPoliciesOptions {
|
|
1564
|
+
/**
|
|
1565
|
+
* Base policies applied to all steps.
|
|
1566
|
+
*/
|
|
1567
|
+
policies: Policy[];
|
|
1568
|
+
/**
|
|
1569
|
+
* Step-specific policy overrides by name or key pattern.
|
|
1570
|
+
*/
|
|
1571
|
+
overrides?: Record<string, Policy>;
|
|
1572
|
+
}
|
|
1573
|
+
/**
|
|
1574
|
+
* Create step options with policies applied.
|
|
1575
|
+
* This is a helper for applying policies inline.
|
|
1576
|
+
*
|
|
1577
|
+
* @param policies - Policies to apply
|
|
1578
|
+
* @param stepOptions - Step-specific options
|
|
1579
|
+
* @returns Merged StepOptions
|
|
1580
|
+
*
|
|
1581
|
+
* @example
|
|
1582
|
+
* ```typescript
|
|
1583
|
+
* const user = await step(
|
|
1584
|
+
* () => fetchUser(id),
|
|
1585
|
+
* withPolicy(servicePolicies.httpApi, { name: 'fetch-user' })
|
|
1586
|
+
* );
|
|
1587
|
+
* ```
|
|
1588
|
+
*/
|
|
1589
|
+
declare function withPolicy(policy: Policy, stepOptions?: StepOptions): StepOptions;
|
|
1590
|
+
/**
|
|
1591
|
+
* Create step options with multiple policies applied.
|
|
1592
|
+
*
|
|
1593
|
+
* @param policies - Policies to apply (in order)
|
|
1594
|
+
* @param stepOptions - Step-specific options
|
|
1595
|
+
* @returns Merged StepOptions
|
|
1596
|
+
*
|
|
1597
|
+
* @example
|
|
1598
|
+
* ```typescript
|
|
1599
|
+
* const user = await step(
|
|
1600
|
+
* 'fetch-user',
|
|
1601
|
+
* () => fetchUser(id),
|
|
1602
|
+
* withPolicies([timeoutPolicies.api, retryPolicies.standard])
|
|
1603
|
+
* );
|
|
1604
|
+
* ```
|
|
1605
|
+
*/
|
|
1606
|
+
declare function withPolicies(policies: Policy[], stepOptions?: StepOptions): StepOptions;
|
|
1607
|
+
/**
|
|
1608
|
+
* Create a policy that applies conditionally.
|
|
1609
|
+
*
|
|
1610
|
+
* @param condition - Condition to check
|
|
1611
|
+
* @param policy - Policy to apply if condition is true
|
|
1612
|
+
* @param elsePolicy - Policy to apply if condition is false (optional)
|
|
1613
|
+
* @returns The selected policy
|
|
1614
|
+
*
|
|
1615
|
+
* @example
|
|
1616
|
+
* ```typescript
|
|
1617
|
+
* const policy = conditionalPolicy(
|
|
1618
|
+
* isProduction,
|
|
1619
|
+
* servicePolicies.httpApi, // Use in production
|
|
1620
|
+
* retryPolicies.none // Skip in development
|
|
1621
|
+
* );
|
|
1622
|
+
* ```
|
|
1623
|
+
*/
|
|
1624
|
+
declare function conditionalPolicy(condition: boolean, policy: Policy, elsePolicy?: Policy): Policy;
|
|
1625
|
+
/**
|
|
1626
|
+
* Create a policy based on environment.
|
|
1627
|
+
*
|
|
1628
|
+
* @param envPolicies - Map of environment names to policies
|
|
1629
|
+
* @param currentEnv - Current environment (defaults to NODE_ENV)
|
|
1630
|
+
* @param defaultPolicy - Default policy if environment not found
|
|
1631
|
+
* @returns The selected policy
|
|
1632
|
+
*
|
|
1633
|
+
* @example
|
|
1634
|
+
* ```typescript
|
|
1635
|
+
* const policy = envPolicy({
|
|
1636
|
+
* production: servicePolicies.httpApi,
|
|
1637
|
+
* development: retryPolicies.none,
|
|
1638
|
+
* test: retryPolicies.none,
|
|
1639
|
+
* });
|
|
1640
|
+
* ```
|
|
1641
|
+
*/
|
|
1642
|
+
declare function envPolicy(envPolicies: Record<string, Policy>, currentEnv?: string, defaultPolicy?: Policy): Policy;
|
|
1643
|
+
/**
|
|
1644
|
+
* A registry for managing named policies.
|
|
1645
|
+
*/
|
|
1646
|
+
interface PolicyRegistry {
|
|
1647
|
+
/**
|
|
1648
|
+
* Register a named policy.
|
|
1649
|
+
*/
|
|
1650
|
+
register(name: string, policy: Policy): void;
|
|
1651
|
+
/**
|
|
1652
|
+
* Get a policy by name.
|
|
1653
|
+
*/
|
|
1654
|
+
get(name: string): Policy | undefined;
|
|
1655
|
+
/**
|
|
1656
|
+
* Check if a policy exists.
|
|
1657
|
+
*/
|
|
1658
|
+
has(name: string): boolean;
|
|
1659
|
+
/**
|
|
1660
|
+
* Get all registered policy names.
|
|
1661
|
+
*/
|
|
1662
|
+
names(): string[];
|
|
1663
|
+
/**
|
|
1664
|
+
* Create step options using a registered policy.
|
|
1665
|
+
*/
|
|
1666
|
+
apply(policyName: string, stepOptions?: StepOptions): StepOptions;
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Create a policy registry for managing named policies.
|
|
1670
|
+
*
|
|
1671
|
+
* @returns PolicyRegistry instance
|
|
1672
|
+
*
|
|
1673
|
+
* @example
|
|
1674
|
+
* ```typescript
|
|
1675
|
+
* const registry = createPolicyRegistry();
|
|
1676
|
+
*
|
|
1677
|
+
* // Register policies
|
|
1678
|
+
* registry.register('api', servicePolicies.httpApi);
|
|
1679
|
+
* registry.register('db', servicePolicies.database);
|
|
1680
|
+
*
|
|
1681
|
+
* // Use in workflow
|
|
1682
|
+
* const user = await step(
|
|
1683
|
+
* 'fetch-user',
|
|
1684
|
+
* () => fetchUser(id),
|
|
1685
|
+
* registry.apply('api')
|
|
1686
|
+
* );
|
|
1687
|
+
* ```
|
|
1688
|
+
*/
|
|
1689
|
+
declare function createPolicyRegistry(): PolicyRegistry;
|
|
1690
|
+
/**
|
|
1691
|
+
* Fluent builder for constructing step options.
|
|
1692
|
+
*/
|
|
1693
|
+
interface StepOptionsBuilder {
|
|
1694
|
+
/**
|
|
1695
|
+
* Set step key for caching.
|
|
1696
|
+
*/
|
|
1697
|
+
key(key: string): StepOptionsBuilder;
|
|
1698
|
+
/**
|
|
1699
|
+
* Apply a policy.
|
|
1700
|
+
*/
|
|
1701
|
+
policy(policy: Policy): StepOptionsBuilder;
|
|
1702
|
+
/**
|
|
1703
|
+
* Set timeout in milliseconds.
|
|
1704
|
+
*/
|
|
1705
|
+
timeout(ms: number): StepOptionsBuilder;
|
|
1706
|
+
/**
|
|
1707
|
+
* Set retry options.
|
|
1708
|
+
*/
|
|
1709
|
+
retry(options: RetryOptions): StepOptionsBuilder;
|
|
1710
|
+
/**
|
|
1711
|
+
* Set retry attempts (with default exponential backoff).
|
|
1712
|
+
*/
|
|
1713
|
+
retries(attempts: number): StepOptionsBuilder;
|
|
1714
|
+
/**
|
|
1715
|
+
* Build the final StepOptions.
|
|
1716
|
+
*/
|
|
1717
|
+
build(): StepOptions;
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Create a fluent builder for step options.
|
|
1721
|
+
*
|
|
1722
|
+
* @returns StepOptionsBuilder instance
|
|
1723
|
+
*
|
|
1724
|
+
* @example
|
|
1725
|
+
* ```typescript
|
|
1726
|
+
* const options = stepOptions()
|
|
1727
|
+
* .key('user:123')
|
|
1728
|
+
* .timeout(5000)
|
|
1729
|
+
* .retries(3)
|
|
1730
|
+
* .build();
|
|
1731
|
+
*
|
|
1732
|
+
* const user = await step('fetch-user', () => fetchUser(id), options);
|
|
1733
|
+
* ```
|
|
1734
|
+
*/
|
|
1735
|
+
declare function stepOptions(): StepOptionsBuilder;
|
|
1736
|
+
|
|
1737
|
+
/**
|
|
1738
|
+
* awaitly/conditional
|
|
1739
|
+
*
|
|
1740
|
+
* Conditional step execution helpers for workflows.
|
|
1741
|
+
* These helpers allow you to conditionally execute steps based on runtime conditions,
|
|
1742
|
+
* with proper event emission for skipped steps.
|
|
1743
|
+
*/
|
|
1744
|
+
|
|
1745
|
+
/**
|
|
1746
|
+
* Options for conditional execution.
|
|
1747
|
+
*/
|
|
1748
|
+
type ConditionalOptions = {
|
|
1749
|
+
/**
|
|
1750
|
+
* Human-readable name for the conditional step.
|
|
1751
|
+
* Used in step_skipped events for debugging and visualization.
|
|
1752
|
+
*/
|
|
1753
|
+
name?: string;
|
|
1754
|
+
/**
|
|
1755
|
+
* Stable identity key for the conditional step.
|
|
1756
|
+
* Used in step_skipped events for tracking and visualization.
|
|
1757
|
+
*/
|
|
1758
|
+
key?: string;
|
|
1759
|
+
/**
|
|
1760
|
+
* Optional reason explaining why the step was skipped.
|
|
1761
|
+
* Included in step_skipped events.
|
|
1762
|
+
*/
|
|
1763
|
+
reason?: string;
|
|
1764
|
+
};
|
|
1765
|
+
/**
|
|
1766
|
+
* Context for conditional execution, used to emit events.
|
|
1767
|
+
*/
|
|
1768
|
+
type ConditionalContext<C = unknown> = {
|
|
1769
|
+
/**
|
|
1770
|
+
* The workflow ID for event emission.
|
|
1771
|
+
*/
|
|
1772
|
+
workflowId: string;
|
|
1773
|
+
/**
|
|
1774
|
+
* Event emitter function.
|
|
1775
|
+
*/
|
|
1776
|
+
onEvent?: (event: WorkflowEvent<unknown, C>) => void;
|
|
1777
|
+
/**
|
|
1778
|
+
* Optional context value to include in emitted events.
|
|
1779
|
+
* When provided, this context is automatically added to step_skipped events.
|
|
1780
|
+
*/
|
|
1781
|
+
context?: C;
|
|
1782
|
+
};
|
|
1783
|
+
/**
|
|
1784
|
+
* Type for operations that can be either sync or async.
|
|
1785
|
+
*/
|
|
1786
|
+
type MaybeAsync<T> = T | Promise<T>;
|
|
1787
|
+
/**
|
|
1788
|
+
* Type for the operation function passed to conditional helpers.
|
|
1789
|
+
*/
|
|
1790
|
+
type Operation<T> = () => MaybeAsync<T>;
|
|
1791
|
+
/**
|
|
1792
|
+
* Run a step only if condition is true, return undefined if skipped.
|
|
1793
|
+
*
|
|
1794
|
+
* Use this when you want to conditionally execute a step and handle
|
|
1795
|
+
* the undefined case yourself. For a version with a default value,
|
|
1796
|
+
* use `whenOr`.
|
|
1797
|
+
*
|
|
1798
|
+
* @param condition - Boolean condition to evaluate
|
|
1799
|
+
* @param operation - Function that performs the step (only called if condition is true)
|
|
1800
|
+
* @param options - Optional configuration for the conditional step
|
|
1801
|
+
* @param ctx - Optional context for event emission
|
|
1802
|
+
* @returns The result of the operation if condition is true, undefined otherwise
|
|
1803
|
+
*
|
|
1804
|
+
* @example
|
|
1805
|
+
* ```typescript
|
|
1806
|
+
* const result = await workflow(async ({ step }) => {
|
|
1807
|
+
* const user = await step('fetchUser', () => fetchUser(id));
|
|
1808
|
+
*
|
|
1809
|
+
* // Only runs if user is premium
|
|
1810
|
+
* const premium = await when(
|
|
1811
|
+
* user.isPremium,
|
|
1812
|
+
* () => step('fetchPremiumData', () => fetchPremiumData(user.id)),
|
|
1813
|
+
* { name: 'check-premium', reason: 'User is not premium' }
|
|
1814
|
+
* );
|
|
1815
|
+
*
|
|
1816
|
+
* return { user, premium };
|
|
1817
|
+
* });
|
|
1818
|
+
* ```
|
|
1819
|
+
*/
|
|
1820
|
+
declare function when<T, C = unknown>(condition: boolean, operation: Operation<T>, options?: ConditionalOptions, ctx?: ConditionalContext<C>): Promise<T | undefined>;
|
|
1821
|
+
/**
|
|
1822
|
+
* Synchronous overload for when the operation returns a non-Promise value.
|
|
1823
|
+
*/
|
|
1824
|
+
declare function when<T, C = unknown>(condition: boolean, operation: () => T, options?: ConditionalOptions, ctx?: ConditionalContext<C>): T | undefined | Promise<T | undefined>;
|
|
1825
|
+
/**
|
|
1826
|
+
* Run a step only if condition is false, return undefined if skipped.
|
|
1827
|
+
*
|
|
1828
|
+
* Use this when you want to conditionally execute a step when a condition
|
|
1829
|
+
* is NOT met. For a version with a default value, use `unlessOr`.
|
|
1830
|
+
*
|
|
1831
|
+
* @param condition - Boolean condition to evaluate
|
|
1832
|
+
* @param operation - Function that performs the step (only called if condition is false)
|
|
1833
|
+
* @param options - Optional configuration for the conditional step
|
|
1834
|
+
* @param ctx - Optional context for event emission
|
|
1835
|
+
* @returns The result of the operation if condition is false, undefined otherwise
|
|
1836
|
+
*
|
|
1837
|
+
* @example
|
|
1838
|
+
* ```typescript
|
|
1839
|
+
* const result = await workflow(async ({ step }) => {
|
|
1840
|
+
* const user = await step(fetchUser(id));
|
|
1841
|
+
*
|
|
1842
|
+
* // Only runs if user is NOT verified
|
|
1843
|
+
* const verification = await unless(
|
|
1844
|
+
* user.isVerified,
|
|
1845
|
+
* () => step(() => sendVerificationEmail(user.email), { name: 'send-verification' }),
|
|
1846
|
+
* { name: 'check-verification', reason: 'User is already verified' }
|
|
1847
|
+
* );
|
|
1848
|
+
*
|
|
1849
|
+
* return { user, verification };
|
|
1850
|
+
* });
|
|
1851
|
+
* ```
|
|
1852
|
+
*/
|
|
1853
|
+
declare function unless<T, C = unknown>(condition: boolean, operation: Operation<T>, options?: ConditionalOptions, ctx?: ConditionalContext<C>): Promise<T | undefined>;
|
|
1854
|
+
/**
|
|
1855
|
+
* Synchronous overload for unless when the operation returns a non-Promise value.
|
|
1856
|
+
*/
|
|
1857
|
+
declare function unless<T, C = unknown>(condition: boolean, operation: () => T, options?: ConditionalOptions, ctx?: ConditionalContext<C>): T | undefined | Promise<T | undefined>;
|
|
1858
|
+
/**
|
|
1859
|
+
* Run a step only if condition is true, return default value if skipped.
|
|
1860
|
+
*
|
|
1861
|
+
* Use this when you want to conditionally execute a step and provide
|
|
1862
|
+
* a fallback value when the condition is not met.
|
|
1863
|
+
*
|
|
1864
|
+
* @param condition - Boolean condition to evaluate
|
|
1865
|
+
* @param operation - Function that performs the step (only called if condition is true)
|
|
1866
|
+
* @param defaultValue - Value to return if condition is false
|
|
1867
|
+
* @param options - Optional configuration for the conditional step
|
|
1868
|
+
* @param ctx - Optional context for event emission
|
|
1869
|
+
* @returns The result of the operation if condition is true, defaultValue otherwise
|
|
1870
|
+
*
|
|
1871
|
+
* @example
|
|
1872
|
+
* ```typescript
|
|
1873
|
+
* const result = await workflow(async ({ step }) => {
|
|
1874
|
+
* const user = await step(fetchUser(id));
|
|
1875
|
+
*
|
|
1876
|
+
* // Get premium limits or use default for non-premium users
|
|
1877
|
+
* const limits = await whenOr(
|
|
1878
|
+
* user.isPremium,
|
|
1879
|
+
* () => step(() => fetchPremiumLimits(user.id), { name: 'premium-limits' }),
|
|
1880
|
+
* { maxRequests: 100, maxStorage: 1000 }, // default for non-premium
|
|
1881
|
+
* { name: 'check-premium-limits', reason: 'Using default limits for non-premium user' }
|
|
1882
|
+
* );
|
|
1883
|
+
*
|
|
1884
|
+
* return { user, limits };
|
|
1885
|
+
* });
|
|
1886
|
+
* ```
|
|
1887
|
+
*/
|
|
1888
|
+
declare function whenOr<T, D, C = unknown>(condition: boolean, operation: Operation<T>, defaultValue: D, options?: ConditionalOptions, ctx?: ConditionalContext<C>): Promise<T | D>;
|
|
1889
|
+
/**
|
|
1890
|
+
* Synchronous overload for whenOr when the operation returns a non-Promise value.
|
|
1891
|
+
*/
|
|
1892
|
+
declare function whenOr<T, D, C = unknown>(condition: boolean, operation: () => T, defaultValue: D, options?: ConditionalOptions, ctx?: ConditionalContext<C>): T | D | Promise<T | D>;
|
|
1893
|
+
/**
|
|
1894
|
+
* Run a step only if condition is false, return default value if skipped.
|
|
1895
|
+
*
|
|
1896
|
+
* Use this when you want to conditionally execute a step when a condition
|
|
1897
|
+
* is NOT met, with a fallback value for when the condition is true.
|
|
1898
|
+
*
|
|
1899
|
+
* @param condition - Boolean condition to evaluate
|
|
1900
|
+
* @param operation - Function that performs the step (only called if condition is false)
|
|
1901
|
+
* @param defaultValue - Value to return if condition is true
|
|
1902
|
+
* @param options - Optional configuration for the conditional step
|
|
1903
|
+
* @param ctx - Optional context for event emission
|
|
1904
|
+
* @returns The result of the operation if condition is false, defaultValue otherwise
|
|
1905
|
+
*
|
|
1906
|
+
* @example
|
|
1907
|
+
* ```typescript
|
|
1908
|
+
* const result = await workflow(async ({ step }) => {
|
|
1909
|
+
* const user = await step(fetchUser(id));
|
|
1910
|
+
*
|
|
1911
|
+
* // Generate new token if user is NOT authenticated, otherwise use existing
|
|
1912
|
+
* const token = await unlessOr(
|
|
1913
|
+
* user.isAuthenticated,
|
|
1914
|
+
* () => step(() => generateNewToken(user.id), { name: 'generate-token' }),
|
|
1915
|
+
* user.existingToken, // use existing token if authenticated
|
|
1916
|
+
* { name: 'check-auth-for-token', reason: 'Using existing token for authenticated user' }
|
|
1917
|
+
* );
|
|
1918
|
+
*
|
|
1919
|
+
* return { user, token };
|
|
1920
|
+
* });
|
|
1921
|
+
* ```
|
|
1922
|
+
*/
|
|
1923
|
+
declare function unlessOr<T, D, C = unknown>(condition: boolean, operation: Operation<T>, defaultValue: D, options?: ConditionalOptions, ctx?: ConditionalContext<C>): Promise<T | D>;
|
|
1924
|
+
/**
|
|
1925
|
+
* Synchronous overload for unlessOr when the operation returns a non-Promise value.
|
|
1926
|
+
*/
|
|
1927
|
+
declare function unlessOr<T, D, C = unknown>(condition: boolean, operation: () => T, defaultValue: D, options?: ConditionalOptions, ctx?: ConditionalContext<C>): T | D | Promise<T | D>;
|
|
1928
|
+
/**
|
|
1929
|
+
* Create a set of conditional helpers bound to a workflow context.
|
|
1930
|
+
*
|
|
1931
|
+
* Use this factory when you want to automatically emit step_skipped events
|
|
1932
|
+
* to the workflow's event stream without passing context manually.
|
|
1933
|
+
*
|
|
1934
|
+
* @param ctx - The workflow context containing workflowId, onEvent, and optional context
|
|
1935
|
+
* @returns Object with bound when, unless, whenOr, and unlessOr functions
|
|
1936
|
+
*
|
|
1937
|
+
* @example
|
|
1938
|
+
* ```typescript
|
|
1939
|
+
* // With run() - context is automatically included in events
|
|
1940
|
+
* const result = await run(async ({ step }) => {
|
|
1941
|
+
* const ctx = { workflowId, onEvent, context: requestContext };
|
|
1942
|
+
* const { when, whenOr } = createConditionalHelpers(ctx);
|
|
1943
|
+
*
|
|
1944
|
+
* const user = await step(fetchUser(id));
|
|
1945
|
+
*
|
|
1946
|
+
* const premium = await when(
|
|
1947
|
+
* user.isPremium,
|
|
1948
|
+
* () => step(() => fetchPremiumData(user.id)),
|
|
1949
|
+
* { name: 'premium-data' }
|
|
1950
|
+
* );
|
|
1951
|
+
*
|
|
1952
|
+
* return { user, premium };
|
|
1953
|
+
* }, { onEvent, workflowId, context: requestContext });
|
|
1954
|
+
*
|
|
1955
|
+
* // With createWorkflow - access context from onEvent callback
|
|
1956
|
+
* const workflow = createWorkflow({ fetchUser }, {
|
|
1957
|
+
* createContext: () => ({ requestId: 'req-123' }),
|
|
1958
|
+
* onEvent: (event, ctx) => {
|
|
1959
|
+
* // ctx is available here, can be passed to conditional helpers
|
|
1960
|
+
* }
|
|
1961
|
+
* });
|
|
1962
|
+
* ```
|
|
1963
|
+
*/
|
|
1964
|
+
declare function createConditionalHelpers<C = unknown>(ctx: ConditionalContext<C>): {
|
|
1965
|
+
/**
|
|
1966
|
+
* Run a step only if condition is true, return undefined if skipped.
|
|
1967
|
+
*/
|
|
1968
|
+
when: <T>(condition: boolean, operation: Operation<T>, options?: ConditionalOptions) => MaybeAsync<T | undefined>;
|
|
1969
|
+
/**
|
|
1970
|
+
* Run a step only if condition is false, return undefined if skipped.
|
|
1971
|
+
*/
|
|
1972
|
+
unless: <T>(condition: boolean, operation: Operation<T>, options?: ConditionalOptions) => MaybeAsync<T | undefined>;
|
|
1973
|
+
/**
|
|
1974
|
+
* Run a step only if condition is true, return default value if skipped.
|
|
1975
|
+
*/
|
|
1976
|
+
whenOr: <T, D>(condition: boolean, operation: Operation<T>, defaultValue: D, options?: ConditionalOptions) => MaybeAsync<T | D>;
|
|
1977
|
+
/**
|
|
1978
|
+
* Run a step only if condition is false, return default value if skipped.
|
|
1979
|
+
*/
|
|
1980
|
+
unlessOr: <T, D>(condition: boolean, operation: Operation<T>, defaultValue: D, options?: ConditionalOptions) => MaybeAsync<T | D>;
|
|
152
1981
|
};
|
|
153
1982
|
|
|
154
|
-
export {
|
|
1983
|
+
export { AsyncResult, type Cache, type CacheConfig, type CacheEntry, type CacheOptions, type CacheStats, type CachedFunctionOptions, type CircuitBreaker, type CircuitBreakerConfig, type CircuitBreakerStats, CircuitOpenError, type CircuitState, type CombinedLimiterConfig, type ConcurrencyLimiter, type ConcurrencyLimiterConfig, type ConcurrencyLimiterStats, type ConditionalContext, type ConditionalOptions, type CostBasedRateLimiter, type CostBasedRateLimiterConfig, type CostBasedRateLimiterStats, Duration, type DurationInput, Duration as DurationType, ErrorOf, type FixedWindowLimiter, type FixedWindowLimiterConfig, type FixedWindowLimiterStats, Match, type Matcher, type MemoizedFunction, type NamedPolicy, type OnceFunction, type Policy, type PolicyDelay, type PolicyFactory, type PolicyFn, type PolicyRegistry, type QueueFullError, type RateLimitExceededError, type RateLimiter, type RateLimiterConfig, type RateLimiterStats, RetryOptions, type RetryPolicyOptions, type SingleflightOptions, StepOptions, type StepOptionsBuilder, type Tagged, TimeoutError, TimeoutOptions, UnexpectedError, type WithPoliciesOptions, WorkflowEvent, cached, cachedFunction, cachedWithTTL, circuitBreakerPresets, conditionalPolicy, createCache, createCircuitBreaker, createCombinedLimiter, createConcurrencyLimiter, createConditionalHelpers, createCostBasedRateLimiter, createFixedWindowLimiter, createPolicyApplier, createPolicyBundle, createPolicyRegistry, createRateLimiter, createSingleflightGroup, envPolicy, exhaustive, fallback, isCircuitOpenError, isOneOf, isQueueFullError, isRateLimitExceededError, is as isTag, orElse as matchOrElse, tag as matchTag, tags as matchTags, matchValue, mergePolicies, once, orElseValue, rateLimiterPresets, retry, retryPolicies, retryPolicy, servicePolicies, singleflight, stepOptions, timeout, timeoutPolicies, timeoutPolicy, unless, unlessOr, when, whenOr, withPolicies, withPolicy };
|