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.
Files changed (241) hide show
  1. package/dist/{duration.d.ts → di-BDlT7InM.d.cts} +15 -1
  2. package/dist/{duration.d.cts → di-BbFFfO8y.d.ts} +15 -1
  3. package/dist/errors-DtXvrCiO.d.cts +708 -0
  4. package/dist/errors-DtXvrCiO.d.ts +708 -0
  5. package/dist/index.cjs +4594 -1
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +1970 -141
  8. package/dist/index.d.ts +1970 -141
  9. package/dist/index.js +4398 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/result.cjs +641 -1
  12. package/dist/result.cjs.map +1 -1
  13. package/dist/result.d.cts +29 -4
  14. package/dist/result.d.ts +29 -4
  15. package/dist/result.js +561 -1
  16. package/dist/result.js.map +1 -1
  17. package/dist/testing.cjs +4202 -8
  18. package/dist/testing.cjs.map +1 -1
  19. package/dist/testing.d.cts +2 -6
  20. package/dist/testing.d.ts +2 -6
  21. package/dist/testing.js +4154 -8
  22. package/dist/testing.js.map +1 -1
  23. package/dist/{run-entry-DGs0tySr.d.cts → types-B8NfNRGX.d.ts} +1078 -1502
  24. package/dist/{run-entry-BOuNyVoO.d.ts → types-BZ2f4MRR.d.cts} +1078 -1502
  25. package/dist/workflow.cjs +7096 -6
  26. package/dist/workflow.cjs.map +1 -1
  27. package/dist/workflow.d.cts +3346 -22
  28. package/dist/workflow.d.ts +3346 -22
  29. package/dist/workflow.js +6929 -6
  30. package/dist/workflow.js.map +1 -1
  31. package/package.json +3 -168
  32. package/dist/adapters.cjs +0 -7
  33. package/dist/adapters.cjs.map +0 -1
  34. package/dist/adapters.d.cts +0 -179
  35. package/dist/adapters.d.ts +0 -179
  36. package/dist/adapters.js +0 -7
  37. package/dist/adapters.js.map +0 -1
  38. package/dist/batch.cjs +0 -7
  39. package/dist/batch.cjs.map +0 -1
  40. package/dist/batch.d.cts +0 -200
  41. package/dist/batch.d.ts +0 -200
  42. package/dist/batch.js +0 -7
  43. package/dist/batch.js.map +0 -1
  44. package/dist/bind-deps.cjs +0 -2
  45. package/dist/bind-deps.cjs.map +0 -1
  46. package/dist/bind-deps.d.cts +0 -28
  47. package/dist/bind-deps.d.ts +0 -28
  48. package/dist/bind-deps.js +0 -2
  49. package/dist/bind-deps.js.map +0 -1
  50. package/dist/cache.cjs +0 -2
  51. package/dist/cache.cjs.map +0 -1
  52. package/dist/cache.d.cts +0 -269
  53. package/dist/cache.d.ts +0 -269
  54. package/dist/cache.js +0 -2
  55. package/dist/cache.js.map +0 -1
  56. package/dist/circuit-breaker.cjs +0 -7
  57. package/dist/circuit-breaker.cjs.map +0 -1
  58. package/dist/circuit-breaker.d.cts +0 -211
  59. package/dist/circuit-breaker.d.ts +0 -211
  60. package/dist/circuit-breaker.js +0 -7
  61. package/dist/circuit-breaker.js.map +0 -1
  62. package/dist/conditional.cjs +0 -2
  63. package/dist/conditional.cjs.map +0 -1
  64. package/dist/conditional.d.cts +0 -252
  65. package/dist/conditional.d.ts +0 -252
  66. package/dist/conditional.js +0 -2
  67. package/dist/conditional.js.map +0 -1
  68. package/dist/core.cjs +0 -7
  69. package/dist/core.cjs.map +0 -1
  70. package/dist/core.d.cts +0 -5
  71. package/dist/core.d.ts +0 -5
  72. package/dist/core.js +0 -7
  73. package/dist/core.js.map +0 -1
  74. package/dist/di-By77n4Fa.d.ts +0 -15
  75. package/dist/di-OJfsohf-.d.cts +0 -15
  76. package/dist/diagnostics.cjs +0 -8
  77. package/dist/diagnostics.cjs.map +0 -1
  78. package/dist/diagnostics.d.cts +0 -68
  79. package/dist/diagnostics.d.ts +0 -68
  80. package/dist/diagnostics.js +0 -8
  81. package/dist/diagnostics.js.map +0 -1
  82. package/dist/durable.cjs +0 -11
  83. package/dist/durable.cjs.map +0 -1
  84. package/dist/durable.d.cts +0 -9
  85. package/dist/durable.d.ts +0 -9
  86. package/dist/durable.js +0 -11
  87. package/dist/durable.js.map +0 -1
  88. package/dist/duration.cjs +0 -2
  89. package/dist/duration.cjs.map +0 -1
  90. package/dist/duration.js +0 -2
  91. package/dist/duration.js.map +0 -1
  92. package/dist/engine.cjs +0 -11
  93. package/dist/engine.cjs.map +0 -1
  94. package/dist/engine.d.cts +0 -115
  95. package/dist/engine.d.ts +0 -115
  96. package/dist/engine.js +0 -11
  97. package/dist/engine.js.map +0 -1
  98. package/dist/errors.cjs +0 -2
  99. package/dist/errors.cjs.map +0 -1
  100. package/dist/errors.d.cts +0 -361
  101. package/dist/errors.d.ts +0 -361
  102. package/dist/errors.js +0 -2
  103. package/dist/errors.js.map +0 -1
  104. package/dist/fetch.cjs +0 -7
  105. package/dist/fetch.cjs.map +0 -1
  106. package/dist/fetch.d.cts +0 -86
  107. package/dist/fetch.d.ts +0 -86
  108. package/dist/fetch.js +0 -7
  109. package/dist/fetch.js.map +0 -1
  110. package/dist/flow.cjs +0 -7
  111. package/dist/flow.cjs.map +0 -1
  112. package/dist/flow.d.cts +0 -163
  113. package/dist/flow.d.ts +0 -163
  114. package/dist/flow.js +0 -7
  115. package/dist/flow.js.map +0 -1
  116. package/dist/functional.cjs +0 -2
  117. package/dist/functional.cjs.map +0 -1
  118. package/dist/functional.d.cts +0 -444
  119. package/dist/functional.d.ts +0 -444
  120. package/dist/functional.js +0 -2
  121. package/dist/functional.js.map +0 -1
  122. package/dist/guards-4sV7mTqj.d.cts +0 -72
  123. package/dist/guards-BIX05ALH.d.ts +0 -72
  124. package/dist/hitl-DFn4Xa_l.d.cts +0 -468
  125. package/dist/hitl-DU5VpKq7.d.ts +0 -468
  126. package/dist/hitl.cjs +0 -7
  127. package/dist/hitl.cjs.map +0 -1
  128. package/dist/hitl.d.cts +0 -442
  129. package/dist/hitl.d.ts +0 -442
  130. package/dist/hitl.js +0 -7
  131. package/dist/hitl.js.map +0 -1
  132. package/dist/index-CnvBryQB.d.ts +0 -417
  133. package/dist/index-DEZEf8Fs.d.cts +0 -417
  134. package/dist/match-entry-DjI2bLpD.d.cts +0 -209
  135. package/dist/match-entry-DjI2bLpD.d.ts +0 -209
  136. package/dist/match.cjs +0 -2
  137. package/dist/match.cjs.map +0 -1
  138. package/dist/match.d.cts +0 -1
  139. package/dist/match.d.ts +0 -1
  140. package/dist/match.js +0 -2
  141. package/dist/match.js.map +0 -1
  142. package/dist/otel.cjs +0 -2
  143. package/dist/otel.cjs.map +0 -1
  144. package/dist/otel.d.cts +0 -188
  145. package/dist/otel.d.ts +0 -188
  146. package/dist/otel.js +0 -2
  147. package/dist/otel.js.map +0 -1
  148. package/dist/persistence-entry-B-8PjnSR.d.cts +0 -831
  149. package/dist/persistence-entry-D8zRkLiT.d.ts +0 -831
  150. package/dist/persistence.cjs +0 -2
  151. package/dist/persistence.cjs.map +0 -1
  152. package/dist/persistence.d.cts +0 -7
  153. package/dist/persistence.d.ts +0 -7
  154. package/dist/persistence.js +0 -2
  155. package/dist/persistence.js.map +0 -1
  156. package/dist/policies.cjs +0 -2
  157. package/dist/policies.cjs.map +0 -1
  158. package/dist/policies.d.cts +0 -379
  159. package/dist/policies.d.ts +0 -379
  160. package/dist/policies.js +0 -2
  161. package/dist/policies.js.map +0 -1
  162. package/dist/ratelimit.cjs +0 -7
  163. package/dist/ratelimit.cjs.map +0 -1
  164. package/dist/ratelimit.d.cts +0 -458
  165. package/dist/ratelimit.d.ts +0 -458
  166. package/dist/ratelimit.js +0 -7
  167. package/dist/ratelimit.js.map +0 -1
  168. package/dist/reliability.cjs +0 -11
  169. package/dist/reliability.cjs.map +0 -1
  170. package/dist/reliability.d.cts +0 -11
  171. package/dist/reliability.d.ts +0 -11
  172. package/dist/reliability.js +0 -11
  173. package/dist/reliability.js.map +0 -1
  174. package/dist/resolver.cjs +0 -7
  175. package/dist/resolver.cjs.map +0 -1
  176. package/dist/resolver.d.cts +0 -68
  177. package/dist/resolver.d.ts +0 -68
  178. package/dist/resolver.js +0 -7
  179. package/dist/resolver.js.map +0 -1
  180. package/dist/resource.cjs +0 -7
  181. package/dist/resource.cjs.map +0 -1
  182. package/dist/resource.d.cts +0 -174
  183. package/dist/resource.d.ts +0 -174
  184. package/dist/resource.js +0 -7
  185. package/dist/resource.js.map +0 -1
  186. package/dist/result/retry.cjs +0 -2
  187. package/dist/result/retry.cjs.map +0 -1
  188. package/dist/result/retry.d.cts +0 -70
  189. package/dist/result/retry.d.ts +0 -70
  190. package/dist/result/retry.js +0 -2
  191. package/dist/result/retry.js.map +0 -1
  192. package/dist/retry.cjs +0 -2
  193. package/dist/retry.cjs.map +0 -1
  194. package/dist/retry.d.cts +0 -388
  195. package/dist/retry.d.ts +0 -388
  196. package/dist/retry.js +0 -2
  197. package/dist/retry.js.map +0 -1
  198. package/dist/run.cjs +0 -7
  199. package/dist/run.cjs.map +0 -1
  200. package/dist/run.d.cts +0 -4
  201. package/dist/run.d.ts +0 -4
  202. package/dist/run.js +0 -7
  203. package/dist/run.js.map +0 -1
  204. package/dist/saga.cjs +0 -11
  205. package/dist/saga.cjs.map +0 -1
  206. package/dist/saga.d.cts +0 -164
  207. package/dist/saga.d.ts +0 -164
  208. package/dist/saga.js +0 -11
  209. package/dist/saga.js.map +0 -1
  210. package/dist/singleflight.cjs +0 -2
  211. package/dist/singleflight.cjs.map +0 -1
  212. package/dist/singleflight.d.cts +0 -145
  213. package/dist/singleflight.d.ts +0 -145
  214. package/dist/singleflight.js +0 -2
  215. package/dist/singleflight.js.map +0 -1
  216. package/dist/slugs.cjs +0 -2
  217. package/dist/slugs.cjs.map +0 -1
  218. package/dist/slugs.d.cts +0 -67
  219. package/dist/slugs.d.ts +0 -67
  220. package/dist/slugs.js +0 -2
  221. package/dist/slugs.js.map +0 -1
  222. package/dist/streaming.cjs +0 -9
  223. package/dist/streaming.cjs.map +0 -1
  224. package/dist/streaming.d.cts +0 -596
  225. package/dist/streaming.d.ts +0 -596
  226. package/dist/streaming.js +0 -9
  227. package/dist/streaming.js.map +0 -1
  228. package/dist/tagged-error.cjs +0 -2
  229. package/dist/tagged-error.cjs.map +0 -1
  230. package/dist/tagged-error.d.cts +0 -275
  231. package/dist/tagged-error.d.ts +0 -275
  232. package/dist/tagged-error.js +0 -2
  233. package/dist/tagged-error.js.map +0 -1
  234. package/dist/types-BziYHFkD.d.ts +0 -323
  235. package/dist/types-C5jLEUqY.d.cts +0 -323
  236. package/dist/webhook.cjs +0 -7
  237. package/dist/webhook.cjs.map +0 -1
  238. package/dist/webhook.d.cts +0 -499
  239. package/dist/webhook.d.ts +0 -499
  240. package/dist/webhook.js +0 -7
  241. package/dist/webhook.js.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,154 +1,1983 @@
