awaitly 1.35.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{duration.d.ts → di-BDlT7InM.d.cts} +15 -1
- package/dist/{duration.d.cts → di-BbFFfO8y.d.ts} +15 -1
- package/dist/errors-DtXvrCiO.d.cts +708 -0
- package/dist/errors-DtXvrCiO.d.ts +708 -0
- package/dist/index.cjs +4594 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1970 -141
- package/dist/index.d.ts +1970 -141
- package/dist/index.js +4398 -1
- package/dist/index.js.map +1 -1
- package/dist/result.cjs +641 -1
- package/dist/result.cjs.map +1 -1
- package/dist/result.d.cts +29 -4
- package/dist/result.d.ts +29 -4
- package/dist/result.js +561 -1
- package/dist/result.js.map +1 -1
- package/dist/testing.cjs +4202 -8
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -6
- package/dist/testing.d.ts +2 -6
- package/dist/testing.js +4154 -8
- package/dist/testing.js.map +1 -1
- package/dist/{run-entry-DGs0tySr.d.cts → types-B8NfNRGX.d.ts} +1078 -1502
- package/dist/{run-entry-BOuNyVoO.d.ts → types-BZ2f4MRR.d.cts} +1078 -1502
- package/dist/workflow.cjs +7096 -6
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +3346 -22
- package/dist/workflow.d.ts +3346 -22
- package/dist/workflow.js +6929 -6
- package/dist/workflow.js.map +1 -1
- package/package.json +3 -168
- package/dist/adapters.cjs +0 -7
- package/dist/adapters.cjs.map +0 -1
- package/dist/adapters.d.cts +0 -179
- package/dist/adapters.d.ts +0 -179
- package/dist/adapters.js +0 -7
- package/dist/adapters.js.map +0 -1
- package/dist/batch.cjs +0 -7
- package/dist/batch.cjs.map +0 -1
- package/dist/batch.d.cts +0 -200
- package/dist/batch.d.ts +0 -200
- package/dist/batch.js +0 -7
- package/dist/batch.js.map +0 -1
- package/dist/bind-deps.cjs +0 -2
- package/dist/bind-deps.cjs.map +0 -1
- package/dist/bind-deps.d.cts +0 -28
- package/dist/bind-deps.d.ts +0 -28
- package/dist/bind-deps.js +0 -2
- package/dist/bind-deps.js.map +0 -1
- package/dist/cache.cjs +0 -2
- package/dist/cache.cjs.map +0 -1
- package/dist/cache.d.cts +0 -269
- package/dist/cache.d.ts +0 -269
- package/dist/cache.js +0 -2
- package/dist/cache.js.map +0 -1
- package/dist/circuit-breaker.cjs +0 -7
- package/dist/circuit-breaker.cjs.map +0 -1
- package/dist/circuit-breaker.d.cts +0 -211
- package/dist/circuit-breaker.d.ts +0 -211
- package/dist/circuit-breaker.js +0 -7
- package/dist/circuit-breaker.js.map +0 -1
- package/dist/conditional.cjs +0 -2
- package/dist/conditional.cjs.map +0 -1
- package/dist/conditional.d.cts +0 -252
- package/dist/conditional.d.ts +0 -252
- package/dist/conditional.js +0 -2
- package/dist/conditional.js.map +0 -1
- package/dist/core.cjs +0 -7
- package/dist/core.cjs.map +0 -1
- package/dist/core.d.cts +0 -5
- package/dist/core.d.ts +0 -5
- package/dist/core.js +0 -7
- package/dist/core.js.map +0 -1
- package/dist/di-By77n4Fa.d.ts +0 -15
- package/dist/di-OJfsohf-.d.cts +0 -15
- package/dist/diagnostics.cjs +0 -8
- package/dist/diagnostics.cjs.map +0 -1
- package/dist/diagnostics.d.cts +0 -68
- package/dist/diagnostics.d.ts +0 -68
- package/dist/diagnostics.js +0 -8
- package/dist/diagnostics.js.map +0 -1
- package/dist/durable.cjs +0 -11
- package/dist/durable.cjs.map +0 -1
- package/dist/durable.d.cts +0 -9
- package/dist/durable.d.ts +0 -9
- package/dist/durable.js +0 -11
- package/dist/durable.js.map +0 -1
- package/dist/duration.cjs +0 -2
- package/dist/duration.cjs.map +0 -1
- package/dist/duration.js +0 -2
- package/dist/duration.js.map +0 -1
- package/dist/engine.cjs +0 -11
- package/dist/engine.cjs.map +0 -1
- package/dist/engine.d.cts +0 -115
- package/dist/engine.d.ts +0 -115
- package/dist/engine.js +0 -11
- package/dist/engine.js.map +0 -1
- package/dist/errors.cjs +0 -2
- package/dist/errors.cjs.map +0 -1
- package/dist/errors.d.cts +0 -361
- package/dist/errors.d.ts +0 -361
- package/dist/errors.js +0 -2
- package/dist/errors.js.map +0 -1
- package/dist/fetch.cjs +0 -7
- package/dist/fetch.cjs.map +0 -1
- package/dist/fetch.d.cts +0 -86
- package/dist/fetch.d.ts +0 -86
- package/dist/fetch.js +0 -7
- package/dist/fetch.js.map +0 -1
- package/dist/flow.cjs +0 -7
- package/dist/flow.cjs.map +0 -1
- package/dist/flow.d.cts +0 -163
- package/dist/flow.d.ts +0 -163
- package/dist/flow.js +0 -7
- package/dist/flow.js.map +0 -1
- package/dist/functional.cjs +0 -2
- package/dist/functional.cjs.map +0 -1
- package/dist/functional.d.cts +0 -444
- package/dist/functional.d.ts +0 -444
- package/dist/functional.js +0 -2
- package/dist/functional.js.map +0 -1
- package/dist/guards-4sV7mTqj.d.cts +0 -72
- package/dist/guards-BIX05ALH.d.ts +0 -72
- package/dist/hitl-DFn4Xa_l.d.cts +0 -468
- package/dist/hitl-DU5VpKq7.d.ts +0 -468
- package/dist/hitl.cjs +0 -7
- package/dist/hitl.cjs.map +0 -1
- package/dist/hitl.d.cts +0 -442
- package/dist/hitl.d.ts +0 -442
- package/dist/hitl.js +0 -7
- package/dist/hitl.js.map +0 -1
- package/dist/index-CnvBryQB.d.ts +0 -417
- package/dist/index-DEZEf8Fs.d.cts +0 -417
- package/dist/match-entry-DjI2bLpD.d.cts +0 -209
- package/dist/match-entry-DjI2bLpD.d.ts +0 -209
- package/dist/match.cjs +0 -2
- package/dist/match.cjs.map +0 -1
- package/dist/match.d.cts +0 -1
- package/dist/match.d.ts +0 -1
- package/dist/match.js +0 -2
- package/dist/match.js.map +0 -1
- package/dist/otel.cjs +0 -2
- package/dist/otel.cjs.map +0 -1
- package/dist/otel.d.cts +0 -188
- package/dist/otel.d.ts +0 -188
- package/dist/otel.js +0 -2
- package/dist/otel.js.map +0 -1
- package/dist/persistence-entry-B-8PjnSR.d.cts +0 -831
- package/dist/persistence-entry-D8zRkLiT.d.ts +0 -831
- package/dist/persistence.cjs +0 -2
- package/dist/persistence.cjs.map +0 -1
- package/dist/persistence.d.cts +0 -7
- package/dist/persistence.d.ts +0 -7
- package/dist/persistence.js +0 -2
- package/dist/persistence.js.map +0 -1
- package/dist/policies.cjs +0 -2
- package/dist/policies.cjs.map +0 -1
- package/dist/policies.d.cts +0 -379
- package/dist/policies.d.ts +0 -379
- package/dist/policies.js +0 -2
- package/dist/policies.js.map +0 -1
- package/dist/ratelimit.cjs +0 -7
- package/dist/ratelimit.cjs.map +0 -1
- package/dist/ratelimit.d.cts +0 -458
- package/dist/ratelimit.d.ts +0 -458
- package/dist/ratelimit.js +0 -7
- package/dist/ratelimit.js.map +0 -1
- package/dist/reliability.cjs +0 -11
- package/dist/reliability.cjs.map +0 -1
- package/dist/reliability.d.cts +0 -11
- package/dist/reliability.d.ts +0 -11
- package/dist/reliability.js +0 -11
- package/dist/reliability.js.map +0 -1
- package/dist/resolver.cjs +0 -7
- package/dist/resolver.cjs.map +0 -1
- package/dist/resolver.d.cts +0 -68
- package/dist/resolver.d.ts +0 -68
- package/dist/resolver.js +0 -7
- package/dist/resolver.js.map +0 -1
- package/dist/resource.cjs +0 -7
- package/dist/resource.cjs.map +0 -1
- package/dist/resource.d.cts +0 -174
- package/dist/resource.d.ts +0 -174
- package/dist/resource.js +0 -7
- package/dist/resource.js.map +0 -1
- package/dist/result/retry.cjs +0 -2
- package/dist/result/retry.cjs.map +0 -1
- package/dist/result/retry.d.cts +0 -70
- package/dist/result/retry.d.ts +0 -70
- package/dist/result/retry.js +0 -2
- package/dist/result/retry.js.map +0 -1
- package/dist/retry.cjs +0 -2
- package/dist/retry.cjs.map +0 -1
- package/dist/retry.d.cts +0 -388
- package/dist/retry.d.ts +0 -388
- package/dist/retry.js +0 -2
- package/dist/retry.js.map +0 -1
- package/dist/run.cjs +0 -7
- package/dist/run.cjs.map +0 -1
- package/dist/run.d.cts +0 -4
- package/dist/run.d.ts +0 -4
- package/dist/run.js +0 -7
- package/dist/run.js.map +0 -1
- package/dist/saga.cjs +0 -11
- package/dist/saga.cjs.map +0 -1
- package/dist/saga.d.cts +0 -164
- package/dist/saga.d.ts +0 -164
- package/dist/saga.js +0 -11
- package/dist/saga.js.map +0 -1
- package/dist/singleflight.cjs +0 -2
- package/dist/singleflight.cjs.map +0 -1
- package/dist/singleflight.d.cts +0 -145
- package/dist/singleflight.d.ts +0 -145
- package/dist/singleflight.js +0 -2
- package/dist/singleflight.js.map +0 -1
- package/dist/slugs.cjs +0 -2
- package/dist/slugs.cjs.map +0 -1
- package/dist/slugs.d.cts +0 -67
- package/dist/slugs.d.ts +0 -67
- package/dist/slugs.js +0 -2
- package/dist/slugs.js.map +0 -1
- package/dist/streaming.cjs +0 -9
- package/dist/streaming.cjs.map +0 -1
- package/dist/streaming.d.cts +0 -596
- package/dist/streaming.d.ts +0 -596
- package/dist/streaming.js +0 -9
- package/dist/streaming.js.map +0 -1
- package/dist/tagged-error.cjs +0 -2
- package/dist/tagged-error.cjs.map +0 -1
- package/dist/tagged-error.d.cts +0 -275
- package/dist/tagged-error.d.ts +0 -275
- package/dist/tagged-error.js +0 -2
- package/dist/tagged-error.js.map +0 -1
- package/dist/types-BziYHFkD.d.ts +0 -323
- package/dist/types-C5jLEUqY.d.cts +0 -323
- package/dist/webhook.cjs +0 -7
- package/dist/webhook.cjs.map +0 -1
- package/dist/webhook.d.cts +0 -499
- package/dist/webhook.d.ts +0 -499
- package/dist/webhook.js +0 -7
- package/dist/webhook.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { UnexpectedError } from './errors.
|
|
1
|
+
import { U as UnexpectedError } from './errors-DtXvrCiO.js';
|
|
2
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Bound steps for the deps-first forms: run(deps, fn) and workflow({ steps }).
|
|
@@ -12,6 +13,7 @@ type AnyFunction$1 = (...args: never[]) => unknown;
|
|
|
12
13
|
/**
|
|
13
14
|
* Success value of a dependency's return type. Result-returning deps
|
|
14
15
|
* contribute their `ok` value; plain (non-Result) deps pass through as-is.
|
|
16
|
+
* Shared with the policy wrappers, which normalize the same way.
|
|
15
17
|
*/
|
|
16
18
|
type DepValueOfReturn<R> = [Extract<Awaited<R>, {
|
|
17
19
|
ok: true;
|
|
@@ -39,9 +41,9 @@ type BoundSteps<Deps extends Record<string, AnyFunction$1>> = {
|
|
|
39
41
|
};
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
|
-
*
|
|
44
|
+
* Core module (internal): Result primitives and the run() function.
|
|
43
45
|
*
|
|
44
|
-
*
|
|
46
|
+
* Surfaced through the root `awaitly` entry (formerly `awaitly/core`).
|
|
45
47
|
* Use this module for minimal bundle size when you don't need the full workflow capabilities
|
|
46
48
|
* (like retries, timeout, or state persistence) provided by `createWorkflow`.
|
|
47
49
|
*
|
|
@@ -65,7 +67,7 @@ type DurationInput = string | DurationObject;
|
|
|
65
67
|
*
|
|
66
68
|
* @example
|
|
67
69
|
* ```typescript
|
|
68
|
-
* const success =
|
|
70
|
+
* const success = ok(42);
|
|
69
71
|
* // Type shown: Ok<number>
|
|
70
72
|
* ```
|
|
71
73
|
*/
|
|
@@ -83,7 +85,7 @@ type Ok<T> = {
|
|
|
83
85
|
*
|
|
84
86
|
* @example
|
|
85
87
|
* ```typescript
|
|
86
|
-
* const failure =
|
|
88
|
+
* const failure = err({ type: "NOT_FOUND", message: "User not found" });
|
|
87
89
|
* // Type shown: Err<{ type: string; message: string }>
|
|
88
90
|
* ```
|
|
89
91
|
*/
|
|
@@ -107,185 +109,7 @@ type Result<T, E = unknown, C = unknown> = Ok<T> | Err<E, C>;
|
|
|
107
109
|
* Use this for asynchronous operations that might fail.
|
|
108
110
|
*/
|
|
109
111
|
type AsyncResult<T, E = unknown, C = unknown> = Promise<Result<T, E, C>>;
|
|
110
|
-
/** Discriminant for PromiseRejectedError type - use in switch statements */
|
|
111
|
-
declare const PROMISE_REJECTED: "PROMISE_REJECTED";
|
|
112
|
-
/**
|
|
113
|
-
* Named error constant for unexpected/unhandled errors.
|
|
114
|
-
* Used by the analyzer when a step doesn't declare errors.
|
|
115
|
-
*/
|
|
116
|
-
declare const AWAITLY_UNEXPECTED: "AWAITLY_UNEXPECTED";
|
|
117
|
-
/**
|
|
118
|
-
* Named error constant for cancelled operations.
|
|
119
|
-
*/
|
|
120
|
-
declare const AWAITLY_CANCELLED: "AWAITLY_CANCELLED";
|
|
121
|
-
/**
|
|
122
|
-
* Named error constant for timed-out operations.
|
|
123
|
-
*/
|
|
124
|
-
declare const AWAITLY_TIMEOUT: "AWAITLY_TIMEOUT";
|
|
125
|
-
/**
|
|
126
|
-
* Helper to create a tuple of string literal tags with preserved literal types.
|
|
127
|
-
* Use this when you need to store error tags in a variable while keeping
|
|
128
|
-
* TypeScript's literal type inference (avoiding widening to string[]).
|
|
129
|
-
*
|
|
130
|
-
* @param t - The string literal tags
|
|
131
|
-
* @returns The same array with preserved literal types
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* ```typescript
|
|
135
|
-
* // Without tags() - type widens to string[]
|
|
136
|
-
* const errs = ['CART_NOT_FOUND', 'CART_EMPTY']; // string[]
|
|
137
|
-
*
|
|
138
|
-
* // With tags() - literal types preserved
|
|
139
|
-
* const errs = tags('CART_NOT_FOUND', 'CART_EMPTY'); // ['CART_NOT_FOUND', 'CART_EMPTY']
|
|
140
|
-
*
|
|
141
|
-
* await step('getCart', () => getCart(id), {
|
|
142
|
-
* errors: errs, // Analyzer can extract literal types
|
|
143
|
-
* out: 'cart',
|
|
144
|
-
* });
|
|
145
|
-
* ```
|
|
146
|
-
*/
|
|
147
|
-
declare const tags: <const T extends readonly string[]>(...t: T) => T;
|
|
148
|
-
|
|
149
|
-
type PromiseRejectedError = {
|
|
150
|
-
type: typeof PROMISE_REJECTED;
|
|
151
|
-
cause: unknown;
|
|
152
|
-
};
|
|
153
|
-
/** Cause type for promise rejections in async batch helpers */
|
|
154
|
-
type PromiseRejectionCause = {
|
|
155
|
-
type: "PROMISE_REJECTION";
|
|
156
|
-
reason: unknown;
|
|
157
|
-
};
|
|
158
|
-
type EmptyInputError = {
|
|
159
|
-
type: "EMPTY_INPUT";
|
|
160
|
-
message: string;
|
|
161
|
-
};
|
|
162
112
|
type MaybeAsyncResult<T, E, C = unknown> = Result<T, E, C> | Promise<Result<T, E, C>>;
|
|
163
|
-
/**
|
|
164
|
-
* Creates a successful Result.
|
|
165
|
-
* Use this when an operation completes successfully.
|
|
166
|
-
*
|
|
167
|
-
* @remarks When to use: Wrap a successful value in a Result for consistent return types.
|
|
168
|
-
*
|
|
169
|
-
* @param value - The success value to wrap
|
|
170
|
-
* @returns An Ok object with `{ ok: true, value }`
|
|
171
|
-
*
|
|
172
|
-
* @example
|
|
173
|
-
* ```typescript
|
|
174
|
-
* const success = Awaitly.ok(42);
|
|
175
|
-
* // Type: Ok<number>
|
|
176
|
-
*
|
|
177
|
-
* function divide(a: number, b: number): Result<number, string> {
|
|
178
|
-
* if (b === 0) return Awaitly.err("Division by zero");
|
|
179
|
-
* return Awaitly.ok(a / b);
|
|
180
|
-
* }
|
|
181
|
-
* ```
|
|
182
|
-
*/
|
|
183
|
-
declare function ok<T>(value: T): Ok<T>;
|
|
184
|
-
/**
|
|
185
|
-
* Creates a failed Result.
|
|
186
|
-
* Use this when an operation fails.
|
|
187
|
-
*
|
|
188
|
-
* @remarks When to use: Return a typed failure without throwing so callers can handle it explicitly.
|
|
189
|
-
*
|
|
190
|
-
* @param error - The error value describing what went wrong (e.g., error code, object)
|
|
191
|
-
* @returns An Err object with `{ ok: false, error }`
|
|
192
|
-
*
|
|
193
|
-
* @example
|
|
194
|
-
* ```typescript
|
|
195
|
-
* // Simple error
|
|
196
|
-
* const r1 = Awaitly.err("NOT_FOUND");
|
|
197
|
-
* // Type: Err<"NOT_FOUND">
|
|
198
|
-
*
|
|
199
|
-
* // Error with context (include in error object)
|
|
200
|
-
* const r2 = Awaitly.err({ type: "PROCESSING_FAILED", cause: originalError });
|
|
201
|
-
* // Type: Err<{ type: string; cause: Error }>
|
|
202
|
-
* ```
|
|
203
|
-
*/
|
|
204
|
-
declare function err<E, C = unknown>(error: E, options?: {
|
|
205
|
-
cause?: C;
|
|
206
|
-
}): Err<E, C>;
|
|
207
|
-
/**
|
|
208
|
-
* Checks if a Result is successful.
|
|
209
|
-
* Use this to narrow the type of a Result to the success case.
|
|
210
|
-
*
|
|
211
|
-
* @remarks When to use: Prefer functional-style checks or array filtering over `result.ok`.
|
|
212
|
-
*
|
|
213
|
-
* @param r - The Result to check
|
|
214
|
-
* @returns `true` if successful, allowing access to `r.value`
|
|
215
|
-
*
|
|
216
|
-
* @example
|
|
217
|
-
* ```typescript
|
|
218
|
-
* const r = someOperation();
|
|
219
|
-
* if (isOk(r)) {
|
|
220
|
-
* // Use r.value (Type is T)
|
|
221
|
-
* processValue(r.value);
|
|
222
|
-
* } else {
|
|
223
|
-
* // Handle r.error (Type is E)
|
|
224
|
-
* handleError(r.error);
|
|
225
|
-
* }
|
|
226
|
-
* ```
|
|
227
|
-
*/
|
|
228
|
-
declare const isOk: <T, E, C>(r: Result<T, E, C>) => r is Ok<T>;
|
|
229
|
-
/**
|
|
230
|
-
* Checks if a Result is a failure.
|
|
231
|
-
* Use this to narrow the type of a Result to the error case.
|
|
232
|
-
*
|
|
233
|
-
* @remarks When to use: Prefer functional-style checks or array filtering over `result.ok`.
|
|
234
|
-
*
|
|
235
|
-
* @param r - The Result to check
|
|
236
|
-
* @returns `true` if failed, allowing access to `r.error` and `r.cause`
|
|
237
|
-
*
|
|
238
|
-
* @example
|
|
239
|
-
* ```typescript
|
|
240
|
-
* if (isErr(r)) {
|
|
241
|
-
* // Handle error case early
|
|
242
|
-
* return;
|
|
243
|
-
* }
|
|
244
|
-
* // Proceed with success case
|
|
245
|
-
* ```
|
|
246
|
-
*/
|
|
247
|
-
declare const isErr: <T, E, C>(r: Result<T, E, C>) => r is Err<E, C>;
|
|
248
|
-
/**
|
|
249
|
-
* Checks if an error is an UnexpectedError.
|
|
250
|
-
* Used internally by the framework but exported for advanced custom handling.
|
|
251
|
-
* Indicates an error that wasn't typed/expected in the `run` signature.
|
|
252
|
-
*
|
|
253
|
-
* @remarks When to use: Distinguish unexpected failures from your typed error union.
|
|
254
|
-
*/
|
|
255
|
-
declare const isUnexpectedError: (e: unknown) => e is UnexpectedError;
|
|
256
|
-
/**
|
|
257
|
-
* Type for exhaustive error handlers mapping string literal errors and UnexpectedError.
|
|
258
|
-
* Each key in E gets a handler, plus UnexpectedError is required.
|
|
259
|
-
*/
|
|
260
|
-
type MatchErrorHandlers<E extends string, R> = {
|
|
261
|
-
[K in Exclude<E, "UnexpectedError">]: (error: K) => R;
|
|
262
|
-
} & {
|
|
263
|
-
UnexpectedError: (error: UnexpectedError) => R;
|
|
264
|
-
};
|
|
265
|
-
/**
|
|
266
|
-
* Exhaustive pattern matching for error types.
|
|
267
|
-
* Handles both string literal errors and UnexpectedError, ensuring all cases are covered.
|
|
268
|
-
*
|
|
269
|
-
* @param error - The error to match (string literal or UnexpectedError)
|
|
270
|
-
* @param handlers - Object with a handler for each error case plus UnexpectedError
|
|
271
|
-
* @returns The result of the matched handler
|
|
272
|
-
*
|
|
273
|
-
* @example
|
|
274
|
-
* ```typescript
|
|
275
|
-
* type FetchError = "NOT_FOUND" | "FETCH_ERROR";
|
|
276
|
-
* const result: Result<User, FetchError | UnexpectedError> = await fetchUser();
|
|
277
|
-
*
|
|
278
|
-
* if (!result.ok) {
|
|
279
|
-
* return matchError(result.error, {
|
|
280
|
-
* NOT_FOUND: () => 404,
|
|
281
|
-
* FETCH_ERROR: () => 500,
|
|
282
|
-
* UnexpectedError: (e) => { throw e.cause; }
|
|
283
|
-
* });
|
|
284
|
-
* }
|
|
285
|
-
* ```
|
|
286
|
-
*/
|
|
287
|
-
declare function matchError<E extends string, R>(handlers: MatchErrorHandlers<E, R>): (error: E | UnexpectedError) => R;
|
|
288
|
-
declare function matchError<E extends string, R>(error: E | UnexpectedError, handlers: MatchErrorHandlers<E, R>): R;
|
|
289
113
|
type AnyFunction = (...args: never[]) => unknown;
|
|
290
114
|
/**
|
|
291
115
|
* Helper to extract the error type from Result or AsyncResult return values.
|
|
@@ -305,12 +129,6 @@ type ErrorOfReturn<R> = [Extract<Awaited<R>, {
|
|
|
305
129
|
* Extract error type from a single function's return type
|
|
306
130
|
*/
|
|
307
131
|
type ErrorOf<T extends AnyFunction> = ErrorOfReturn<ReturnType<T>>;
|
|
308
|
-
/**
|
|
309
|
-
* Extract union of error types from multiple functions (tuple form)
|
|
310
|
-
*/
|
|
311
|
-
type Errors<T extends AnyFunction[]> = {
|
|
312
|
-
[K in keyof T]: ErrorOf<T[K]>;
|
|
313
|
-
}[number];
|
|
314
132
|
/**
|
|
315
133
|
* Extract union of error types from a deps object.
|
|
316
134
|
*
|
|
@@ -324,27 +142,6 @@ type Errors<T extends AnyFunction[]> = {
|
|
|
324
142
|
type ErrorsOf<Deps extends Record<string, AnyFunction>> = {
|
|
325
143
|
[K in keyof Deps]: ErrorOf<Deps[K]>;
|
|
326
144
|
}[keyof Deps];
|
|
327
|
-
/**
|
|
328
|
-
* Extract value type from Result
|
|
329
|
-
*/
|
|
330
|
-
type ExtractValue<T> = T extends {
|
|
331
|
-
ok: true;
|
|
332
|
-
value: infer U;
|
|
333
|
-
} ? U : never;
|
|
334
|
-
/**
|
|
335
|
-
* Extract error type from Result
|
|
336
|
-
*/
|
|
337
|
-
type ExtractError<T> = T extends {
|
|
338
|
-
ok: false;
|
|
339
|
-
error: infer E;
|
|
340
|
-
} ? E : never;
|
|
341
|
-
/**
|
|
342
|
-
* Extract cause type from Result
|
|
343
|
-
*/
|
|
344
|
-
type ExtractCause<T> = T extends {
|
|
345
|
-
ok: false;
|
|
346
|
-
cause?: infer C;
|
|
347
|
-
} ? C : never;
|
|
348
145
|
/**
|
|
349
146
|
* Helper to extract the cause type from Result or AsyncResult return values.
|
|
350
147
|
* Works even when a function is declared to return a union of both forms.
|
|
@@ -617,14 +414,6 @@ interface StepErrorDiagnostics {
|
|
|
617
414
|
cumulativeDurationMs?: number;
|
|
618
415
|
origin: 'result' | 'throw' | 'timeout';
|
|
619
416
|
}
|
|
620
|
-
/** Extract canonical error tag. Priority: _tag > tag > code > Error.name > "unknown".
|
|
621
|
-
* Tags are case-sensitive, whitespace-trimmed, otherwise raw.
|
|
622
|
-
* Note: Error.name is fallback-grade (often too coarse like "Error", "TypeError"). */
|
|
623
|
-
declare function extractErrorTag(error: unknown): string;
|
|
624
|
-
/** Look up ErrorClassification from errorMeta for a given tag. */
|
|
625
|
-
declare function lookupErrorClassification(tag: string, errorMeta?: Record<string, ErrorClassification>): ErrorClassification | undefined;
|
|
626
|
-
/** Extract StepMetadata from StepOptions (returns undefined when empty). */
|
|
627
|
-
declare function extractStepMetadata(options: StepOptions): StepMetadata | undefined;
|
|
628
417
|
/**
|
|
629
418
|
* Backoff strategy for retry operations.
|
|
630
419
|
*/
|
|
@@ -1702,6 +1491,18 @@ type WorkflowEvent<E, C = unknown> = {
|
|
|
1702
1491
|
ts: number;
|
|
1703
1492
|
metadata?: StepMetadata;
|
|
1704
1493
|
context?: C;
|
|
1494
|
+
} | {
|
|
1495
|
+
type: "decision";
|
|
1496
|
+
workflowId: string;
|
|
1497
|
+
workflowName?: string;
|
|
1498
|
+
decisionId: string;
|
|
1499
|
+
label?: string;
|
|
1500
|
+
branch: string;
|
|
1501
|
+
value: unknown;
|
|
1502
|
+
phase?: "start" | "end";
|
|
1503
|
+
durationMs?: number;
|
|
1504
|
+
ts: number;
|
|
1505
|
+
context?: C;
|
|
1705
1506
|
} | {
|
|
1706
1507
|
type: "scope_start";
|
|
1707
1508
|
workflowId: string;
|
|
@@ -1874,6 +1675,23 @@ type WorkflowEvent<E, C = unknown> = {
|
|
|
1874
1675
|
lastStepKey?: string;
|
|
1875
1676
|
context?: C;
|
|
1876
1677
|
};
|
|
1678
|
+
/**
|
|
1679
|
+
* A declared workflow graph for strict runtime validation.
|
|
1680
|
+
*
|
|
1681
|
+
* Pass either a list of step/decision ids or a WorkflowDiagramDSL-shaped
|
|
1682
|
+
* object (`{ states: [{ id }] }`, as produced by awaitly-analyze).
|
|
1683
|
+
* When provided, any runtime step or decision id not present in the graph
|
|
1684
|
+
* fails the workflow immediately — so the static diagram is guaranteed to
|
|
1685
|
+
* match what actually runs. Ids containing `{...}` placeholders
|
|
1686
|
+
* (e.g. "item-{i}") match any value in that position.
|
|
1687
|
+
*/
|
|
1688
|
+
type DeclaredGraph = readonly string[] | {
|
|
1689
|
+
readonly states: ReadonlyArray<{
|
|
1690
|
+
readonly id: string;
|
|
1691
|
+
/** Authored id when the unique diagram id needed a collision suffix. */
|
|
1692
|
+
readonly semanticId?: string;
|
|
1693
|
+
}>;
|
|
1694
|
+
};
|
|
1877
1695
|
type RunOptionsWithCatch<E, C = void> = {
|
|
1878
1696
|
/**
|
|
1879
1697
|
* Handler for expected errors.
|
|
@@ -1909,6 +1727,11 @@ type RunOptionsWithCatch<E, C = void> = {
|
|
|
1909
1727
|
* Useful for passing request IDs, user IDs, or loggers.
|
|
1910
1728
|
*/
|
|
1911
1729
|
context?: C;
|
|
1730
|
+
/**
|
|
1731
|
+
* Declared workflow graph for strict runtime validation.
|
|
1732
|
+
* Undeclared step/decision ids fail the workflow immediately.
|
|
1733
|
+
*/
|
|
1734
|
+
graph?: DeclaredGraph;
|
|
1912
1735
|
/**
|
|
1913
1736
|
* @internal External signal for workflow-level cancellation.
|
|
1914
1737
|
* Used by createWorkflow() to pass the workflow signal to steps.
|
|
@@ -1936,6 +1759,11 @@ type RunOptionsWithoutCatch<E, C = void> = {
|
|
|
1936
1759
|
*/
|
|
1937
1760
|
workflowName?: string;
|
|
1938
1761
|
context?: C;
|
|
1762
|
+
/**
|
|
1763
|
+
* Declared workflow graph for strict runtime validation.
|
|
1764
|
+
* Undeclared step/decision ids fail the workflow immediately.
|
|
1765
|
+
*/
|
|
1766
|
+
graph?: DeclaredGraph;
|
|
1939
1767
|
/**
|
|
1940
1768
|
* @internal External signal for workflow-level cancellation.
|
|
1941
1769
|
* Used by createWorkflow() to pass the workflow signal to steps.
|
|
@@ -2010,7 +1838,7 @@ declare function isEarlyExit<E>(e: unknown): e is EarlyExit<E>;
|
|
|
2010
1838
|
/**
|
|
2011
1839
|
* run() with catchUnexpected: closed union Result<T, E>.
|
|
2012
1840
|
*/
|
|
2013
|
-
declare function
|
|
1841
|
+
declare function runFn<T, E, C = void>(fn: (context: {
|
|
2014
1842
|
step: RunStep<E>;
|
|
2015
1843
|
}) => Promise<T> | T, options: RunOptionsWithCatch<E, C>): AsyncResult<T, E, unknown>;
|
|
2016
1844
|
/**
|
|
@@ -2019,7 +1847,7 @@ declare function run<T, E, C = void>(fn: (context: {
|
|
|
2019
1847
|
* uncaught exceptions are possible. Step errors pass through as-is.
|
|
2020
1848
|
* When E is never (default), step is RunStep<unknown> so any operation is allowed.
|
|
2021
1849
|
*/
|
|
2022
|
-
declare function
|
|
1850
|
+
declare function runFn<T, E = never, C = void>(fn: (context: {
|
|
2023
1851
|
step: [E] extends [never] ? RunStep<unknown> : RunStep<E>;
|
|
2024
1852
|
}) => Promise<T> | T, options?: {
|
|
2025
1853
|
onError?: (error: E | UnexpectedError, stepName?: string, ctx?: C) => void;
|
|
@@ -2027,6 +1855,7 @@ declare function run<T, E = never, C = void>(fn: (context: {
|
|
|
2027
1855
|
workflowId?: string;
|
|
2028
1856
|
workflowName?: string;
|
|
2029
1857
|
context?: C;
|
|
1858
|
+
graph?: DeclaredGraph;
|
|
2030
1859
|
/** @internal External signal for workflow-level cancellation. */
|
|
2031
1860
|
_workflowSignal?: AbortSignal;
|
|
2032
1861
|
}): AsyncResult<T, E | UnexpectedError, unknown>;
|
|
@@ -2053,7 +1882,7 @@ declare function run<T, E = never, C = void>(fn: (context: {
|
|
|
2053
1882
|
* // result.error: OrderNotFound | UserNotFound | ChargeDeclined | UnexpectedError
|
|
2054
1883
|
* ```
|
|
2055
1884
|
*/
|
|
2056
|
-
declare function
|
|
1885
|
+
declare function runFn<const Deps extends Record<string, AnyFunction>, T, C = void>(deps: Deps, fn: (steps: BoundSteps<Deps>, context: {
|
|
2057
1886
|
step: [ErrorsOf<Deps>] extends [never] ? RunStep<unknown> : RunStep<ErrorsOf<Deps>>;
|
|
2058
1887
|
}) => Promise<T> | T, options?: {
|
|
2059
1888
|
onError?: (error: ErrorsOf<Deps> | UnexpectedError, stepName?: string, ctx?: C) => void;
|
|
@@ -2061,11 +1890,20 @@ declare function run<const Deps extends Record<string, AnyFunction>, T, C = void
|
|
|
2061
1890
|
workflowId?: string;
|
|
2062
1891
|
workflowName?: string;
|
|
2063
1892
|
context?: C;
|
|
1893
|
+
graph?: DeclaredGraph;
|
|
2064
1894
|
/** @internal External signal for workflow-level cancellation. */
|
|
2065
1895
|
_workflowSignal?: AbortSignal;
|
|
2066
1896
|
}): AsyncResult<T, ErrorsOf<Deps> | UnexpectedError, unknown>;
|
|
2067
|
-
|
|
2068
|
-
|
|
1897
|
+
/**
|
|
1898
|
+
* The public run(): the engine with `.strict` attached.
|
|
1899
|
+
*
|
|
1900
|
+
* Assembled with a PURE-annotated Object.assign instead of a top-level
|
|
1901
|
+
* `run.strict = ...` mutation — a top-level property assignment is a side
|
|
1902
|
+
* effect that pins run (and the whole step engine) into every consumer
|
|
1903
|
+
* bundle even when only Result primitives are imported.
|
|
1904
|
+
*/
|
|
1905
|
+
declare const run: typeof runFn & {
|
|
1906
|
+
strict: <T, E, C = void>(fn: (context: {
|
|
2069
1907
|
step: RunStep<E>;
|
|
2070
1908
|
}) => Promise<T> | T, options: {
|
|
2071
1909
|
onError?: (error: E, stepName?: string, ctx?: C) => void;
|
|
@@ -2082,1427 +1920,1165 @@ declare namespace run {
|
|
|
2082
1920
|
/** @internal External signal for workflow-level cancellation. */
|
|
2083
1921
|
_workflowSignal?: AbortSignal;
|
|
2084
1922
|
}) => AsyncResult<T, E, unknown>;
|
|
2085
|
-
}
|
|
1923
|
+
};
|
|
1924
|
+
|
|
2086
1925
|
/**
|
|
2087
|
-
*
|
|
1926
|
+
* awaitly/streaming - Types
|
|
2088
1927
|
*
|
|
2089
|
-
*
|
|
2090
|
-
*
|
|
1928
|
+
* Core types for Result-aware streaming in workflows.
|
|
1929
|
+
* All stream operations return Result types, enabling typed error handling
|
|
1930
|
+
* throughout the streaming pipeline.
|
|
2091
1931
|
*/
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
1932
|
+
|
|
1933
|
+
/** Discriminant for stream write errors */
|
|
1934
|
+
declare const STREAM_WRITE_ERROR: "STREAM_WRITE_ERROR";
|
|
1935
|
+
/** Discriminant for stream read errors */
|
|
1936
|
+
declare const STREAM_READ_ERROR: "STREAM_READ_ERROR";
|
|
1937
|
+
/** Discriminant for stream close errors */
|
|
1938
|
+
declare const STREAM_CLOSE_ERROR: "STREAM_CLOSE_ERROR";
|
|
1939
|
+
/** Discriminant for stream store errors */
|
|
1940
|
+
declare const STREAM_STORE_ERROR: "STREAM_STORE_ERROR";
|
|
1941
|
+
/** Discriminant for stream ended marker */
|
|
1942
|
+
declare const STREAM_ENDED: "STREAM_ENDED";
|
|
1943
|
+
/** Discriminant for stream backpressure errors */
|
|
1944
|
+
declare const STREAM_BACKPRESSURE_ERROR: "STREAM_BACKPRESSURE_ERROR";
|
|
1945
|
+
/**
|
|
1946
|
+
* Error returned when a write operation fails.
|
|
1947
|
+
*/
|
|
1948
|
+
type StreamWriteError = {
|
|
1949
|
+
type: typeof STREAM_WRITE_ERROR;
|
|
1950
|
+
reason: "closed" | "aborted" | "store_error";
|
|
1951
|
+
message: string;
|
|
1952
|
+
cause?: unknown;
|
|
1953
|
+
};
|
|
2097
1954
|
/**
|
|
2098
|
-
*
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
*
|
|
2108
|
-
*
|
|
2109
|
-
* ## Why Avoid This
|
|
2110
|
-
*
|
|
2111
|
-
* **Prefer alternatives** in production code:
|
|
2112
|
-
* - `unwrapOr(defaultValue)` - Provide a fallback value
|
|
2113
|
-
* - `unwrapOrElse(fn)` - Compute fallback from error
|
|
2114
|
-
* - `match()` - Handle both cases explicitly
|
|
2115
|
-
* - `isOk()` / `isErr()` - Type-safe pattern matching
|
|
2116
|
-
*
|
|
2117
|
-
* Throwing errors makes error handling harder and can crash your application.
|
|
2118
|
-
*
|
|
2119
|
-
* @param r - The Result to unwrap
|
|
2120
|
-
* @returns The success value if the Result is successful
|
|
2121
|
-
* @throws {UnwrapError} If the Result is an error (includes the error and cause)
|
|
2122
|
-
*
|
|
2123
|
-
* @example
|
|
2124
|
-
* ```typescript
|
|
2125
|
-
* // Safe usage after checking
|
|
2126
|
-
* const result = someOperation();
|
|
2127
|
-
* if (isOk(result)) {
|
|
2128
|
-
* const value = unwrap(result); // Safe - we know it's ok
|
|
2129
|
-
* }
|
|
2130
|
-
*
|
|
2131
|
-
* // Unsafe usage (not recommended)
|
|
2132
|
-
* const value = unwrap(someOperation()); // May throw!
|
|
2133
|
-
* ```
|
|
1955
|
+
* Error returned when a read operation fails.
|
|
1956
|
+
*/
|
|
1957
|
+
type StreamReadError = {
|
|
1958
|
+
type: typeof STREAM_READ_ERROR;
|
|
1959
|
+
reason: "closed" | "store_error";
|
|
1960
|
+
message: string;
|
|
1961
|
+
cause?: unknown;
|
|
1962
|
+
};
|
|
1963
|
+
/**
|
|
1964
|
+
* Error returned when closing a stream fails.
|
|
2134
1965
|
*/
|
|
2135
|
-
|
|
1966
|
+
type StreamCloseError = {
|
|
1967
|
+
type: typeof STREAM_CLOSE_ERROR;
|
|
1968
|
+
reason: "already_closed" | "store_error";
|
|
1969
|
+
message: string;
|
|
1970
|
+
cause?: unknown;
|
|
1971
|
+
};
|
|
2136
1972
|
/**
|
|
2137
|
-
*
|
|
2138
|
-
*
|
|
2139
|
-
* @remarks When to use: Provide a safe fallback without branching.
|
|
2140
|
-
*
|
|
2141
|
-
* ## When to Use
|
|
2142
|
-
*
|
|
2143
|
-
* Use `unwrapOr()` when:
|
|
2144
|
-
* - You have a sensible default value for errors
|
|
2145
|
-
* - You want to continue execution even on failure
|
|
2146
|
-
* - The default value is cheap to compute (use `unwrapOrElse` if expensive)
|
|
2147
|
-
*
|
|
2148
|
-
* ## Why Use This
|
|
2149
|
-
*
|
|
2150
|
-
* - **Safe**: Never throws, always returns a value
|
|
2151
|
-
* - **Simple**: One-liner for common error handling
|
|
2152
|
-
* - **Type-safe**: TypeScript knows you'll always get a `T`
|
|
2153
|
-
*
|
|
2154
|
-
* @param r - The Result to unwrap
|
|
2155
|
-
* @param defaultValue - The value to return if the Result is an error
|
|
2156
|
-
* @returns The success value if successful, otherwise the default value
|
|
2157
|
-
*
|
|
2158
|
-
* @example
|
|
2159
|
-
* ```typescript
|
|
2160
|
-
* // Provide default for missing data
|
|
2161
|
-
* const user = unwrapOr(fetchUser(id), { id: 'anonymous', name: 'Guest' });
|
|
2162
|
-
*
|
|
2163
|
-
* // Provide default for numeric operations
|
|
2164
|
-
* const count = unwrapOr(parseCount(input), 0);
|
|
2165
|
-
*
|
|
2166
|
-
* // Provide default for optional features
|
|
2167
|
-
* const config = unwrapOr(loadConfig(), getDefaultConfig());
|
|
2168
|
-
* ```
|
|
1973
|
+
* Error returned from StreamStore operations.
|
|
2169
1974
|
*/
|
|
2170
|
-
|
|
1975
|
+
type StreamStoreError = {
|
|
1976
|
+
type: typeof STREAM_STORE_ERROR;
|
|
1977
|
+
reason: "read_error" | "write_error" | "metadata_error" | "close_error";
|
|
1978
|
+
message: string;
|
|
1979
|
+
cause?: unknown;
|
|
1980
|
+
};
|
|
2171
1981
|
/**
|
|
2172
|
-
*
|
|
2173
|
-
*
|
|
2174
|
-
* @remarks When to use: Compute a fallback from the error (logging, metrics, or derived defaults).
|
|
2175
|
-
*
|
|
2176
|
-
* ## When to Use
|
|
2177
|
-
*
|
|
2178
|
-
* Use `unwrapOrElse()` when:
|
|
2179
|
-
* - The default value is expensive to compute (lazy evaluation)
|
|
2180
|
-
* - You need to log or handle the error before providing a default
|
|
2181
|
-
* - The default depends on the error type or cause
|
|
2182
|
-
* - You want to transform the error into a success value
|
|
2183
|
-
*
|
|
2184
|
-
* ## Why Use This Instead of `unwrapOr`
|
|
2185
|
-
*
|
|
2186
|
-
* - **Lazy**: Default is only computed if needed (better performance)
|
|
2187
|
-
* - **Error-aware**: You can inspect the error before providing default
|
|
2188
|
-
* - **Flexible**: Default can depend on error type or cause
|
|
2189
|
-
*
|
|
2190
|
-
* @param r - The Result to unwrap
|
|
2191
|
-
* @param fn - Function that receives the error and optional cause, returns the default value
|
|
2192
|
-
* @returns The success value if successful, otherwise the result of calling `fn(error, cause)`
|
|
2193
|
-
*
|
|
2194
|
-
* @example
|
|
2195
|
-
* ```typescript
|
|
2196
|
-
* // Compute default based on error type
|
|
2197
|
-
* const port = unwrapOrElse(parsePort(env.PORT), (error) => {
|
|
2198
|
-
* if (error === 'INVALID_FORMAT') return 3000;
|
|
2199
|
-
* if (error === 'OUT_OF_RANGE') return 8080;
|
|
2200
|
-
* return 4000; // default
|
|
2201
|
-
* });
|
|
2202
|
-
*
|
|
2203
|
-
* // Log error before providing default
|
|
2204
|
-
* const data = unwrapOrElse(fetchData(), (error, cause) => {
|
|
2205
|
-
* console.error('Failed to fetch:', error, cause);
|
|
2206
|
-
* return getCachedData();
|
|
2207
|
-
* });
|
|
2208
|
-
*
|
|
2209
|
-
* // Transform error into success value
|
|
2210
|
-
* const result = unwrapOrElse(operation(), (error) => {
|
|
2211
|
-
* return { success: false, reason: String(error) };
|
|
2212
|
-
* });
|
|
2213
|
-
* ```
|
|
1982
|
+
* Marker indicating stream has ended (not an error, but a terminal state).
|
|
1983
|
+
* Used as the "error" type when stream is exhausted.
|
|
2214
1984
|
*/
|
|
2215
|
-
|
|
1985
|
+
type StreamEndedMarker = {
|
|
1986
|
+
type: typeof STREAM_ENDED;
|
|
1987
|
+
finalPosition: number;
|
|
1988
|
+
};
|
|
2216
1989
|
/**
|
|
2217
|
-
*
|
|
2218
|
-
*
|
|
2219
|
-
* The Result is already computed; use when you want the value or throw (e.g. at boundaries or in tests).
|
|
2220
|
-
*
|
|
2221
|
-
* @param r - The Result to unwrap
|
|
2222
|
-
* @returns The success value if the Result is successful
|
|
2223
|
-
* @throws {UnwrapError} If the Result is an error (includes the error and cause)
|
|
1990
|
+
* Backpressure error when writer is paused.
|
|
2224
1991
|
*/
|
|
2225
|
-
|
|
1992
|
+
type StreamBackpressureError = {
|
|
1993
|
+
type: typeof STREAM_BACKPRESSURE_ERROR;
|
|
1994
|
+
bufferedCount: number;
|
|
1995
|
+
highWaterMark: number;
|
|
1996
|
+
};
|
|
2226
1997
|
/**
|
|
2227
|
-
*
|
|
2228
|
-
*
|
|
2229
|
-
* The returned promise **resolves with T** on success and **rejects with UnwrapError** on failure.
|
|
2230
|
-
* UnwrapError extends Error and carries the original `error` and `cause` from the Err.
|
|
2231
|
-
*
|
|
2232
|
-
* @param ar - A Promise or thenable that resolves to a Result
|
|
2233
|
-
* @returns A Promise that resolves with the success value or rejects with UnwrapError
|
|
1998
|
+
* Union of all stream errors.
|
|
2234
1999
|
*/
|
|
2235
|
-
|
|
2000
|
+
type StreamError = StreamWriteError | StreamReadError | StreamCloseError | StreamStoreError | StreamBackpressureError;
|
|
2236
2001
|
/**
|
|
2237
|
-
*
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2002
|
+
* A single item in the stream with metadata.
|
|
2003
|
+
*/
|
|
2004
|
+
interface StreamItem<T> {
|
|
2005
|
+
/** The value stored in this stream item */
|
|
2006
|
+
value: T;
|
|
2007
|
+
/** Position in the stream (0-indexed) */
|
|
2008
|
+
position: number;
|
|
2009
|
+
/** Timestamp when item was written */
|
|
2010
|
+
ts: number;
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* Metadata about a stream.
|
|
2241
2014
|
*/
|
|
2242
|
-
|
|
2015
|
+
interface StreamMetadata {
|
|
2016
|
+
/** Unique identifier for the stream (workflowId + namespace) */
|
|
2017
|
+
id: string;
|
|
2018
|
+
/** Namespace within the workflow */
|
|
2019
|
+
namespace: string;
|
|
2020
|
+
/** Workflow ID that owns this stream */
|
|
2021
|
+
workflowId: string;
|
|
2022
|
+
/** Number of items in the stream */
|
|
2023
|
+
length: number;
|
|
2024
|
+
/** Whether the stream has been closed */
|
|
2025
|
+
closed: boolean;
|
|
2026
|
+
/** Timestamp when stream was created */
|
|
2027
|
+
createdAt: number;
|
|
2028
|
+
/** Timestamp when stream was last written to */
|
|
2029
|
+
lastWriteAt?: number;
|
|
2030
|
+
/** Timestamp when stream was closed */
|
|
2031
|
+
closedAt?: number;
|
|
2032
|
+
}
|
|
2243
2033
|
/**
|
|
2244
|
-
*
|
|
2245
|
-
*
|
|
2246
|
-
* @param r - The Result to unwrap
|
|
2247
|
-
* @returns The success value if successful, otherwise undefined
|
|
2034
|
+
* Options for creating a writable stream.
|
|
2248
2035
|
*/
|
|
2249
|
-
|
|
2036
|
+
interface StreamOptions {
|
|
2037
|
+
/** Named streams (default: 'default') */
|
|
2038
|
+
namespace?: string;
|
|
2039
|
+
/** Backpressure threshold (default: 16) */
|
|
2040
|
+
highWaterMark?: number;
|
|
2041
|
+
}
|
|
2250
2042
|
/**
|
|
2251
|
-
*
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
*
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2043
|
+
* Options for creating a readable stream.
|
|
2044
|
+
*/
|
|
2045
|
+
interface StreamReadOptions {
|
|
2046
|
+
/** Named streams (default: 'default') */
|
|
2047
|
+
namespace?: string;
|
|
2048
|
+
/** Resume from position (0-indexed) */
|
|
2049
|
+
startIndex?: number;
|
|
2050
|
+
}
|
|
2051
|
+
/**
|
|
2052
|
+
* Options for streamForEach operation.
|
|
2053
|
+
*/
|
|
2054
|
+
interface StreamForEachOptions {
|
|
2055
|
+
/** Name for the operation (used in events) */
|
|
2056
|
+
name?: string;
|
|
2057
|
+
/** Checkpoint after every N items (default: 1 = checkpoint each item) */
|
|
2058
|
+
checkpointInterval?: number;
|
|
2059
|
+
/** Maximum concurrent processors (default: 1 = sequential) */
|
|
2060
|
+
concurrency?: number;
|
|
2061
|
+
}
|
|
2062
|
+
/**
|
|
2063
|
+
* Result from streamForEach operation.
|
|
2064
|
+
*/
|
|
2065
|
+
interface StreamForEachResult<R> {
|
|
2066
|
+
/** Results from each processed item */
|
|
2067
|
+
results: R[];
|
|
2068
|
+
/** Total items processed */
|
|
2069
|
+
processedCount: number;
|
|
2070
|
+
/** Position of last processed item */
|
|
2071
|
+
lastPosition: number;
|
|
2072
|
+
}
|
|
2073
|
+
/**
|
|
2074
|
+
* Writable stream interface - never throws, returns Results.
|
|
2264
2075
|
*
|
|
2265
|
-
*
|
|
2266
|
-
*
|
|
2267
|
-
* - **Composable**: Results can be chained with `andThen`, `map`, etc.
|
|
2268
|
-
* - **Explicit errors**: Forces you to handle errors explicitly
|
|
2076
|
+
* Use within a step to write values to a stream that can be consumed
|
|
2077
|
+
* by readers (e.g., HTTP response streaming, AI token streaming).
|
|
2269
2078
|
*
|
|
2270
|
-
* @
|
|
2271
|
-
* @returns A Result with the function's return value or the thrown error
|
|
2079
|
+
* @template T - Type of values written to the stream
|
|
2272
2080
|
*
|
|
2273
2081
|
* @example
|
|
2274
2082
|
* ```typescript
|
|
2275
|
-
*
|
|
2276
|
-
* const parsed = from(() => JSON.parse('{"key": "value"}'));
|
|
2277
|
-
* // parsed: { ok: true, value: { key: "value" } }
|
|
2083
|
+
* const writer = step.getWritable<string>({ namespace: 'ai-response' });
|
|
2278
2084
|
*
|
|
2279
|
-
*
|
|
2280
|
-
*
|
|
2085
|
+
* await step(() => generateAI({
|
|
2086
|
+
* prompt: 'Hello',
|
|
2087
|
+
* onToken: async (token) => { await writer.write(token); }
|
|
2088
|
+
* }), { key: 'generate' });
|
|
2089
|
+
*
|
|
2090
|
+
* await writer.close();
|
|
2281
2091
|
* ```
|
|
2282
2092
|
*/
|
|
2283
|
-
|
|
2093
|
+
interface StreamWriter<T> {
|
|
2094
|
+
/**
|
|
2095
|
+
* Write a value to the stream.
|
|
2096
|
+
* Returns an error if the stream is closed, aborted, or store fails.
|
|
2097
|
+
*/
|
|
2098
|
+
write(value: T): AsyncResult<void, StreamWriteError>;
|
|
2099
|
+
/**
|
|
2100
|
+
* Close the stream normally.
|
|
2101
|
+
* Signals to readers that no more data will be written.
|
|
2102
|
+
*/
|
|
2103
|
+
close(): AsyncResult<void, StreamCloseError>;
|
|
2104
|
+
/**
|
|
2105
|
+
* Abort the stream with a reason.
|
|
2106
|
+
* Use for error conditions that should terminate the stream.
|
|
2107
|
+
*/
|
|
2108
|
+
abort(reason: unknown): void;
|
|
2109
|
+
/** Whether the stream is still writable */
|
|
2110
|
+
readonly writable: boolean;
|
|
2111
|
+
/** Current write position (number of items written) */
|
|
2112
|
+
readonly position: number;
|
|
2113
|
+
/** Stream namespace */
|
|
2114
|
+
readonly namespace: string;
|
|
2115
|
+
}
|
|
2284
2116
|
/**
|
|
2285
|
-
*
|
|
2117
|
+
* Readable stream interface - returns STREAM_ENDED marker when complete.
|
|
2286
2118
|
*
|
|
2287
|
-
* Use
|
|
2119
|
+
* Use to consume values from a stream, with support for resuming from
|
|
2120
|
+
* a specific position.
|
|
2288
2121
|
*
|
|
2289
|
-
* @
|
|
2290
|
-
* @param onError - Function to map the thrown exception to a typed error
|
|
2291
|
-
* @returns A Result with the function's return value or the mapped error
|
|
2122
|
+
* @template T - Type of values read from the stream
|
|
2292
2123
|
*
|
|
2293
2124
|
* @example
|
|
2294
2125
|
* ```typescript
|
|
2295
|
-
*
|
|
2296
|
-
* const parsed = from(
|
|
2297
|
-
* () => JSON.parse(input),
|
|
2298
|
-
* (cause) => ({ type: 'PARSE_ERROR' as const, cause })
|
|
2299
|
-
* );
|
|
2300
|
-
* // parsed.error: { type: 'PARSE_ERROR', cause: SyntaxError }
|
|
2301
|
-
*
|
|
2302
|
-
* // Map to simple error codes
|
|
2303
|
-
* const value = from(
|
|
2304
|
-
* () => riskyOperation(),
|
|
2305
|
-
* () => 'OPERATION_FAILED' as const
|
|
2306
|
-
* );
|
|
2307
|
-
* ```
|
|
2308
|
-
*/
|
|
2309
|
-
declare function from<T, E>(fn: () => T, onError: (cause: unknown) => E): Ok<T> | Err<E, unknown>;
|
|
2310
|
-
/**
|
|
2311
|
-
* Wraps a Promise in a Result, converting rejections to errors.
|
|
2312
|
-
*
|
|
2313
|
-
* @remarks When to use: Wrap a Promise and keep the raw rejection as Err; use tryAsync to map errors.
|
|
2126
|
+
* const reader = getStreamReader<string>(runId, { namespace: 'ai-response' });
|
|
2314
2127
|
*
|
|
2315
|
-
*
|
|
2316
|
-
*
|
|
2317
|
-
*
|
|
2318
|
-
*
|
|
2319
|
-
*
|
|
2320
|
-
* - You're working with libraries that return Promises (fetch, database clients)
|
|
2321
|
-
* - You need to handle rejections without .catch() chains
|
|
2322
|
-
*
|
|
2323
|
-
* ## Why Use This
|
|
2324
|
-
*
|
|
2325
|
-
* - **Type-safe errors**: Convert Promise rejections to typed Result errors
|
|
2326
|
-
* - **Composable**: Results can be chained with `andThen`, `map`, etc.
|
|
2327
|
-
* - **Explicit handling**: Forces you to handle errors explicitly
|
|
2328
|
-
* - **No .catch() chains**: Cleaner than Promise.catch() patterns
|
|
2329
|
-
*
|
|
2330
|
-
* @param promise - The Promise to await (may reject)
|
|
2331
|
-
* @returns A Promise resolving to a Result with the resolved value or rejection reason
|
|
2128
|
+
* let result = await reader.read();
|
|
2129
|
+
* while (result.ok) {
|
|
2130
|
+
* response.write(result.value);
|
|
2131
|
+
* result = await reader.read();
|
|
2132
|
+
* }
|
|
2332
2133
|
*
|
|
2333
|
-
*
|
|
2334
|
-
*
|
|
2335
|
-
*
|
|
2336
|
-
* const result = await fromPromise(
|
|
2337
|
-
* fetch('/api').then(r => r.json())
|
|
2338
|
-
* );
|
|
2339
|
-
* // result.ok: true if fetch succeeded, false if rejected
|
|
2134
|
+
* if (result.error.type === 'STREAM_ENDED') {
|
|
2135
|
+
* console.log('Stream complete at position', result.error.finalPosition);
|
|
2136
|
+
* }
|
|
2340
2137
|
* ```
|
|
2341
2138
|
*/
|
|
2342
|
-
|
|
2139
|
+
interface StreamReader<T> {
|
|
2140
|
+
/**
|
|
2141
|
+
* Read the next value from the stream.
|
|
2142
|
+
* Returns StreamEndedMarker when stream is exhausted.
|
|
2143
|
+
*/
|
|
2144
|
+
read(): AsyncResult<T, StreamReadError | StreamEndedMarker>;
|
|
2145
|
+
/**
|
|
2146
|
+
* Close the reader (stop consuming).
|
|
2147
|
+
* Does not affect the underlying stream.
|
|
2148
|
+
*/
|
|
2149
|
+
close(): void;
|
|
2150
|
+
/** Whether there may be more data to read */
|
|
2151
|
+
readonly readable: boolean;
|
|
2152
|
+
/** Current read position */
|
|
2153
|
+
readonly position: number;
|
|
2154
|
+
/** Stream namespace */
|
|
2155
|
+
readonly namespace: string;
|
|
2156
|
+
}
|
|
2157
|
+
/** Unsubscribe function returned by subscribe */
|
|
2158
|
+
type Unsubscribe = () => void;
|
|
2343
2159
|
/**
|
|
2344
|
-
*
|
|
2345
|
-
*
|
|
2346
|
-
* Use this overload when you want to map Promise rejections to your typed error union.
|
|
2160
|
+
* Storage backend for stream data.
|
|
2161
|
+
* Follows the same patterns as persistence.ts adapters.
|
|
2347
2162
|
*
|
|
2348
|
-
* @
|
|
2349
|
-
*
|
|
2350
|
-
*
|
|
2163
|
+
* @example In-memory store
|
|
2164
|
+
* ```typescript
|
|
2165
|
+
* const store = createMemoryStreamStore();
|
|
2166
|
+
* ```
|
|
2351
2167
|
*
|
|
2352
|
-
* @example
|
|
2168
|
+
* @example File-based store
|
|
2353
2169
|
* ```typescript
|
|
2354
|
-
*
|
|
2355
|
-
* const result = await fromPromise(
|
|
2356
|
-
* fetch('/api').then(r => {
|
|
2357
|
-
* if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
2358
|
-
* return r.json();
|
|
2359
|
-
* }),
|
|
2360
|
-
* () => 'FETCH_FAILED' as const
|
|
2361
|
-
* );
|
|
2362
|
-
* // result.error: 'FETCH_FAILED' if fetch failed
|
|
2363
|
-
*
|
|
2364
|
-
* // Map with error details
|
|
2365
|
-
* const data = await fromPromise(
|
|
2366
|
-
* db.query(sql),
|
|
2367
|
-
* (cause) => ({ type: 'DB_ERROR' as const, message: String(cause) })
|
|
2368
|
-
* );
|
|
2170
|
+
* const store = createFileStreamStore({ directory: './streams', fs });
|
|
2369
2171
|
* ```
|
|
2370
2172
|
*/
|
|
2371
|
-
|
|
2372
|
-
/**
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
*
|
|
2400
|
-
* });
|
|
2401
|
-
* ```
|
|
2173
|
+
interface StreamStore {
|
|
2174
|
+
/**
|
|
2175
|
+
* Append an item to the stream.
|
|
2176
|
+
*/
|
|
2177
|
+
append<T>(workflowId: string, namespace: string, item: StreamItem<T>): AsyncResult<void, StreamStoreError>;
|
|
2178
|
+
/**
|
|
2179
|
+
* Read items from the stream starting at an index.
|
|
2180
|
+
* @param startIndex - Position to start reading from (0-indexed)
|
|
2181
|
+
* @param limit - Maximum number of items to read (default: all remaining)
|
|
2182
|
+
*/
|
|
2183
|
+
read<T>(workflowId: string, namespace: string, startIndex: number, limit?: number): AsyncResult<StreamItem<T>[], StreamStoreError>;
|
|
2184
|
+
/**
|
|
2185
|
+
* Get metadata about a stream.
|
|
2186
|
+
* Returns undefined if stream doesn't exist.
|
|
2187
|
+
*/
|
|
2188
|
+
getMetadata(workflowId: string, namespace: string): AsyncResult<StreamMetadata | undefined, StreamStoreError>;
|
|
2189
|
+
/**
|
|
2190
|
+
* Mark stream as closed.
|
|
2191
|
+
*/
|
|
2192
|
+
closeStream(workflowId: string, namespace: string): AsyncResult<void, StreamStoreError>;
|
|
2193
|
+
/**
|
|
2194
|
+
* Subscribe to new items in a stream.
|
|
2195
|
+
* Callback is invoked for each new item written.
|
|
2196
|
+
* Returns unsubscribe function.
|
|
2197
|
+
*/
|
|
2198
|
+
subscribe<T>(workflowId: string, namespace: string, callback: (item: StreamItem<T>) => void): Unsubscribe;
|
|
2199
|
+
}
|
|
2200
|
+
/**
|
|
2201
|
+
* Check if an error is a StreamEndedMarker.
|
|
2402
2202
|
*/
|
|
2403
|
-
declare function
|
|
2203
|
+
declare function isStreamEnded(error: unknown): error is StreamEndedMarker;
|
|
2404
2204
|
/**
|
|
2405
|
-
*
|
|
2406
|
-
*
|
|
2407
|
-
* Use this overload when you want to map errors to your typed error union.
|
|
2408
|
-
*
|
|
2409
|
-
* @param fn - The async function to execute (may throw or reject)
|
|
2410
|
-
* @param onError - Function to map the error (exception or rejection) to a typed error
|
|
2411
|
-
* @returns A Promise resolving to a Result with the function's return value or mapped error
|
|
2412
|
-
*
|
|
2413
|
-
* @example
|
|
2414
|
-
* ```typescript
|
|
2415
|
-
* // Map errors to typed errors
|
|
2416
|
-
* const result = await tryAsync(
|
|
2417
|
-
* async () => await fetchData(),
|
|
2418
|
-
* () => 'FETCH_ERROR' as const
|
|
2419
|
-
* );
|
|
2420
|
-
*
|
|
2421
|
-
* // Map with error details
|
|
2422
|
-
* const data = await tryAsync(
|
|
2423
|
-
* async () => await processFile(path),
|
|
2424
|
-
* (cause) => ({ type: 'PROCESSING_ERROR' as const, cause })
|
|
2425
|
-
* );
|
|
2426
|
-
* ```
|
|
2205
|
+
* Check if an error is a StreamWriteError.
|
|
2427
2206
|
*/
|
|
2428
|
-
declare function
|
|
2207
|
+
declare function isStreamWriteError(error: unknown): error is StreamWriteError;
|
|
2429
2208
|
/**
|
|
2430
|
-
*
|
|
2431
|
-
*
|
|
2432
|
-
* @remarks When to use: Turn null/undefined into a typed error before continuing.
|
|
2433
|
-
*
|
|
2434
|
-
* ## When to Use
|
|
2435
|
-
*
|
|
2436
|
-
* Use `fromNullable()` when:
|
|
2437
|
-
* - You have a value that might be `null` or `undefined`
|
|
2438
|
-
* - You want to treat null/undefined as an error case
|
|
2439
|
-
* - You're working with APIs that return nullable values (DOM APIs, optional properties)
|
|
2440
|
-
* - You want to avoid null checks scattered throughout your code
|
|
2441
|
-
*
|
|
2442
|
-
* ## Why Use This
|
|
2443
|
-
*
|
|
2444
|
-
* - **Type-safe**: Converts nullable types to non-nullable Results
|
|
2445
|
-
* - **Explicit errors**: Forces you to handle null/undefined cases
|
|
2446
|
-
* - **Composable**: Results can be chained with `andThen`, `map`, etc.
|
|
2447
|
-
* - **No null checks**: Eliminates need for `if (value == null)` checks
|
|
2448
|
-
*
|
|
2449
|
-
* @param value - The value that may be null or undefined
|
|
2450
|
-
* @param onNull - Function that returns an error when value is null/undefined
|
|
2451
|
-
* @returns A Result with the value if not null/undefined, otherwise the error from `onNull`
|
|
2452
|
-
*
|
|
2453
|
-
* @example
|
|
2454
|
-
* ```typescript
|
|
2455
|
-
* // Convert DOM element lookup
|
|
2456
|
-
* const element = fromNullable(
|
|
2457
|
-
* document.getElementById('app'),
|
|
2458
|
-
* () => 'ELEMENT_NOT_FOUND' as const
|
|
2459
|
-
* );
|
|
2460
|
-
*
|
|
2461
|
-
* // Convert optional property
|
|
2462
|
-
* const userId = fromNullable(
|
|
2463
|
-
* user.id,
|
|
2464
|
-
* () => 'USER_ID_MISSING' as const
|
|
2465
|
-
* );
|
|
2466
|
-
*
|
|
2467
|
-
* // Convert database query result
|
|
2468
|
-
* const record = fromNullable(
|
|
2469
|
-
* await db.find(id),
|
|
2470
|
-
* () => ({ type: 'NOT_FOUND' as const, id })
|
|
2471
|
-
* );
|
|
2472
|
-
* ```
|
|
2209
|
+
* Check if an error is a StreamReadError.
|
|
2473
2210
|
*/
|
|
2474
|
-
declare function
|
|
2211
|
+
declare function isStreamReadError(error: unknown): error is StreamReadError;
|
|
2475
2212
|
/**
|
|
2476
|
-
*
|
|
2477
|
-
*
|
|
2478
|
-
* @remarks When to use: Transform only the Ok value while leaving Err untouched.
|
|
2479
|
-
*
|
|
2480
|
-
* ## When to Use
|
|
2481
|
-
*
|
|
2482
|
-
* Use `map()` when:
|
|
2483
|
-
* - You need to transform a success value to another type
|
|
2484
|
-
* - You want to apply a pure function to the value
|
|
2485
|
-
* - You're building a pipeline of transformations
|
|
2486
|
-
* - The transformation cannot fail (use `andThen` if it can fail)
|
|
2487
|
-
*
|
|
2488
|
-
* ## Why Use This
|
|
2489
|
-
*
|
|
2490
|
-
* - **Functional style**: Composable, chainable transformations
|
|
2491
|
-
* - **Error-preserving**: Errors pass through unchanged
|
|
2492
|
-
* - **Type-safe**: TypeScript tracks the transformation
|
|
2493
|
-
* - **No unwrapping**: Avoids manual `if (r.ok)` checks
|
|
2494
|
-
*
|
|
2495
|
-
* @param r - The Result to transform
|
|
2496
|
-
* @param fn - Pure function that transforms the success value (must not throw)
|
|
2497
|
-
* @returns A new Result with the transformed value, or the original error if `r` was an error
|
|
2498
|
-
*
|
|
2499
|
-
* @example
|
|
2500
|
-
* ```typescript
|
|
2501
|
-
* // Transform numeric value
|
|
2502
|
-
* const doubled = map(ok(21), n => n * 2);
|
|
2503
|
-
* // doubled: { ok: true, value: 42 }
|
|
2504
|
-
*
|
|
2505
|
-
* // Transform object property
|
|
2506
|
-
* const name = map(fetchUser(id), user => user.name);
|
|
2507
|
-
*
|
|
2508
|
-
* // Chain transformations
|
|
2509
|
-
* const formatted = map(
|
|
2510
|
-
* map(parseNumber(input), n => n * 2),
|
|
2511
|
-
* n => `Result: ${n}`
|
|
2512
|
-
* );
|
|
2513
|
-
* ```
|
|
2213
|
+
* Check if an error is a StreamStoreError.
|
|
2514
2214
|
*/
|
|
2515
|
-
declare function
|
|
2516
|
-
declare function map<T, U, E, C>(r: Err<E, C>, fn: (value: T) => U): Err<E, C>;
|
|
2517
|
-
declare function map<T, U, E, C>(r: Result<T, E, C>, fn: (value: T) => U): Result<U, E, C>;
|
|
2215
|
+
declare function isStreamStoreError(error: unknown): error is StreamStoreError;
|
|
2518
2216
|
/**
|
|
2519
|
-
*
|
|
2520
|
-
*
|
|
2521
|
-
* @remarks When to use: Retype or normalize errors while leaving Ok values unchanged.
|
|
2522
|
-
*
|
|
2523
|
-
* ## When to Use
|
|
2524
|
-
*
|
|
2525
|
-
* Use `mapError()` when:
|
|
2526
|
-
* - You need to normalize or transform error types
|
|
2527
|
-
* - You want to convert errors to a different error type
|
|
2528
|
-
* - You're building error handling pipelines
|
|
2529
|
-
* - You need to format error messages or codes
|
|
2530
|
-
*
|
|
2531
|
-
* ## Why Use This
|
|
2532
|
-
*
|
|
2533
|
-
* - **Error normalization**: Convert errors to a common format
|
|
2534
|
-
* - **Type transformation**: Change error type while preserving value type
|
|
2535
|
-
* - **Composable**: Can be chained with other transformers
|
|
2536
|
-
* - **Success-preserving**: Success values pass through unchanged
|
|
2537
|
-
*
|
|
2538
|
-
* @param r - The Result to transform
|
|
2539
|
-
* @param fn - Function that transforms the error value (must not throw)
|
|
2540
|
-
* @returns A new Result with the original value, or the transformed error if `r` was an error
|
|
2541
|
-
*
|
|
2542
|
-
* @example
|
|
2543
|
-
* ```typescript
|
|
2544
|
-
* // Normalize error codes
|
|
2545
|
-
* const normalized = mapError(err('not_found'), e => e.toUpperCase());
|
|
2546
|
-
* // normalized: { ok: false, error: 'NOT_FOUND' }
|
|
2547
|
-
*
|
|
2548
|
-
* // Convert error types
|
|
2549
|
-
* const typed = mapError(
|
|
2550
|
-
* err('404'),
|
|
2551
|
-
* code => ({ type: 'HTTP_ERROR' as const, status: parseInt(code) })
|
|
2552
|
-
* );
|
|
2553
|
-
*
|
|
2554
|
-
* // Format error messages
|
|
2555
|
-
* const formatted = mapError(
|
|
2556
|
-
* err('PARSE_ERROR'),
|
|
2557
|
-
* code => `Failed to parse: ${code}`
|
|
2558
|
-
* );
|
|
2559
|
-
* ```
|
|
2217
|
+
* Check if an error is a StreamBackpressureError.
|
|
2560
2218
|
*/
|
|
2561
|
-
declare function
|
|
2219
|
+
declare function isStreamBackpressureError(error: unknown): error is StreamBackpressureError;
|
|
2562
2220
|
/**
|
|
2563
|
-
*
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
*
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
*
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
*
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
*
|
|
2580
|
-
|
|
2221
|
+
* Create a StreamWriteError.
|
|
2222
|
+
*/
|
|
2223
|
+
declare function streamWriteError(reason: StreamWriteError["reason"], message: string, cause?: unknown): StreamWriteError;
|
|
2224
|
+
/**
|
|
2225
|
+
* Create a StreamReadError.
|
|
2226
|
+
*/
|
|
2227
|
+
declare function streamReadError(reason: StreamReadError["reason"], message: string, cause?: unknown): StreamReadError;
|
|
2228
|
+
/**
|
|
2229
|
+
* Create a StreamCloseError.
|
|
2230
|
+
*/
|
|
2231
|
+
declare function streamCloseError(reason: StreamCloseError["reason"], message: string, cause?: unknown): StreamCloseError;
|
|
2232
|
+
/**
|
|
2233
|
+
* Create a StreamStoreError.
|
|
2234
|
+
*/
|
|
2235
|
+
declare function streamStoreError(reason: StreamStoreError["reason"], message: string, cause?: unknown): StreamStoreError;
|
|
2236
|
+
/**
|
|
2237
|
+
* Create a StreamEndedMarker.
|
|
2238
|
+
*/
|
|
2239
|
+
declare function streamEnded(finalPosition: number): StreamEndedMarker;
|
|
2240
|
+
/**
|
|
2241
|
+
* Create a StreamBackpressureError.
|
|
2242
|
+
*/
|
|
2243
|
+
declare function streamBackpressureError(bufferedCount: number, highWaterMark: number): StreamBackpressureError;
|
|
2244
|
+
|
|
2245
|
+
/**
|
|
2246
|
+
* awaitly/persistence
|
|
2581
2247
|
*
|
|
2582
|
-
*
|
|
2583
|
-
*
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2248
|
+
* Simplified Persistence API for workflow snapshots.
|
|
2249
|
+
* Provides JSON-serializable snapshot format and store adapters.
|
|
2250
|
+
*/
|
|
2251
|
+
|
|
2252
|
+
/**
|
|
2253
|
+
* Enforce JSON-safety at type level.
|
|
2254
|
+
* Only allows values that can be safely serialized with JSON.stringify.
|
|
2255
|
+
*/
|
|
2256
|
+
type JSONValue = null | boolean | number | string | JSONValue[] | {
|
|
2257
|
+
[k: string]: JSONValue;
|
|
2258
|
+
};
|
|
2259
|
+
/**
|
|
2260
|
+
* Canonical error wire format - handles both Error instances and thrown non-Errors.
|
|
2261
|
+
* This is the single source of truth for serialized errors in snapshots.
|
|
2262
|
+
*/
|
|
2263
|
+
type SerializedCause = {
|
|
2264
|
+
type: "error";
|
|
2265
|
+
name: string;
|
|
2266
|
+
message: string;
|
|
2267
|
+
stack?: string;
|
|
2268
|
+
cause?: SerializedCause;
|
|
2269
|
+
} | {
|
|
2270
|
+
type: "thrown";
|
|
2271
|
+
originalType?: string;
|
|
2272
|
+
value?: JSONValue;
|
|
2273
|
+
stringRepresentation: string;
|
|
2274
|
+
truncated?: true;
|
|
2275
|
+
};
|
|
2276
|
+
/**
|
|
2277
|
+
* Single source of truth for step outcome (no error/cause confusion).
|
|
2278
|
+
* Uses discriminated union with `ok` field.
|
|
2279
|
+
*/
|
|
2280
|
+
type StepResult = {
|
|
2281
|
+
ok: true;
|
|
2282
|
+
value: JSONValue;
|
|
2283
|
+
} | {
|
|
2284
|
+
ok: false;
|
|
2285
|
+
error: JSONValue;
|
|
2286
|
+
cause: SerializedCause;
|
|
2287
|
+
meta?: {
|
|
2288
|
+
origin: "result" | "throw";
|
|
2289
|
+
};
|
|
2290
|
+
};
|
|
2291
|
+
/**
|
|
2292
|
+
* JSON-serializable workflow snapshot.
|
|
2293
|
+
* Designed to be passed directly to JSON.stringify without special handling.
|
|
2587
2294
|
*
|
|
2588
2295
|
* @example
|
|
2589
2296
|
* ```typescript
|
|
2590
|
-
* //
|
|
2591
|
-
*
|
|
2592
|
-
* ok: (user) => `Hello ${user.name}`,
|
|
2593
|
-
* err: (error) => `Error: ${error}`,
|
|
2594
|
-
* });
|
|
2595
|
-
*
|
|
2596
|
-
* // Transform to API response
|
|
2597
|
-
* const response = match(operation(), {
|
|
2598
|
-
* ok: (data) => ({ status: 200, body: data }),
|
|
2599
|
-
* err: (error) => ({ status: 400, error: String(error) }),
|
|
2600
|
-
* });
|
|
2297
|
+
* // Persist
|
|
2298
|
+
* localStorage.setItem('wf-123', JSON.stringify(wf.getSnapshot()));
|
|
2601
2299
|
*
|
|
2602
|
-
* //
|
|
2603
|
-
* const
|
|
2604
|
-
*
|
|
2605
|
-
*
|
|
2606
|
-
* });
|
|
2300
|
+
* // Restore (safe pattern - storage can be empty/corrupt)
|
|
2301
|
+
* const raw = localStorage.getItem('wf-123');
|
|
2302
|
+
* const snapshot = raw ? JSON.parse(raw) : null;
|
|
2303
|
+
* createWorkflow(deps, { snapshot }); // null = fresh start
|
|
2607
2304
|
* ```
|
|
2608
2305
|
*/
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
*
|
|
2650
|
-
* andThen(operation2(value1), value2 =>
|
|
2651
|
-
* ok({ value1, value2 })
|
|
2652
|
-
* )
|
|
2653
|
-
* )
|
|
2654
|
-
* ```
|
|
2655
|
-
*
|
|
2656
|
-
* @param r - The first Result
|
|
2657
|
-
* @param fn - Function that takes the success value and returns a new Result (may fail)
|
|
2658
|
-
* @returns The Result from `fn` if `r` was successful, otherwise the original error
|
|
2659
|
-
*
|
|
2660
|
-
* @example
|
|
2661
|
-
* ```typescript
|
|
2662
|
-
* // Chain dependent operations
|
|
2663
|
-
* const userPosts = andThen(
|
|
2664
|
-
* fetchUser('1'),
|
|
2665
|
-
* user => fetchPosts(user.id)
|
|
2666
|
-
* );
|
|
2667
|
-
*
|
|
2668
|
-
* // Build complex pipelines
|
|
2669
|
-
* const result = andThen(parseInput(input), parsed =>
|
|
2670
|
-
* andThen(validate(parsed), validated =>
|
|
2671
|
-
* process(validated)
|
|
2672
|
-
* )
|
|
2673
|
-
* );
|
|
2674
|
-
*
|
|
2675
|
-
* // Chain with different error types
|
|
2676
|
-
* const data = andThen(
|
|
2677
|
-
* fetchUser(id), // Returns Result<User, 'FETCH_ERROR'>
|
|
2678
|
-
* user => fetchPosts(user.id) // Returns Result<Post[], 'NOT_FOUND'>
|
|
2679
|
-
* );
|
|
2680
|
-
* // data.error: 'FETCH_ERROR' | 'NOT_FOUND'
|
|
2681
|
-
* ```
|
|
2306
|
+
interface WorkflowSnapshot {
|
|
2307
|
+
/** Snapshot format version (literal type - bump when shape changes) */
|
|
2308
|
+
formatVersion: 1;
|
|
2309
|
+
/** Workflow name (from createWorkflow first argument). */
|
|
2310
|
+
workflowName?: string;
|
|
2311
|
+
/** Step results keyed by step ID. Uses Object.create(null) internally. */
|
|
2312
|
+
steps: Record<string, StepResult>;
|
|
2313
|
+
/** Execution state metadata */
|
|
2314
|
+
execution: {
|
|
2315
|
+
status: "running" | "completed" | "failed";
|
|
2316
|
+
/** ISO timestamp (UTC toISOString()) */
|
|
2317
|
+
lastUpdated: string;
|
|
2318
|
+
/** ISO timestamp if finished */
|
|
2319
|
+
completedAt?: string;
|
|
2320
|
+
/**
|
|
2321
|
+
* For paused/running workflows: the step key of the current step.
|
|
2322
|
+
* Aligns with Workflow Diagram DSL step state ids (see awaitly/workflow diagram-dsl)
|
|
2323
|
+
* so visualizers can highlight the current node.
|
|
2324
|
+
*/
|
|
2325
|
+
currentStepId?: string;
|
|
2326
|
+
};
|
|
2327
|
+
/** Optional metadata for workflow identification and replay */
|
|
2328
|
+
metadata?: {
|
|
2329
|
+
/** Detect wrong snapshot for wrong workflow */
|
|
2330
|
+
workflowId?: string;
|
|
2331
|
+
/** Optional: detect definition changes (user-supplied, advisory only) */
|
|
2332
|
+
definitionHash?: string;
|
|
2333
|
+
/** Original input for replay */
|
|
2334
|
+
input?: JSONValue;
|
|
2335
|
+
[key: string]: JSONValue | undefined;
|
|
2336
|
+
};
|
|
2337
|
+
/** Warnings for lossy serialization (keeps step results pure) */
|
|
2338
|
+
warnings?: Array<{
|
|
2339
|
+
type: "lossy_value";
|
|
2340
|
+
stepId: string;
|
|
2341
|
+
path: string;
|
|
2342
|
+
reason: "non-json" | "circular" | "encode-failed";
|
|
2343
|
+
}>;
|
|
2344
|
+
}
|
|
2345
|
+
/**
|
|
2346
|
+
* Warning entry for lossy value serialization.
|
|
2682
2347
|
*/
|
|
2683
|
-
|
|
2684
|
-
declare function andThen<T, F, C2>(r: Ok<T>, fn: (value: T) => Err<F, C2>): Err<F, C2>;
|
|
2685
|
-
declare function andThen<T, U, F, C2>(r: Ok<T>, fn: (value: T) => Result<U, F, C2>): Result<U, F, C2>;
|
|
2686
|
-
declare function andThen<T, U, E, F, C1, C2>(r: Err<E, C1>, fn: (value: T) => Result<U, F, C2>): Err<E, C1>;
|
|
2687
|
-
declare function andThen<T, U, E, F, C1, C2>(r: Result<T, E, C1>, fn: (value: T) => Result<U, F, C2>): Result<U, E | F, C1 | C2>;
|
|
2348
|
+
type SnapshotWarning = NonNullable<WorkflowSnapshot["warnings"]>[number];
|
|
2688
2349
|
/**
|
|
2689
|
-
*
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
*
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2350
|
+
* Error thrown when snapshot structure is invalid.
|
|
2351
|
+
*/
|
|
2352
|
+
declare class SnapshotFormatError extends Error {
|
|
2353
|
+
readonly errors: string[];
|
|
2354
|
+
constructor(message: string, errors?: string[]);
|
|
2355
|
+
}
|
|
2356
|
+
/**
|
|
2357
|
+
* Error thrown when snapshot doesn't match workflow (unknown steps, workflowId mismatch).
|
|
2358
|
+
*/
|
|
2359
|
+
declare class SnapshotMismatchError extends Error {
|
|
2360
|
+
readonly mismatchType: "unknown_steps" | "workflow_id" | "definition_hash";
|
|
2361
|
+
readonly details?: {
|
|
2362
|
+
unknownSteps?: string[];
|
|
2363
|
+
snapshotWorkflowId?: string;
|
|
2364
|
+
expectedWorkflowId?: string;
|
|
2365
|
+
snapshotHash?: string;
|
|
2366
|
+
expectedHash?: string;
|
|
2367
|
+
} | undefined;
|
|
2368
|
+
constructor(message: string, mismatchType: "unknown_steps" | "workflow_id" | "definition_hash", details?: {
|
|
2369
|
+
unknownSteps?: string[];
|
|
2370
|
+
snapshotWorkflowId?: string;
|
|
2371
|
+
expectedWorkflowId?: string;
|
|
2372
|
+
snapshotHash?: string;
|
|
2373
|
+
expectedHash?: string;
|
|
2374
|
+
} | undefined);
|
|
2375
|
+
}
|
|
2376
|
+
/**
|
|
2377
|
+
* Error thrown when decode fails during restore.
|
|
2378
|
+
*/
|
|
2379
|
+
declare class SnapshotDecodeError extends Error {
|
|
2380
|
+
readonly stepId: string;
|
|
2381
|
+
readonly originalError?: unknown | undefined;
|
|
2382
|
+
constructor(message: string, stepId: string, originalError?: unknown | undefined);
|
|
2383
|
+
}
|
|
2384
|
+
/**
|
|
2385
|
+
* Light check to see if an object looks like a WorkflowSnapshot.
|
|
2386
|
+
* Cheap check for basic structure - use validateSnapshot() for full validation.
|
|
2711
2387
|
*
|
|
2712
2388
|
* @example
|
|
2713
2389
|
* ```typescript
|
|
2714
|
-
*
|
|
2715
|
-
*
|
|
2716
|
-
*
|
|
2717
|
-
*
|
|
2718
|
-
* // Debug in pipeline
|
|
2719
|
-
* const debugged = pipe(
|
|
2720
|
-
* fetchUser(id),
|
|
2721
|
-
* r => tap(r, user => console.log('Fetched:', user)),
|
|
2722
|
-
* r => map(r, user => user.name)
|
|
2723
|
-
* );
|
|
2724
|
-
*
|
|
2725
|
-
* // Mutate external state
|
|
2726
|
-
* const tracked = tap(result, data => {
|
|
2727
|
-
* analytics.track('operation_success', data);
|
|
2728
|
-
* });
|
|
2390
|
+
* const raw = JSON.parse(localStorage.getItem('wf-123') || 'null');
|
|
2391
|
+
* if (looksLikeWorkflowSnapshot(raw)) {
|
|
2392
|
+
* createWorkflow(deps, { snapshot: raw });
|
|
2393
|
+
* }
|
|
2729
2394
|
* ```
|
|
2730
2395
|
*/
|
|
2731
|
-
declare function
|
|
2396
|
+
declare function looksLikeWorkflowSnapshot(obj: unknown): obj is WorkflowSnapshot;
|
|
2732
2397
|
/**
|
|
2733
|
-
*
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
*
|
|
2738
|
-
*
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2398
|
+
* Type guard for WorkflowSnapshot. Same as looksLikeWorkflowSnapshot; use for consistent naming with isResumeState / isSerializedResumeState.
|
|
2399
|
+
*/
|
|
2400
|
+
declare const isWorkflowSnapshot: typeof looksLikeWorkflowSnapshot;
|
|
2401
|
+
/**
|
|
2402
|
+
* Full validation with detailed errors.
|
|
2403
|
+
* Returns either a validated snapshot or an array of validation errors.
|
|
2404
|
+
*/
|
|
2405
|
+
declare function validateSnapshot(obj: unknown): {
|
|
2406
|
+
valid: true;
|
|
2407
|
+
snapshot: WorkflowSnapshot;
|
|
2408
|
+
} | {
|
|
2409
|
+
valid: false;
|
|
2410
|
+
errors: string[];
|
|
2411
|
+
};
|
|
2412
|
+
/**
|
|
2413
|
+
* Throwing helper for cleaner code.
|
|
2414
|
+
* Validates a snapshot and throws SnapshotFormatError if invalid.
|
|
2746
2415
|
*
|
|
2747
|
-
*
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2416
|
+
* @throws {SnapshotFormatError} If snapshot is invalid
|
|
2417
|
+
*/
|
|
2418
|
+
declare function assertValidSnapshot(obj: unknown): WorkflowSnapshot;
|
|
2419
|
+
/**
|
|
2420
|
+
* Merge two snapshots (for incremental updates).
|
|
2421
|
+
* Delta steps overwrite base steps; execution from delta; metadata shallow merge.
|
|
2422
|
+
*/
|
|
2423
|
+
declare function mergeSnapshots(base: WorkflowSnapshot, delta: WorkflowSnapshot): WorkflowSnapshot;
|
|
2424
|
+
/**
|
|
2425
|
+
* Serialize an Error object to SerializedCause format.
|
|
2426
|
+
* Preserves Error.cause recursively.
|
|
2427
|
+
*/
|
|
2428
|
+
declare function serializeError(error: Error): SerializedCause;
|
|
2429
|
+
/**
|
|
2430
|
+
* Serialize a non-Error thrown value to SerializedCause format.
|
|
2431
|
+
*/
|
|
2432
|
+
declare function serializeThrown(value: unknown): SerializedCause;
|
|
2433
|
+
/**
|
|
2434
|
+
* Deserialize a SerializedCause back to its original form.
|
|
2435
|
+
*/
|
|
2436
|
+
declare function deserializeCauseNew(serialized: SerializedCause): unknown;
|
|
2437
|
+
/**
|
|
2438
|
+
* Simplified store interface for workflow snapshot persistence.
|
|
2439
|
+
* Works directly with WorkflowSnapshot objects.
|
|
2751
2440
|
*
|
|
2752
|
-
*
|
|
2753
|
-
*
|
|
2754
|
-
*
|
|
2441
|
+
* Adapters may implement an extended contract (see awaitly/workflow): save can accept
|
|
2442
|
+
* WorkflowSnapshot | ResumeState; load can return WorkflowSnapshot | ResumeState | null.
|
|
2443
|
+
* Use isWorkflowSnapshot / isSerializedResumeState and serializeResumeState / deserializeResumeState
|
|
2444
|
+
* when branching. For type-safe restore, use store.loadResumeState(id) or toResumeState(await store.load(id)).
|
|
2755
2445
|
*
|
|
2756
2446
|
* @example
|
|
2757
2447
|
* ```typescript
|
|
2758
|
-
*
|
|
2759
|
-
*
|
|
2760
|
-
* console.error('Error:', error, cause);
|
|
2761
|
-
* });
|
|
2448
|
+
* import { postgres } from 'awaitly-postgres';
|
|
2449
|
+
* import { createWorkflow } from 'awaitly/workflow';
|
|
2762
2450
|
*
|
|
2763
|
-
*
|
|
2764
|
-
* const
|
|
2765
|
-
* errorTracker.report(error, cause);
|
|
2766
|
-
* });
|
|
2451
|
+
* const store = postgres('postgresql://localhost/mydb');
|
|
2452
|
+
* const workflow = createWorkflow(deps);
|
|
2767
2453
|
*
|
|
2768
|
-
* //
|
|
2769
|
-
* const
|
|
2770
|
-
*
|
|
2771
|
-
*
|
|
2772
|
-
*
|
|
2773
|
-
* );
|
|
2454
|
+
* // Run and persist resume state
|
|
2455
|
+
* const { result, resumeState } = await workflow.runWithState(fn);
|
|
2456
|
+
* await store.save('wf-123', resumeState);
|
|
2457
|
+
*
|
|
2458
|
+
* // Restore
|
|
2459
|
+
* const loaded = await store.load('wf-123');
|
|
2460
|
+
* const resumeState = toResumeState(loaded);
|
|
2461
|
+
* if (resumeState) await workflow.run(fn, { resumeState });
|
|
2774
2462
|
* ```
|
|
2775
2463
|
*/
|
|
2776
|
-
|
|
2464
|
+
interface SnapshotStore {
|
|
2465
|
+
/** Save a workflow snapshot (upsert - insert or update). Adapters may also accept ResumeState. */
|
|
2466
|
+
save(id: string, snapshot: WorkflowSnapshot): Promise<void>;
|
|
2467
|
+
/** Load a workflow snapshot. Returns null if not found. Adapters may return ResumeState when stored as such. */
|
|
2468
|
+
load(id: string): Promise<WorkflowSnapshot | null>;
|
|
2469
|
+
/** Delete a workflow snapshot. */
|
|
2470
|
+
delete(id: string): Promise<void>;
|
|
2471
|
+
/** List workflow IDs with their last update time. */
|
|
2472
|
+
list(options?: {
|
|
2473
|
+
prefix?: string;
|
|
2474
|
+
limit?: number;
|
|
2475
|
+
}): Promise<Array<{
|
|
2476
|
+
id: string;
|
|
2477
|
+
updatedAt: string;
|
|
2478
|
+
}>>;
|
|
2479
|
+
/** Clean shutdown for tests/graceful exit. */
|
|
2480
|
+
close(): Promise<void>;
|
|
2481
|
+
}
|
|
2777
2482
|
/**
|
|
2778
|
-
*
|
|
2779
|
-
*
|
|
2780
|
-
* @remarks When to use: Transform Ok values with a function that might throw and capture the failure.
|
|
2781
|
-
*
|
|
2782
|
-
* ## When to Use
|
|
2783
|
-
*
|
|
2784
|
-
* Use `mapTry()` when:
|
|
2785
|
-
* - Your transform function might throw exceptions
|
|
2786
|
-
* - You want to convert transform errors to typed errors
|
|
2787
|
-
* - You're working with libraries that throw (e.g., JSON.parse, Date parsing)
|
|
2788
|
-
* - You need to handle both Result errors and transform exceptions
|
|
2789
|
-
*
|
|
2790
|
-
* ## Why Use This Instead of `map`
|
|
2791
|
-
*
|
|
2792
|
-
* - **Exception-safe**: Catches exceptions from the transform function
|
|
2793
|
-
* - **Error mapping**: Converts thrown exceptions to typed errors
|
|
2794
|
-
* - **Dual error handling**: Handles both Result errors and transform exceptions
|
|
2795
|
-
*
|
|
2796
|
-
* @param result - The Result to transform
|
|
2797
|
-
* @param transform - Function to transform the success value (may throw exceptions)
|
|
2798
|
-
* @param onError - Function to map thrown exceptions to a typed error
|
|
2799
|
-
* @returns A Result with:
|
|
2800
|
-
* - Transformed value if both Result and transform succeed
|
|
2801
|
-
* - Original error if Result was an error
|
|
2802
|
-
* - Transform error if transform threw an exception
|
|
2803
|
-
*
|
|
2804
|
-
* @example
|
|
2805
|
-
* ```typescript
|
|
2806
|
-
* // Safe JSON parsing
|
|
2807
|
-
* const parsed = mapTry(
|
|
2808
|
-
* ok('{"key": "value"}'),
|
|
2809
|
-
* JSON.parse,
|
|
2810
|
-
* () => 'PARSE_ERROR' as const
|
|
2811
|
-
* );
|
|
2812
|
-
*
|
|
2813
|
-
* // Safe date parsing
|
|
2814
|
-
* const date = mapTry(
|
|
2815
|
-
* ok('2024-01-01'),
|
|
2816
|
-
* str => new Date(str),
|
|
2817
|
-
* () => 'INVALID_DATE' as const
|
|
2818
|
-
* );
|
|
2819
|
-
*
|
|
2820
|
-
* // Transform with error details
|
|
2821
|
-
* const processed = mapTry(
|
|
2822
|
-
* result,
|
|
2823
|
-
* value => riskyTransform(value),
|
|
2824
|
-
* (cause) => ({ type: 'TRANSFORM_ERROR' as const, cause })
|
|
2825
|
-
* );
|
|
2826
|
-
* ```
|
|
2483
|
+
* Options for the in-memory cache adapter.
|
|
2827
2484
|
*/
|
|
2828
|
-
|
|
2485
|
+
interface MemoryCacheOptions {
|
|
2486
|
+
/**
|
|
2487
|
+
* Maximum number of entries to store.
|
|
2488
|
+
* Oldest entries are evicted when limit is reached.
|
|
2489
|
+
*/
|
|
2490
|
+
maxSize?: number;
|
|
2491
|
+
/**
|
|
2492
|
+
* Time-to-live in milliseconds.
|
|
2493
|
+
* Entries are automatically removed after this duration.
|
|
2494
|
+
*/
|
|
2495
|
+
ttl?: number;
|
|
2496
|
+
}
|
|
2829
2497
|
/**
|
|
2830
|
-
*
|
|
2831
|
-
*
|
|
2832
|
-
* @remarks When to use: Transform errors when the mapping might throw and you want that captured.
|
|
2833
|
-
*
|
|
2834
|
-
* ## When to Use
|
|
2835
|
-
*
|
|
2836
|
-
* Use `mapErrorTry()` when:
|
|
2837
|
-
* - Your error transform function might throw exceptions
|
|
2838
|
-
* - You're doing complex error transformations (e.g., string formatting, object construction)
|
|
2839
|
-
* - You want to handle both Result errors and transform exceptions
|
|
2840
|
-
* - You need to safely normalize error types
|
|
2498
|
+
* Create an in-memory StepCache with optional LRU eviction and TTL.
|
|
2841
2499
|
*
|
|
2842
|
-
*
|
|
2843
|
-
*
|
|
2844
|
-
* - **Exception-safe**: Catches exceptions from the error transform function
|
|
2845
|
-
* - **Error mapping**: Converts thrown exceptions to typed errors
|
|
2846
|
-
* - **Dual error handling**: Handles both Result errors and transform exceptions
|
|
2847
|
-
*
|
|
2848
|
-
* @param result - The Result to transform
|
|
2849
|
-
* @param transform - Function to transform the error value (may throw exceptions)
|
|
2850
|
-
* @param onError - Function to map thrown exceptions to a typed error
|
|
2851
|
-
* @returns A Result with:
|
|
2852
|
-
* - Original value if Result was successful
|
|
2853
|
-
* - Transformed error if both Result was error and transform succeeded
|
|
2854
|
-
* - Transform error if transform threw an exception
|
|
2500
|
+
* @param options - Cache options
|
|
2501
|
+
* @returns StepCache implementation
|
|
2855
2502
|
*
|
|
2856
2503
|
* @example
|
|
2857
2504
|
* ```typescript
|
|
2858
|
-
*
|
|
2859
|
-
* const
|
|
2860
|
-
* err('not_found'),
|
|
2861
|
-
* e => e.toUpperCase(), // Might throw if e is not a string
|
|
2862
|
-
* () => 'FORMAT_ERROR' as const
|
|
2863
|
-
* );
|
|
2864
|
-
*
|
|
2865
|
-
* // Complex error transformation
|
|
2866
|
-
* const normalized = mapErrorTry(
|
|
2867
|
-
* result,
|
|
2868
|
-
* error => ({ type: 'NORMALIZED', message: String(error) }),
|
|
2869
|
-
* () => 'TRANSFORM_ERROR' as const
|
|
2870
|
-
* );
|
|
2505
|
+
* const cache = createMemoryCache({ maxSize: 1000, ttl: 60000 });
|
|
2506
|
+
* const workflow = createWorkflow(deps, { cache });
|
|
2871
2507
|
* ```
|
|
2872
2508
|
*/
|
|
2873
|
-
declare function
|
|
2509
|
+
declare function createMemoryCache(options?: MemoryCacheOptions): StepCache;
|
|
2510
|
+
|
|
2511
|
+
/**
|
|
2512
|
+
* Workflow type definitions.
|
|
2513
|
+
* Pure types and interfaces; no runtime code.
|
|
2514
|
+
*/
|
|
2515
|
+
|
|
2874
2516
|
/**
|
|
2875
|
-
*
|
|
2517
|
+
* Interface for step result caching.
|
|
2518
|
+
* Implement this interface to provide custom caching strategies.
|
|
2519
|
+
* A simple Map<string, Result> works for in-memory caching.
|
|
2876
2520
|
*
|
|
2877
|
-
* ## When
|
|
2521
|
+
* ## When Cache is Populated
|
|
2878
2522
|
*
|
|
2879
|
-
*
|
|
2880
|
-
*
|
|
2881
|
-
* - You're normalizing Results to a common format
|
|
2882
|
-
* - You want symmetric transformation of both cases
|
|
2883
|
-
* - You're building adapters between different Result types
|
|
2523
|
+
* The cache `set()` method is called after each step completes (success or error)
|
|
2524
|
+
* when the step has a `key` option. Both calling patterns work identically:
|
|
2884
2525
|
*
|
|
2885
|
-
*
|
|
2526
|
+
* ```typescript
|
|
2527
|
+
* // Function-wrapped pattern - cache is populated
|
|
2528
|
+
* await step(() => fetchUser("1"), { key: "user:1" });
|
|
2886
2529
|
*
|
|
2887
|
-
*
|
|
2888
|
-
*
|
|
2889
|
-
*
|
|
2530
|
+
* // Direct AsyncResult pattern - cache is also populated
|
|
2531
|
+
* await step(fetchUser("1"), { key: "user:1" });
|
|
2532
|
+
* ```
|
|
2890
2533
|
*
|
|
2891
|
-
*
|
|
2892
|
-
*
|
|
2893
|
-
*
|
|
2894
|
-
*
|
|
2534
|
+
* Note: Cache stores Result<unknown, unknown, unknown> because different steps
|
|
2535
|
+
* have different value/error/cause types. The actual runtime values are preserved;
|
|
2536
|
+
* only the static types are widened. For error results, the cause value is encoded
|
|
2537
|
+
* in CachedErrorCause to preserve metadata for proper replay.
|
|
2895
2538
|
*
|
|
2896
2539
|
* @example
|
|
2897
|
-
*
|
|
2898
|
-
*
|
|
2899
|
-
*
|
|
2900
|
-
*
|
|
2901
|
-
*
|
|
2902
|
-
*
|
|
2903
|
-
* )
|
|
2904
|
-
*
|
|
2905
|
-
*
|
|
2906
|
-
*
|
|
2907
|
-
*
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2540
|
+
* // Simple in-memory cache
|
|
2541
|
+
* const cache = new Map<string, Result<unknown, unknown, unknown>>();
|
|
2542
|
+
*
|
|
2543
|
+
* // Or implement custom cache with TTL, LRU, etc.
|
|
2544
|
+
* const cache: StepCache = {
|
|
2545
|
+
* get: (key) => myCache.get(key),
|
|
2546
|
+
* set: (key, result) => myCache.set(key, result, { ttl: 60000 }),
|
|
2547
|
+
* has: (key) => myCache.has(key),
|
|
2548
|
+
* delete: (key) => myCache.delete(key),
|
|
2549
|
+
* clear: () => myCache.clear(),
|
|
2550
|
+
* };
|
|
2551
|
+
*/
|
|
2552
|
+
interface StepCache {
|
|
2553
|
+
get(key: string): Result<unknown, unknown, unknown> | undefined;
|
|
2554
|
+
set(key: string, result: Result<unknown, unknown, unknown>, options?: {
|
|
2555
|
+
ttl?: number;
|
|
2556
|
+
}): void;
|
|
2557
|
+
has(key: string): boolean;
|
|
2558
|
+
delete(key: string): boolean;
|
|
2559
|
+
clear(): void;
|
|
2560
|
+
}
|
|
2561
|
+
/**
|
|
2562
|
+
* Entry for a saved step result with optional metadata.
|
|
2563
|
+
* The meta field preserves origin information for proper replay.
|
|
2919
2564
|
*/
|
|
2920
|
-
|
|
2565
|
+
interface ResumeStateEntry {
|
|
2566
|
+
result: Result<unknown, unknown, unknown>;
|
|
2567
|
+
/** Optional metadata for error origin (from step_complete event) */
|
|
2568
|
+
meta?: StepFailureMeta;
|
|
2569
|
+
}
|
|
2921
2570
|
/**
|
|
2922
|
-
*
|
|
2923
|
-
*
|
|
2924
|
-
*
|
|
2925
|
-
* @remarks When to use: Recover from Err by returning a fallback Result or retyping the error.
|
|
2926
|
-
*
|
|
2927
|
-
* ## When to Use
|
|
2928
|
-
*
|
|
2929
|
-
* Use `orElse()` when:
|
|
2930
|
-
* - You want to recover from errors with fallback operations
|
|
2931
|
-
* - The recovery might also fail (returns a Result)
|
|
2932
|
-
* - You need to chain fallback strategies
|
|
2933
|
-
* - You're implementing retry or fallback patterns
|
|
2571
|
+
* Resume state for workflow replay.
|
|
2572
|
+
* Pre-populate step results to skip execution on resume.
|
|
2934
2573
|
*
|
|
2935
|
-
*
|
|
2574
|
+
* Note: When saving to persistent storage, you may need custom serialization
|
|
2575
|
+
* for complex cause types. JSON.stringify works for simple values, but Error
|
|
2576
|
+
* objects and other non-plain types require special handling.
|
|
2936
2577
|
*
|
|
2937
|
-
*
|
|
2938
|
-
*
|
|
2939
|
-
*
|
|
2940
|
-
*
|
|
2941
|
-
*
|
|
2942
|
-
*
|
|
2943
|
-
*
|
|
2944
|
-
* @returns The original Result if successful, or the result of the recovery function
|
|
2578
|
+
* @example
|
|
2579
|
+
* // Collect from step_complete events using the helper
|
|
2580
|
+
* const collector = createResumeStateCollector();
|
|
2581
|
+
* const workflow = createWorkflow({ fetchUser }, {
|
|
2582
|
+
* onEvent: collector.handleEvent,
|
|
2583
|
+
* });
|
|
2584
|
+
* // Later: collector.getResumeState() returns ResumeState
|
|
2945
2585
|
*
|
|
2946
2586
|
* @example
|
|
2947
|
-
*
|
|
2948
|
-
*
|
|
2949
|
-
*
|
|
2950
|
-
*
|
|
2951
|
-
* error => error === 'NOT_FOUND' ? ok(defaultUser) : err(error)
|
|
2952
|
-
* );
|
|
2953
|
-
*
|
|
2954
|
-
* // Try cache, then database, then fail
|
|
2955
|
-
* const data = orElse(
|
|
2956
|
-
* orElse(
|
|
2957
|
-
* fetchFromCache(key),
|
|
2958
|
-
* () => fetchFromDatabase(key)
|
|
2959
|
-
* ),
|
|
2960
|
-
* () => err('DATA_UNAVAILABLE' as const)
|
|
2961
|
-
* );
|
|
2962
|
-
*
|
|
2963
|
-
* // Convert specific errors to success
|
|
2964
|
-
* const result = orElse(
|
|
2965
|
-
* riskyOperation(),
|
|
2966
|
-
* error => error.code === 'RETRY' ? ok(defaultValue) : err(error)
|
|
2967
|
-
* );
|
|
2968
|
-
* ```
|
|
2587
|
+
* // Resume with saved state
|
|
2588
|
+
* const workflow = createWorkflow({ fetchUser }, {
|
|
2589
|
+
* resumeState: { steps: savedSteps }
|
|
2590
|
+
* });
|
|
2969
2591
|
*/
|
|
2970
|
-
|
|
2592
|
+
interface ResumeState {
|
|
2593
|
+
/** Map of step keys to their cached results with optional metadata */
|
|
2594
|
+
steps: Map<string, ResumeStateEntry>;
|
|
2595
|
+
}
|
|
2971
2596
|
/**
|
|
2972
|
-
*
|
|
2973
|
-
*
|
|
2974
|
-
* @param r - The Result or AsyncResult to potentially recover from
|
|
2975
|
-
* @param fn - Async function that takes the error and returns a new Result
|
|
2976
|
-
* @returns Promise of the original Result if successful, or the result of the recovery function
|
|
2977
|
-
*
|
|
2978
|
-
* @example
|
|
2979
|
-
* ```typescript
|
|
2980
|
-
* // Try primary API, fall back to secondary
|
|
2981
|
-
* const data = await orElseAsync(
|
|
2982
|
-
* await fetchFromPrimaryApi(),
|
|
2983
|
-
* async (error) => {
|
|
2984
|
-
* if (error === 'UNAVAILABLE') {
|
|
2985
|
-
* return await fetchFromSecondaryApi();
|
|
2986
|
-
* }
|
|
2987
|
-
* return err(error);
|
|
2988
|
-
* }
|
|
2989
|
-
* );
|
|
2990
|
-
* ```
|
|
2597
|
+
* Constraint for Result-returning functions
|
|
2598
|
+
* Used by createWorkflow to ensure only valid functions are passed
|
|
2991
2599
|
*/
|
|
2992
|
-
|
|
2600
|
+
type AnyResultFn = (...args: any[]) => Result<any, any, any> | Promise<Result<any, any, any>>;
|
|
2993
2601
|
/**
|
|
2994
|
-
*
|
|
2995
|
-
*
|
|
2996
|
-
*
|
|
2997
|
-
* ## When to Use
|
|
2998
|
-
*
|
|
2999
|
-
* Use `recover()` when:
|
|
3000
|
-
* - You want to provide a fallback value on error
|
|
3001
|
-
* - Recovery cannot fail (unlike orElse which returns a Result)
|
|
3002
|
-
* - You're implementing default value patterns
|
|
3003
|
-
* - You want to guarantee a successful Result
|
|
3004
|
-
*
|
|
3005
|
-
* ## Why Use This Instead of `orElse`
|
|
3006
|
-
*
|
|
3007
|
-
* - **Simpler**: Recovery function returns plain value, not Result
|
|
3008
|
-
* - **Guaranteed success**: Always returns ok() after recovery
|
|
3009
|
-
* - **Clearer intent**: Shows recovery cannot fail
|
|
3010
|
-
*
|
|
3011
|
-
* @param r - The Result to potentially recover from
|
|
3012
|
-
* @param fn - Function that takes the error and returns a recovery value
|
|
3013
|
-
* @returns The original Result if successful, or ok(recoveryValue) if error
|
|
3014
|
-
*
|
|
3015
|
-
* @example
|
|
3016
|
-
* ```typescript
|
|
3017
|
-
* // Provide default user on NOT_FOUND
|
|
3018
|
-
* const user = recover(
|
|
3019
|
-
* fetchUser(id),
|
|
3020
|
-
* error => error === 'NOT_FOUND' ? defaultUser : guestUser
|
|
3021
|
-
* );
|
|
3022
|
-
*
|
|
3023
|
-
* // Convert all errors to default
|
|
3024
|
-
* const config = recover(
|
|
3025
|
-
* loadConfig(),
|
|
3026
|
-
* () => defaultConfig
|
|
3027
|
-
* );
|
|
3028
|
-
*
|
|
3029
|
-
* // Recover with error-based defaults
|
|
3030
|
-
* const value = recover(
|
|
3031
|
-
* parseNumber(input),
|
|
3032
|
-
* error => error === 'EMPTY' ? 0 : -1
|
|
3033
|
-
* );
|
|
3034
|
-
* ```
|
|
2602
|
+
* Extract union of error types from a deps object
|
|
2603
|
+
* Example: ErrorsOfDeps<{ fetchUser: typeof fetchUser, fetchPosts: typeof fetchPosts }>
|
|
2604
|
+
* yields: 'NOT_FOUND' | 'FETCH_ERROR'
|
|
3035
2605
|
*/
|
|
3036
|
-
|
|
2606
|
+
type ErrorsOfDeps<Deps extends Record<string, AnyResultFn>> = {
|
|
2607
|
+
[K in keyof Deps]: ErrorOf<Deps[K]>;
|
|
2608
|
+
}[keyof Deps];
|
|
3037
2609
|
/**
|
|
3038
|
-
*
|
|
2610
|
+
* Extract union of cause types from a deps object.
|
|
2611
|
+
* Example: CausesOfDeps<{ fetchUser: typeof fetchUser }> where fetchUser returns Result<User, "NOT_FOUND", Error>
|
|
2612
|
+
* yields: Error
|
|
3039
2613
|
*
|
|
3040
|
-
*
|
|
3041
|
-
*
|
|
3042
|
-
*
|
|
3043
|
-
*
|
|
3044
|
-
* @example
|
|
3045
|
-
* ```typescript
|
|
3046
|
-
* // Recover by fetching default from API
|
|
3047
|
-
* const user = await recoverAsync(
|
|
3048
|
-
* await fetchUser(id),
|
|
3049
|
-
* async (error) => await fetchDefaultUser()
|
|
3050
|
-
* );
|
|
3051
|
-
* ```
|
|
2614
|
+
* Note: This represents the domain cause types from declared functions.
|
|
2615
|
+
* However, workflow results may also have unknown causes from step.try failures
|
|
2616
|
+
* or uncaught exceptions, so the actual Result cause type is `unknown`.
|
|
3052
2617
|
*/
|
|
3053
|
-
|
|
2618
|
+
type CausesOfDeps<Deps extends Record<string, AnyResultFn>> = CauseOf<Deps[keyof Deps]>;
|
|
3054
2619
|
/**
|
|
3055
|
-
*
|
|
3056
|
-
*
|
|
3057
|
-
* Since this library uses plain objects for Results, serialization is trivial -
|
|
3058
|
-
* the serialized form IS the Result. This function validates the structure and
|
|
3059
|
-
* provides type-safe narrowing.
|
|
3060
|
-
*
|
|
3061
|
-
* ## When to Use
|
|
3062
|
-
*
|
|
3063
|
-
* Use `hydrate()` when:
|
|
3064
|
-
* - Receiving Results over RPC/network
|
|
3065
|
-
* - Deserializing Results from storage
|
|
3066
|
-
* - Validating untrusted data as Results
|
|
2620
|
+
* Execution-time options that can override creation-time options.
|
|
2621
|
+
* Pass these to `workflow.run(fn, execOptions)` for per-run configuration.
|
|
3067
2622
|
*
|
|
3068
|
-
*
|
|
3069
|
-
* @returns The value as a typed Result, or null if invalid
|
|
2623
|
+
* Rule: Use `workflow(...)` for normal runs. Use `workflow.run(...)` when you need per-run hooks/options.
|
|
3070
2624
|
*
|
|
3071
2625
|
* @example
|
|
3072
2626
|
* ```typescript
|
|
3073
|
-
*
|
|
3074
|
-
* const parsed = JSON.parse(jsonString);
|
|
3075
|
-
* const result = hydrate<User, ApiError>(parsed);
|
|
3076
|
-
* if (result) {
|
|
3077
|
-
* // result is Result<User, ApiError>
|
|
3078
|
-
* }
|
|
2627
|
+
* const workflow = createWorkflow(deps, { cache, onEvent: defaultHandler });
|
|
3079
2628
|
*
|
|
3080
|
-
* //
|
|
3081
|
-
*
|
|
3082
|
-
* const result = hydrate<Data, ServiceError>(rpcResponse);
|
|
3083
|
-
* ```
|
|
3084
|
-
*/
|
|
3085
|
-
declare function hydrate<T, E, C = unknown>(value: unknown): Result<T, E, C> | null;
|
|
3086
|
-
/**
|
|
3087
|
-
* Type guard to check if a value is a valid serialized Result.
|
|
2629
|
+
* // Normal run uses creation-time options
|
|
2630
|
+
* await workflow(async ({ step }) => { ... });
|
|
3088
2631
|
*
|
|
3089
|
-
*
|
|
3090
|
-
*
|
|
2632
|
+
* // Per-run options override creation-time options
|
|
2633
|
+
* await workflow.run(async ({ step }) => { ... }, { onEvent: viz.handleEvent });
|
|
3091
2634
|
*
|
|
3092
|
-
*
|
|
3093
|
-
*
|
|
3094
|
-
*
|
|
3095
|
-
* // data is Result<unknown, unknown, unknown>
|
|
3096
|
-
* if (data.ok) {
|
|
3097
|
-
* console.log(data.value);
|
|
3098
|
-
* }
|
|
3099
|
-
* }
|
|
2635
|
+
* // Pre-bind defaults with .with() (overridable by .run())
|
|
2636
|
+
* const visualized = workflow.with({ onEvent: viz.handleEvent });
|
|
2637
|
+
* await visualized(async ({ step }) => { ... });
|
|
3100
2638
|
* ```
|
|
3101
2639
|
*/
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
2640
|
+
type ExecutionOptions<E, U = UnexpectedError, C = void> = {
|
|
2641
|
+
/**
|
|
2642
|
+
* Event handler for workflow and step lifecycle events.
|
|
2643
|
+
* Overrides `onEvent` from creation-time options.
|
|
2644
|
+
*/
|
|
2645
|
+
onEvent?: (event: WorkflowEvent<E | U, C>, ctx: C) => void;
|
|
2646
|
+
/**
|
|
2647
|
+
* Error handler called when a step fails.
|
|
2648
|
+
* Overrides `onError` from creation-time options.
|
|
2649
|
+
*/
|
|
2650
|
+
onError?: (error: E | U, stepName?: string, ctx?: C) => void;
|
|
2651
|
+
/**
|
|
2652
|
+
* AbortSignal for workflow-level cancellation.
|
|
2653
|
+
* Overrides `signal` from creation-time options.
|
|
2654
|
+
*/
|
|
2655
|
+
signal?: AbortSignal;
|
|
2656
|
+
/**
|
|
2657
|
+
* Factory to create per-run context. Can be async.
|
|
2658
|
+
* Overrides `createContext` from creation-time options.
|
|
2659
|
+
*/
|
|
2660
|
+
createContext?: () => C | Promise<C>;
|
|
2661
|
+
/**
|
|
2662
|
+
* Resume state for workflow replay. Can be a factory function (sync or async).
|
|
2663
|
+
* Overrides `resumeState` from creation-time options.
|
|
2664
|
+
*/
|
|
2665
|
+
resumeState?: ResumeState | (() => ResumeState | Promise<ResumeState>);
|
|
2666
|
+
/**
|
|
2667
|
+
* Hook to check if workflow should run (concurrency control).
|
|
2668
|
+
* Overrides `shouldRun` from creation-time options.
|
|
2669
|
+
*/
|
|
2670
|
+
shouldRun?: (workflowId: string, context: C) => boolean | Promise<boolean>;
|
|
2671
|
+
/**
|
|
2672
|
+
* Hook called before workflow execution starts.
|
|
2673
|
+
* Overrides `onBeforeStart` from creation-time options.
|
|
2674
|
+
*/
|
|
2675
|
+
onBeforeStart?: (workflowId: string, context: C) => boolean | Promise<boolean>;
|
|
2676
|
+
/**
|
|
2677
|
+
* Hook called after each step completes (only for steps with a `key`).
|
|
2678
|
+
* Overrides `onAfterStep` from creation-time options.
|
|
2679
|
+
*/
|
|
2680
|
+
onAfterStep?: (stepKey: string, result: Result<unknown, unknown, unknown>, workflowId: string, context: C) => void | Promise<void>;
|
|
2681
|
+
/**
|
|
2682
|
+
* Enable strict mode for this specific run (analyzer validation only).
|
|
2683
|
+
*/
|
|
2684
|
+
strict?: boolean;
|
|
2685
|
+
/**
|
|
2686
|
+
* Declared workflow graph for strict runtime validation.
|
|
2687
|
+
* Undeclared step/decision ids fail the run immediately.
|
|
2688
|
+
* Overrides `graph` from creation-time options.
|
|
2689
|
+
*/
|
|
2690
|
+
graph?: DeclaredGraph;
|
|
2691
|
+
/**
|
|
2692
|
+
* Enable development warnings for this run.
|
|
2693
|
+
* Only active when NODE_ENV !== 'production'.
|
|
2694
|
+
*/
|
|
2695
|
+
devWarnings?: boolean;
|
|
3105
2696
|
};
|
|
3106
|
-
type AllErrors<T extends readonly Result<unknown, unknown, unknown>[]> = {
|
|
3107
|
-
[K in keyof T]: T[K] extends Ok<unknown> ? never : T[K] extends Err<infer E, unknown> ? E : T[K] extends Result<unknown, infer E, unknown> ? E : never;
|
|
3108
|
-
}[number];
|
|
3109
|
-
type AllCauses<T extends readonly Result<unknown, unknown, unknown>[]> = {
|
|
3110
|
-
[K in keyof T]: T[K] extends Ok<unknown> ? never : T[K] extends Err<unknown, infer C> ? C : T[K] extends Result<unknown, unknown, infer C> ? C : never;
|
|
3111
|
-
}[number];
|
|
3112
|
-
type AllResult<T extends readonly Result<unknown, unknown, unknown>[]> = [
|
|
3113
|
-
AllErrors<T>
|
|
3114
|
-
] extends [never] ? Ok<AllValues<T>> : Result<AllValues<T>, AllErrors<T>, AllCauses<T>>;
|
|
3115
2697
|
/**
|
|
3116
|
-
*
|
|
3117
|
-
*
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
*
|
|
3131
|
-
*
|
|
3132
|
-
*
|
|
3133
|
-
* ## Important
|
|
3134
|
-
*
|
|
3135
|
-
* - **Short-circuits**: Returns first error immediately, doesn't wait for all Results
|
|
3136
|
-
* - **All must succeed**: If any Result fails, the entire operation fails
|
|
3137
|
-
* - **Use `allSettled`**: If you need to collect all errors (e.g., form validation)
|
|
3138
|
-
*
|
|
3139
|
-
* @param results - Array of Results to combine (all must succeed)
|
|
3140
|
-
* @returns A Result with an array of all success values, or the first error encountered
|
|
3141
|
-
*
|
|
3142
|
-
* @example
|
|
3143
|
-
* ```typescript
|
|
3144
|
-
* // Combine multiple successful Results
|
|
3145
|
-
* const combined = all([Awaitly.ok(1), Awaitly.ok(2), Awaitly.ok(3)]);
|
|
3146
|
-
* // combined: { ok: true, value: [1, 2, 3] }
|
|
3147
|
-
*
|
|
3148
|
-
* // Short-circuits on first error
|
|
3149
|
-
* const error = all([Awaitly.ok(1), Awaitly.err('ERROR'), Awaitly.ok(3)]);
|
|
3150
|
-
* // error: { ok: false, error: 'ERROR' }
|
|
3151
|
-
* // Note: Awaitly.ok(3) is never evaluated
|
|
3152
|
-
*
|
|
3153
|
-
* // Combine API responses
|
|
3154
|
-
* const data = all([
|
|
3155
|
-
* fetchUser(id),
|
|
3156
|
-
* fetchPosts(id),
|
|
3157
|
-
* fetchComments(id)
|
|
3158
|
-
* ]);
|
|
3159
|
-
* // data.value: [user, posts, comments] if all succeed
|
|
3160
|
-
* ```
|
|
2698
|
+
* Per-run configuration. Extends ExecutionOptions with dep overrides.
|
|
2699
|
+
* Pass to `workflow.run(fn, config)` or `workflow.run(name, fn, config)`.
|
|
2700
|
+
*/
|
|
2701
|
+
type RunConfig<E, U = UnexpectedError, C = void, Deps = unknown> = ExecutionOptions<E, U, C> & {
|
|
2702
|
+
/** Override creation-time deps (partial merge). */
|
|
2703
|
+
deps?: Partial<Deps>;
|
|
2704
|
+
/** Step result cache for this run. */
|
|
2705
|
+
cache?: StepCache;
|
|
2706
|
+
/** Restore workflow from a previously saved snapshot. */
|
|
2707
|
+
snapshot?: WorkflowSnapshot | null;
|
|
2708
|
+
/** Stream store for this run. */
|
|
2709
|
+
streamStore?: StreamStore;
|
|
2710
|
+
};
|
|
2711
|
+
/**
|
|
2712
|
+
* Workflow options. Error union is always closed: E | U.
|
|
2713
|
+
* When catchUnexpected is omitted, U defaults to UnexpectedError.
|
|
3161
2714
|
*/
|
|
3162
|
-
|
|
2715
|
+
type WorkflowOptions<E, U = UnexpectedError, C = void, Errs extends readonly string[] = readonly string[]> = {
|
|
2716
|
+
/** Standard Schema for input validation. Works with Zod, Valibot, ArkType, etc. */
|
|
2717
|
+
inputSchema?: StandardSchemaV1;
|
|
2718
|
+
/** Input data to validate against inputSchema and pass to workflow context. */
|
|
2719
|
+
input?: unknown;
|
|
2720
|
+
/** Short description for labels/tooltips (static analysis) */
|
|
2721
|
+
description?: string;
|
|
2722
|
+
/** Full markdown documentation (static analysis) */
|
|
2723
|
+
markdown?: string;
|
|
2724
|
+
/**
|
|
2725
|
+
* Map uncaught exceptions (and cancellation) to your error type U.
|
|
2726
|
+
* When omitted, U = UnexpectedError and the default mapper returns an UnexpectedError instance.
|
|
2727
|
+
*/
|
|
2728
|
+
catchUnexpected?: (cause: unknown) => U;
|
|
2729
|
+
/**
|
|
2730
|
+
* Declared errors for the workflow (strict validation).
|
|
2731
|
+
* When provided, the analyzer validates that computed errors match declared errors.
|
|
2732
|
+
*/
|
|
2733
|
+
errors?: Errs;
|
|
2734
|
+
onError?: (error: E | U, stepName?: string, ctx?: C) => void;
|
|
2735
|
+
/**
|
|
2736
|
+
* Unified event stream for workflow and step lifecycle.
|
|
2737
|
+
*
|
|
2738
|
+
* Context is automatically included in `event.context` when provided via `createContext`.
|
|
2739
|
+
* The separate `ctx` parameter is provided for convenience.
|
|
2740
|
+
*/
|
|
2741
|
+
onEvent?: (event: WorkflowEvent<E | U, C>, ctx: C) => void;
|
|
2742
|
+
/** Create per-run context for event correlation */
|
|
2743
|
+
createContext?: () => C;
|
|
2744
|
+
/** Step result cache - only steps with a `key` option are cached */
|
|
2745
|
+
cache?: StepCache;
|
|
2746
|
+
/** Pre-populate cache from saved state for workflow resume. Prefer `snapshot` option. */
|
|
2747
|
+
resumeState?: ResumeState | (() => ResumeState | Promise<ResumeState>);
|
|
2748
|
+
/**
|
|
2749
|
+
* Restore workflow from a previously saved snapshot.
|
|
2750
|
+
* Pass `null` for fresh start (e.g., when store.load() returns nothing).
|
|
2751
|
+
*/
|
|
2752
|
+
snapshot?: WorkflowSnapshot | null;
|
|
2753
|
+
/**
|
|
2754
|
+
* Custom serialization for encoding/decoding values during snapshot operations.
|
|
2755
|
+
*/
|
|
2756
|
+
serialization?: {
|
|
2757
|
+
encode?: (value: unknown) => JSONValue;
|
|
2758
|
+
decode?: (value: JSONValue) => unknown;
|
|
2759
|
+
};
|
|
2760
|
+
snapshotSerialization?: {
|
|
2761
|
+
strict?: boolean;
|
|
2762
|
+
};
|
|
2763
|
+
onUnknownSteps?: "warn" | "error" | "ignore";
|
|
2764
|
+
onDefinitionChange?: "warn" | "error" | "ignore";
|
|
2765
|
+
/**
|
|
2766
|
+
* External AbortSignal for workflow-level cancellation.
|
|
2767
|
+
* Cancellation is mapped through catchUnexpected (default: UnexpectedError with cause.thrown = WorkflowCancelledError).
|
|
2768
|
+
*/
|
|
2769
|
+
signal?: AbortSignal;
|
|
2770
|
+
onBeforeStart?: (workflowId: string, context: C) => boolean | Promise<boolean>;
|
|
2771
|
+
onAfterStep?: (stepKey: string, result: Result<unknown, unknown, unknown>, workflowId: string, context: C) => void | Promise<void>;
|
|
2772
|
+
shouldRun?: (workflowId: string, context: C) => boolean | Promise<boolean>;
|
|
2773
|
+
streamStore?: StreamStore;
|
|
2774
|
+
/**
|
|
2775
|
+
* Declared workflow graph for strict runtime validation.
|
|
2776
|
+
* When provided, any runtime step/decision id not present in the graph
|
|
2777
|
+
* fails the workflow immediately, guaranteeing the static diagram matches
|
|
2778
|
+
* what actually runs. Produce it with awaitly-analyze's renderWorkflowDSL,
|
|
2779
|
+
* or pass a plain list of ids.
|
|
2780
|
+
*/
|
|
2781
|
+
graph?: DeclaredGraph;
|
|
2782
|
+
/**
|
|
2783
|
+
* Enable development warnings.
|
|
2784
|
+
* Only active when NODE_ENV !== 'production'.
|
|
2785
|
+
*/
|
|
2786
|
+
devWarnings?: boolean;
|
|
2787
|
+
};
|
|
3163
2788
|
/**
|
|
3164
|
-
*
|
|
3165
|
-
*
|
|
3166
|
-
*
|
|
3167
|
-
*
|
|
3168
|
-
* Use `allAsync()` when:
|
|
3169
|
-
* - You have multiple async operations that all must succeed
|
|
3170
|
-
* - You want to run operations in parallel (better performance)
|
|
3171
|
-
* - You want to short-circuit on the first error (fail-fast)
|
|
3172
|
-
* - You need all values together from parallel operations
|
|
3173
|
-
*
|
|
3174
|
-
* ## Why Use This Instead of `all`
|
|
3175
|
-
*
|
|
3176
|
-
* - **Parallel execution**: All Promises start immediately (faster)
|
|
3177
|
-
* - **Async support**: Works with Promises and AsyncResults
|
|
3178
|
-
* - **Promise rejection handling**: Converts Promise rejections to `PromiseRejectedError`
|
|
3179
|
-
*
|
|
3180
|
-
* ## Important
|
|
3181
|
-
*
|
|
3182
|
-
* - **Short-circuits**: Returns first error immediately, cancels remaining operations
|
|
3183
|
-
* - **Parallel**: All operations start simultaneously (unlike sequential `andThen`)
|
|
3184
|
-
* - **Use `allSettledAsync`**: If you need to collect all errors
|
|
3185
|
-
*
|
|
3186
|
-
* @param results - Array of Results or Promises of Results to combine (all must succeed)
|
|
3187
|
-
* @returns A Promise resolving to a Result with an array of all success values, or the first error
|
|
3188
|
-
*
|
|
3189
|
-
* @example
|
|
3190
|
-
* ```typescript
|
|
3191
|
-
* // Parallel API calls
|
|
3192
|
-
* const combined = await allAsync([
|
|
3193
|
-
* fetchUser('1'),
|
|
3194
|
-
* fetchPosts('1'),
|
|
3195
|
-
* fetchComments('1')
|
|
3196
|
-
* ]);
|
|
3197
|
-
* // All three calls start simultaneously
|
|
3198
|
-
* // combined: { ok: true, value: [user, posts, comments] } if all succeed
|
|
3199
|
-
*
|
|
3200
|
-
* // Mix Results and Promises
|
|
3201
|
-
* const data = await allAsync([
|
|
3202
|
-
* ok(cachedUser), // Already resolved
|
|
3203
|
-
* fetchPosts(userId), // Promise
|
|
3204
|
-
* ]);
|
|
3205
|
-
* ```
|
|
2789
|
+
* Workflow context provided to callbacks, containing workflow metadata
|
|
2790
|
+
* and data store for step outputs.
|
|
2791
|
+
* This allows conditional helpers and other utilities to access workflowId, onEvent, and context.
|
|
3206
2792
|
*/
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
2793
|
+
type WorkflowContext<C = void, Input = Record<string, unknown>, Data = Record<string, unknown>> = {
|
|
2794
|
+
/**
|
|
2795
|
+
* Unique ID for this workflow run.
|
|
2796
|
+
*/
|
|
2797
|
+
workflowId: string;
|
|
2798
|
+
/**
|
|
2799
|
+
* Event emitter function for workflow events.
|
|
2800
|
+
* Can be used with conditional helpers to emit step_skipped events.
|
|
2801
|
+
*/
|
|
2802
|
+
onEvent?: (event: WorkflowEvent<unknown, C>) => void;
|
|
2803
|
+
/**
|
|
2804
|
+
* Per-run context created by createContext (or undefined if not provided).
|
|
2805
|
+
* Automatically included in all workflow events.
|
|
2806
|
+
*/
|
|
2807
|
+
context?: C;
|
|
2808
|
+
/**
|
|
2809
|
+
* Workflow-level AbortSignal (if provided in workflow options).
|
|
2810
|
+
* Use this to check cancellation or pass to operations that support AbortSignal.
|
|
2811
|
+
*
|
|
2812
|
+
* @example
|
|
2813
|
+
* ```typescript
|
|
2814
|
+
* const result = await workflow(async ({ step, deps, ctx }) => {
|
|
2815
|
+
* // Pass signal to fetch
|
|
2816
|
+
* const response = await fetch(url, { signal: ctx.signal });
|
|
2817
|
+
* // Or check manually
|
|
2818
|
+
* if (ctx.signal?.aborted) return early();
|
|
2819
|
+
* });
|
|
2820
|
+
* ```
|
|
2821
|
+
*/
|
|
2822
|
+
signal?: AbortSignal;
|
|
2823
|
+
/**
|
|
2824
|
+
* Input data passed to the workflow.
|
|
2825
|
+
* Access via `ctx.input.key` for static analysis tracking.
|
|
2826
|
+
*
|
|
2827
|
+
* @example
|
|
2828
|
+
* ```typescript
|
|
2829
|
+
* await step('getCart', () => getCart(ctx.input.cartId), {
|
|
2830
|
+
* errors: ['CART_NOT_FOUND'],
|
|
2831
|
+
* });
|
|
2832
|
+
* ```
|
|
2833
|
+
*/
|
|
2834
|
+
input: Input;
|
|
2835
|
+
/**
|
|
2836
|
+
* Get a value from the workflow data store by key.
|
|
2837
|
+
* Preferred over `ctx.get()` for static analysis as it's easier to trace.
|
|
2838
|
+
*
|
|
2839
|
+
* @param key - The key to retrieve
|
|
2840
|
+
* @returns The value at that key
|
|
2841
|
+
*
|
|
2842
|
+
* @example
|
|
2843
|
+
* ```typescript
|
|
2844
|
+
* // Use ctx.ref() inside step callbacks for tracked dependencies
|
|
2845
|
+
* await step('charge', () => chargeCard(ctx.ref('cart').total), {
|
|
2846
|
+
* errors: ['CARD_DECLINED'],
|
|
2847
|
+
* });
|
|
2848
|
+
* ```
|
|
2849
|
+
*/
|
|
2850
|
+
ref: <K extends keyof Data>(key: K) => Data[K];
|
|
2851
|
+
/**
|
|
2852
|
+
* Set a value in the workflow data store.
|
|
2853
|
+
* Prefer using `out` option on steps instead for better static analysis.
|
|
2854
|
+
*
|
|
2855
|
+
* @param key - The key to set
|
|
2856
|
+
* @param value - The value to store
|
|
2857
|
+
*
|
|
2858
|
+
* @example
|
|
2859
|
+
* ```typescript
|
|
2860
|
+
* // Prefer out option:
|
|
2861
|
+
* await step('getCart', () => getCart(id), { out: 'cart' });
|
|
2862
|
+
*
|
|
2863
|
+
* // Escape hatch (less analyzable):
|
|
2864
|
+
* const cart = await step('getCart', () => getCart(id));
|
|
2865
|
+
* ctx.set('cart', cart);
|
|
2866
|
+
* ```
|
|
2867
|
+
*/
|
|
2868
|
+
set: <K extends string>(key: K, value: unknown) => void;
|
|
2869
|
+
/**
|
|
2870
|
+
* Get a value from the workflow data store.
|
|
2871
|
+
* Prefer `ctx.ref()` for better static analysis.
|
|
2872
|
+
*
|
|
2873
|
+
* @param key - The key to retrieve
|
|
2874
|
+
* @returns The value at that key (or undefined)
|
|
2875
|
+
*/
|
|
2876
|
+
get: <K extends keyof Data>(key: K) => Data[K] | undefined;
|
|
3217
2877
|
};
|
|
3218
|
-
type AllSettledResult<T extends readonly Result<unknown, unknown, unknown>[]> = [
|
|
3219
|
-
AllErrors<T>
|
|
3220
|
-
] extends [never] ? Ok<AllValues<T>> : Result<AllValues<T>, SettledError<AllErrors<T>, AllCauses<T>>[]>;
|
|
3221
2878
|
/**
|
|
3222
|
-
*
|
|
3223
|
-
*
|
|
3224
|
-
*
|
|
3225
|
-
*
|
|
3226
|
-
* Use `allSettled()` when:
|
|
3227
|
-
* - You need to see ALL errors, not just the first one
|
|
3228
|
-
* - You're doing form validation (show all field errors)
|
|
3229
|
-
* - You want to collect partial results (some succeed, some fail)
|
|
3230
|
-
* - You need to process all Results regardless of failures
|
|
3231
|
-
*
|
|
3232
|
-
* ## Why Use This Instead of `all`
|
|
3233
|
-
*
|
|
3234
|
-
* - **Collects all errors**: Returns array of all errors, not just first
|
|
3235
|
-
* - **No short-circuit**: Evaluates all Results even if some fail
|
|
3236
|
-
* - **Partial success**: Can see which operations succeeded and which failed
|
|
3237
|
-
* - **Better UX**: Show users all validation errors at once
|
|
3238
|
-
*
|
|
3239
|
-
* ## Important
|
|
3240
|
-
*
|
|
3241
|
-
* - **No short-circuit**: All Results are evaluated (slower if many fail early)
|
|
3242
|
-
* - **Error array**: Returns array of `{ error, cause }` objects, not single error
|
|
3243
|
-
* - **Use `all`**: If you want fail-fast behavior (better performance)
|
|
3244
|
-
*
|
|
3245
|
-
* @param results - Array of Results to combine (all are evaluated)
|
|
3246
|
-
* @returns A Result with:
|
|
3247
|
-
* - Array of all success values if all succeed
|
|
3248
|
-
* - Array of `{ error, cause }` objects if any fail
|
|
3249
|
-
*
|
|
3250
|
-
* @example
|
|
3251
|
-
* ```typescript
|
|
3252
|
-
* // Form validation - show all errors
|
|
3253
|
-
* const validated = allSettled([
|
|
3254
|
-
* validateEmail(email),
|
|
3255
|
-
* validatePassword(password),
|
|
3256
|
-
* validateAge(age),
|
|
3257
|
-
* ]);
|
|
3258
|
-
* // If email and password fail:
|
|
3259
|
-
* // { ok: false, error: [
|
|
3260
|
-
* // { error: 'INVALID_EMAIL' },
|
|
3261
|
-
* // { error: 'WEAK_PASSWORD' }
|
|
3262
|
-
* // ]}
|
|
3263
|
-
*
|
|
3264
|
-
* // Collect partial results
|
|
3265
|
-
* const results = allSettled([
|
|
3266
|
-
* fetchUser('1'), // succeeds
|
|
3267
|
-
* fetchUser('2'), // fails
|
|
3268
|
-
* fetchUser('3'), // succeeds
|
|
3269
|
-
* ]);
|
|
3270
|
-
* // Can see which succeeded and which failed
|
|
3271
|
-
* ```
|
|
2879
|
+
* Bound steps for a workflow's deps: each dep key becomes a step function
|
|
2880
|
+
* with the dep's arguments that unwraps ok / early-exits on err — same as
|
|
2881
|
+
* the deps-first run(deps, fn) form. `never` deps (no deps) yield no steps.
|
|
3272
2882
|
*/
|
|
3273
|
-
|
|
2883
|
+
type WorkflowSteps<Deps> = [Deps] extends [
|
|
2884
|
+
Record<string, (...args: never[]) => unknown>
|
|
2885
|
+
] ? BoundSteps<Deps> : Record<string, never>;
|
|
2886
|
+
/** Workflow function type (no args). E is the full step error union (deps errors + any ExtraE from step.workflow/withFallback). */
|
|
2887
|
+
type WorkflowFn<T, E, Deps, C = void> = (context: {
|
|
2888
|
+
step: RunStep<E>;
|
|
2889
|
+
steps: WorkflowSteps<Deps>;
|
|
2890
|
+
deps: Deps;
|
|
2891
|
+
ctx: WorkflowContext<C>;
|
|
2892
|
+
}) => T | Promise<T>;
|
|
3274
2893
|
/**
|
|
3275
|
-
*
|
|
3276
|
-
*
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
*
|
|
3284
|
-
*
|
|
3285
|
-
*
|
|
3286
|
-
*
|
|
3287
|
-
* - **Simple separation**: One call splits successes and errors
|
|
3288
|
-
* - **Type-safe**: TypeScript knows `values` is `T[]` and `errors` is `E[]`
|
|
3289
|
-
* - **No unwrapping**: Doesn't require manual `if (r.ok)` checks
|
|
3290
|
-
* - **Preserves order**: Maintains original array order in both arrays
|
|
3291
|
-
*
|
|
3292
|
-
* ## Common Pattern
|
|
3293
|
-
*
|
|
3294
|
-
* Often used after `Promise.all()` with Results:
|
|
3295
|
-
* ```typescript
|
|
3296
|
-
* const results = await Promise.all(ids.map(id => fetchUser(id)));
|
|
3297
|
-
* const { values: users, errors } = partition(results);
|
|
3298
|
-
* // Process successful users, handle errors separately
|
|
3299
|
-
* ```
|
|
2894
|
+
* Return type of runWithState: result plus resume state for persistence.
|
|
2895
|
+
* resumeState is always present, even when the run fails or returns an error Result.
|
|
2896
|
+
*/
|
|
2897
|
+
type RunWithStateResult<T, E, U> = {
|
|
2898
|
+
result: Result<T, E | U, unknown>;
|
|
2899
|
+
resumeState: ResumeState;
|
|
2900
|
+
};
|
|
2901
|
+
/**
|
|
2902
|
+
* Workflow return type. Error union is always closed: E | ExtraE | U (default U = UnexpectedError).
|
|
2903
|
+
* ExtraE is inferred from the callback when using step.workflow or step.withFallback with errors not in deps.
|
|
2904
|
+
* Methods: .run() (4 overloads) and .runWithState() (4 overloads) for run-and-persist flows.
|
|
3300
2905
|
*
|
|
3301
|
-
*
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
2906
|
+
* Cause type is `unknown` because step.try/catchUnexpected receive thrown values.
|
|
2907
|
+
*/
|
|
2908
|
+
interface Workflow<E, U = UnexpectedError, Deps = unknown, C = void> {
|
|
2909
|
+
/**
|
|
2910
|
+
* Pre-bind dependency overrides and return another `Workflow`.
|
|
2911
|
+
* Chain `.withDeps()` and call `.run()` / `.runWithState()` as normal.
|
|
2912
|
+
* Precedence: createWorkflow deps < withDeps deps < run config deps.
|
|
2913
|
+
*/
|
|
2914
|
+
withDeps(overrides: Partial<Deps>): Workflow<E, U, Deps, C>;
|
|
2915
|
+
/**
|
|
2916
|
+
* Execute workflow (anonymous run).
|
|
2917
|
+
* ExtraE is inferred from the callback (e.g. from step.workflow / step.withFallback); result is Result<T, E | ExtraE | U>.
|
|
2918
|
+
* T is inferred from the callback return type. For nested workflows (calling another workflow.run() inside the callback),
|
|
2919
|
+
* inference can sometimes fall back to `any`; adding an explicit return type to the callback (e.g.
|
|
2920
|
+
* `async (ctx): Promise<{ user: User; enriched: Enriched }> => { ... }`) gives the compiler a target and preserves types.
|
|
2921
|
+
*/
|
|
2922
|
+
run<T, ExtraE = never>(fn: WorkflowFn<T, E | ExtraE, Deps, C>): AsyncResult<T, E | ExtraE | U, unknown>;
|
|
2923
|
+
/**
|
|
2924
|
+
* Execute workflow with config overrides.
|
|
2925
|
+
*/
|
|
2926
|
+
run<T, ExtraE = never>(fn: WorkflowFn<T, E | ExtraE, Deps, C>, config: RunConfig<E, U, C, Deps>): AsyncResult<T, E | ExtraE | U, unknown>;
|
|
2927
|
+
/**
|
|
2928
|
+
* Execute named workflow run (for logging, tracing, resume).
|
|
2929
|
+
*/
|
|
2930
|
+
run<T, ExtraE = never>(name: string, fn: WorkflowFn<T, E | ExtraE, Deps, C>): AsyncResult<T, E | ExtraE | U, unknown>;
|
|
2931
|
+
/**
|
|
2932
|
+
* Execute named workflow run with config overrides.
|
|
2933
|
+
*/
|
|
2934
|
+
run<T, ExtraE = never>(name: string, fn: WorkflowFn<T, E | ExtraE, Deps, C>, config: RunConfig<E, U, C, Deps>): AsyncResult<T, E | ExtraE | U, unknown>;
|
|
2935
|
+
/**
|
|
2936
|
+
* Execute workflow and return result plus resume state for persistence.
|
|
2937
|
+
* resumeState is always present (even on failure) so callers can persist partial state.
|
|
2938
|
+
* Same overloads as run(); does not throw — follows the same "never throw, always Result" contract as run().
|
|
2939
|
+
*
|
|
2940
|
+
* @example
|
|
2941
|
+
* const { result, resumeState } = await workflow.runWithState(fn);
|
|
2942
|
+
* await store.save(id, resumeState);
|
|
2943
|
+
*/
|
|
2944
|
+
runWithState<T, ExtraE = never>(fn: WorkflowFn<T, E | ExtraE, Deps, C>): Promise<RunWithStateResult<T, E | ExtraE, U>>;
|
|
2945
|
+
/**
|
|
2946
|
+
* Execute workflow with config overrides and return result plus resume state.
|
|
2947
|
+
*/
|
|
2948
|
+
runWithState<T, ExtraE = never>(fn: WorkflowFn<T, E | ExtraE, Deps, C>, config: RunConfig<E, U, C, Deps>): Promise<RunWithStateResult<T, E | ExtraE, U>>;
|
|
2949
|
+
/**
|
|
2950
|
+
* Execute named workflow run and return result plus resume state.
|
|
2951
|
+
*/
|
|
2952
|
+
runWithState<T, ExtraE = never>(name: string, fn: WorkflowFn<T, E | ExtraE, Deps, C>): Promise<RunWithStateResult<T, E | ExtraE, U>>;
|
|
2953
|
+
/**
|
|
2954
|
+
* Execute named workflow run with config overrides and return result plus resume state.
|
|
2955
|
+
*/
|
|
2956
|
+
runWithState<T, ExtraE = never>(name: string, fn: WorkflowFn<T, E | ExtraE, Deps, C>, config: RunConfig<E, U, C, Deps>): Promise<RunWithStateResult<T, E | ExtraE, U>>;
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* Error returned when a workflow is cancelled via AbortSignal.
|
|
3305
2960
|
*
|
|
3306
2961
|
* @example
|
|
3307
2962
|
* ```typescript
|
|
3308
|
-
*
|
|
3309
|
-
* const
|
|
3310
|
-
* const { values, errors } = partition(results);
|
|
3311
|
-
* // values: [1, 3]
|
|
3312
|
-
* // errors: ['ERROR_1', 'ERROR_2']
|
|
3313
|
-
*
|
|
3314
|
-
* // Process batch operations
|
|
3315
|
-
* const userResults = await Promise.all(userIds.map(id => fetchUser(id)));
|
|
3316
|
-
* const { values: users, errors: fetchErrors } = partition(userResults);
|
|
2963
|
+
* const controller = new AbortController();
|
|
2964
|
+
* const workflow = createWorkflow(deps, { signal: controller.signal });
|
|
3317
2965
|
*
|
|
3318
|
-
* //
|
|
3319
|
-
*
|
|
2966
|
+
* // Later:
|
|
2967
|
+
* controller.abort('User navigated away');
|
|
3320
2968
|
*
|
|
3321
|
-
*
|
|
3322
|
-
*
|
|
2969
|
+
* const result = await workflowPromise;
|
|
2970
|
+
* if (!result.ok && isWorkflowCancelled(result.error)) {
|
|
2971
|
+
* console.log('Cancelled:', result.error.reason);
|
|
2972
|
+
* }
|
|
3323
2973
|
* ```
|
|
3324
2974
|
*/
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
2975
|
+
type WorkflowCancelledError = {
|
|
2976
|
+
type: "WORKFLOW_CANCELLED";
|
|
2977
|
+
/** Reason from AbortSignal.reason (if provided) */
|
|
2978
|
+
reason?: string;
|
|
2979
|
+
/** Last successfully completed keyed step (for resume purposes) */
|
|
2980
|
+
lastStepKey?: string;
|
|
3328
2981
|
};
|
|
3329
|
-
type AnyValue<T extends readonly Result<unknown, unknown, unknown>[]> = T[number] extends Result<infer U, unknown, unknown> ? U : never;
|
|
3330
|
-
type AnyErrors<T extends readonly Result<unknown, unknown, unknown>[]> = {
|
|
3331
|
-
-readonly [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> ? E : never;
|
|
3332
|
-
}[number];
|
|
3333
|
-
type AnyCauses<T extends readonly Result<unknown, unknown, unknown>[]> = {
|
|
3334
|
-
-readonly [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> ? C : never;
|
|
3335
|
-
}[number];
|
|
3336
2982
|
/**
|
|
3337
|
-
*
|
|
3338
|
-
*
|
|
3339
|
-
* ## When to Use
|
|
3340
|
-
*
|
|
3341
|
-
* Use `any()` when:
|
|
3342
|
-
* - You have multiple fallback options and need the first that succeeds
|
|
3343
|
-
* - You're trying multiple strategies (e.g., cache → DB → API)
|
|
3344
|
-
* - You want fail-fast success (stops on first success)
|
|
3345
|
-
* - You have redundant data sources and any one will do
|
|
3346
|
-
*
|
|
3347
|
-
* ## Why Use This
|
|
3348
|
-
*
|
|
3349
|
-
* - **Succeeds fast**: Returns immediately on first success (better performance)
|
|
3350
|
-
* - **Fallback pattern**: Perfect for trying multiple options
|
|
3351
|
-
* - **Short-circuits**: Stops evaluating after first success
|
|
3352
|
-
* - **Type-safe**: TypeScript infers the success type
|
|
3353
|
-
*
|
|
3354
|
-
* ## Important
|
|
3355
|
-
*
|
|
3356
|
-
* - **First success wins**: Returns first successful Result, ignores rest
|
|
3357
|
-
* - **All errors**: If all fail, returns first error (not all errors)
|
|
3358
|
-
* - **Empty array**: Returns `EmptyInputError` if array is empty
|
|
3359
|
-
* - **Use `all`**: If you need ALL to succeed
|
|
3360
|
-
*
|
|
3361
|
-
* @param results - Array of Results to check (evaluated in order)
|
|
3362
|
-
* @returns The first successful Result, or first error if all fail, or `EmptyInputError` if empty
|
|
2983
|
+
* Standard error type for steps awaiting human approval.
|
|
2984
|
+
* Use this as the error type for approval-gated steps.
|
|
3363
2985
|
*
|
|
3364
2986
|
* @example
|
|
3365
|
-
*
|
|
3366
|
-
*
|
|
3367
|
-
*
|
|
3368
|
-
*
|
|
3369
|
-
*
|
|
3370
|
-
*
|
|
3371
|
-
*
|
|
3372
|
-
* // Returns first that succeeds
|
|
3373
|
-
*
|
|
3374
|
-
* // Try multiple formats
|
|
3375
|
-
* const parsed = any([
|
|
3376
|
-
* parseJSON(input),
|
|
3377
|
-
* parseXML(input),
|
|
3378
|
-
* parseYAML(input)
|
|
3379
|
-
* ]);
|
|
3380
|
-
*
|
|
3381
|
-
* // All errors case
|
|
3382
|
-
* const allErrors = any([err('A'), err('B'), err('C')]);
|
|
3383
|
-
* // allErrors: { ok: false, error: 'A' } (first error)
|
|
3384
|
-
* ```
|
|
2987
|
+
* const requireApproval = async (userId: string): AsyncResult<Approval, PendingApproval> => {
|
|
2988
|
+
* const status = await checkApprovalStatus(userId);
|
|
2989
|
+
* if (status === 'pending') {
|
|
2990
|
+
* return err({ type: 'PENDING_APPROVAL', stepKey: `approval:${userId}` });
|
|
2991
|
+
* }
|
|
2992
|
+
* return ok(status.approval);
|
|
2993
|
+
* };
|
|
3385
2994
|
*/
|
|
3386
|
-
|
|
3387
|
-
type
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
2995
|
+
type PendingApproval = {
|
|
2996
|
+
type: "PENDING_APPROVAL";
|
|
2997
|
+
/** Step key for correlation when resuming */
|
|
2998
|
+
stepKey: string;
|
|
2999
|
+
/** Optional reason for the pending state */
|
|
3000
|
+
reason?: string;
|
|
3001
|
+
/** Optional metadata for the approval request */
|
|
3002
|
+
metadata?: Record<string, unknown>;
|
|
3003
|
+
};
|
|
3394
3004
|
/**
|
|
3395
|
-
*
|
|
3396
|
-
*
|
|
3397
|
-
*
|
|
3398
|
-
*
|
|
3399
|
-
* Use `anyAsync()` when:
|
|
3400
|
-
* - You have multiple async fallback options and need the first that succeeds
|
|
3401
|
-
* - You're trying multiple async strategies in parallel (cache → DB → API)
|
|
3402
|
-
* - You want fail-fast success from parallel operations
|
|
3403
|
-
* - You have redundant async data sources and any one will do
|
|
3404
|
-
*
|
|
3405
|
-
* ## Why Use This Instead of `any`
|
|
3406
|
-
*
|
|
3407
|
-
* - **Parallel execution**: All Promises start immediately (faster)
|
|
3408
|
-
* - **Async support**: Works with Promises and AsyncResults
|
|
3409
|
-
* - **Promise rejection handling**: Converts Promise rejections to `PromiseRejectedError`
|
|
3410
|
-
*
|
|
3411
|
-
* ## Important
|
|
3412
|
-
*
|
|
3413
|
-
* - **First success wins**: Returns first successful Result (from any Promise)
|
|
3414
|
-
* - **Parallel**: All operations run simultaneously
|
|
3415
|
-
* - **All errors**: If all fail, returns first error encountered
|
|
3416
|
-
*
|
|
3417
|
-
* @param results - Array of Results or Promises of Results to check (all start in parallel)
|
|
3418
|
-
* @returns A Promise resolving to the first successful Result, or first error if all fail
|
|
3419
|
-
*
|
|
3420
|
-
* @example
|
|
3421
|
-
* ```typescript
|
|
3422
|
-
* // Try multiple async fallbacks in parallel
|
|
3423
|
-
* const data = await anyAsync([
|
|
3424
|
-
* fetchFromCache(id), // Fastest wins
|
|
3425
|
-
* fetchFromDB(id),
|
|
3426
|
-
* fetchFromAPI(id)
|
|
3427
|
-
* ]);
|
|
3428
|
-
*
|
|
3429
|
-
* // Try multiple API endpoints
|
|
3430
|
-
* const response = await anyAsync([
|
|
3431
|
-
* fetch('/api/v1/data'),
|
|
3432
|
-
* fetch('/api/v2/data'),
|
|
3433
|
-
* fetch('/backup-api/data')
|
|
3434
|
-
* ]);
|
|
3435
|
-
* ```
|
|
3005
|
+
* Standard error type for steps awaiting an HTTP callback (webhook).
|
|
3006
|
+
* Use with injectHook() to resume when the app receives the callback.
|
|
3007
|
+
* stepKey is always "hook:" + hookId for resume state.
|
|
3436
3008
|
*/
|
|
3437
|
-
|
|
3438
|
-
type
|
|
3439
|
-
|
|
3009
|
+
type PendingHook = {
|
|
3010
|
+
type: "PENDING_HOOK";
|
|
3011
|
+
hookId: string;
|
|
3012
|
+
/** Step key used in resume state; always "hook:" + hookId */
|
|
3013
|
+
stepKey: string;
|
|
3014
|
+
metadata?: Record<string, unknown>;
|
|
3440
3015
|
};
|
|
3441
|
-
type AllAsyncErrors<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {
|
|
3442
|
-
[K in keyof T]: Awaited<T[K]> extends Result<unknown, infer E, unknown> ? E : never;
|
|
3443
|
-
}[number];
|
|
3444
|
-
type AllAsyncCauses<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {
|
|
3445
|
-
[K in keyof T]: Awaited<T[K]> extends Result<unknown, unknown, infer C> ? C : never;
|
|
3446
|
-
}[number];
|
|
3447
3016
|
/**
|
|
3448
|
-
*
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
*
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
*
|
|
3480
|
-
* @returns A Promise resolving to a Result with:
|
|
3481
|
-
* - `ok(values[])` - Array of all success values if ALL succeed
|
|
3482
|
-
* - `err(errors[])` - Array of `SettledError` objects if ANY fail
|
|
3483
|
-
*
|
|
3484
|
-
* @example
|
|
3485
|
-
* ```typescript
|
|
3486
|
-
* // Async form validation - see all errors at once
|
|
3487
|
-
* const validated = await allSettledAsync([
|
|
3488
|
-
* validateEmailAsync(email),
|
|
3489
|
-
* validatePasswordAsync(password),
|
|
3490
|
-
* checkUsernameAvailableAsync(username),
|
|
3491
|
-
* ]);
|
|
3492
|
-
*
|
|
3493
|
-
* if (!validated.ok) {
|
|
3494
|
-
* // validated.error is array of all validation failures
|
|
3495
|
-
* console.log('Errors:', validated.error.map(e => e.error));
|
|
3496
|
-
* }
|
|
3497
|
-
*
|
|
3498
|
-
* // Parallel API calls with error collection
|
|
3499
|
-
* const results = await allSettledAsync([
|
|
3500
|
-
* fetchUser('1'),
|
|
3501
|
-
* fetchUser('2'),
|
|
3502
|
-
* fetchUser('3'),
|
|
3503
|
-
* ]);
|
|
3504
|
-
* ```
|
|
3017
|
+
* Error returned when approval is rejected.
|
|
3018
|
+
*/
|
|
3019
|
+
type ApprovalRejected = {
|
|
3020
|
+
type: "APPROVAL_REJECTED";
|
|
3021
|
+
/** Step key for correlation */
|
|
3022
|
+
stepKey: string;
|
|
3023
|
+
/** Reason the approval was rejected */
|
|
3024
|
+
reason: string;
|
|
3025
|
+
};
|
|
3026
|
+
/**
|
|
3027
|
+
* Options for creating an approval-gated step.
|
|
3028
|
+
*/
|
|
3029
|
+
interface ApprovalStepOptions<T> {
|
|
3030
|
+
/** Stable key for this approval step (used for resume) */
|
|
3031
|
+
key: string;
|
|
3032
|
+
/** Function to check current approval status from external source */
|
|
3033
|
+
checkApproval: () => Promise<{
|
|
3034
|
+
status: "pending";
|
|
3035
|
+
} | {
|
|
3036
|
+
status: "approved";
|
|
3037
|
+
value: T;
|
|
3038
|
+
} | {
|
|
3039
|
+
status: "rejected";
|
|
3040
|
+
reason: string;
|
|
3041
|
+
}>;
|
|
3042
|
+
/** Optional reason shown when pending */
|
|
3043
|
+
pendingReason?: string;
|
|
3044
|
+
/** Optional metadata for the approval request */
|
|
3045
|
+
metadata?: Record<string, unknown>;
|
|
3046
|
+
}
|
|
3047
|
+
/**
|
|
3048
|
+
* Options for creating a gated (pre-approval) step.
|
|
3505
3049
|
*/
|
|
3506
|
-
|
|
3050
|
+
interface GatedStepOptions<TArgs, T> {
|
|
3051
|
+
/** Stable key for this gated step (used for approval tracking) */
|
|
3052
|
+
key: string;
|
|
3053
|
+
/**
|
|
3054
|
+
* Condition to check if approval is required.
|
|
3055
|
+
* If returns true, execution pauses for approval.
|
|
3056
|
+
* If returns false, operation executes immediately.
|
|
3057
|
+
*/
|
|
3058
|
+
requiresApproval: boolean | ((args: TArgs) => boolean | Promise<boolean>);
|
|
3059
|
+
/**
|
|
3060
|
+
* Human-readable description of what this operation does.
|
|
3061
|
+
* Shown in the approval UI so humans understand what they're approving.
|
|
3062
|
+
*/
|
|
3063
|
+
description: string | ((args: TArgs) => string);
|
|
3064
|
+
/**
|
|
3065
|
+
* Check if approval has been granted externally.
|
|
3066
|
+
* If not provided, the step always returns PendingApproval when gated.
|
|
3067
|
+
*/
|
|
3068
|
+
checkApproval?: () => Promise<{
|
|
3069
|
+
status: "pending";
|
|
3070
|
+
} | {
|
|
3071
|
+
status: "approved";
|
|
3072
|
+
value?: T;
|
|
3073
|
+
} | {
|
|
3074
|
+
status: "rejected";
|
|
3075
|
+
reason: string;
|
|
3076
|
+
}>;
|
|
3077
|
+
/**
|
|
3078
|
+
* Optional metadata to include in the approval request.
|
|
3079
|
+
* The args are automatically included as `pendingArgs`.
|
|
3080
|
+
*/
|
|
3081
|
+
metadata?: Record<string, unknown>;
|
|
3082
|
+
}
|
|
3507
3083
|
|
|
3508
|
-
export {
|
|
3084
|
+
export { STREAM_ENDED as $, type AsyncResult as A, type BackoffStrategy as B, type WorkflowSnapshot as C, type DepValueOfReturn as D, EARLY_EXIT_SYMBOL as E, type ApprovalStepOptions as F, type GatedStepOptions as G, type Workflow as H, type WorkflowOptions as I, type SnapshotStore as J, type ResumeStateEntry as K, type StreamStore as L, type StreamReader as M, type CausesOfDeps as N, type Ok as O, type PendingApproval as P, type ExecutionOptions as Q, type Result as R, type StepOptions as S, type TimeoutOptions as T, type JSONValue as U, type MemoryCacheOptions as V, type WorkflowEvent as W, type RunConfig as X, type RunWithStateResult as Y, STREAM_BACKPRESSURE_ERROR as Z, STREAM_CLOSE_ERROR as _, type RetryOptions as a, STREAM_READ_ERROR as a0, STREAM_STORE_ERROR as a1, STREAM_WRITE_ERROR as a2, type SerializedCause as a3, SnapshotDecodeError as a4, SnapshotFormatError as a5, SnapshotMismatchError as a6, type SnapshotWarning as a7, type StepCache as a8, type StepErrorDiagnostics as a9, isWorkflowSnapshot as aA, looksLikeWorkflowSnapshot as aB, mergeSnapshots as aC, serializeError as aD, serializeThrown as aE, streamBackpressureError as aF, streamCloseError as aG, streamEnded as aH, streamReadError as aI, streamStoreError as aJ, streamWriteError as aK, validateSnapshot as aL, type StepMetadata as aa, type StreamBackpressureError as ab, type StreamCloseError as ac, type StreamEndedMarker as ad, type StreamError as ae, type StreamForEachOptions as af, type StreamForEachResult as ag, type StreamItem as ah, type StreamMetadata as ai, type StreamOptions as aj, type StreamReadError as ak, type StreamReadOptions as al, type StreamStoreError as am, type StreamWriteError as an, type StreamWriter as ao, type Unsubscribe as ap, type WorkflowFn as aq, type WorkflowSteps as ar, assertValidSnapshot as as, createMemoryCache as at, deserializeCauseNew as au, isStreamBackpressureError as av, isStreamEnded as aw, isStreamReadError as ax, isStreamStoreError as ay, isStreamWriteError as az, type BoundSteps as b, type EarlyExit as c, type RunOptions as d, type RunOptionsWithCatch as e, type RunOptionsWithoutCatch as f, type RunStep as g, STEP_TIMEOUT_MARKER as h, type ScopeType as i, type StepFailureMeta as j, type StepTimeoutError as k, type StepTimeoutMarkerMeta as l, createEarlyExit as m, getStepTimeoutMeta as n, isEarlyExit as o, isStepTimeoutError as p, type AnyResultFn as q, run as r, type ErrorsOfDeps as s, type WorkflowContext as t, type Err as u, type ApprovalRejected as v, type PendingHook as w, type ResumeState as x, type WorkflowCancelledError as y, type StepResult as z };
|