awaitly 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/di-Biw_plBn.d.cts +15 -0
  2. package/dist/di-DxyH2N3i.d.ts +15 -0
  3. package/dist/durable.cjs +4670 -0
  4. package/dist/durable.cjs.map +1 -0
  5. package/dist/durable.d.cts +9 -0
  6. package/dist/durable.d.ts +9 -0
  7. package/dist/durable.js +4610 -0
  8. package/dist/durable.js.map +1 -0
  9. package/dist/{di-BbFFfO8y.d.ts → duration-BjVn3QpB.d.cts} +1 -15
  10. package/dist/{di-BDlT7InM.d.cts → duration-BjVn3QpB.d.ts} +1 -15
  11. package/dist/engine.cjs +4501 -0
  12. package/dist/engine.cjs.map +1 -0
  13. package/dist/engine.d.cts +113 -0
  14. package/dist/engine.d.ts +113 -0
  15. package/dist/engine.js +4474 -0
  16. package/dist/engine.js.map +1 -0
  17. package/dist/{errors-DtXvrCiO.d.cts → errors-sDSeaZBO.d.cts} +1 -1
  18. package/dist/{errors-DtXvrCiO.d.ts → errors-sDSeaZBO.d.ts} +1 -1
  19. package/dist/guards-CwQZro1F.d.ts +72 -0
  20. package/dist/guards-DhhOJZda.d.cts +72 -0
  21. package/dist/hitl-BnnnLzh2.d.cts +468 -0
  22. package/dist/hitl-pYfkQMv_.d.ts +468 -0
  23. package/dist/hitl.cjs +646 -0
  24. package/dist/hitl.cjs.map +1 -0
  25. package/dist/hitl.d.cts +440 -0
  26. package/dist/hitl.d.ts +440 -0
  27. package/dist/hitl.js +605 -0
  28. package/dist/hitl.js.map +1 -0
  29. package/dist/index-Bew12SZ8.d.ts +417 -0
  30. package/dist/index-Bwt-iz50.d.cts +417 -0
  31. package/dist/{types-B8NfNRGX.d.ts → index-Cv4K_3sZ.d.ts} +2 -1162
  32. package/dist/{types-BZ2f4MRR.d.cts → index-ICmfQi08.d.cts} +2 -1162
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +11 -1527
  35. package/dist/index.d.ts +11 -1527
  36. package/dist/persistence.cjs +552 -0
  37. package/dist/persistence.cjs.map +1 -0
  38. package/dist/persistence.d.cts +254 -0
  39. package/dist/persistence.d.ts +254 -0
  40. package/dist/persistence.js +499 -0
  41. package/dist/persistence.js.map +1 -0
  42. package/dist/reliability.cjs +1651 -0
  43. package/dist/reliability.cjs.map +1 -0
  44. package/dist/reliability.d.cts +1442 -0
  45. package/dist/reliability.d.ts +1442 -0
  46. package/dist/reliability.js +1589 -0
  47. package/dist/reliability.js.map +1 -0
  48. package/dist/result.d.cts +1 -1
  49. package/dist/result.d.ts +1 -1
  50. package/dist/run.cjs +2283 -0
  51. package/dist/run.cjs.map +1 -0
  52. package/dist/run.d.cts +90 -0
  53. package/dist/run.d.ts +90 -0
  54. package/dist/run.js +2247 -0
  55. package/dist/run.js.map +1 -0
  56. package/dist/saga.cjs +3699 -0
  57. package/dist/saga.cjs.map +1 -0
  58. package/dist/saga.d.cts +162 -0
  59. package/dist/saga.d.ts +162 -0
  60. package/dist/saga.js +3670 -0
  61. package/dist/saga.js.map +1 -0
  62. package/dist/store-contract-BI98VYmX.d.ts +66 -0
  63. package/dist/store-contract-C2HyR_o6.d.cts +66 -0
  64. package/dist/streaming.cjs +895 -0
  65. package/dist/streaming.cjs.map +1 -0
  66. package/dist/streaming.d.cts +594 -0
  67. package/dist/streaming.d.ts +594 -0
  68. package/dist/streaming.js +832 -0
  69. package/dist/streaming.js.map +1 -0
  70. package/dist/testing.d.cts +4 -2
  71. package/dist/testing.d.ts +4 -2
  72. package/dist/types-BDTxgKKc.d.ts +845 -0
  73. package/dist/types-C9Y71dua.d.cts +845 -0
  74. package/dist/types-d8q8iQlk.d.cts +323 -0
  75. package/dist/types-sObbY4mX.d.ts +323 -0
  76. package/dist/webhook.cjs +461 -0
  77. package/dist/webhook.cjs.map +1 -0
  78. package/dist/webhook.d.cts +497 -0
  79. package/dist/webhook.d.ts +497 -0
  80. package/dist/webhook.js +422 -0
  81. package/dist/webhook.js.map +1 -0
  82. package/dist/workflow.cjs +2 -2614
  83. package/dist/workflow.cjs.map +1 -1
  84. package/dist/workflow.d.cts +12 -3023
  85. package/dist/workflow.d.ts +12 -3023
  86. package/dist/workflow.js +2 -2535
  87. package/dist/workflow.js.map +1 -1
  88. package/package.json +46 -1
@@ -1,603 +1,15 @@
1
- import { v as ApprovalRejected, P as PendingApproval, w as PendingHook, x as ResumeState, W as WorkflowEvent, y as WorkflowCancelledError, z as StepResult, C as WorkflowSnapshot, F as ApprovalStepOptions, A as AsyncResult, G as GatedStepOptions, u as Err, H as Workflow, q as AnyResultFn, I as WorkflowOptions, s as ErrorsOfDeps, R as Result, J as SnapshotStore, g as RunStep, t as WorkflowContext, K as ResumeStateEntry, L as StreamStore, M as StreamReader } from './types-B8NfNRGX.js';
2
- export { B as BackoffStrategy, N as CausesOfDeps, Q as ExecutionOptions, U as JSONValue, V as MemoryCacheOptions, a as RetryOptions, X as RunConfig, d as RunOptions, e as RunOptionsWithCatch, f as RunOptionsWithoutCatch, Y as RunWithStateResult, h as STEP_TIMEOUT_MARKER, Z as STREAM_BACKPRESSURE_ERROR, _ as STREAM_CLOSE_ERROR, $ as STREAM_ENDED, a0 as STREAM_READ_ERROR, a1 as STREAM_STORE_ERROR, a2 as STREAM_WRITE_ERROR, i as ScopeType, a3 as SerializedCause, a4 as SnapshotDecodeError, a5 as SnapshotFormatError, a6 as SnapshotMismatchError, a7 as SnapshotWarning, a8 as StepCache, a9 as StepErrorDiagnostics, aa as StepMetadata, S as StepOptions, k as StepTimeoutError, l as StepTimeoutMarkerMeta, ab as StreamBackpressureError, ac as StreamCloseError, ad as StreamEndedMarker, ae as StreamError, af as StreamForEachOptions, ag as StreamForEachResult, ah as StreamItem, ai as StreamMetadata, aj as StreamOptions, ak as StreamReadError, al as StreamReadOptions, am as StreamStoreError, an as StreamWriteError, ao as StreamWriter, T as TimeoutOptions, ap as Unsubscribe, aq as WorkflowFn, ar as WorkflowSteps, as as assertValidSnapshot, at as createMemoryCache, au as deserializeCauseNew, n as getStepTimeoutMeta, p as isStepTimeoutError, av as isStreamBackpressureError, aw as isStreamEnded, ax as isStreamReadError, ay as isStreamStoreError, az as isStreamWriteError, aA as isWorkflowSnapshot, aB as looksLikeWorkflowSnapshot, aC as mergeSnapshots, r as run, aD as serializeError, aE as serializeThrown, aF as streamBackpressureError, aG as streamCloseError, aH as streamEnded, aI as streamReadError, aJ as streamStoreError, aK as streamWriteError, aL as validateSnapshot } from './types-B8NfNRGX.js';
3
- import { U as UnexpectedError } from './errors-DtXvrCiO.js';
4
- export { D as Duration, D as DurationType, d as days, i as hours, k as isDuration, r as millis, t as minutes, w as seconds, y as toDays, z as toHours, A as toMillis, B as toMinutes, C as toSeconds, E as withDeps } from './di-BbFFfO8y.js';
1
+ import { q as Err, R as Result, A as AsyncResult } from './index-Cv4K_3sZ.js';
2
+ export { B as BackoffStrategy, d as RetryOptions, e as RunOptions, f as RunOptionsWithCatch, g as RunOptionsWithoutCatch, a as RunStep, S as STEP_TIMEOUT_MARKER, h as ScopeType, t as StepErrorDiagnostics, u as StepMetadata, j as StepOptions, k as StepTimeoutError, l as StepTimeoutMarkerMeta, T as TimeoutOptions, W as WorkflowEvent, n as getStepTimeoutMeta, p as isStepTimeoutError, r as run } from './index-Cv4K_3sZ.js';
3
+ import { g as PendingHook, W as Workflow, c as AnyResultFn, f as WorkflowOptions, E as ErrorsOfDeps } from './types-BDTxgKKc.js';
4
+ export { C as CausesOfDeps, i as ExecutionOptions, J as JSONValue, R as ResumeState, j as ResumeStateEntry, k as RunConfig, l as RunWithStateResult, m as SerializedCause, n as SnapshotDecodeError, o as SnapshotFormatError, p as SnapshotMismatchError, q as SnapshotWarning, r as StepCache, h as StepResult, e as WorkflowCancelledError, d as WorkflowContext, s as WorkflowFn, b as WorkflowSnapshot, t as WorkflowSteps, u as assertValidSnapshot, v as isWorkflowSnapshot, w as looksLikeWorkflowSnapshot, x as mergeSnapshots, y as validateSnapshot } from './types-BDTxgKKc.js';
5
+ export { i as isApprovalRejected, a as isPendingApproval, b as isPendingHook, c as isResumeState, d as isStepComplete, e as isWorkflowCancelled } from './guards-CwQZro1F.js';
6
+ export { P as PersistedWorkflowState, S as SerializedResumeState, a as StoreLoadResult, b as StoreSaveInput, d as deserializeResumeState, i as isSerializedResumeState, s as serializeResumeState, t as toResumeState } from './store-contract-BI98VYmX.js';
7
+ export { c as clearStep, a as createApprovalStateCollector, b as createApprovalStep, e as createResumeStateCollector, g as gatedStep, d as getPendingApprovals, f as getPendingHooks, h as hasPendingApproval, j as hasPendingHook, i as injectApproval, k as injectHook, p as pendingApproval } from './hitl-pYfkQMv_.js';
8
+ import { U as UnexpectedError } from './errors-sDSeaZBO.js';
9
+ export { w as withDeps } from './di-DxyH2N3i.js';
5
10
  import { StandardSchemaV1 } from '@standard-schema/spec';