1
- import { Result, ok, err, matchError, from, fromPromise, tryAsync, fromNullable, map, mapError, match, andThen, tap, tapError, mapTry, mapErrorTry, bimap, orElse, orElseAsync, recover, recoverAsync, hydrate, isSerializedResult, all, allAsync, allSettled, allSettledAsync, partition, any, anyAsync, zip, zipAsync, flatten, deserialize, serialize, matchErrorPartial, Ok, Err, PromiseRejectedError, UnwrapError } from './result.cjs';
2
- export { AWAITLY_CANCELLED, AWAITLY_TIMEOUT, AWAITLY_UNEXPECTED, AsyncResult, CauseOf, DESERIALIZATION_ERROR, DeserializationError, EmptyInputError, ErrorOf, Errors, ErrorsOf, ExtractCause, ExtractError, ExtractValue, MatchErrorHandlers, MaybeAsyncResult, PROMISE_REJECTED, PromiseRejectionCause, SerializedResult, SettledError, isErr, isOk, isPromiseRejectedError, isUnexpectedError, runOrNull, runOrThrow, runOrThrowAsync, runOrUndefined, tags, unwrap, unwrapOr, unwrapOrElse } from './result.cjs';
3
- import { TaggedError } from './tagged-error.cjs';
4
- export { ErrorByTag, PropsOf, TagOf, TaggedErrorBase, TaggedErrorConstructor, TaggedErrorCreateOptions, TaggedErrorOptions } from './tagged-error.cjs';
5
- import { pipe, flow, compose, recoverWith, getOrElse, getOrElseLazy, mapAsync, flatMapAsync, tapAsync, tapErrorAsync, race, traverse, traverseAsync, traverseParallel } from './functional.cjs';
6
- export { R, identity } from './functional.cjs';
7
- export { w as withDeps } from './di-OJfsohf-.cjs';
8
- export { ah as BackoffStrategy, ai as BoundSteps, ag as RetryOptions } from './run-entry-DGs0tySr.cjs';
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
- * awaitly
12
+ * Per-dependency policies: retry, timeout, fallback.
17
13
  *