6
-
7
- /**
8
- * Type guards for workflow events and errors.
9
- */
10
-
11
- /**
12
- * Type guard to check if an event is a step_complete event.
13
- * Use this to filter events for state persistence.
14
- *
15
- * @param event - The workflow event to check
16
- * @returns `true` if the event is a step_complete event, `false` otherwise
17
- *
18
- * @example
19
- * ```typescript
20
- * const savedSteps = new Map<string, Result<unknown, unknown>>();
21
- *
22
- * const workflow = createWorkflow({ fetchUser }, {
23
- * onEvent: (event) => {
24
- * if (isStepComplete(event)) {
25
- * savedSteps.set(event.stepKey, event.result);
26
- * }
27
- * }
28
- * });
29
- * ```
30
- */
31
- /**
32
- * Type guard for runtime ResumeState (steps is a Map). Use to discriminate from WorkflowSnapshot when loading.
33
- */
34
- declare function isResumeState(x: unknown): x is ResumeState;
35
- declare function isStepComplete(event: WorkflowEvent<unknown>): event is Extract<WorkflowEvent<unknown>, {
36
- type: "step_complete";
37
- }>;
38
- /**
39
- * Type guard to check if an error is a WorkflowCancelledError.
40
- *
41
- * @param error - The error to check
42
- * @returns `true` if the error is a WorkflowCancelledError, `false` otherwise
43
- */
44
- declare function isWorkflowCancelled(error: unknown): error is WorkflowCancelledError;
45
- /**
46
- * Type guard to check if an error is a PendingApproval.
47
- *
48
- * @param error - The error to check
49
- * @returns `true` if the error is a PendingApproval, `false` otherwise
50
- *
51
- * @example
52
- * ```typescript
53
- * const result = await workflow(...);
54
- * if (!result.ok && isPendingApproval(result.error)) {
55
- * console.log(`Waiting for approval: ${result.error.stepKey}`);
56
- * }
57
- * ```
58
- */
59
- declare function isPendingApproval(error: unknown): error is PendingApproval;
60
- /**
61
- * Type guard to check if an error is an ApprovalRejected.
62
- *
63
- * @param error - The error to check
64
- * @returns `true` if the error is an ApprovalRejected, `false` otherwise
65
- */
66
- declare function isApprovalRejected(error: unknown): error is ApprovalRejected;
67
- /**
68
- * Type guard to check if an error is a PendingHook.
69
- *
70
- * @param error - The error to check
71
- * @returns `true` if the error is a PendingHook, `false` otherwise
72
- */
73
- declare function isPendingHook(error: unknown): error is PendingHook;
74
-
75
- /**
76
- * Serialization for ResumeState to/from JSON-safe format.
77
- * Centralizes Map serialization so adapters don't re-implement it.
78
- */
79
-
80
- /**
81
- * JSON-serializable resume state. Use this shape when persisting to storage.
82
- * Discriminator `kind: "ResumeState"` enables adapters and migrations to detect format.
83
- */
84
- type SerializedResumeState = {
85
- kind: "ResumeState";
86
- steps: [string, StepResult][];
87
- };
88
- /**
89
- * Serialize resume state to a JSON-safe object. Preserves step order (array).
90
- * Use with JSON.stringify for storage; adapters can rely on this shape.
91
- *
92
- * @example
93
- * const serialized = serializeResumeState(collector.getResumeState());
94
- * await store.save(id, JSON.stringify(serialized));
95
- */
96
- declare function serializeResumeState(state: ResumeState): SerializedResumeState;
97
- /**
98
- * Type guard for SerializedResumeState. Use when loading from storage to discriminate from WorkflowSnapshot.
99
- */
100
- declare function isSerializedResumeState(x: unknown): x is SerializedResumeState;
101
- /**
102
- * Deserialize from JSON-parsed object back to ResumeState (runtime Map).
103
- *
104
- * @example
105
- * const parsed = JSON.parse(await store.load(id));
106
- * if (isSerializedResumeState(parsed)) {
107
- * const state = deserializeResumeState(parsed);
108
- * await workflow.run(fn, { resumeState: state });
109
- * }
110
- */
111
- declare function deserializeResumeState(raw: SerializedResumeState): ResumeState;
112
-
113
- /**
114
- * Extended persistence contract: save/load accept both WorkflowSnapshot and ResumeState.
115
- * Use type guards (isWorkflowSnapshot, isResumeState, isSerializedResumeState) in adapters.
116
- * Core persistence.SnapshotStore stays narrow; this module defines the broad contract and helpers.
117
- */
118
-
119
- /** What stores persist (JSON/database shape). Use for type discrimination in adapters. */
120
- type PersistedWorkflowState = WorkflowSnapshot | SerializedResumeState;
121
- /** What save() can accept: snapshot or runtime resume state. Adapters branch with type guards. */
122
- type StoreSaveInput = WorkflowSnapshot | ResumeState;
123
- /** What load() can return. Use toResumeState() or loadResumeState for type-safe restore. */
124
- type StoreLoadResult = WorkflowSnapshot | ResumeState | null;
125
- /**
126
- * Convert a loaded value to ResumeState for workflow.run(fn, { resumeState }).
127
- * - If loaded is already ResumeState, returns it.
128
- * - If loaded is null or WorkflowSnapshot, returns undefined (use run(fn, { snapshot }) for snapshots).
129
- * Two explicit flows: resume from ResumeState vs resume from snapshot; don't pass snapshot to resumeState.
130
- *
131
- * @example
132
- * const loaded = await store.load('wf-123');
133
- * const resumeState = toResumeState(loaded);
134
- * if (resumeState) await workflow.run(fn, { resumeState });
135
- */
136
- declare function toResumeState(loaded: StoreLoadResult): ResumeState | undefined;
137
-
138
- /**
139
- * Resume state helpers: collectors and state manipulation for workflow replay.
140
- */
141
-
142
- /**
143
- * Create a collector for step results to build resume state.
144
- *
145
- * ## When to Use
146
- *
147
- * Use `createResumeStateCollector` when you need to:
148
- * - **Save workflow state** for later replay/resume
149
- * - **Persist step results** to a database or file system
150
- * - **Build resume state** from workflow execution
151
- * - **Enable workflow replay** after application restarts
152
- *
153
- * ## Why Use This Instead of Manual Collection
154
- *
155
- * - **Automatic filtering**: Only collects `step_complete` events (ignores other events)
156
- * - **Metadata preservation**: Captures both result and meta for proper error replay
157
- * - **Type-safe**: Returns properly typed `ResumeState`
158
- * - **Convenient API**: Simple `handleEvent` → `getResumeState` pattern
159
- *
160
- * ## How It Works
161
- *
162
- * 1. Create collector and pass `handleEvent` to workflow's `onEvent` option
163
- * 2. Workflow emits `step_complete` events for keyed steps
164
- * 3. Collector automatically captures these events
165
- * 4. Call `getResumeState()` to get the collected `ResumeState`
166
- * 5. Persist state (e.g., to database) for later resume
167
- *
168
- * ## When step_complete Events Are Emitted
169
- *
170
- * Events are emitted for ANY step that has a `key` option, regardless of calling pattern:
171
- *
172
- * ```typescript
173
- * // Function-wrapped pattern - emits step_complete
174
- * await step(() => fetchUser("1"), { key: "user:1" });
175
- *
176
- * // Direct AsyncResult pattern - also emits step_complete
177
- * await step(fetchUser("1"), { key: "user:1" });
178
- * ```
179
- *
180
- * Both patterns above will emit `step_complete` events and be captured by the collector.
181
- *
182
- * ## Important Notes
183
- *
184
- * - Only steps with a `key` option are collected (unkeyed steps are not saved)
185
- * - The collector preserves error metadata for proper replay behavior
186
- * - State can be serialized to JSON (but complex cause types may need custom handling)
187
- *
188
- * @returns An object with:
189
- * - `handleEvent`: Function to pass to workflow's `onEvent` option
190
- * - `getResumeState`: Get collected resume state (call after workflow execution)
191
- * - `clear`: Clears the collector's internal recorded entries (does not mutate workflow state)
192
- *
193
- * @example
194
- * ```typescript
195
- * // Collect state during workflow execution
196
- * const collector = createResumeStateCollector();
197
- *
198
- * const workflow = createWorkflow({ fetchUser, fetchPosts }, {
199
- * onEvent: collector.handleEvent, // Pass collector's handler
200
- * });
201
- *
202
- * await workflow(async ({ step }) => {
203
- * // Only keyed steps are collected
204
- * const user = await step(() => fetchUser("1"), { key: "user:1" });
205
- * const posts = await step(() => fetchPosts(user.id), { key: `posts:${user.id}` });
206
- * return { user, posts };
207
- * });
208
- *
209
- * // Get collected state for persistence
210
- * const state = collector.getResumeState();
211
- * // state.steps contains: 'user:1' and 'posts:1' entries
212
- *
213
- * // Save to database
214
- * await db.saveWorkflowState(workflowId, state);
215
- * ```
216
- *
217
- * @example
218
- * ```typescript
219
- * // Resume workflow from saved state
220
- * const savedState = await db.loadWorkflowState(workflowId);
221
- * const workflow = createWorkflow({ fetchUser, fetchPosts }, {
222
- * resumeState: savedState // Pre-populate cache from saved state
223
- * });
224
- *
225
- * // Cached steps skip execution, new steps run normally
226
- * await workflow(async ({ step }) => {
227
- * const user = await step(() => fetchUser("1"), { key: "user:1" }); // Cache hit
228
- * const posts = await step(() => fetchPosts(user.id), { key: `posts:${user.id}` }); // Cache hit
229
- * return { user, posts };
230
- * });
231
- * ```
232
- */
233
- declare function createResumeStateCollector(): {
234
- /** Handle workflow events. Pass this to workflow's `onEvent` option. */
235
- handleEvent: (event: WorkflowEvent<unknown>) => void;
236
- /** Get the collected resume state. Call after workflow execution. */
237
- getResumeState: () => ResumeState;
238
- /** Clears the collector's internal recorded entries (does not mutate workflow state). */
239
- clear: () => void;
240
- };
241
- /**
242
- * Inject an approved value into resume state.
243
- * Use this when an external approval is granted and you want to resume the workflow.
244
- *
245
- * @param state - The resume state to update
246
- * @param options - Object with stepKey and the approved value
247
- * @returns A new ResumeState with the approval injected
248
- *
249
- * @example
250
- * ```typescript
251
- * // When approval is granted externally:
252
- * const updatedState = injectApproval(savedState, {
253
- * stepKey: 'deploy:prod',
254
- * value: { approvedBy: 'admin', approvedAt: Date.now() }
255
- * });
256
- *
257
- * // Resume workflow with the approval injected
258
- * const workflow = createWorkflow({ ... }, { resumeState: updatedState });
259
- * ```
260
- */
261
- declare function injectApproval<T>(state: ResumeState, options: {
262
- stepKey: string;
263
- value: T;
264
- }): ResumeState;
265
- /**
266
- * Remove a step from resume state (e.g., to force re-execution).
267
- * This is an immutable operation - returns a new ResumeState without modifying the original.
268
- *
269
- * @param state - The resume state to update
270
- * @param stepKey - The key of the step to remove
271
- * @returns A new ResumeState with the step removed (original is unchanged)
272
- *
273
- * @example
274
- * ```typescript
275
- * // Force a step to re-execute on resume
276
- * const updatedState = clearStep(savedState, 'approval:123');
277
- * ```
278
- */
279
- declare function clearStep(state: ResumeState, stepKey: string): ResumeState;
280
- /**
281
- * Check if a step in resume state has a pending approval error.
282
- *
283
- * @param state - The resume state to check
284
- * @param stepKey - The key of the step to check
285
- * @returns `true` if the step has a pending approval, `false` otherwise
286
- *
287
- * @example
288
- * ```typescript
289
- * if (hasPendingApproval(savedState, 'deploy:prod')) {
290
- * // Show approval UI
291
- * }
292
- * ```
293
- */
294
- declare function hasPendingApproval(state: ResumeState, stepKey: string): boolean;
295
- /**
296
- * Get all pending approval step keys from resume state.
297
- *
298
- * @param state - The resume state to check
299
- * @returns Array of step keys that have pending approvals
300
- *
301
- * @example
302
- * ```typescript
303
- * const pendingKeys = getPendingApprovals(savedState);
304
- * // ['deploy:prod', 'deploy:staging']
305
- * ```
306
- */
307
- declare function getPendingApprovals(state: ResumeState): string[];
308
- /**
309
- * Inject a hook callback value into resume state.
310
- * Call this when the app receives the HTTP callback (e.g. POST /hook/:hookId) and pass the request body as value.
311
- *
312
- * @param state - The resume state to update
313
- * @param options - hookId (from the callback URL) and value (e.g. request body)
314
- * @returns A new ResumeState with the hook step set to ok(value)
315
- */
316
- declare function injectHook<T>(state: ResumeState, options: {
317
- hookId: string;
318
- value: T;
319
- }): ResumeState;
320
- /**
321
- * Check if a step in resume state has a pending hook error.
322
- *
323
- * @param state - The resume state to check
324
- * @param hookId - The hook id (from createHook() or the callback URL)
325
- * @returns `true` if that hook step is pending, `false` otherwise
326
- */
327
- declare function hasPendingHook(state: ResumeState, hookId: string): boolean;
328
- /**
329
- * Get all pending hook hookIds from resume state.
330
- *
331
- * @param state - The resume state to check
332
- * @returns Array of hookIds that have pending callbacks
333
- */
334
- declare function getPendingHooks(state: ResumeState): string[];
335
- /**
336
- * Extended resume state collector that tracks pending approvals.
337
- * Use this for human-in-the-loop workflows that need to track approval state.
338
- *
339
- * @returns An object with methods to handle events, get state, and manage approvals
340
- *
341
- * @example
342
- * ```typescript
343
- * const collector = createApprovalStateCollector();
344
- *
345
- * const workflow = createWorkflow({ fetchUser, requireApproval }, {
346
- * onEvent: collector.handleEvent,
347
- * });
348
- *
349
- * const result = await workflow(async ({ step }) => {
350
- * const user = await step(() => fetchUser("1"), { key: "user:1" });
351
- * const approval = await step(requireApproval, { key: "approval:1" });
352
- * return { user, approval };
353
- * });
354
- *
355
- * // Check for pending approvals
356
- * if (collector.hasPendingApprovals()) {
357
- * const pending = collector.getPendingApprovals();
358
- * // pending: [{ stepKey: 'approval:1', error: PendingApproval }]
359
- * await saveToDatabase(collector.getResumeState());
360
- * }
361
- *
362
- * // Later, when approved:
363
- * const resumeState = collector.injectApproval('approval:1', { approvedBy: 'admin' });
364
- * ```
365
- */
366
- declare function createApprovalStateCollector(): {
367
- /** Handle workflow events. Pass this to workflow's `onEvent` option. */
368
- handleEvent: (event: WorkflowEvent<unknown>) => void;
369
- /** Get the collected resume state. Call after workflow execution. */
370
- getResumeState: () => ResumeState;
371
- /** Clears the collector's internal recorded entries (does not mutate workflow state). */
372
- clear: () => void;
373
- /** Check if any steps have pending approvals */
374
- hasPendingApprovals: () => boolean;
375
- /** Get all pending approval entries with their errors */
376
- getPendingApprovals: () => Array<{
377
- stepKey: string;
378
- error: PendingApproval;
379
- }>;
380
- /** Inject an approval result, updating the collector's internal state. Returns a copy for use as resumeState. */
381
- injectApproval: <T>(stepKey: string, value: T) => ResumeState;
382
- };
383
-
384
- /**
385
- * Human-in-the-Loop (HITL) helpers: pendingApproval, createApprovalStep, gatedStep.
386
- */
387
-
388
- /**
389
- * Create a PendingApproval error result.
390
- * Convenience helper for approval-gated steps.
391
- *
392
- * @param stepKey - Stable key for this approval step (used for resume)
393
- * @param options - Optional reason and metadata for the pending approval
394
- * @returns A Result with a PendingApproval error
395
- *
396
- * @example
397
- * ```typescript
398
- * const requireApproval = async (userId: string) => {
399
- * const status = await db.getApproval(userId);
400
- * if (!status) return pendingApproval(`approval:${userId}`);
401
- * return ok(status);
402
- * };
403
- * ```
404
- */
405
- declare function pendingApproval(stepKey: string, options?: {
406
- reason?: string;
407
- metadata?: Record<string, unknown>;
408
- }): Err<PendingApproval>;
409
- /**
410
- * Create a Result-returning function that checks external approval status.
411
- *
412
- * ## When to Use
413
- *
414
- * Use `createApprovalStep` when you need:
415
- * - **Human-in-the-loop workflows**: Steps that require human approval
416
- * - **External approval systems**: Integrate with approval databases/APIs
417
- * - **Workflow pausing**: Workflows that pause and resume after approval
418
- * - **Approval tracking**: Track who approved what and when
419
- *
420
- * ## Why Use This Instead of Manual Approval Checks
421
- *
422
- * - **Standardized pattern**: Consistent approval step interface
423
- * - **Type-safe**: Returns typed `PendingApproval` or `ApprovalRejected` errors
424
- * - **Resume-friendly**: Works seamlessly with `injectApproval()` and resume state
425
- * - **Metadata support**: Can include approval reason and metadata
426
- *
427
- * ## How It Works
428
- *
429
- * 1. Create approval step with `checkApproval` function
430
- * 2. `checkApproval` returns one of:
431
- * - `{ status: 'pending' }` - Approval not yet granted (workflow pauses)
432
- * - `{ status: 'approved', value: T }` - Approval granted (workflow continues)
433
- * - `{ status: 'rejected', reason: string }` - Approval rejected (workflow fails)
434
- * 3. Use in workflow with `step()` - workflow pauses if pending
435
- * 4. When approval granted externally, use `injectApproval()` to resume
436
- *
437
- * ## Typical Approval Flow
438
- *
439
- * 1. Workflow executes → reaches approval step
440
- * 2. `checkApproval()` called → returns `{ status: 'pending' }`
441
- * 3. Workflow returns `PendingApproval` error
442
- * 4. Save workflow state → persist for later resume
443
- * 5. Show approval UI → user sees pending approval
444
- * 6. User grants/rejects → update approval system
445
- * 7. Inject approval → call `injectApproval()` with approved value
446
- * 8. Resume workflow → continue from approval step
447
- *
448
- * @param options - Configuration for the approval step:
449
- * - `key`: Stable key for this approval (must match step key in workflow)
450
- * - `checkApproval`: Async function that checks current approval status
451
- * - `pendingReason`: Optional reason shown when approval is pending
452
- * - `metadata`: Optional metadata attached to the approval request
453
- *
454
- * @returns A function that returns an AsyncResult checking approval status.
455
- * The function can be used directly with `step()` in workflows.
456
- *
457
- * @example
458
- * ```typescript
459
- * // Create approval step that checks database
460
- * const requireManagerApproval = createApprovalStep<{ approvedBy: string }>({
461
- * key: 'manager-approval',
462
- * checkApproval: async () => {
463
- * const approval = await db.getApproval('manager-approval');
464
- * if (!approval) {
465
- * return { status: 'pending' }; // Workflow pauses here
466
- * }
467
- * if (approval.rejected) {
468
- * return { status: 'rejected', reason: approval.reason };
469
- * }
470
- * return {
471
- * status: 'approved',
472
- * value: { approvedBy: approval.approvedBy }
473
- * };
474
- * },
475
- * pendingReason: 'Waiting for manager approval',
476
- * });
477
- *
478
- * // Use in workflow
479
- * const workflow = createWorkflow({ requireManagerApproval });
480
- * const result = await workflow(async ({ step }) => {
481
- * const approval = await step(requireManagerApproval, { key: 'manager-approval' });
482
- * // If pending, workflow exits with PendingApproval error
483
- * // If approved, continues with approval value
484
- * return approval;
485
- * });
486
- *
487
- * // Handle pending state
488
- * if (!result.ok && isPendingApproval(result.error)) {
489
- * // Workflow paused - show approval UI
490
- * showApprovalUI(result.error.stepKey);
491
- * }
492
- * ```
493
- *
494
- * @example
495
- * ```typescript
496
- * // With approval injection for resume
497
- * const collector = createApprovalStateCollector();
498
- * const workflow = createWorkflow({ requireApproval }, {
499
- * onEvent: collector.handleEvent,
500
- * });
501
- *
502
- * const result = await workflow(async ({ step }) => {
503
- * const approval = await step(requireApproval, { key: 'approval:1' });
504
- * return approval;
505
- * });
506
- *
507
- * // When approval granted externally
508
- * if (collector.hasPendingApprovals()) {
509
- * const resumeState = collector.injectApproval('approval:1', {
510
- * approvedBy: 'admin@example.com'
511
- * });
512
- *
513
- * // Resume workflow
514
- * const workflow2 = createWorkflow({ requireApproval }, { resumeState });
515
- * const result2 = await workflow2(async ({ step }) => {
516
- * const approval = await step(requireApproval, { key: 'approval:1' });
517
- * return approval; // Now succeeds with injected value
518
- * });
519
- * }
520
- * ```
521
- */
522
- declare function createApprovalStep<T>(options: ApprovalStepOptions<T>): () => AsyncResult<T, PendingApproval | ApprovalRejected>;
523
- /**
524
- * Create a gated step that requires approval before execution.
525
- *
526
- * This is the AI SDK / LangChain-style pattern where you intercept
527
- * tool calls *before* they execute, allowing humans to see the args
528
- * and approve, edit, or reject before the operation runs.
529
- *
530
- * ## When to Use
531
- *
532
- * Use `gatedStep` when you want to:
533
- * - **Show args before execution**: Let humans see what the operation will do
534
- * - **Allow editing args**: Humans can modify args before operation runs
535
- * - **Conditional gating**: Only require approval for certain conditions
536
- * - **AI safety**: Gate dangerous AI tool calls (send email, delete file, etc.)
537
- *
538
- * ## Difference from createApprovalStep
539
- *
540
- * - `createApprovalStep`: Checks external approval status, operation already defined
541
- * - `gatedStep`: Gates before operation, shows args, allows editing, then executes
542
- *
543
- * ## Flow
544
- *
545
- * 1. Call gatedStep with args
546
- * 2. Check if approval is required (based on requiresApproval condition)
547
- * 3. If required and not approved:
548
- * - Return PendingApproval with args visible in metadata
549
- * - Human sees: "Send email to external@example.com with subject X"
550
- * - Human can approve (run as-is), edit (modify args), or reject
551
- * 4. If approved or not required:
552
- * - Execute the operation with (potentially edited) args
553
- *
554
- * @param operation - The operation to gate (a function returning AsyncResult)
555
- * @param options - Gating configuration
556
- * @returns A gated function that checks approval before execution
557
- *
558
- * @example
559
- * ```typescript
560
- * // Gate external email sends
561
- * const sendEmail = async (to: string, subject: string, body: string) => { ... };
562
- *
563
- * const gatedSendEmail = gatedStep(
564
- * sendEmail,
565
- * {
566
- * key: 'email',
567
- * requiresApproval: (args) => !args.to.endsWith('@mycompany.com'),
568
- * description: (args) => `Send email to ${args.to}: "${args.subject}"`,
569
- * }
570
- * );
571
- *
572
- * // In workflow:
573
- * const result = await step(
574
- * () => gatedSendEmail({ to: 'external@other.com', subject: 'Hello', body: '...' }),
575
- * { key: 'send-welcome-email' }
576
- * );
577
- *
578
- * // If gated, returns PendingApproval with:
579
- * // {
580
- * // stepKey: 'email',
581
- * // reason: 'Send email to external@other.com: "Hello"',
582
- * // metadata: { pendingArgs: { to: '...', subject: '...', body: '...' } }
583
- * // }
584
- * ```
585
- *
586
- * @example
587
- * ```typescript
588
- * // Gate file deletion with explicit approval check
589
- * const gatedDelete = gatedStep(
590
- * (path: string) => deleteFile(path),
591
- * {
592
- * key: 'delete-file',
593
- * requiresApproval: true, // Always require approval
594
- * description: (args) => `Delete file: ${args.path}`,
595
- * checkApproval: () => approvalStore.getApproval('delete-file'),
596
- * }
597
- * );
598
- * ```
599
- */
600
- declare function gatedStep<TArgs extends Record<string, unknown>, T, E>(operation: (args: TArgs) => AsyncResult<T, E>, options: GatedStepOptions<TArgs, T>): (args: TArgs) => AsyncResult<T, E | PendingApproval | ApprovalRejected>;
11
+ export { D as Duration, D as DurationType, d as days, i as hours, k as isDuration, r as millis, t as minutes, w as seconds, y as toDays, z as toHours, A as toMillis, B as toMinutes, C as toSeconds } from './duration-BjVn3QpB.js';
12
+ import './types-sObbY4mX.js';
601
13
 