18
- * Result types for typed error handling without exceptions.
19
- * Optimized for serverless with minimal bundle size.
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
- * ## Quick Start
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
- * import { Awaitly, type AsyncResult } from 'awaitly';
25
- * import { run } from 'awaitly/run';
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
- * // Define Result-returning functions
28
- * async function getUser(id: string): AsyncResult<User, 'NOT_FOUND'> {
29
- * const user = await db.find(id);
30
- * return user ? Awaitly.ok(user) : Awaitly.err('NOT_FOUND');
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
- * // Compose with run() - clean do-notation style
34
- * const result = await run(async ({ step }) => {
35
- * const user = await step(getUser(id));
36
- * const posts = await step(getPosts(user.id));
37
- * return { user, posts };
38
- * });
39
- * ```
40
- *
41
- * ## Entry Points
42
- *
43
- * **Core (this package):**
44
- * - `awaitly` - Awaitly namespace (Result types, transformers, tagged errors, pipe/flow)
45
- * - `awaitly/result` - Result types only (minimal bundle, no namespace)
46
- * - `awaitly/run` - run() function with step orchestration
47
- *
48
- * **Workflow Engine:**
49
- * - `awaitly/workflow` - createWorkflow, Duration, state management
50
- * - `awaitly/hitl` - Human-in-the-loop approval flows
51
- *
52
- * **Reliability:**
53
- * - `awaitly/retry` - Composable retry/backoff strategies
54
- * - `awaitly/circuit-breaker` - Circuit breaker pattern
55
- * - `awaitly/ratelimit` - Rate limiting
56
- * - `awaitly/saga` - Saga compensation pattern
57
- *
58
- * **Utilities:**
59
- * - `awaitly/duration` - Type-safe time durations
60
- * - `awaitly/match` - Pattern matching
61
- * - `awaitly/persistence` - State persistence
62
- * - `awaitly/durable` - Durable execution with automatic checkpointing
63
- */
64
-
65
- declare const Awaitly: {
66
- readonly TaggedError: typeof TaggedError;
67
- readonly pipe: typeof pipe;
68
- readonly flow: typeof flow;
69
- readonly compose: typeof compose;
70
- readonly identity: <A>(a: A) => A;
71
- readonly R: {
72
- map: <T, U, E, C>(fn: (value: T) => U) => (result: Result<T, E, C>) => Result<U, E, C>;
73
- flatMap: <T, U, E1, E2, C1, C2>(fn: (value: T) => Result<U, E2, C2>) => (result: Result<T, E1, C1>) => Result<U, E1 | E2, C1 | C2>;
74
- bimap: <T, U, E1, E2, C>(onOk: (value: T) => U, onErr: (error: E1) => E2) => (result: Result<T, E1, C>) => Result<U, E2, C>;
75
- mapError: <T, E1, E2, C>(fn: (error: E1) => E2) => (result: Result<T, E1, C>) => Result<T, E2, C>;
76
- tap: <T, E, C>(fn: (value: T) => void) => (result: Result<T, E, C>) => Result<T, E, C>;
77
- tapError: <T, E, C>(fn: (error: E) => void) => (result: Result<T, E, C>) => Result<T, E, C>;
78
- match: <T, E, U, C>(patterns: {
79
- ok: (value: T) => U;
80
- err: (error: E, cause?: C) => U;
81
- }) => (result: Result<T, E, C>) => U;
82
- recover: <T, E, C>(fn: (error: E) => T) => (result: Result<T, E, C>) => T;
83
- recoverWith: <T, E1, E2, C1, C2>(fn: (error: E1) => Result<T, E2, C2>) => (result: Result<T, E1, C1>) => Result<T, E2, C1 | C2>;
84
- getOrElse: <T, E, C>(defaultValue: T) => (result: Result<T, E, C>) => T;
85
- getOrElseLazy: <T, E, C>(fn: () => T) => (result: Result<T, E, C>) => T;
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 orElseAsync: typeof orElseAsync;
116
- readonly recover: typeof recover;
117
- readonly recoverAsync: typeof recoverAsync;
118
- readonly hydrate: typeof hydrate;
119
- readonly isSerializedResult: typeof isSerializedResult;
120
- readonly all: typeof all;
121
- readonly allAsync: typeof allAsync;
122
- readonly allSettled: typeof allSettled;
123
- readonly allSettledAsync: typeof allSettledAsync;
124
- readonly partition: typeof partition;
125
- readonly any: typeof any;
126
- readonly anyAsync: typeof anyAsync;
127
- readonly zip: typeof zip;
128
- readonly zipAsync: typeof zipAsync;
129
- readonly flatten: typeof flatten;
130
- readonly deserialize: typeof deserialize;
131
- readonly serialize: typeof serialize;
132
- readonly matchErrorPartial: typeof matchErrorPartial;
133
- readonly PROMISE_REJECTED: "PROMISE_REJECTED";
134
- readonly AWAITLY_UNEXPECTED: "AWAITLY_UNEXPECTED";
135
- readonly AWAITLY_CANCELLED: "AWAITLY_CANCELLED";
136
- readonly AWAITLY_TIMEOUT: "AWAITLY_TIMEOUT";
137
- readonly tags: <const T extends readonly string[]>(...t: T) => T;
138
- readonly UnexpectedError: typeof UnexpectedError;
139
- readonly isOk: <T, E, C>(r: Result<T, E, C>) => r is Ok<T>;
140
- readonly isErr: <T, E, C>(r: Result<T, E, C>) => r is Err<E, C>;
141
- readonly isUnexpectedError: (e: unknown) => e is UnexpectedError;
142
- readonly isPromiseRejectedError: (e: unknown) => e is PromiseRejectedError;
143
- readonly UnwrapError: typeof UnwrapError;
144
- readonly unwrap: <T, E, C>(r: Result<T, E, C>) => T;
145
- readonly unwrapOr: <T, E, C>(r: Result<T, E, C>, defaultValue: T) => T;
146
- readonly unwrapOrElse: <T, E, C>(r: Result<T, E, C>, fn: (error: E, cause?: C) => T) => T;
147
- readonly runOrThrow: <T, E, C>(r: Result<T, E, C>) => T;
148
- readonly runOrThrowAsync: <T, E, C>(ar: PromiseLike<Result<T, E, C>>) => Promise<T>;
149
- readonly runOrNull: <T, E, C>(r: Result<T, E, C>) => T | null;
150
- readonly runOrUndefined: <T, E, C>(r: Result<T, E, C>) => T | undefined;
151
- readonly DESERIALIZATION_ERROR: "DESERIALIZATION_ERROR";
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 { Awaitly, Err, Ok, PromiseRejectedError, Result, TaggedError, UnexpectedError, UnwrapError, all, allAsync, allSettled, allSettledAsync, andThen, any, anyAsync, bimap, compose, deserialize, err, flatMapAsync, flatten, flow, from, fromNullable, fromPromise, getOrElse, getOrElseLazy, hydrate, isSerializedResult, map, mapAsync, mapError, mapErrorTry, mapTry, match, matchError, matchErrorPartial, ok, orElse, orElseAsync, partition, pipe, race, recover, recoverAsync, recoverWith, serialize, tap, tapAsync, tapError, tapErrorAsync, traverse, traverseAsync, traverseParallel, tryAsync, zip, zipAsync };
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 };