602
14
  /**
603
15
  * Hook primitive: suspend workflow until app receives HTTP callback, then resume via injectHook.
@@ -947,2429 +359,6 @@ interface WorkflowDiagramDSL {
947
359
  workflowReturnType?: string;
948
360
  }
949
361
 
950
- /**
951
- * awaitly/durable
952
- *
953
- * Durable execution with automatic state persistence.
954
- * Workflows automatically checkpoint after each keyed step and can resume from any point.
955
- */
956
-
957
- /**
958
- * Error returned when workflow cannot resume due to version mismatch.
959
- * Indicates the stored state was created with a different workflow version.
960
- * Fail-fast contract: bump version when you change step keys, order, or outputs.
961
- */
962
- type VersionMismatchError = {
963
- type: "VERSION_MISMATCH";
964
- /** Workflow execution ID */
965
- workflowId: string;
966
- /** Version stored in persisted state */
967
- storedVersion: number;
968
- /** Version requested by this run */
969
- requestedVersion: number;
970
- /** Guidance message with suggested actions */
971
- message: string;
972
- /** Use requestedVersion. */
973
- currentVersion?: number;
974
- };
975
- /**
976
- * Error returned when workflow execution is rejected due to concurrent run.
977
- */
978
- type ConcurrentExecutionError = {
979
- type: "CONCURRENT_EXECUTION";
980
- /** The workflow ID that is already running */
981
- workflowId: string;
982
- /** Guidance message */
983
- message: string;
984
- /**
985
- * Distinguishes in-process (activeWorkflows) from cross-process (lock held).
986
- * Enables debuggability and branching without parsing the message.
987
- */
988
- reason?: "in-process" | "cross-process";
989
- };
990
- /**
991
- * Error returned when a persistence store operation fails.
992
- */
993
- type PersistenceError = {
994
- type: "PERSISTENCE_ERROR";
995
- /** The operation that failed */
996
- operation: "load" | "save" | "delete";
997
- /** The workflow ID */
998
- workflowId: string;
999
- /** The underlying error */
1000
- cause: unknown;
1001
- /** Guidance message */
1002
- message: string;
1003
- };
1004
- /**
1005
- * Type guard to check if an error is a VersionMismatchError.
1006
- */
1007
- declare function isVersionMismatch(error: unknown): error is VersionMismatchError;
1008
- /**
1009
- * Type guard to check if an error is a ConcurrentExecutionError.
1010
- */
1011
- declare function isConcurrentExecution(error: unknown): error is ConcurrentExecutionError;
1012
- /**
1013
- * Type guard to check if an error is a PersistenceError.
1014
- */
1015
- declare function isPersistenceError(error: unknown): error is PersistenceError;
1016
- /**
1017
- * Error returned when a workflow's lease expires mid-execution.
1018
- * Indicates the lock was lost and another process may have reclaimed the workflow.
1019
- */
1020
- type LeaseExpiredError = {
1021
- type: "LEASE_EXPIRED";
1022
- /** The workflow ID whose lease expired */
1023
- workflowId: string;
1024
- /** Guidance message */
1025
- message: string;
1026
- };
1027
- /**
1028
- * Type guard to check if an error is a LeaseExpiredError.
1029
- */
1030
- declare function isLeaseExpired(error: unknown): error is LeaseExpiredError;
1031
- /**
1032
- * Error returned when an idempotency key is reused with different input.
1033
- */
1034
- type IdempotencyConflictError = {
1035
- type: "IDEMPOTENCY_CONFLICT";
1036
- idempotencyKey: string;
1037
- workflowId: string;
1038
- message: string;
1039
- };
1040
- declare function isIdempotencyConflict(error: unknown): error is IdempotencyConflictError;
1041
- /**
1042
- * Optional cross-process lock interface.
1043
- * When a store implements this, durable.run uses it to ensure only one process
1044
- * runs a given workflow ID at a time (when allowConcurrent is false).
1045
- *
1046
- * Uses a lease (TTL) + owner token so a crashed worker does not wedge a workflow
1047
- * indefinitely. Release must verify the owner token so one process never
1048
- * unlocks another's lease.
1049
- *
1050
- * Mid-run lock loss (e.g. lease expires during a long workflow) is handled
1051
- * adapter-side; core assumes the lock is held until release in finally.
1052
- * Adapters may implement heartbeats/renewal.
1053
- */
1054
- interface WorkflowLock {
1055
- /**
1056
- * Try to acquire a lease for the workflow ID.
1057
- * @param id - Workflow execution ID
1058
- * @param options - Optional TTL for the lease (adapter default if omitted)
1059
- * @returns Owner token if acquired, null if already held by another
1060
- */
1061
- tryAcquire(id: string, options?: {
1062
- ttlMs?: number;
1063
- }): Promise<{
1064
- ownerToken: string;
1065
- } | null>;
1066
- /**
1067
- * Release the lease. Must verify owner token; no-op or ignore if token
1068
- * does not match (e.g. lease already expired or taken by another).
1069
- */
1070
- release(id: string, ownerToken: string): Promise<void>;
1071
- /**
1072
- * Extend the lease for an already-held lock.
1073
- * Returns true if renewed, false if lost.
1074
- * Optional — when not implemented, no heartbeat runs.
1075
- */
1076
- renew?(id: string, ownerToken: string, options?: {
1077
- ttlMs?: number;
1078
- }): Promise<boolean>;
1079
- }
1080
- /**
1081
- * Options for durable workflow execution.
1082
- */
1083
- interface DurableOptions<C = void> {
1084
- /**
1085
- * Unique workflow execution ID.
1086
- * Used as the key for state persistence.
1087
- *
1088
- * @example 'order-checkout-123', 'user-onboarding-abc'
1089
- */
1090
- id: string;
1091
- /**
1092
- * Snapshot store for persistence. Optional. When omitted, an in-memory store is used (per process).
1093
- * Same-process resume/retry works; state is lost on restart. Override with postgres/mongo/libsql for persistence.
1094
- *
1095
- * @example
1096
- * ```typescript
1097
- * // Zero-config: uses in-memory store (per process)
1098
- * await durable.run(deps, fn, { id: 'my-id' });
1099
- *
1100
- * // Override: pass a store for persistence across restarts
1101
- * import { postgres } from 'awaitly-postgres';
1102
- * const store = postgres('postgresql://localhost/mydb');
1103
- * await durable.run(deps, fn, { id: 'my-id', store });
1104
- * ```
1105
- */
1106
- store?: SnapshotStore;
1107
- /**
1108
- * Workflow logic version.
1109
- * If stored state has a different version, workflow will reject resume with VersionMismatchError
1110
- * unless onVersionMismatch is used to clear or migrate.
1111
- *
1112
- * Bump when you change step keys, reorder steps, or change step outputs in a way old checkpoints can't satisfy.
1113
- *
1114
- * @default 1
1115
- */
1116
- version?: number;
1117
- /**
1118
- * When stored state version differs from requested version, either throw (default), clear state and run from scratch, or supply migrated snapshot.
1119
- * Use for migration or one-off clear without wrapping durable.run.
1120
- *
1121
- * @default 'throw'
1122
- */
1123
- onVersionMismatch?: (ctx: {
1124
- id: string;
1125
- storedVersion: number;
1126
- requestedVersion: number;
1127
- }) => "throw" | "clear" | {
1128
- migratedSnapshot: WorkflowSnapshot;
1129
- } | Promise<"throw" | "clear" | {
1130
- migratedSnapshot: WorkflowSnapshot;
1131
- }>;
1132
- /**
1133
- * Allow concurrent executions with the same workflow ID.
1134
- * When `false` (default), a second run with the same ID will be rejected while one is active.
1135
- *
1136
- * @default false
1137
- */
1138
- allowConcurrent?: boolean;
1139
- /**
1140
- * Lease TTL in milliseconds for cross-process locking.
1141
- * Only used when the store implements WorkflowLock and allowConcurrent is false.
1142
- * A crashed worker's lease expires after this duration so the workflow can be picked up again.
1143
- *
1144
- * @default 60000 (1 minute)
1145
- */
1146
- lockTtlMs?: number;
1147
- /**
1148
- * Heartbeat interval for lease renewal (ms).
1149
- * Only active when store implements WorkflowLock with renew().
1150
- * @default lockTtlMs / 3
1151
- */
1152
- heartbeatIntervalMs?: number;
1153
- /**
1154
- * Whether to abort the workflow when lease is lost mid-execution.
1155
- * @default true
1156
- */
1157
- abortOnLeaseLoss?: boolean;
1158
- /**
1159
- * Metadata to store alongside workflow state.
1160
- * Useful for debugging, auditing, or filtering workflows.
1161
- *
1162
- * @example { userId: 'user-123', source: 'api' }
1163
- */
1164
- metadata?: Record<string, unknown>;
1165
- /**
1166
- * External AbortSignal for workflow-level cancellation.
1167
- * Cancellation persists state up to the last completed step.
1168
- */
1169
- signal?: AbortSignal;
1170
- /**
1171
- * Create per-run context for event correlation.
1172
- */
1173
- createContext?: () => C;
1174
- /**
1175
- * Unified event stream for workflow and step lifecycle.
1176
- * Includes durable-specific events: `persist_success` and `persist_error`.
1177
- */
1178
- onEvent?: (event: DurableWorkflowEvent<unknown, C>, ctx: C) => void;
1179
- /**
1180
- * Handler for expected and unexpected errors.
1181
- */
1182
- onError?: (error: unknown, stepName?: string, ctx?: C) => void;
1183
- /**
1184
- * Idempotency key for deduplication.
1185
- * If provided and a completed workflow with this key exists in the store,
1186
- * the stored result is returned without re-execution.
1187
- * If the stored input differs, an IdempotencyConflictError is returned.
1188
- */
1189
- idempotencyKey?: string;
1190
- /**
1191
- * Workflow input for idempotency conflict detection.
1192
- * When idempotencyKey is set, this is compared against stored input.
1193
- * Must be JSON-serializable.
1194
- */
1195
- input?: unknown;
1196
- }
1197
- /**
1198
- * Extended workflow event type that includes durable-specific events.
1199
- * E is the deps error type - the full error type includes E | UnexpectedError.
1200
- */
1201
- type DurableWorkflowEvent<E, C = void> = WorkflowEvent<E | UnexpectedError, C> | {
1202
- type: "persist_success";
1203
- workflowId: string;
1204
- stepKey: string;
1205
- ts: number;
1206
- context?: C;
1207
- } | {
1208
- type: "persist_error";
1209
- workflowId: string;
1210
- stepKey: string;
1211
- error: unknown;
1212
- ts: number;
1213
- context?: C;
1214
- };
1215
- /**
1216
- * Options for bulk delete of workflow state.
1217
- */
1218
- interface DeleteStatesOptions {
1219
- /**
1220
- * Max number of concurrent delete calls when store has no deleteMany.
1221
- * @default 10
1222
- */
1223
- concurrency?: number;
1224
- /**
1225
- * When true, collect errors and return them; when false, throw on first error.
1226
- * @default true
1227
- */
1228
- continueOnError?: boolean;
1229
- }
1230
- /**
1231
- * Result of bulk delete of workflow state.
1232
- */
1233
- interface DeleteStatesResult {
1234
- /** Number of entries successfully deleted. */
1235
- deleted: number;
1236
- /** Per-id errors when continueOnError was true and some deletes failed. */
1237
- errors?: Array<{
1238
- id: string;
1239
- error: unknown;
1240
- }>;
1241
- }
1242
- /**
1243
- * Durable workflow execution namespace.
1244
- */
1245
- declare const durable: {
1246
- /**
1247
- * Execute a workflow with automatic state persistence.
1248
- *
1249
- * Features:
1250
- * - **Automatic checkpointing**: State is saved after each keyed step
1251
- * - **Crash recovery**: Resume from the last completed step on restart
1252
- * - **Version checking**: Reject resume if workflow logic version changed
1253
- * - **Concurrency control**: Prevent duplicate executions of the same workflow ID
1254
- * - **Cancellation support**: Integrates with AbortSignal for graceful shutdown
1255
- *
1256
- * ## How It Works
1257
- *
1258
- * 1. On start: Load existing state from store (if any)
1259
- * 2. Check version compatibility (reject if mismatch)
1260
- * 3. Pre-populate cache from loaded state (skip completed steps)
1261
- * 4. Execute workflow, persisting state after each keyed step
1262
- * 5. On completion: Delete stored state (clean up)
1263
- * 6. On error/cancellation: State remains for future resume
1264
- *
1265
- * ## Important Notes
1266
- *
1267
- * - **Only keyed steps are durable**: Use `{ key: 'step-name' }` option
1268
- * - **Steps should be idempotent**: They may be retried on resume
1269
- * - **Serialization**: State is JSON-serialized; complex objects may lose fidelity
1270
- *
1271
- * @param deps - Workflow dependencies (Result-returning functions)
1272
- * @param fn - Workflow function receiving ({ step, deps, ctx })
1273
- * @param options - Durable execution options
1274
- * @returns AsyncResult with workflow result or error
1275
- *
1276
- * @example
1277
- * ```typescript
1278
- * import { durable } from 'awaitly/workflow';
1279
- *
1280
- * // Zero-config: uses in-memory store (per process)
1281
- * const result = await durable.run(
1282
- * { fetchUser, createOrder, sendEmail },
1283
- * async ({ step, deps: { fetchUser, createOrder, sendEmail } }) => {
1284
- * const user = await step(() => fetchUser('123'), { key: 'fetch-user' });
1285
- * const order = await step(() => createOrder(user), { key: 'create-order' });
1286
- * await step(() => sendEmail(order), { key: 'send-email' });
1287
- * return order;
1288
- * },
1289
- * { id: 'checkout-123' }
1290
- * );
1291
- *
1292
- * // Persistence across restarts: pass a store adapter
1293
- * import { postgres } from 'awaitly-postgres'; // or awaitly-mongo, awaitly-libsql
1294
- * const store = postgres(process.env.DATABASE_URL);
1295
- * await durable.run(deps, fn, { id: 'checkout-123', store });
1296
- *
1297
- * if (result.ok) {
1298
- * console.log('Order completed:', result.value);
1299
- * } else if (isWorkflowCancelled(result.error)) {
1300
- * console.log('Workflow cancelled at:', result.error.lastStepKey);
1301
- * }
1302
- * ```
1303
- */
1304
- run<const Deps extends Readonly<Record<string, AnyResultFn>>, T, C = void>(deps: Deps, fn: (context: {
1305
- step: RunStep<ErrorsOfDeps<Deps>>;
1306
- deps: Deps;
1307
- ctx: WorkflowContext<C>;
1308
- }) => T | Promise<T>, options: DurableOptions<C>): Promise<Result<T, ErrorsOfDeps<Deps> | UnexpectedError | WorkflowCancelledError | VersionMismatchError | ConcurrentExecutionError | PersistenceError | LeaseExpiredError | IdempotencyConflictError, unknown>>;
1309
- /**
1310
- * Clear all persisted workflow state from the store.
1311
- * Use for admin/testing. If the store implements `clear()`, that is used;
1312
- * otherwise clears by listing and deleting in pages.
1313
- *
1314
- * @param store - Snapshot store
1315
- */
1316
- clearState(store: SnapshotStore): Promise<void>;
1317
- /**
1318
- * Check if a workflow ID has persisted state (can be resumed).
1319
- *
1320
- * @param store - Snapshot store
1321
- * @param id - Workflow execution ID
1322
- * @returns `true` if state exists, `false` otherwise (including on store errors)
1323
- */
1324
- hasState(store: SnapshotStore, id: string): Promise<boolean>;
1325
- /**
1326
- * Delete persisted state for a workflow (cancel resume capability).
1327
- * Deleting is effectively an ack/reset: the workflow can no longer resume from that state.
1328
- * If you delete while a run is in flight, the run continues; on success it may delete again (no-op) or save (recreating state).
1329
- * For multi-worker safety, prefer deleting only when the workflow is not running or when you hold the lock.
1330
- *
1331
- * @param store - Snapshot store
1332
- * @param id - Workflow execution ID
1333
- * @returns `true` on success, `false` on store errors
1334
- */
1335
- deleteState(store: SnapshotStore, id: string): Promise<boolean>;
1336
- /**
1337
- * Bulk delete persisted state for multiple workflow IDs (best-effort).
1338
- * Use for admin/cleanup. Deletes in a loop with optional concurrency.
1339
- *
1340
- * @param store - Snapshot store
1341
- * @param ids - Workflow execution IDs to delete
1342
- * @param options - Optional concurrency and error handling
1343
- * @returns Count of deleted entries and any errors when continueOnError is true
1344
- */
1345
- deleteStates(store: SnapshotStore, ids: string[], options?: DeleteStatesOptions): Promise<DeleteStatesResult>;
1346
- /**
1347
- * List workflow IDs with persisted state.
1348
- *
1349
- * @param store - Snapshot store
1350
- * @param options - Optional prefix and limit
1351
- * @returns Array of { id, updatedAt } entries
1352
- */
1353
- listPending(store: SnapshotStore, options?: {
1354
- prefix?: string;
1355
- limit?: number;
1356
- }): Promise<Array<{
1357
- id: string;
1358
- updatedAt: string;
1359
- }>>;
1360
- };
1361
-
1362
- /**
1363
- * Workflow Versioning and Migration
1364
- *
1365
- * Handle schema changes when resuming workflows that were persisted
1366
- * with older step shapes.
1367
- *
1368
- * @example
1369
- * ```typescript
1370
- * import { createVersionedWorkflow } from 'awaitly';
1371
- *
1372
- * const workflow = createVersionedWorkflow(
1373
- * { fetchUser, chargeCard },
1374
- * {
1375
- * version: 2,
1376
- * migrations: {
1377
- * 1: (state) => migrateV1ToV2(state),
1378
- * },
1379
- * resumeState: loadState(runId),
1380
- * }
1381
- * );
1382
- * ```
1383
- */
1384
-
1385
- /**
1386
- * Version number type.
1387
- */
1388
- type Version = number;
1389
- /**
1390
- * Migration function that transforms state from one version to the next.
1391
- */
1392
- type MigrationFn = (state: ResumeState) => ResumeState | Promise<ResumeState>;
1393
- /**
1394
- * Map of migrations keyed by the source version.
1395
- * Migration at key N transforms state from version N to version N+1.
1396
- */
1397
- type Migrations = Record<Version, MigrationFn>;
1398
- /**
1399
- * Versioned state includes the version number.
1400
- */
1401
- interface VersionedState {
1402
- version: Version;
1403
- state: ResumeState;
1404
- }
1405
- /**
1406
- * Configuration for versioned workflow.
1407
- */
1408
- interface VersionedWorkflowConfig {
1409
- /**
1410
- * Current workflow version.
1411
- */
1412
- version: Version;
1413
- /**
1414
- * Migrations for upgrading old states.
1415
- * Key is the source version, value transforms to next version.
1416
- */
1417
- migrations?: Migrations;
1418
- /**
1419
- * Strict mode - fail if state version is higher than current.
1420
- * @default true
1421
- */
1422
- strictVersioning?: boolean;
1423
- }
1424
- /**
1425
- * Error when version migration fails.
1426
- */
1427
- interface MigrationError {
1428
- type: "MIGRATION_ERROR";
1429
- fromVersion: Version;
1430
- toVersion: Version;
1431
- cause: unknown;
1432
- }
1433
- /**
1434
- * Error when state version is incompatible.
1435
- */
1436
- interface VersionIncompatibleError {
1437
- type: "VERSION_INCOMPATIBLE";
1438
- stateVersion: Version;
1439
- currentVersion: Version;
1440
- reason: string;
1441
- }
1442
- /**
1443
- * Type guard for MigrationError.
1444
- */
1445
- declare function isMigrationError(error: unknown): error is MigrationError;
1446
- /**
1447
- * Type guard for VersionIncompatibleError.
1448
- */
1449
- declare function isVersionIncompatibleError(error: unknown): error is VersionIncompatibleError;
1450
- /**
1451
- * Create a versioned resume state loader.
1452
- *
1453
- * This wraps a state loader to automatically apply migrations
1454
- * when loading older state versions.
1455
- *
1456
- * @param config - Versioning configuration
1457
- * @returns A function that loads and migrates state
1458
- *
1459
- * @example
1460
- * ```typescript
1461
- * const loadVersionedState = createVersionedStateLoader({
1462
- * version: 3,
1463
- * migrations: {
1464
- * 1: migrateV1ToV2,
1465
- * 2: migrateV2ToV3,
1466
- * },
1467
- * });
1468
- *
1469
- * // In workflow
1470
- * const workflow = createWorkflow(deps, {
1471
- * resumeState: () => loadVersionedState(savedState),
1472
- * });
1473
- * ```
1474
- */
1475
- declare function createVersionedStateLoader(config: VersionedWorkflowConfig): (versionedState: VersionedState | null | undefined) => Promise<Result<ResumeState | undefined, MigrationError | VersionIncompatibleError>>;
1476
- /**
1477
- * Create versioned state from current resume state.
1478
- *
1479
- * Use this when saving state to storage.
1480
- *
1481
- * @param state - The current resume state
1482
- * @param version - The current workflow version
1483
- * @returns A versioned state object
1484
- *
1485
- * @example
1486
- * ```typescript
1487
- * const collector = createResumeStateCollector();
1488
- * // ... run workflow ...
1489
- *
1490
- * const versionedState = createVersionedState(collector.getResumeState(), 2);
1491
- * await db.saveWorkflowState(workflowId, versionedState);
1492
- * ```
1493
- */
1494
- declare function createVersionedState(state: ResumeState, version: Version): VersionedState;
1495
- /**
1496
- * Parse versioned state from JSON.
1497
- *
1498
- * Handles the serialization/deserialization of ResumeState with Map.
1499
- *
1500
- * @param json - The JSON string or parsed object
1501
- * @returns The versioned state or null if invalid
1502
- *
1503
- * @example
1504
- * ```typescript
1505
- * const json = await db.loadWorkflowState(workflowId);
1506
- * const versionedState = parseVersionedState(json);
1507
- * if (versionedState) {
1508
- * const loader = createVersionedStateLoader(config);
1509
- * const state = await loader(versionedState);
1510
- * }
1511
- * ```
1512
- */
1513
- interface SerializedVersionedState {
1514
- version: number;
1515
- state: {
1516
- steps: Array<[string, ResumeStateEntry]>;
1517
- };
1518
- }
1519
- declare function parseVersionedState(json: string | SerializedVersionedState | null | undefined): VersionedState | null;
1520
- /**
1521
- * Serialize versioned state to JSON.
1522
- *
1523
- * Converts the Map to an array for JSON serialization.
1524
- *
1525
- * @param state - The versioned state
1526
- * @returns JSON string
1527
- *
1528
- * @example
1529
- * ```typescript
1530
- * const json = stringifyVersionedState(versionedState);
1531
- * await db.saveWorkflowState(workflowId, json);
1532
- * ```
1533
- */
1534
- declare function stringifyVersionedState(state: VersionedState): string;
1535
- /**
1536
- * Create a migration that renames step keys.
1537
- *
1538
- * @param renames - Map of old key to new key
1539
- * @returns A migration function
1540
- *
1541
- * @example
1542
- * ```typescript
1543
- * const migrations = {
1544
- * 1: createKeyRenameMigration({
1545
- * 'user:fetch': 'user:load',
1546
- * 'order:create': 'order:submit',
1547
- * }),
1548
- * };
1549
- * ```
1550
- */
1551
- declare function createKeyRenameMigration(renames: Record<string, string>): MigrationFn;
1552
- /**
1553
- * Create a migration that removes specific step keys.
1554
- *
1555
- * @param keysToRemove - Array of keys to remove
1556
- * @returns A migration function
1557
- *
1558
- * @example
1559
- * ```typescript
1560
- * const migrations = {
1561
- * 1: createKeyRemoveMigration(['deprecated:step', 'old:cache']),
1562
- * };
1563
- * ```
1564
- */
1565
- declare function createKeyRemoveMigration(keysToRemove: string[]): MigrationFn;
1566
- /**
1567
- * Create a migration that transforms step values.
1568
- *
1569
- * @param transforms - Map of key to transform function
1570
- * @returns A migration function
1571
- *
1572
- * @example
1573
- * ```typescript
1574
- * const migrations = {
1575
- * 1: createValueTransformMigration({
1576
- * 'user:fetch': (entry) => ({
1577
- * ...entry,
1578
- * result: entry.result.ok
1579
- * ? ok({ ...entry.result.value, newField: 'default' })
1580
- * : entry.result,
1581
- * }),
1582
- * }),
1583
- * };
1584
- * ```
1585
- */
1586
- declare function createValueTransformMigration(transforms: Record<string, (entry: ResumeStateEntry) => ResumeStateEntry>): MigrationFn;
1587
- /**
1588
- * Compose multiple migrations into a single migration.
1589
- *
1590
- * @param migrations - Array of migration functions
1591
- * @returns A single migration function that applies all migrations in order
1592
- *
1593
- * @example
1594
- * ```typescript
1595
- * const migrations = {
1596
- * 1: composeMigrations([
1597
- * createKeyRenameMigration({ 'old': 'new' }),
1598
- * createKeyRemoveMigration(['deprecated']),
1599
- * ]),
1600
- * };
1601
- * ```
1602
- */
1603
- declare function composeMigrations(migrations: MigrationFn[]): MigrationFn;
1604
-
1605
- /**
1606
- * awaitly/hitl
1607
- *
1608
- * Human-in-the-Loop Orchestration Helpers.
1609
- * Provides pollers, webhook handlers, and resume injectors
1610
- * for production-ready approval workflows.
1611
- */
1612
-
1613
- /**
1614
- * Options passed to the workflow factory by the HITL orchestrator.
1615
- */
1616
- interface HITLWorkflowFactoryOptions {
1617
- /** Resume state for replaying completed steps */
1618
- resumeState?: ResumeState;
1619
- /** Event handler for tracking workflow events (required for HITL) */
1620
- onEvent: (event: WorkflowEvent<unknown>) => void;
1621
- }
1622
- /**
1623
- * Approval status returned from the approval store.
1624
- */
1625
- type ApprovalStatus<T = unknown> = {
1626
- status: "pending";
1627
- } | {
1628
- status: "approved";
1629
- value: T;
1630
- approvedBy?: string;
1631
- approvedAt?: number;
1632
- } | {
1633
- status: "rejected";
1634
- reason: string;
1635
- rejectedBy?: string;
1636
- rejectedAt?: number;
1637
- } | {
1638
- status: "expired";
1639
- expiredAt: number;
1640
- } | {
1641
- status: "edited";
1642
- originalValue: T;
1643
- editedValue: T;
1644
- editedBy?: string;
1645
- editedAt?: number;
1646
- };
1647
- /**
1648
- * Context passed to notification channel when an approval is needed.
1649
- */
1650
- interface ApprovalNeededContext {
1651
- /** Unique approval key for correlation */
1652
- approvalKey: string;
1653
- /** Workflow run ID */
1654
- runId: string;
1655
- /** Workflow name/type */
1656
- workflowName: string;
1657
- /** Human-readable reason for the approval */
1658
- reason?: string;
1659
- /** Custom metadata attached to the approval */
1660
- metadata?: Record<string, unknown>;
1661
- /** When the approval expires (timestamp) */
1662
- expiresAt?: number;
1663
- /** Human-readable summary for notifications */
1664
- summary?: string;
1665
- /** For gated steps: the operation args that need approval */
1666
- pendingArgs?: Record<string, unknown>;
1667
- }
1668
- /**
1669
- * Context passed to notification channel when an approval is resolved.
1670
- */
1671
- interface ApprovalResolvedContext {
1672
- /** Unique approval key */
1673
- approvalKey: string;
1674
- /** Resolution action */
1675
- action: "approved" | "rejected" | "edited" | "expired" | "cancelled";
1676
- /** Who performed the action (if available) */
1677
- actorId?: string;
1678
- /** Timestamp of resolution */
1679
- resolvedAt: number;
1680
- /** Reason (for rejections) */
1681
- reason?: string;
1682
- /** Value (for approvals/edits) */
1683
- value?: unknown;
1684
- /** Original value (for edits) */
1685
- originalValue?: unknown;
1686
- }
1687
- /**
1688
- * Notification channel for external integrations (Slack, email, etc).
1689
- * Implement this interface to receive push notifications when approvals
1690
- * are created or resolved.
1691
- */
1692
- interface NotificationChannel {
1693
- /**
1694
- * Called when a new approval request is created.
1695
- * Use this to send Slack messages, emails, or push to a UI.
1696
- */
1697
- onApprovalNeeded(context: ApprovalNeededContext): Promise<void>;
1698
- /**
1699
- * Called when an approval is granted, rejected, edited, or expires.
1700
- * Use this to update Slack messages, send confirmation emails, etc.
1701
- */
1702
- onApprovalResolved?(context: ApprovalResolvedContext): Promise<void>;
1703
- }
1704
- /**
1705
- * Interface for approval storage backends.
1706
- */
1707
- interface ApprovalStore {
1708
- /**
1709
- * Get the status of an approval.
1710
- */
1711
- getApproval(key: string): Promise<ApprovalStatus>;
1712
- /**
1713
- * Create or update a pending approval request.
1714
- */
1715
- createApproval(key: string, options?: {
1716
- metadata?: Record<string, unknown>;
1717
- expiresAt?: number;
1718
- requestedBy?: string;
1719
- }): Promise<void>;
1720
- /**
1721
- * Grant an approval.
1722
- */
1723
- grantApproval<T>(key: string, value: T, options?: {
1724
- approvedBy?: string;
1725
- }): Promise<void>;
1726
- /**
1727
- * Reject an approval.
1728
- */
1729
- rejectApproval(key: string, reason: string, options?: {
1730
- rejectedBy?: string;
1731
- }): Promise<void>;
1732
- /**
1733
- * Edit an approval (approve with modifications).
1734
- * Records both the original proposed value and the edited value.
1735
- */
1736
- editApproval<T>(key: string, originalValue: T, editedValue: T, options?: {
1737
- editedBy?: string;
1738
- }): Promise<void>;
1739
- /**
1740
- * Cancel a pending approval.
1741
- */
1742
- cancelApproval(key: string): Promise<void>;
1743
- /**
1744
- * List all pending approvals.
1745
- */
1746
- listPending(options?: {
1747
- prefix?: string;
1748
- }): Promise<string[]>;
1749
- }
1750
- /**
1751
- * Saved workflow state for resumption.
1752
- */
1753
- interface SavedWorkflowState {
1754
- /** Unique identifier for this workflow run */
1755
- runId: string;
1756
- /** Workflow name/type */
1757
- workflowName: string;
1758
- /** Resume state with step results */
1759
- resumeState: ResumeState;
1760
- /** Pending approval keys */
1761
- pendingApprovals: string[];
1762
- /** Input that was passed to the workflow */
1763
- input?: unknown;
1764
- /** Custom metadata */
1765
- metadata?: Record<string, unknown>;
1766
- /** When the workflow was started */
1767
- startedAt: number;
1768
- /** When the state was last updated */
1769
- updatedAt: number;
1770
- }
1771
- /**
1772
- * Interface for workflow state storage.
1773
- */
1774
- interface WorkflowStateStore {
1775
- /**
1776
- * Save workflow state.
1777
- */
1778
- save(state: SavedWorkflowState): Promise<void>;
1779
- /**
1780
- * Load workflow state by run ID.
1781
- */
1782
- load(runId: string): Promise<SavedWorkflowState | undefined>;
1783
- /**
1784
- * Delete workflow state.
1785
- */
1786
- delete(runId: string): Promise<void>;
1787
- /**
1788
- * List all saved workflow states.
1789
- */
1790
- list(options?: {
1791
- workflowName?: string;
1792
- hasPendingApprovals?: boolean;
1793
- }): Promise<string[]>;
1794
- /**
1795
- * Find workflows waiting for a specific approval.
1796
- */
1797
- findByPendingApproval(approvalKey: string): Promise<string[]>;
1798
- }
1799
- /**
1800
- * Options for the HITL orchestrator.
1801
- */
1802
- interface HITLOrchestratorOptions {
1803
- /** Approval store for managing approval states */
1804
- approvalStore: ApprovalStore;
1805
- /** Workflow state store for persisting workflow state */
1806
- workflowStateStore: WorkflowStateStore;
1807
- /** Default expiration time for approvals (in milliseconds) */
1808
- defaultExpirationMs?: number;
1809
- /** Logger function */
1810
- logger?: (message: string) => void;
1811
- /**
1812
- * Notification channel for external integrations.
1813
- * When provided, the orchestrator will call onApprovalNeeded when
1814
- * an approval is created, and onApprovalResolved when resolved.
1815
- */
1816
- notificationChannel?: NotificationChannel;
1817
- }
1818
- /**
1819
- * Result of executing a workflow that may pause for approval.
1820
- * Uses unknown for error type since workflows add UnexpectedError to the union.
1821
- */
1822
- type HITLExecutionResult<T, E> = {
1823
- status: "completed";
1824
- result: Result<T, E | unknown>;
1825
- } | {
1826
- status: "paused";
1827
- runId: string;
1828
- pendingApprovals: string[];
1829
- reason?: string;
1830
- } | {
1831
- status: "resumed";
1832
- runId: string;
1833
- result: Result<T, E | unknown>;
1834
- };
1835
- /**
1836
- * Poller configuration.
1837
- */
1838
- interface PollerOptions {
1839
- /** Polling interval in milliseconds */
1840
- intervalMs: number;
1841
- /** Maximum number of polls (undefined = unlimited) */
1842
- maxPolls?: number;
1843
- /** Timeout for the entire polling operation */
1844
- timeoutMs?: number;
1845
- /** Callback when polling starts */
1846
- onPollStart?: () => void;
1847
- /** Callback when a poll completes */
1848
- onPollComplete?: (result: ApprovalStatus) => void;
1849
- }
1850
- /**
1851
- * Create an in-memory approval store for development/testing.
1852
- */
1853
- declare function createMemoryApprovalStore(): ApprovalStore;
1854
- /**
1855
- * Create an in-memory workflow state store for development/testing.
1856
- */
1857
- declare function createMemoryWorkflowStateStore(): WorkflowStateStore;
1858
- /**
1859
- * HITL orchestrator interface.
1860
- */
1861
- interface HITLOrchestrator {
1862
- /**
1863
- * Execute a workflow that may pause for approvals.
1864
- * If the workflow pauses, state is automatically saved.
1865
- *
1866
- * The workflowFactory receives options including onEvent handler which MUST be
1867
- * passed to createWorkflow for HITL tracking to work.
1868
- */
1869
- execute<T, E, TInput>(workflowName: string, workflowFactory: (options: HITLWorkflowFactoryOptions) => Workflow<E, unknown>, workflowFn: (context: {
1870
- step: unknown;
1871
- deps: unknown;
1872
- args: TInput;
1873
- }) => Promise<T>, input: TInput, options?: {
1874
- runId?: string;
1875
- metadata?: Record<string, unknown>;
1876
- }): Promise<HITLExecutionResult<T, E>>;
1877
- /**
1878
- * Resume a paused workflow after approvals have been granted.
1879
- */
1880
- resume<T, E, TInput>(runId: string, workflowFactory: (options: HITLWorkflowFactoryOptions) => Workflow<E, unknown>, workflowFn: (context: {
1881
- step: unknown;
1882
- deps: unknown;
1883
- args: TInput;
1884
- }) => Promise<T>): Promise<HITLExecutionResult<T, E>>;
1885
- /**
1886
- * Grant an approval and automatically resume any waiting workflows.
1887
- */
1888
- grantApproval<T>(approvalKey: string, value: T, options?: {
1889
- approvedBy?: string;
1890
- autoResume?: boolean;
1891
- }): Promise<{
1892
- grantedAt: number;
1893
- resumedWorkflows: string[];
1894
- }>;
1895
- /**
1896
- * Reject an approval.
1897
- */
1898
- rejectApproval(approvalKey: string, reason: string, options?: {
1899
- rejectedBy?: string;
1900
- }): Promise<void>;
1901
- /**
1902
- * Edit an approval (approve with modifications).
1903
- * Use this when a human wants to approve but with changes to the proposed value.
1904
- * Records both the original and edited values for audit trail.
1905
- */
1906
- editApproval<T>(approvalKey: string, originalValue: T, editedValue: T, options?: {
1907
- editedBy?: string;
1908
- }): Promise<{
1909
- editedAt: number;
1910
- }>;
1911
- /**
1912
- * Poll for an approval to be granted.
1913
- */
1914
- pollApproval<T>(approvalKey: string, options?: PollerOptions): Promise<ApprovalStatus<T>>;
1915
- /**
1916
- * Get the status of a workflow run.
1917
- */
1918
- getWorkflowStatus(runId: string): Promise<SavedWorkflowState | undefined>;
1919
- /**
1920
- * List all pending workflows.
1921
- */
1922
- listPendingWorkflows(workflowName?: string): Promise<string[]>;
1923
- /**
1924
- * Clean up completed workflows older than the specified age.
1925
- */
1926
- cleanup(maxAgeMs: number): Promise<number>;
1927
- }
1928
- /**
1929
- * Create a HITL orchestrator for managing approval workflows.
1930
- *
1931
- * @example
1932
- * ```typescript
1933
- * const orchestrator = createHITLOrchestrator({
1934
- * approvalStore: createMemoryApprovalStore(),
1935
- * workflowStateStore: createMemoryWorkflowStateStore(),
1936
- * });
1937
- *
1938
- * // Execute workflow - IMPORTANT: pass onEvent to createWorkflow!
1939
- * const result = await orchestrator.execute(
1940
- * 'order-approval',
1941
- * ({ resumeState, onEvent }) => createWorkflow('order-approval', deps, { resumeState, onEvent }),
1942
- * async ({ step, deps, args: input }) => {
1943
- * const order = await step(() => deps.createOrder(input));
1944
- * const approval = await step(() => deps.requireApproval(order.id), { key: `approval:${order.id}` });
1945
- * await step(() => deps.processOrder(order.id));
1946
- * return { orderId: order.id, approvedBy: approval.approvedBy };
1947
- * },
1948
- * { items: [...], total: 100 }
1949
- * );
1950
- *
1951
- * if (result.status === 'paused') {
1952
- * console.log(`Workflow paused, waiting for: ${result.pendingApprovals}`);
1953
- * }
1954
- *
1955
- * // Later, grant approval
1956
- * await orchestrator.grantApproval(
1957
- * `approval:${orderId}`,
1958
- * { approvedBy: 'manager@example.com' },
1959
- * { autoResume: true }
1960
- * );
1961
- * ```
1962
- */
1963
- declare function createHITLOrchestrator(options: HITLOrchestratorOptions): HITLOrchestrator;
1964
- /**
1965
- * Approval webhook request body.
1966
- */
1967
- interface ApprovalWebhookRequest {
1968
- /** Approval key */
1969
- key: string;
1970
- /** Action: approve, reject, edit, or cancel */
1971
- action: "approve" | "reject" | "edit" | "cancel";
1972
- /** Value to inject (for approve) */
1973
- value?: unknown;
1974
- /** Original value (for edit - what was proposed) */
1975
- originalValue?: unknown;
1976
- /** Edited value (for edit - what human changed it to) */
1977
- editedValue?: unknown;
1978
- /** Reason (for reject) */
1979
- reason?: string;
1980
- /** Who performed this action */
1981
- actorId?: string;
1982
- }
1983
- /**
1984
- * Approval webhook response.
1985
- */
1986
- interface ApprovalWebhookResponse {
1987
- success: boolean;
1988
- message: string;
1989
- data?: {
1990
- key: string;
1991
- action: string;
1992
- timestamp: number;
1993
- };
1994
- }
1995
- /**
1996
- * Create a webhook handler for approval actions.
1997
- *
1998
- * @example
1999
- * ```typescript
2000
- * const handleApproval = createApprovalWebhookHandler(approvalStore);
2001
- *
2002
- * // Express
2003
- * app.post('/api/approvals', async (req, res) => {
2004
- * const result = await handleApproval(req.body);
2005
- * res.json(result);
2006
- * });
2007
- * ```
2008
- */
2009
- declare function createApprovalWebhookHandler(store: ApprovalStore): (request: ApprovalWebhookRequest) => Promise<ApprovalWebhookResponse>;
2010
- /**
2011
- * Create an approval checker function for use in approval steps.
2012
- * This wraps the approval store with the standard checkApproval interface.
2013
- *
2014
- * @example
2015
- * ```typescript
2016
- * const checkApproval = createApprovalChecker(approvalStore);
2017
- *
2018
- * const requireManagerApproval = createApprovalStep<{ approvedBy: string }>({
2019
- * key: 'manager-approval',
2020
- * checkApproval: checkApproval('manager-approval'),
2021
- * pendingReason: 'Waiting for manager approval',
2022
- * });
2023
- * ```
2024
- */
2025
- declare function createApprovalChecker<T>(store: ApprovalStore): (key: string) => () => Promise<{
2026
- status: "pending";
2027
- } | {
2028
- status: "approved";
2029
- value: T;
2030
- } | {
2031
- status: "rejected";
2032
- reason: string;
2033
- }>;
2034
-
2035
- /**
2036
- * Saga / Compensation Pattern
2037
- *
2038
- * Compensation is a first-class step option on every workflow. Pass `{ compensate }`
2039
- * to any step and the workflow will run compensations in reverse order if anything
2040
- * downstream fails.
2041
- *
2042
- * `createSagaWorkflow` is a thin alias for `createWorkflow` whose result error union
2043
- * also includes `SagaCompensationError` — useful when you know you'll be using
2044
- * compensation and want the type system to remind you.
2045
- *
2046
- * @example
2047
- * ```typescript
2048
- * import { createSagaWorkflow, isSagaCompensationError } from 'awaitly/workflow';
2049
- *
2050
- * const checkout = createSagaWorkflow('checkout', {
2051
- * reserveInventory, releaseInventory,
2052
- * chargeCard, refundPayment,
2053
- * sendEmail,
2054
- * });
2055
- *
2056
- * const result = await checkout.run(async ({ step, deps }) => {
2057
- * const r = await step('reserve', () => deps.reserveInventory(items), {
2058
- * compensate: (r) => deps.releaseInventory(r.id),
2059
- * });
2060
- * const p = await step('charge', () => deps.chargeCard(amount), {
2061
- * compensate: (p) => deps.refundPayment(p.id),
2062
- * });
2063
- * await step('notify', () => deps.sendEmail(userId));
2064
- * return { r, p };
2065
- * });
2066
- *
2067
- * if (!result.ok && isSagaCompensationError(result.error)) {
2068
- * // result.error.originalError — what triggered the rollback
2069
- * // result.error.compensationErrors — which cleanups failed
2070
- * }
2071
- * ```
2072
- */
2073
-
2074
- /** A compensation action to run on rollback. */
2075
- type CompensationAction<T> = (value: T) => void | Promise<void>;
2076
- /** Options for a saga step (kept for back-compat — `compensate` lives on `StepOptions`). */
2077
- interface SagaStepOptions<T> {
2078
- compensate?: CompensationAction<T>;
2079
- }
2080
- /**
2081
- * @deprecated Use `WorkflowOptions` from `awaitly/workflow`. Kept as an alias for back-compat.
2082
- */
2083
- type SagaWorkflowOptions<E> = {
2084
- onError?: (error: E | UnexpectedError | SagaCompensationError, stepName?: string) => void;
2085
- onEvent?: (event: unknown) => void;
2086
- throwOnCompensationFailure?: boolean;
2087
- };
2088
- /** Error returned when one or more compensation actions fail. */
2089
- interface SagaCompensationError {
2090
- type: "SAGA_COMPENSATION_ERROR";
2091
- /** The original error that triggered the rollback. */
2092
- originalError: unknown;
2093
- /** Errors from failed compensation actions. */
2094
- compensationErrors: Array<{
2095
- stepName?: string;
2096
- error: unknown;
2097
- }>;
2098
- }
2099
- /** Type guard for SagaCompensationError. */
2100
- declare function isSagaCompensationError(error: unknown): error is SagaCompensationError;
2101
- /**
2102
- * A `Workflow` whose result error union includes `SagaCompensationError`.
2103
- * Identical to `Workflow` at runtime — only the static type is widened.
2104
- */
2105
- type SagaWorkflow<E, U = UnexpectedError, Deps = unknown, C = void> = Workflow<E | SagaCompensationError, U, Deps, C>;
2106
- /**
2107
- * Create a workflow that uses compensation. Identical to `createWorkflow` —
2108
- * only the result type is widened to include `SagaCompensationError`.
2109
- *
2110
- * Prefer this when you intend to use `step(..., { compensate })` so the type
2111
- * system reminds you to handle the SAGA_COMPENSATION_ERROR case.
2112
- *
2113
- * @example
2114
- * ```typescript
2115
- * const saga = createSagaWorkflow('checkout', { reserve, release, charge, refund });
2116
- *
2117
- * const result = await saga.run(async ({ step, deps }) => {
2118
- * const r = await step('reserve', () => deps.reserve(...), {
2119
- * compensate: (r) => deps.release(r.id),
2120
- * });
2121
- * await step('charge', () => deps.charge(...), {
2122
- * compensate: (p) => deps.refund(p.id),
2123
- * });
2124
- * return r;
2125
- * });
2126
- * ```
2127
- */
2128
- declare function createSagaWorkflow<const Deps extends Readonly<Record<string, AnyResultFn>>, U = UnexpectedError, C = void>(workflowName: string, deps: Deps, options?: WorkflowOptions<ErrorsOfDeps<Deps>, U, C>): SagaWorkflow<ErrorsOfDeps<Deps>, U, Deps, C>;
2129
- /** Saga events emitted by `runSaga` for observability. */
2130
- type SagaEvent = {
2131
- type: "saga_start";
2132
- sagaId: string;
2133
- ts: number;
2134
- } | {
2135
- type: "saga_success";
2136
- sagaId: string;
2137
- ts: number;
2138
- durationMs: number;
2139
- } | {
2140
- type: "saga_error";
2141
- sagaId: string;
2142
- ts: number;
2143
- durationMs: number;
2144
- error: unknown;
2145
- } | {
2146
- type: "saga_compensation_start";
2147
- sagaId: string;
2148
- ts: number;
2149
- stepCount: number;
2150
- } | {
2151
- type: "saga_compensation_step";
2152
- sagaId: string;
2153
- stepName?: string;
2154
- ts: number;
2155
- success: boolean;
2156
- error?: unknown;
2157
- } | {
2158
- type: "saga_compensation_end";
2159
- sagaId: string;
2160
- ts: number;
2161
- durationMs: number;
2162
- success: boolean;
2163
- failedCount: number;
2164
- };
2165
- type SagaResult<T, E> = Result<T, E | UnexpectedError | SagaCompensationError, unknown>;
2166
- /** Saga step function — like RunStep but every step takes an optional compensate. */
2167
- interface SagaStep<E = unknown> {
2168
- <T, StepE extends E, StepC = unknown>(name: string, operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>, options?: SagaStepOptions<T>): Promise<T>;
2169
- try: <T, Err extends E>(name: string, operation: () => T | Promise<T>, options: {
2170
- error: Err;
2171
- compensate?: CompensationAction<T>;
2172
- } | {
2173
- onError: (cause: unknown) => Err;
2174
- compensate?: CompensationAction<T>;
2175
- }) => Promise<T>;
2176
- }
2177
- /**
2178
- * Run a saga with explicit error typing — for cases where you don't have a
2179
- * deps object to infer errors from. Most users should reach for
2180
- * `createSagaWorkflow` (or just `createWorkflow` with `step({ compensate })`).
2181
- */
2182
- declare function runSaga<T, E>(fn: (context: {
2183
- step: SagaStep<E>;
2184
- }) => Promise<T>, options?: {
2185
- onError?: (error: E | UnexpectedError | SagaCompensationError) => void;
2186
- onEvent?: (event: SagaEvent) => void;
2187
- throwOnCompensationFailure?: boolean;
2188
- }): Promise<SagaResult<T, E>>;
2189
-
2190
- /**
2191
- * awaitly/streaming - Backpressure Controller
2192
- *
2193
- * Implements flow control for streams using high-water mark.
2194
- * When buffered items exceed the threshold, writers are paused
2195
- * until consumers catch up.
2196
- */
2197
- /**
2198
- * State of the backpressure controller.
2199
- */
2200
- type BackpressureState = "flowing" | "paused";
2201
- /**
2202
- * Callback invoked when backpressure state changes.
2203
- */
2204
- type BackpressureCallback = (state: BackpressureState) => void;
2205
- /**
2206
- * Options for creating a BackpressureController.
2207
- */
2208
- interface BackpressureOptions {
2209
- /** High-water mark threshold (default: 16) */
2210
- highWaterMark?: number;
2211
- /** Low-water mark to resume (default: highWaterMark / 2) */
2212
- lowWaterMark?: number;
2213
- /** Callback when state changes */
2214
- onStateChange?: BackpressureCallback;
2215
- }
2216
- /**
2217
- * Controller for managing stream backpressure.
2218
- *
2219
- * When the number of buffered items exceeds the high-water mark,
2220
- * the controller enters "paused" state. It returns to "flowing"
2221
- * when items are consumed and the buffer drops below the low-water mark.
2222
- *
2223
- * @example
2224
- * ```typescript
2225
- * const controller = createBackpressureController({ highWaterMark: 16 });
2226
- *
2227
- * // Track writes
2228
- * controller.increment();
2229
- * if (controller.state === 'paused') {
2230
- * await controller.waitForDrain();
2231
- * }
2232
- *
2233
- * // Track reads (consumer)
2234
- * controller.decrement();
2235
- * ```
2236
- */
2237
- interface BackpressureController {
2238
- /** Current state */
2239
- readonly state: BackpressureState;
2240
- /** Current number of buffered items */
2241
- readonly bufferedCount: number;
2242
- /** High-water mark threshold */
2243
- readonly highWaterMark: number;
2244
- /** Low-water mark threshold */
2245
- readonly lowWaterMark: number;
2246
- /** Increment buffered count (called on write) */
2247
- increment(): void;
2248
- /** Decrement buffered count (called on read/consume) */
2249
- decrement(): void;
2250
- /** Set buffered count directly (for resuming) */
2251
- setCount(count: number): void;
2252
- /**
2253
- * Wait for the buffer to drain below low-water mark.
2254
- * Resolves immediately if already flowing.
2255
- */
2256
- waitForDrain(): Promise<void>;
2257
- /** Reset the controller to initial state */
2258
- reset(): void;
2259
- }
2260
- /**
2261
- * Create a backpressure controller.
2262
- *
2263
- * @param options - Configuration options
2264
- * @returns BackpressureController instance
2265
- */
2266
- declare function createBackpressureController(options?: BackpressureOptions): BackpressureController;
2267
- /**
2268
- * Check if backpressure should be applied.
2269
- */
2270
- declare function shouldApplyBackpressure(controller: BackpressureController): boolean;
2271
-
2272
- /**
2273
- * awaitly/streaming - Memory Stream Store
2274
- *
2275
- * In-memory implementation of StreamStore for development and testing.
2276
- * Data is not persisted across process restarts.
2277
- */
2278
-
2279
- /**
2280
- * Options for creating a memory stream store.
2281
- */
2282
- interface MemoryStreamStoreOptions {
2283
- /** Maximum items per stream (default: Infinity) */
2284
- maxItemsPerStream?: number;
2285
- }
2286
- /**
2287
- * Create an in-memory StreamStore.
2288
- *
2289
- * @param options - Configuration options
2290
- * @returns StreamStore implementation
2291
- *
2292
- * @example
2293
- * ```typescript
2294
- * const store = createMemoryStreamStore();
2295
- *
2296
- * // Use with workflow
2297
- * const workflow = createWorkflow(deps, { streamStore: store });
2298
- * ```
2299
- */
2300
- declare function createMemoryStreamStore(options?: MemoryStreamStoreOptions): StreamStore;
2301
- /**
2302
- * Create a memory stream store with a Map-like interface for testing.
2303
- * Exposes additional methods for inspection.
2304
- */
2305
- interface TestableMemoryStreamStore extends StreamStore {
2306
- /** Clear all streams */
2307
- clear(): void;
2308
- /** Get all stream keys */
2309
- keys(): string[];
2310
- /** Check if a stream exists */
2311
- has(workflowId: string, namespace: string): boolean;
2312
- /** Delete a stream */
2313
- delete(workflowId: string, namespace: string): boolean;
2314
- }
2315
- /**
2316
- * Create a testable memory stream store with additional inspection methods.
2317
- */
2318
- declare function createTestableMemoryStreamStore(options?: MemoryStreamStoreOptions): TestableMemoryStreamStore;
2319
-
2320
- /**
2321
- * awaitly/streaming - File Stream Store
2322
- *
2323
- * File-based implementation of StreamStore for persistent storage.
2324
- * Follows the patterns from persistence.ts.
2325
- */
2326
-
2327
- /**
2328
- * Minimal file system interface for stream operations.
2329
- * Same as FileSystemInterface in persistence.ts.
2330
- */
2331
- interface FileSystemInterface {
2332
- readFile(path: string): Promise<string>;
2333
- writeFile(path: string, data: string): Promise<void>;
2334
- unlink(path: string): Promise<void>;
2335
- exists(path: string): Promise<boolean>;
2336
- readdir(path: string): Promise<string[]>;
2337
- mkdir(path: string, options?: {
2338
- recursive?: boolean;
2339
- }): Promise<void>;
2340
- }
2341
- /**
2342
- * Options for creating a file stream store.
2343
- */
2344
- interface FileStreamStoreOptions {
2345
- /** Directory to store stream files */
2346
- directory: string;
2347
- /** File system implementation */
2348
- fs: FileSystemInterface;
2349
- }
2350
- /**
2351
- * Create a file-based StreamStore.
2352
- *
2353
- * Each stream is stored in a directory structure:
2354
- * - `{directory}/{workflowId}/{namespace}/metadata.json` - stream metadata
2355
- * - `{directory}/{workflowId}/{namespace}/items.jsonl` - items in JSON lines format
2356
- *
2357
- * @param options - Configuration options
2358
- * @returns StreamStore implementation
2359
- *
2360
- * @example
2361
- * ```typescript
2362
- * import * as fs from 'fs/promises';
2363
- *
2364
- * const store = createFileStreamStore({
2365
- * directory: './streams',
2366
- * fs: {
2367
- * readFile: (path) => fs.readFile(path, 'utf-8'),
2368
- * writeFile: (path, data) => fs.writeFile(path, data, 'utf-8'),
2369
- * unlink: (path) => fs.unlink(path),
2370
- * exists: async (path) => {
2371
- * try { await fs.access(path); return true; }
2372
- * catch { return false; }
2373
- * },
2374
- * readdir: (path) => fs.readdir(path),
2375
- * mkdir: (path, options) => fs.mkdir(path, options),
2376
- * },
2377
- * });
2378
- * ```
2379
- */
2380
- declare function createFileStreamStore(options: FileStreamStoreOptions): StreamStore;
2381
-
2382
- /**
2383
- * awaitly/streaming - Stream Transformers
2384
- *
2385
- * Utilities for transforming streams: map, filter, chunk, flatMapAsync.
2386
- * All transformers work with both StreamReader and AsyncIterable sources.
2387
- */
2388
-
2389
- /**
2390
- * A transform function that can be applied to stream items.
2391
- */
2392
- type TransformFn<T, U> = (item: T, index: number) => U | Promise<U>;
2393
- /**
2394
- * A filter predicate for stream items.
2395
- */
2396
- type FilterFn<T> = (item: T, index: number) => boolean | Promise<boolean>;
2397
- /**
2398
- * An async transform function that returns a Result.
2399
- */
2400
- type AsyncTransformFn<T, U, E> = (item: T, index: number) => AsyncResult<U, E>;
2401
- /**
2402
- * Convert a StreamReader to an AsyncIterable.
2403
- *
2404
- * This allows using for-await-of with StreamReaders.
2405
- *
2406
- * @param reader - StreamReader to convert
2407
- * @returns AsyncIterable that yields stream values
2408
- *
2409
- * @example
2410
- * ```typescript
2411
- * const reader = step.getReadable<string>({ namespace: 'tokens' });
2412
- *
2413
- * for await (const token of toAsyncIterable(reader)) {
2414
- * process.stdout.write(token);
2415
- * }
2416
- * ```
2417
- */
2418
- declare function toAsyncIterable<T>(reader: StreamReader<T>): AsyncIterable<T>;
2419
- /**
2420
- * Transform each item in a stream.
2421
- *
2422
- * @param source - StreamReader or AsyncIterable to transform
2423
- * @param fn - Transform function applied to each item
2424
- * @returns AsyncIterable of transformed values
2425
- *
2426
- * @example
2427
- * ```typescript
2428
- * const reader = step.getReadable<number>({ namespace: 'numbers' });
2429
- *
2430
- * for await (const doubled of map(reader, (n) => n * 2)) {
2431
- * console.log(doubled);
2432
- * }
2433
- * ```
2434
- */
2435
- declare function map<T, U>(source: StreamReader<T> | AsyncIterable<T>, fn: TransformFn<T, U>): AsyncIterable<U>;
2436
- /**
2437
- * Filter items in a stream.
2438
- *
2439
- * @param source - StreamReader or AsyncIterable to filter
2440
- * @param predicate - Filter function that returns true to keep item
2441
- * @returns AsyncIterable of filtered values
2442
- *
2443
- * @example
2444
- * ```typescript
2445
- * const reader = step.getReadable<number>({ namespace: 'numbers' });
2446
- *
2447
- * for await (const even of filter(reader, (n) => n % 2 === 0)) {
2448
- * console.log(even);
2449
- * }
2450
- * ```
2451
- */
2452
- declare function filter<T>(source: StreamReader<T> | AsyncIterable<T>, predicate: FilterFn<T>): AsyncIterable<T>;
2453
- /**
2454
- * Group stream items into fixed-size chunks.
2455
- *
2456
- * @param source - StreamReader or AsyncIterable to chunk
2457
- * @param size - Maximum number of items per chunk
2458
- * @returns AsyncIterable of item arrays
2459
- *
2460
- * @example
2461
- * ```typescript
2462
- * const reader = step.getReadable<string>({ namespace: 'messages' });
2463
- *
2464
- * for await (const batch of chunk(reader, 10)) {
2465
- * await processBatch(batch);
2466
- * }
2467
- * ```
2468
- */
2469
- declare function chunk<T>(source: StreamReader<T> | AsyncIterable<T>, size: number): AsyncIterable<T[]>;
2470
- /**
2471
- * Transform each item to multiple items and flatten.
2472
- *
2473
- * @param source - StreamReader or AsyncIterable to transform
2474
- * @param fn - Transform function that returns an iterable
2475
- * @returns AsyncIterable of flattened values
2476
- *
2477
- * @example
2478
- * ```typescript
2479
- * const reader = step.getReadable<string>({ namespace: 'lines' });
2480
- *
2481
- * for await (const word of flatMap(reader, (line) => line.split(' '))) {
2482
- * console.log(word);
2483
- * }
2484
- * ```
2485
- */
2486
- declare function flatMap<T, U>(source: StreamReader<T> | AsyncIterable<T>, fn: (item: T, index: number) => Iterable<U> | AsyncIterable<U>): AsyncIterable<U>;
2487
- /**
2488
- * Transform each item with a Result-returning async function.
2489
- *
2490
- * This is the Result-aware version of flatMap. If the transform function
2491
- * returns an error Result, the stream is terminated with that error.
2492
- *
2493
- * @param source - StreamReader or AsyncIterable to transform
2494
- * @param fn - Transform function returning AsyncResult
2495
- * @returns AsyncIterable of transformed values wrapped in Results
2496
- *
2497
- * @example
2498
- * ```typescript
2499
- * const reader = step.getReadable<Message>({ namespace: 'messages' });
2500
- *
2501
- * for await (const result of flatMapAsync(reader, async (msg) => {
2502
- * const processed = await processMessage(msg);
2503
- * return ok(processed);
2504
- * })) {
2505
- * if (!result.ok) {
2506
- * console.error('Processing failed:', result.error);
2507
- * break;
2508
- * }
2509
- * console.log('Processed:', result.value);
2510
- * }
2511
- * ```
2512
- */
2513
- declare function flatMapAsync<T, U, E>(source: StreamReader<T> | AsyncIterable<T>, fn: AsyncTransformFn<T, Iterable<U> | AsyncIterable<U>, E>): AsyncIterable<Result<U, E>>;
2514
- /**
2515
- * Transform each item with a Result-returning async function.
2516
- *
2517
- * @param source - StreamReader or AsyncIterable to transform
2518
- * @param fn - Transform function returning AsyncResult
2519
- * @returns AsyncIterable of Results
2520
- *
2521
- * @example
2522
- * ```typescript
2523
- * const reader = step.getReadable<number>({ namespace: 'numbers' });
2524
- *
2525
- * for await (const result of mapAsync(reader, async (n) => {
2526
- * if (n < 0) return err('NEGATIVE_NUMBER');
2527
- * return ok(Math.sqrt(n));
2528
- * })) {
2529
- * if (result.ok) {
2530
- * console.log('Sqrt:', result.value);
2531
- * }
2532
- * }
2533
- * ```
2534
- */
2535
- declare function mapAsync<T, U, E>(source: StreamReader<T> | AsyncIterable<T>, fn: AsyncTransformFn<T, U, E>): AsyncIterable<Result<U, E>>;
2536
- /**
2537
- * Take the first N items from a stream.
2538
- *
2539
- * @param source - StreamReader or AsyncIterable to take from
2540
- * @param count - Maximum number of items to take
2541
- * @returns AsyncIterable of up to N items
2542
- *
2543
- * @example
2544
- * ```typescript
2545
- * const reader = step.getReadable<string>({ namespace: 'messages' });
2546
- *
2547
- * for await (const message of take(reader, 10)) {
2548
- * console.log(message);
2549
- * }
2550
- * ```
2551
- */
2552
- declare function take<T>(source: StreamReader<T> | AsyncIterable<T>, count: number): AsyncIterable<T>;
2553
- /**
2554
- * Skip the first N items from a stream.
2555
- *
2556
- * @param source - StreamReader or AsyncIterable to skip from
2557
- * @param count - Number of items to skip
2558
- * @returns AsyncIterable starting after N items
2559
- *
2560
- * @example
2561
- * ```typescript
2562
- * const reader = step.getReadable<string>({ namespace: 'messages' });
2563
- *
2564
- * for await (const message of skip(reader, 100)) {
2565
- * console.log(message); // Messages 101+
2566
- * }
2567
- * ```
2568
- */
2569
- declare function skip<T>(source: StreamReader<T> | AsyncIterable<T>, count: number): AsyncIterable<T>;
2570
- /**
2571
- * Take items while predicate returns true.
2572
- *
2573
- * @param source - StreamReader or AsyncIterable
2574
- * @param predicate - Predicate function
2575
- * @returns AsyncIterable of items while predicate is true
2576
- *
2577
- * @example
2578
- * ```typescript
2579
- * const reader = step.getReadable<number>({ namespace: 'numbers' });
2580
- *
2581
- * for await (const n of takeWhile(reader, (n) => n < 100)) {
2582
- * console.log(n);
2583
- * }
2584
- * ```
2585
- */
2586
- declare function takeWhile<T>(source: StreamReader<T> | AsyncIterable<T>, predicate: FilterFn<T>): AsyncIterable<T>;
2587
- /**
2588
- * Skip items while predicate returns true.
2589
- *
2590
- * @param source - StreamReader or AsyncIterable
2591
- * @param predicate - Predicate function
2592
- * @returns AsyncIterable of items after predicate becomes false
2593
- *
2594
- * @example
2595
- * ```typescript
2596
- * const reader = step.getReadable<number>({ namespace: 'numbers' });
2597
- *
2598
- * for await (const n of skipWhile(reader, (n) => n < 100)) {
2599
- * console.log(n); // First n >= 100 and all after
2600
- * }
2601
- * ```
2602
- */
2603
- declare function skipWhile<T>(source: StreamReader<T> | AsyncIterable<T>, predicate: FilterFn<T>): AsyncIterable<T>;
2604
- /**
2605
- * Collect all stream items into an array.
2606
- *
2607
- * Warning: This loads all items into memory. Only use when you know
2608
- * the stream is bounded.
2609
- *
2610
- * @param source - StreamReader or AsyncIterable
2611
- * @returns Promise resolving to array of all items
2612
- *
2613
- * @example
2614
- * ```typescript
2615
- * const reader = step.getReadable<string>({ namespace: 'small-data' });
2616
- * const items = await collect(reader);
2617
- * console.log('Got', items.length, 'items');
2618
- * ```
2619
- */
2620
- declare function collect<T>(source: StreamReader<T> | AsyncIterable<T>): Promise<T[]>;
2621
- /**
2622
- * Reduce stream items to a single value.
2623
- *
2624
- * @param source - StreamReader or AsyncIterable
2625
- * @param reducer - Reducer function
2626
- * @param initial - Initial accumulator value
2627
- * @returns Promise resolving to final accumulated value
2628
- *
2629
- * @example
2630
- * ```typescript
2631
- * const reader = step.getReadable<number>({ namespace: 'numbers' });
2632
- * const sum = await reduce(reader, (acc, n) => acc + n, 0);
2633
- * console.log('Sum:', sum);
2634
- * ```
2635
- */
2636
- declare function reduce<T, U>(source: StreamReader<T> | AsyncIterable<T>, reducer: (accumulator: U, item: T, index: number) => U | Promise<U>, initial: U): Promise<U>;
2637
- /**
2638
- * Pipe a source through multiple transformers.
2639
- *
2640
- * @param source - Initial source
2641
- * @param transformers - Array of transformer functions
2642
- * @returns Final transformed AsyncIterable
2643
- *
2644
- * @example
2645
- * ```typescript
2646
- * const reader = step.getReadable<number>({ namespace: 'numbers' });
2647
- *
2648
- * const result = pipe(
2649
- * reader,
2650
- * (s) => filter(s, (n) => n > 0),
2651
- * (s) => map(s, (n) => n * 2),
2652
- * (s) => take(s, 10)
2653
- * );
2654
- *
2655
- * for await (const n of result) {
2656
- * console.log(n);
2657
- * }
2658
- * ```
2659
- */
2660
- declare function pipe<T>(source: StreamReader<T> | AsyncIterable<T>): AsyncIterable<T>;
2661
- declare function pipe<T, A>(source: StreamReader<T> | AsyncIterable<T>, t1: (s: AsyncIterable<T>) => AsyncIterable<A>): AsyncIterable<A>;
2662
- declare function pipe<T, A, B>(source: StreamReader<T> | AsyncIterable<T>, t1: (s: AsyncIterable<T>) => AsyncIterable<A>, t2: (s: AsyncIterable<A>) => AsyncIterable<B>): AsyncIterable<B>;
2663
- declare function pipe<T, A, B, C>(source: StreamReader<T> | AsyncIterable<T>, t1: (s: AsyncIterable<T>) => AsyncIterable<A>, t2: (s: AsyncIterable<A>) => AsyncIterable<B>, t3: (s: AsyncIterable<B>) => AsyncIterable<C>): AsyncIterable<C>;
2664
- declare function pipe<T, A, B, C, D>(source: StreamReader<T> | AsyncIterable<T>, t1: (s: AsyncIterable<T>) => AsyncIterable<A>, t2: (s: AsyncIterable<A>) => AsyncIterable<B>, t3: (s: AsyncIterable<B>) => AsyncIterable<C>, t4: (s: AsyncIterable<C>) => AsyncIterable<D>): AsyncIterable<D>;
2665
-
2666
- /**
2667
- * awaitly/streaming
2668
- *
2669
- * Result-aware streaming for workflows.
2670
- * All stream operations return Result types, enabling typed error handling
2671
- * throughout the streaming pipeline.
2672
- *
2673
- * @example Basic usage
2674
- * ```typescript
2675
- * import { createWorkflow } from 'awaitly/workflow';
2676
- * import { createMemoryStreamStore } from 'awaitly/workflow';
2677
- *
2678
- * const streamStore = createMemoryStreamStore();
2679
- * const workflow = createWorkflow(deps, { streamStore });
2680
- *
2681
- * const result = await workflow(async ({ step }) => {
2682
- * const writer = step.getWritable<string>({ namespace: 'tokens' });
2683
- *
2684
- * await step(() => generateAI({
2685
- * prompt: 'Hello',
2686
- * onToken: async (token) => { await writer.write(token); }
2687
- * }), { key: 'generate' });
2688
- *
2689
- * await writer.close();
2690
- * });
2691
- * ```
2692
- *
2693
- * @example Consuming a stream
2694
- * ```typescript
2695
- * import { toAsyncIterable } from 'awaitly/workflow';
2696
- *
2697
- * const reader = step.getReadable<string>({ namespace: 'tokens' });
2698
- *
2699
- * for await (const token of toAsyncIterable(reader)) {
2700
- * process.stdout.write(token);
2701
- * }
2702
- * ```
2703
- *
2704
- * @example Stream transformations
2705
- * ```typescript
2706
- * import { map, filter, chunk, collect } from 'awaitly/workflow';
2707
- *
2708
- * const reader = step.getReadable<number>({ namespace: 'numbers' });
2709
- *
2710
- * // Transform pipeline
2711
- * const evens = filter(reader, n => n % 2 === 0);
2712
- * const doubled = map(evens, n => n * 2);
2713
- * const batches = chunk(doubled, 10);
2714
- *
2715
- * for await (const batch of batches) {
2716
- * await processBatch(batch);
2717
- * }
2718
- * ```
2719
- */
2720
-
2721
- /**
2722
- * Options for creating an external stream reader.
2723
- */
2724
- interface ExternalReaderOptions {
2725
- /** Stream store instance */
2726
- store: StreamStore;
2727
- /** Workflow ID that owns the stream */
2728
- workflowId: string;
2729
- /** Stream namespace (default: 'default') */
2730
- namespace?: string;
2731
- /** Start reading from this position (default: 0) */
2732
- startIndex?: number;
2733
- /** Poll interval in ms when waiting for new items (default: 100) */
2734
- pollInterval?: number;
2735
- /** Stop polling after this many ms with no new items (default: 30000) */
2736
- pollTimeout?: number;
2737
- }
2738
- /**
2739
- * Create a stream reader for external consumption (outside workflows).
2740
- *
2741
- * Use this in HTTP handlers, WebSocket handlers, or other contexts
2742
- * where you need to consume a stream created by a workflow.
2743
- *
2744
- * @param options - Reader configuration
2745
- * @returns StreamReader that can be used with toAsyncIterable()
2746
- *
2747
- * @example HTTP streaming response
2748
- * ```typescript
2749
- * app.get('/stream/:workflowId', async (req, res) => {
2750
- * const reader = getStreamReader({
2751
- * store: streamStore,
2752
- * workflowId: req.params.workflowId,
2753
- * namespace: 'ai-response',
2754
- * });
2755
- *
2756
- * res.setHeader('Content-Type', 'text/event-stream');
2757
- *
2758
- * for await (const chunk of toAsyncIterable(reader)) {
2759
- * res.write(`data: ${chunk}\n\n`);
2760
- * }
2761
- *
2762
- * res.end();
2763
- * });
2764
- * ```
2765
- *
2766
- * @example Resume from last position
2767
- * ```typescript
2768
- * const reader = getStreamReader({
2769
- * store: streamStore,
2770
- * workflowId: runId,
2771
- * namespace: 'tokens',
2772
- * startIndex: lastReceivedPosition + 1,
2773
- * });
2774
- * ```
2775
- */
2776
- declare function getStreamReader<T>(options: ExternalReaderOptions): StreamReader<T>;
2777
-
2778
- /**
2779
- * awaitly/webhook
2780
- *
2781
- * Webhook and event trigger adapters for exposing workflows as HTTP endpoints.
2782
- * Framework-agnostic handlers that work with Express, Hono, Fastify, etc.
2783
- */
2784
-
2785
- /**
2786
- * Generic HTTP request representation.
2787
- * Abstracts away framework-specific request objects.
2788
- */
2789
- interface WebhookRequest<Body = unknown> {
2790
- /** HTTP method (GET, POST, PUT, DELETE, etc.) */
2791
- method: string;
2792
- /** Request path (e.g., "/api/checkout") */
2793
- path: string;
2794
- /** Request headers */
2795
- headers: Record<string, string | string[] | undefined>;
2796
- /** Parsed request body (JSON) */
2797
- body: Body;
2798
- /** Query parameters */
2799
- query: Record<string, string | string[] | undefined>;
2800
- /** Path parameters (e.g., { id: "123" }) */
2801
- params: Record<string, string>;
2802
- /** Raw request object from framework (for advanced use cases) */
2803
- raw?: unknown;
2804
- }
2805
- /**
2806
- * Generic HTTP response representation.
2807
- */
2808
- interface WebhookResponse<T = unknown> {
2809
- /** HTTP status code */
2810
- status: number;
2811
- /** Response headers */
2812
- headers?: Record<string, string>;
2813
- /** Response body (will be JSON serialized) */
2814
- body: T;
2815
- }
2816
- /**
2817
- * Error response body structure.
2818
- */
2819
- interface ErrorResponseBody {
2820
- error: {
2821
- type: string;
2822
- message?: string;
2823
- details?: unknown;
2824
- };
2825
- }
2826
- /**
2827
- * Input validation result.
2828
- */
2829
- type ValidationResult<T, E = string> = Result<T, E>;
2830
- /**
2831
- * Standard validation error type.
2832
- */
2833
- interface ValidationError {
2834
- type: "VALIDATION_ERROR";
2835
- message: string;
2836
- field?: string;
2837
- details?: unknown;
2838
- }
2839
- /**
2840
- * Type guard for ValidationError.
2841
- */
2842
- declare function isValidationError(e: unknown): e is ValidationError;
2843
- /**
2844
- * Configuration for creating a webhook handler.
2845
- *
2846
- * @template TInput - The validated input type
2847
- * @template TOutput - The workflow output type
2848
- * @template TError - The workflow error type
2849
- * @template TBody - The raw request body type
2850
- * @template TUnexpected - The workflow's unexpected error type (default UnexpectedError)
2851
- */
2852
- interface WebhookHandlerConfig<TInput, TOutput, TError, TBody = unknown, TUnexpected = UnexpectedError> {
2853
- /**
2854
- * Validate and transform the incoming request.
2855
- * Return ok(input) to proceed, or err(validationError) to reject.
2856
- *
2857
- * @param req - The incoming request
2858
- * @returns Validated input or validation error
2859
- */
2860
- validateInput: (req: WebhookRequest<TBody>) => ValidationResult<TInput, ValidationError> | Promise<ValidationResult<TInput, ValidationError>>;
2861
- /**
2862
- * Map workflow result to HTTP response.
2863
- * Called for both success and error cases.
2864
- *
2865
- * @param result - The workflow result (error union is TError | TUnexpected)
2866
- * @param req - The original request (for context)
2867
- * @returns HTTP response
2868
- */
2869
- mapResult: (result: Result<TOutput, TError | TUnexpected>, req: WebhookRequest<TBody>) => WebhookResponse;
2870
- /**
2871
- * Optional: Map validation errors to HTTP response.
2872
- * Defaults to 400 Bad Request with error details.
2873
- *
2874
- * @param error - The validation error
2875
- * @param req - The original request
2876
- * @returns HTTP response
2877
- */
2878
- mapValidationError?: (error: ValidationError, req: WebhookRequest<TBody>) => WebhookResponse<ErrorResponseBody>;
2879
- /**
2880
- * Optional: Handle unexpected errors during request processing.
2881
- * Defaults to 500 Internal Server Error.
2882
- *
2883
- * @param error - The unexpected error
2884
- * @param req - The original request
2885
- * @returns HTTP response
2886
- */
2887
- mapUnexpectedError?: (error: unknown, req: WebhookRequest<TBody>) => WebhookResponse<ErrorResponseBody>;
2888
- /**
2889
- * Optional: Request middleware.
2890
- * Transform or enrich the request before validation.
2891
- *
2892
- * @param req - The incoming request
2893
- * @returns Transformed request
2894
- */
2895
- beforeValidation?: (req: WebhookRequest<TBody>) => WebhookRequest<TBody> | Promise<WebhookRequest<TBody>>;
2896
- /**
2897
- * Optional: Response middleware.
2898
- * Transform the response before sending.
2899
- *
2900
- * @param response - The response to send
2901
- * @param req - The original request
2902
- * @returns Transformed response
2903
- */
2904
- afterResponse?: (response: WebhookResponse, req: WebhookRequest<TBody>) => WebhookResponse | Promise<WebhookResponse>;
2905
- }
2906
- /**
2907
- * A webhook handler function that processes requests.
2908
- */
2909
- type WebhookHandler<TBody = unknown> = (req: WebhookRequest<TBody>) => Promise<WebhookResponse>;
2910
- /**
2911
- * Default validation error mapper.
2912
- * Returns 400 Bad Request with error details.
2913
- */
2914
- declare function defaultValidationErrorMapper(error: ValidationError): WebhookResponse<ErrorResponseBody>;
2915
- /**
2916
- * Default unexpected error mapper.
2917
- * Returns 500 Internal Server Error.
2918
- */
2919
- declare function defaultUnexpectedErrorMapper(error: unknown): WebhookResponse<ErrorResponseBody>;
2920
- /**
2921
- * Create a webhook handler for a workflow.
2922
- *
2923
- * This factory creates an HTTP handler function that:
2924
- * 1. Validates the incoming request
2925
- * 2. Executes the workflow with the validated input
2926
- * 3. Maps the result to an HTTP response
2927
- *
2928
- * The handler is framework-agnostic and returns a standard response object.
2929
- * Use framework adapters (createExpressHandler, createHonoHandler, etc.) to
2930
- * integrate with specific frameworks.
2931
- *
2932
- * @template TInput - The validated input type passed to the workflow
2933
- * @template TOutput - The workflow output type
2934
- * @template TError - The workflow error type
2935
- * @template TBody - The raw request body type
2936
- * @template TDeps - The workflow dependencies type
2937
- *
2938
- * @param workflow - The workflow function to execute
2939
- * @param workflowFn - The workflow body function ({ step, deps, args }) => output
2940
- * @param config - Handler configuration
2941
- * @returns A webhook handler function
2942
- *
2943
- * @example
2944
- * ```typescript
2945
- * const checkoutWorkflow = createWorkflow('checkout', { chargeCard, sendEmail });
2946
- *
2947
- * const handler = createWebhookHandler(
2948
- * checkoutWorkflow,
2949
- * async ({ step, deps, args: input }) => {
2950
- * const charge = await step('chargeCard', () => deps.chargeCard(input.amount));
2951
- * await step('sendEmail', () => deps.sendEmail(input.email, charge.receiptUrl));
2952
- * return { chargeId: charge.id };
2953
- * },
2954
- * {
2955
- * validateInput: (req) => {
2956
- * const { amount, email } = req.body;
2957
- * if (!amount || !email) {
2958
- * return err({ type: 'VALIDATION_ERROR', message: 'Missing required fields' });
2959
- * }
2960
- * return ok({ amount, email });
2961
- * },
2962
- * mapResult: (result) => {
2963
- * if (result.ok) {
2964
- * return { status: 200, body: result.value };
2965
- * }
2966
- * if (result.error === 'CARD_DECLINED') {
2967
- * return { status: 402, body: { error: { type: 'CARD_DECLINED' } } };
2968
- * }
2969
- * return { status: 500, body: { error: { type: 'UNKNOWN' } } };
2970
- * },
2971
- * }
2972
- * );
2973
- *
2974
- * // Use with Express
2975
- * app.post('/checkout', async (req, res) => {
2976
- * const response = await handler(toWebhookRequest(req));
2977
- * res.status(response.status).json(response.body);
2978
- * });
2979
- * ```
2980
- */
2981
- declare function createWebhookHandler<TInput, TOutput, TError, TUnexpected, TBody = unknown, TDeps = unknown>(workflow: Workflow<TError, TUnexpected, TDeps>, workflowFn: (context: {
2982
- step: RunStep<TError | TUnexpected>;
2983
- deps: TDeps;
2984
- args: TInput;
2985
- }) => TOutput | Promise<TOutput>, config: WebhookHandlerConfig<TInput, TOutput, TError, TBody, TUnexpected>): WebhookHandler<TBody>;
2986
- declare function createWebhookHandler<TInput, TOutput, TError, TBody = unknown, TDeps = unknown>(workflow: Workflow<TError, UnexpectedError, TDeps>, workflowFn: (context: {
2987
- step: RunStep<TError | UnexpectedError>;
2988
- deps: TDeps;
2989
- args: TInput;
2990
- }) => TOutput | Promise<TOutput>, config: WebhookHandlerConfig<TInput, TOutput, TError, TBody, UnexpectedError>): WebhookHandler<TBody>;
2991
- /**
2992
- * Configuration for a simple webhook handler without workflow.
2993
- * Use with createSimpleHandler for endpoints that don't need step orchestration.
2994
- *
2995
- * @template TInput - Validated input type
2996
- * @template TOutput - Handler success output type
2997
- * @template TError - Handler error type
2998
- * @template TBody - Raw request body type
2999
- */
3000
- interface SimpleHandlerConfig<TInput, TOutput, TError, TBody = unknown> {
3001
- /** Validate and transform the incoming request; return ok(input) or err(ValidationError). */
3002
- validateInput: (req: WebhookRequest<TBody>) => ValidationResult<TInput, ValidationError> | Promise<ValidationResult<TInput, ValidationError>>;
3003
- /** Execute the business logic; return Result. */
3004
- handler: (input: TInput, req: WebhookRequest<TBody>) => AsyncResult<TOutput, TError>;
3005
- /** Map the handler result to an HTTP response. */
3006
- mapResult: (result: Result<TOutput, TError>, req: WebhookRequest<TBody>) => WebhookResponse;
3007
- /** Optional: map validation errors to response. Defaults to 400 with error details. */
3008
- mapValidationError?: (error: ValidationError, req: WebhookRequest<TBody>) => WebhookResponse<ErrorResponseBody>;
3009
- /** Optional: handle unexpected errors. Defaults to 500. */
3010
- mapUnexpectedError?: (error: unknown, req: WebhookRequest<TBody>) => WebhookResponse<ErrorResponseBody>;
3011
- }
3012
- /**
3013
- * Create a simple webhook handler without workflow orchestration.
3014
- * Useful for simple endpoints that don't need step-based error handling.
3015
- *
3016
- * @example
3017
- * ```typescript
3018
- * const handler = createSimpleHandler({
3019
- * validateInput: (req) => {
3020
- * const { id } = req.params;
3021
- * if (!id) return err({ type: 'VALIDATION_ERROR', message: 'Missing id' });
3022
- * return ok({ id });
3023
- * },
3024
- * handler: async ({ id }) => {
3025
- * const user = await db.findUser(id);
3026
- * return user ? ok(user) : err('NOT_FOUND' as const);
3027
- * },
3028
- * mapResult: (result) => {
3029
- * if (result.ok) return { status: 200, body: result.value };
3030
- * return { status: 404, body: { error: { type: 'NOT_FOUND' } } };
3031
- * },
3032
- * });
3033
- * ```
3034
- */
3035
- declare function createSimpleHandler<TInput, TOutput, TError, TBody = unknown>(config: SimpleHandlerConfig<TInput, TOutput, TError, TBody>): WebhookHandler<TBody>;
3036
- /**
3037
- * Standard error mapping configuration.
3038
- */
3039
- interface ErrorMapping<TError> {
3040
- /** The error value to match */
3041
- error: TError;
3042
- /** HTTP status code for this error */
3043
- status: number;
3044
- /** Optional custom message */
3045
- message?: string;
3046
- }
3047
- /**
3048
- * Create a result mapper from error mappings.
3049
- * Provides a declarative way to map workflow errors to HTTP responses.
3050
- *
3051
- * @param mappings - Array of error mappings
3052
- * @param defaultStatus - Default status for unmapped errors (default: 500)
3053
- * @returns A mapResult function for use in handler config
3054
- *
3055
- * @example
3056
- * ```typescript
3057
- * const mapResult = createResultMapper<CheckoutOutput, CheckoutError>([
3058
- * { error: 'NOT_FOUND', status: 404, message: 'Resource not found' },
3059
- * { error: 'CARD_DECLINED', status: 402, message: 'Payment failed' },
3060
- * { error: 'RATE_LIMITED', status: 429, message: 'Too many requests' },
3061
- * ]);
3062
- *
3063
- * const handler = createWebhookHandler(workflow, workflowFn, {
3064
- * validateInput,
3065
- * mapResult,
3066
- * });
3067
- * ```
3068
- */
3069
- declare function createResultMapper<TOutput, TError>(mappings: ErrorMapping<TError>[], options?: {
3070
- defaultStatus?: number;
3071
- successStatus?: number;
3072
- }): (result: Result<TOutput, TError | UnexpectedError>) => WebhookResponse;
3073
- /**
3074
- * Express-style request object (minimal interface).
3075
- */
3076
- interface ExpressLikeRequest {
3077
- method: string;
3078
- path: string;
3079
- headers: Record<string, string | string[] | undefined>;
3080
- body: unknown;
3081
- query: Record<string, string | string[] | undefined>;
3082
- params: Record<string, string>;
3083
- }
3084
- /**
3085
- * Express-style response object (minimal interface).
3086
- */
3087
- interface ExpressLikeResponse {
3088
- status(code: number): ExpressLikeResponse;
3089
- set(headers: Record<string, string>): ExpressLikeResponse;
3090
- json(body: unknown): void;
3091
- }
3092
- /**
3093
- * Convert an Express-like request to WebhookRequest.
3094
- *
3095
- * @param req - Express-like request object
3096
- * @returns WebhookRequest
3097
- *
3098
- * @example
3099
- * ```typescript
3100
- * app.post('/checkout', async (req, res) => {
3101
- * const webhookReq = toWebhookRequest(req);
3102
- * const response = await handler(webhookReq);
3103
- * res.status(response.status).json(response.body);
3104
- * });
3105
- * ```
3106
- */
3107
- declare function toWebhookRequest<TBody = unknown>(req: ExpressLikeRequest): WebhookRequest<TBody>;
3108
- /**
3109
- * Send a WebhookResponse using an Express-like response object.
3110
- *
3111
- * @param res - Express-like response object
3112
- * @param response - WebhookResponse to send
3113
- *
3114
- * @example
3115
- * ```typescript
3116
- * app.post('/checkout', async (req, res) => {
3117
- * const response = await handler(toWebhookRequest(req));
3118
- * sendWebhookResponse(res, response);
3119
- * });
3120
- * ```
3121
- */
3122
- declare function sendWebhookResponse(res: ExpressLikeResponse, response: WebhookResponse): void;
3123
- /**
3124
- * Create an Express-compatible middleware from a webhook handler.
3125
- *
3126
- * @param handler - Webhook handler function
3127
- * @returns Express middleware function
3128
- *
3129
- * @example
3130
- * ```typescript
3131
- * const handler = createWebhookHandler(workflow, workflowFn, config);
3132
- * const middleware = createExpressHandler(handler);
3133
- * app.post('/checkout', middleware);
3134
- * ```
3135
- */
3136
- declare function createExpressHandler<TBody = unknown>(handler: WebhookHandler<TBody>): (req: ExpressLikeRequest, res: ExpressLikeResponse) => Promise<void>;
3137
- /**
3138
- * Create a validation error.
3139
- *
3140
- * @param message - Error message
3141
- * @param field - Optional field name
3142
- * @param details - Optional additional details
3143
- * @returns ValidationError
3144
- */
3145
- declare function validationError(message: string, field?: string, details?: unknown): ValidationError;
3146
- /**
3147
- * Create a required field validator.
3148
- *
3149
- * @param fields - Field names to validate
3150
- * @returns Validation function
3151
- *
3152
- * @example
3153
- * ```typescript
3154
- * const validateRequired = requireFields(['email', 'password']);
3155
- *
3156
- * const validateInput = (req) => {
3157
- * const result = validateRequired(req.body);
3158
- * if (!result.ok) return result;
3159
- * return ok(req.body as LoginInput);
3160
- * };
3161
- * ```
3162
- */
3163
- declare function requireFields(fields: string[]): (body: Record<string, unknown>) => ValidationResult<void, ValidationError>;
3164
- /**
3165
- * Compose multiple validators into a single validator.
3166
- *
3167
- * @param validators - Validators to compose
3168
- * @returns Combined validator function
3169
- *
3170
- * @example
3171
- * ```typescript
3172
- * const validate = composeValidators(
3173
- * requireFields(['email', 'password']),
3174
- * validateEmailFormat,
3175
- * validatePasswordStrength
3176
- * );
3177
- * ```
3178
- */
3179
- declare function composeValidators<T>(...validators: Array<(input: T) => ValidationResult<void, ValidationError>>): (input: T) => ValidationResult<void, ValidationError>;
3180
- /**
3181
- * Generic event message for queue-based triggers.
3182
- */
3183
- interface EventMessage<T = unknown> {
3184
- /** Unique message ID */
3185
- id: string;
3186
- /** Event type/name */
3187
- type: string;
3188
- /** Event payload */
3189
- payload: T;
3190
- /** Event metadata */
3191
- metadata?: {
3192
- timestamp?: number;
3193
- source?: string;
3194
- correlationId?: string;
3195
- [key: string]: unknown;
3196
- };
3197
- }
3198
- /**
3199
- * Result of processing an event.
3200
- */
3201
- interface EventProcessingResult {
3202
- /** Whether the event was processed successfully */
3203
- success: boolean;
3204
- /** Should the message be acknowledged (removed from queue)? */
3205
- ack: boolean;
3206
- /** Optional error details */
3207
- error?: {
3208
- type: string;
3209
- message?: string;
3210
- retryable?: boolean;
3211
- };
3212
- }
3213
- /**
3214
- * Configuration for event trigger handlers.
3215
- */
3216
- interface EventTriggerConfig<TPayload, TOutput, TError, TUnexpected = UnexpectedError> {
3217
- /** Validate the event payload */
3218
- validatePayload: (event: EventMessage<TPayload>) => ValidationResult<TPayload, ValidationError>;
3219
- /** Map workflow result to processing result */
3220
- mapResult: (result: Result<TOutput, TError | TUnexpected>, event: EventMessage<TPayload>) => EventProcessingResult;
3221
- /** Optional: Determine if error is retryable */
3222
- isRetryable?: (error: TError | TUnexpected) => boolean;
3223
- }
3224
- /**
3225
- * Event handler function type.
3226
- */
3227
- type EventHandler<TPayload = unknown> = (event: EventMessage<TPayload>) => Promise<EventProcessingResult>;
3228
- /**
3229
- * Create an event handler for queue-based triggers.
3230
- *
3231
- * @example
3232
- * ```typescript
3233
- * const handler = createEventHandler(
3234
- * checkoutWorkflow,
3235
- * async ({ step, deps, args: payload }) => {
3236
- * const charge = await step('chargeCard', () => deps.chargeCard(payload.amount));
3237
- * return { chargeId: charge.id };
3238
- * },
3239
- * {
3240
- * validatePayload: (event) => {
3241
- * if (!event.payload.amount) {
3242
- * return err({ type: 'VALIDATION_ERROR', message: 'Missing amount' });
3243
- * }
3244
- * return ok(event.payload);
3245
- * },
3246
- * mapResult: (result) => ({
3247
- * success: result.ok,
3248
- * ack: result.ok || !isRetryableError(result.error),
3249
- * error: result.ok ? undefined : { type: String(result.error) },
3250
- * }),
3251
- * }
3252
- * );
3253
- *
3254
- * // Use with SQS, RabbitMQ, etc.
3255
- * queue.consume(async (message) => {
3256
- * const result = await handler(message);
3257
- * if (result.ack) await message.ack();
3258
- * else await message.nack();
3259
- * });
3260
- * ```
3261
- */
3262
- declare function createEventHandler<TPayload, TOutput, TError, TDeps = unknown, TUnexpected = UnexpectedError>(workflow: Workflow<TError, TUnexpected, TDeps>, workflowFn: (context: {
3263
- step: RunStep<TError | TUnexpected>;
3264
- deps: TDeps;
3265
- args: TPayload;
3266
- }) => TOutput | Promise<TOutput>, config: EventTriggerConfig<TPayload, TOutput, TError, TUnexpected>): EventHandler<TPayload>;
3267
-
3268
- /** A registered workflow definition */
3269
- interface WorkflowRegistration<Deps extends Readonly<Record<string, AnyResultFn>> = Readonly<Record<string, AnyResultFn>>> {
3270
- /** Workflow dependencies (Result-returning functions) */
3271
- deps: Deps;
3272
- /** Workflow function */
3273
- fn: (context: {
3274
- step: RunStep<any>;
3275
- deps: Deps;
3276
- ctx: WorkflowContext;
3277
- }) => any;
3278
- /** Default durable options (version, lockTtlMs, etc.) */
3279
- durableDefaults?: Partial<Pick<DurableOptions, 'version' | 'lockTtlMs' | 'heartbeatIntervalMs'>>;
3280
- }
3281
- interface EnqueueOptions {
3282
- /** Custom workflow execution ID (default: auto-generated UUID) */
3283
- id?: string;
3284
- /** Idempotency key for deduplication */
3285
- idempotencyKey?: string;
3286
- /** Workflow input for idempotency conflict detection */
3287
- input?: unknown;
3288
- /** Custom metadata */
3289
- metadata?: Record<string, unknown>;
3290
- }
3291
- interface ScheduleOptions {
3292
- /** Repeat interval in milliseconds */
3293
- intervalMs: number;
3294
- /** Run immediately on schedule creation */
3295
- immediate?: boolean;
3296
- }
3297
- interface EngineOptions {
3298
- /** Snapshot store for workflow persistence */
3299
- store: SnapshotStore;
3300
- /** Registered workflows keyed by name */
3301
- workflows: Record<string, WorkflowRegistration>;
3302
- /** Max parallel workflow runs per tick (default: 5) */
3303
- concurrency?: number;
3304
- /** Event handler */
3305
- onEvent?: (event: EngineEvent) => void;
3306
- /** Error handler for background operations */
3307
- onError?: (error: unknown) => void;
3308
- }
3309
- type EngineEvent = {
3310
- type: "engine_start";
3311
- ts: number;
3312
- } | {
3313
- type: "engine_stop";
3314
- ts: number;
3315
- } | {
3316
- type: "engine_tick";
3317
- ts: number;
3318
- processed: number;
3319
- } | {
3320
- type: "workflow_enqueued";
3321
- workflowName: string;
3322
- id: string;
3323
- ts: number;
3324
- } | {
3325
- type: "workflow_started";
3326
- workflowName: string;
3327
- id: string;
3328
- ts: number;
3329
- } | {
3330
- type: "workflow_completed";
3331
- workflowName: string;
3332
- id: string;
3333
- ts: number;
3334
- } | {
3335
- type: "workflow_failed";
3336
- workflowName: string;
3337
- id: string;
3338
- error: unknown;
3339
- ts: number;
3340
- } | {
3341
- type: "schedule_created";
3342
- workflowName: string;
3343
- scheduleId: string;
3344
- intervalMs: number;
3345
- ts: number;
3346
- } | {
3347
- type: "schedule_removed";
3348
- scheduleId: string;
3349
- ts: number;
3350
- };
3351
- interface Engine {
3352
- /** Enqueue a workflow for execution. Returns the workflow execution ID. */
3353
- enqueue(name: string, options?: EnqueueOptions): Promise<string>;
3354
- /** Schedule recurring workflow execution. Returns the schedule ID. */
3355
- schedule(name: string, options: ScheduleOptions & EnqueueOptions): string;
3356
- /** Remove a schedule */
3357
- unschedule(scheduleId: string): boolean;
3358
- /** Start the polling loop */
3359
- start(pollIntervalMs?: number): void;
3360
- /** Stop the polling loop gracefully */
3361
- stop(): Promise<void>;
3362
- /** Execute a single tick manually (process pending workflows). Returns number processed. */
3363
- tick(): Promise<number>;
3364
- /** Get engine status */
3365
- status(): {
3366
- running: boolean;
3367
- pendingSchedules: number;
3368
- };
3369
- }
3370
-
3371
- declare function createEngine(options: EngineOptions): Engine;
3372
-
3373
362
  /**
3374
363
  * awaitly/resource
3375
364
  *
@@ -3732,4 +721,4 @@ declare const batchPresets: {
3732
721
  };
3733
722
  };
3734
723
 
3735
- export { AnyResultFn, type ApprovalNeededContext, ApprovalRejected, type ApprovalResolvedContext, type ApprovalStatus, ApprovalStepOptions, type ApprovalStore, type ApprovalWebhookRequest, type ApprovalWebhookResponse, type AsyncTransformFn, type BackpressureCallback, type BackpressureController, type BackpressureOptions, type BackpressureState, type BatchConfig, type BatchOptions, type BatchProcessingError, type BatchProgress, type CompensationAction, type ConcurrentExecutionError, type DeleteStatesOptions, type DeleteStatesResult, type DurableOptions, type DurableWorkflowEvent, type Engine, type EngineEvent, type EngineOptions, type EnqueueOptions, type ErrorMapping, type ErrorResponseBody, ErrorsOfDeps, type EventHandler, type EventMessage, type EventProcessingResult, type EventTriggerConfig, type ExpressLikeRequest, type ExpressLikeResponse, type ExternalReaderOptions, type FileStreamStoreOptions, type FileSystemInterface, type FilterFn, GatedStepOptions, type HITLExecutionResult, type HITLOrchestrator, type HITLOrchestratorOptions, type HITLWorkflowFactoryOptions, HOOK_STEP_KEY_PREFIX, type IdempotencyConflictError, type InputValidationError, type InvalidBatchConfigError, type LeaseExpiredError, type MemoryStreamStoreOptions, type MigrationError, type MigrationFn, type Migrations, type NotificationChannel, PendingApproval, PendingHook, type PersistedWorkflowState, type PersistenceError, type PollerOptions, type Resource, type ResourceCleanupError, type ResourceScope, ResumeState, ResumeStateEntry, RunStep, type SagaCompensationError, type SagaEvent, type SagaResult, type SagaStep, type SagaStepOptions, type SagaWorkflow, type SagaWorkflowOptions, type SavedWorkflowState, type ScheduleOptions, type SerializedResumeState, type SimpleHandlerConfig, SnapshotStore, StepResult, type StoreLoadResult, type StoreSaveInput, StreamReader, StreamStore, type TestableMemoryStreamStore, type TransformFn, UnexpectedError, type ValidationError, type ValidationResult, type Version, type VersionIncompatibleError, type VersionMismatchError, type VersionedState, type VersionedWorkflowConfig, type WebhookHandler, type WebhookHandlerConfig, type WebhookRequest, type WebhookResponse, Workflow, WorkflowCancelledError, WorkflowContext, type WorkflowDiagramDSL, type WorkflowDiagramSourceLocation, type WorkflowDiagramState, type WorkflowDiagramStateType, type WorkflowDiagramTransition, WorkflowEvent, type WorkflowLock, WorkflowOptions, type WorkflowRegistration, WorkflowSnapshot, type WorkflowStateStore, batchPresets, chunk, clearStep, collect, composeMigrations, composeValidators, createApprovalChecker, createApprovalStateCollector, createApprovalStep, createApprovalWebhookHandler, createBackpressureController, createEngine, createEventHandler, createExpressHandler, createFileStreamStore, createHITLOrchestrator, createHook, createKeyRemoveMigration, createKeyRenameMigration, createMemoryApprovalStore, createMemoryStreamStore, createMemoryWorkflowStateStore, createResource, createResourceScope, createResultMapper, createResumeStateCollector, createSagaWorkflow, createSimpleHandler, createTestableMemoryStreamStore, createValueTransformMigration, createVersionedState, createVersionedStateLoader, createWebhookHandler, createWorkflow, defaultUnexpectedErrorMapper, defaultValidationErrorMapper, deserializeResumeState, durable, filter, flatMap, flatMapAsync, gatedStep, getPendingApprovals, getPendingHooks, getStreamReader, hasPendingApproval, hasPendingHook, injectApproval, injectHook, isApprovalRejected, isBatchProcessingError, isConcurrentExecution, isIdempotencyConflict, isInputValidationError, isInvalidBatchConfigError, isLeaseExpired, isMigrationError, isPendingApproval, isPendingHook, isPersistenceError, isResourceCleanupError, isResumeState, isSagaCompensationError, isSerializedResumeState, isStepComplete, isValidationError, isVersionIncompatibleError, isVersionMismatch, isWorkflowCancelled, map, mapAsync, parseVersionedState, pendingApproval, pendingHook, pipe, processInBatches, reduce, requireFields, runSaga, sendWebhookResponse, serializeResumeState, shouldApplyBackpressure, skip, skipWhile, stringifyVersionedState, take, takeWhile, toAsyncIterable, toResumeState, toWebhookRequest, validateInput, validationError, withScope };
724
+ export { AnyResultFn, type BatchConfig, type BatchOptions, type BatchProcessingError, type BatchProgress, ErrorsOfDeps, HOOK_STEP_KEY_PREFIX, type InputValidationError, type InvalidBatchConfigError, PendingHook, type Resource, type ResourceCleanupError, type ResourceScope, UnexpectedError, Workflow, type WorkflowDiagramDSL, type WorkflowDiagramSourceLocation, type WorkflowDiagramState, type WorkflowDiagramStateType, type WorkflowDiagramTransition, WorkflowOptions, batchPresets, createHook, createResource, createResourceScope, createWorkflow, isBatchProcessingError, isInputValidationError, isInvalidBatchConfigError, isResourceCleanupError, pendingHook, processInBatches, validateInput, withScope };