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.
- package/dist/di-Biw_plBn.d.cts +15 -0
- package/dist/di-DxyH2N3i.d.ts +15 -0
- package/dist/durable.cjs +4670 -0
- package/dist/durable.cjs.map +1 -0
- package/dist/durable.d.cts +9 -0
- package/dist/durable.d.ts +9 -0
- package/dist/durable.js +4610 -0
- package/dist/durable.js.map +1 -0
- package/dist/{di-BbFFfO8y.d.ts → duration-BjVn3QpB.d.cts} +1 -15
- package/dist/{di-BDlT7InM.d.cts → duration-BjVn3QpB.d.ts} +1 -15
- package/dist/engine.cjs +4501 -0
- package/dist/engine.cjs.map +1 -0
- package/dist/engine.d.cts +113 -0
- package/dist/engine.d.ts +113 -0
- package/dist/engine.js +4474 -0
- package/dist/engine.js.map +1 -0
- package/dist/{errors-DtXvrCiO.d.cts → errors-sDSeaZBO.d.cts} +1 -1
- package/dist/{errors-DtXvrCiO.d.ts → errors-sDSeaZBO.d.ts} +1 -1
- package/dist/guards-CwQZro1F.d.ts +72 -0
- package/dist/guards-DhhOJZda.d.cts +72 -0
- package/dist/hitl-BnnnLzh2.d.cts +468 -0
- package/dist/hitl-pYfkQMv_.d.ts +468 -0
- package/dist/hitl.cjs +646 -0
- package/dist/hitl.cjs.map +1 -0
- package/dist/hitl.d.cts +440 -0
- package/dist/hitl.d.ts +440 -0
- package/dist/hitl.js +605 -0
- package/dist/hitl.js.map +1 -0
- package/dist/index-Bew12SZ8.d.ts +417 -0
- package/dist/index-Bwt-iz50.d.cts +417 -0
- package/dist/{types-B8NfNRGX.d.ts → index-Cv4K_3sZ.d.ts} +2 -1162
- package/dist/{types-BZ2f4MRR.d.cts → index-ICmfQi08.d.cts} +2 -1162
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -1527
- package/dist/index.d.ts +11 -1527
- package/dist/persistence.cjs +552 -0
- package/dist/persistence.cjs.map +1 -0
- package/dist/persistence.d.cts +254 -0
- package/dist/persistence.d.ts +254 -0
- package/dist/persistence.js +499 -0
- package/dist/persistence.js.map +1 -0
- package/dist/reliability.cjs +1651 -0
- package/dist/reliability.cjs.map +1 -0
- package/dist/reliability.d.cts +1442 -0
- package/dist/reliability.d.ts +1442 -0
- package/dist/reliability.js +1589 -0
- package/dist/reliability.js.map +1 -0
- package/dist/result.d.cts +1 -1
- package/dist/result.d.ts +1 -1
- package/dist/run.cjs +2283 -0
- package/dist/run.cjs.map +1 -0
- package/dist/run.d.cts +90 -0
- package/dist/run.d.ts +90 -0
- package/dist/run.js +2247 -0
- package/dist/run.js.map +1 -0
- package/dist/saga.cjs +3699 -0
- package/dist/saga.cjs.map +1 -0
- package/dist/saga.d.cts +162 -0
- package/dist/saga.d.ts +162 -0
- package/dist/saga.js +3670 -0
- package/dist/saga.js.map +1 -0
- package/dist/store-contract-BI98VYmX.d.ts +66 -0
- package/dist/store-contract-C2HyR_o6.d.cts +66 -0
- package/dist/streaming.cjs +895 -0
- package/dist/streaming.cjs.map +1 -0
- package/dist/streaming.d.cts +594 -0
- package/dist/streaming.d.ts +594 -0
- package/dist/streaming.js +832 -0
- package/dist/streaming.js.map +1 -0
- package/dist/testing.d.cts +4 -2
- package/dist/testing.d.ts +4 -2
- package/dist/types-BDTxgKKc.d.ts +845 -0
- package/dist/types-C9Y71dua.d.cts +845 -0
- package/dist/types-d8q8iQlk.d.cts +323 -0
- package/dist/types-sObbY4mX.d.ts +323 -0
- package/dist/webhook.cjs +461 -0
- package/dist/webhook.cjs.map +1 -0
- package/dist/webhook.d.cts +497 -0
- package/dist/webhook.d.ts +497 -0
- package/dist/webhook.js +422 -0
- package/dist/webhook.js.map +1 -0
- package/dist/workflow.cjs +2 -2614
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +12 -3023
- package/dist/workflow.d.ts +12 -3023
- package/dist/workflow.js +2 -2535
- package/dist/workflow.js.map +1 -1
- package/package.json +46 -1
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import { W as WorkflowEvent, A as AsyncResult, q as Err } from './index-Cv4K_3sZ.js';
|
|
2
|
+
import { R as ResumeState, P as PendingApproval, a as ApprovalStepOptions, A as ApprovalRejected, G as GatedStepOptions } from './types-BDTxgKKc.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resume state helpers: collectors and state manipulation for workflow replay.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Create a collector for step results to build resume state.
|
|
10
|
+
*
|
|
11
|
+
* ## When to Use
|
|
12
|
+
*
|
|
13
|
+
* Use `createResumeStateCollector` when you need to:
|
|
14
|
+
* - **Save workflow state** for later replay/resume
|
|
15
|
+
* - **Persist step results** to a database or file system
|
|
16
|
+
* - **Build resume state** from workflow execution
|
|
17
|
+
* - **Enable workflow replay** after application restarts
|
|
18
|
+
*
|
|
19
|
+
* ## Why Use This Instead of Manual Collection
|
|
20
|
+
*
|
|
21
|
+
* - **Automatic filtering**: Only collects `step_complete` events (ignores other events)
|
|
22
|
+
* - **Metadata preservation**: Captures both result and meta for proper error replay
|
|
23
|
+
* - **Type-safe**: Returns properly typed `ResumeState`
|
|
24
|
+
* - **Convenient API**: Simple `handleEvent` → `getResumeState` pattern
|
|
25
|
+
*
|
|
26
|
+
* ## How It Works
|
|
27
|
+
*
|
|
28
|
+
* 1. Create collector and pass `handleEvent` to workflow's `onEvent` option
|
|
29
|
+
* 2. Workflow emits `step_complete` events for keyed steps
|
|
30
|
+
* 3. Collector automatically captures these events
|
|
31
|
+
* 4. Call `getResumeState()` to get the collected `ResumeState`
|
|
32
|
+
* 5. Persist state (e.g., to database) for later resume
|
|
33
|
+
*
|
|
34
|
+
* ## When step_complete Events Are Emitted
|
|
35
|
+
*
|
|
36
|
+
* Events are emitted for ANY step that has a `key` option, regardless of calling pattern:
|
|
37
|
+
*
|
|
38
|
+
* ```typescript
|
|
39
|
+
* // Function-wrapped pattern - emits step_complete
|
|
40
|
+
* await step(() => fetchUser("1"), { key: "user:1" });
|
|
41
|
+
*
|
|
42
|
+
* // Direct AsyncResult pattern - also emits step_complete
|
|
43
|
+
* await step(fetchUser("1"), { key: "user:1" });
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* Both patterns above will emit `step_complete` events and be captured by the collector.
|
|
47
|
+
*
|
|
48
|
+
* ## Important Notes
|
|
49
|
+
*
|
|
50
|
+
* - Only steps with a `key` option are collected (unkeyed steps are not saved)
|
|
51
|
+
* - The collector preserves error metadata for proper replay behavior
|
|
52
|
+
* - State can be serialized to JSON (but complex cause types may need custom handling)
|
|
53
|
+
*
|
|
54
|
+
* @returns An object with:
|
|
55
|
+
* - `handleEvent`: Function to pass to workflow's `onEvent` option
|
|
56
|
+
* - `getResumeState`: Get collected resume state (call after workflow execution)
|
|
57
|
+
* - `clear`: Clears the collector's internal recorded entries (does not mutate workflow state)
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* // Collect state during workflow execution
|
|
62
|
+
* const collector = createResumeStateCollector();
|
|
63
|
+
*
|
|
64
|
+
* const workflow = createWorkflow({ fetchUser, fetchPosts }, {
|
|
65
|
+
* onEvent: collector.handleEvent, // Pass collector's handler
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* await workflow(async ({ step }) => {
|
|
69
|
+
* // Only keyed steps are collected
|
|
70
|
+
* const user = await step(() => fetchUser("1"), { key: "user:1" });
|
|
71
|
+
* const posts = await step(() => fetchPosts(user.id), { key: `posts:${user.id}` });
|
|
72
|
+
* return { user, posts };
|
|
73
|
+
* });
|
|
74
|
+
*
|
|
75
|
+
* // Get collected state for persistence
|
|
76
|
+
* const state = collector.getResumeState();
|
|
77
|
+
* // state.steps contains: 'user:1' and 'posts:1' entries
|
|
78
|
+
*
|
|
79
|
+
* // Save to database
|
|
80
|
+
* await db.saveWorkflowState(workflowId, state);
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* // Resume workflow from saved state
|
|
86
|
+
* const savedState = await db.loadWorkflowState(workflowId);
|
|
87
|
+
* const workflow = createWorkflow({ fetchUser, fetchPosts }, {
|
|
88
|
+
* resumeState: savedState // Pre-populate cache from saved state
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* // Cached steps skip execution, new steps run normally
|
|
92
|
+
* await workflow(async ({ step }) => {
|
|
93
|
+
* const user = await step(() => fetchUser("1"), { key: "user:1" }); // Cache hit
|
|
94
|
+
* const posts = await step(() => fetchPosts(user.id), { key: `posts:${user.id}` }); // Cache hit
|
|
95
|
+
* return { user, posts };
|
|
96
|
+
* });
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
declare function createResumeStateCollector(): {
|
|
100
|
+
/** Handle workflow events. Pass this to workflow's `onEvent` option. */
|
|
101
|
+
handleEvent: (event: WorkflowEvent<unknown>) => void;
|
|
102
|
+
/** Get the collected resume state. Call after workflow execution. */
|
|
103
|
+
getResumeState: () => ResumeState;
|
|
104
|
+
/** Clears the collector's internal recorded entries (does not mutate workflow state). */
|
|
105
|
+
clear: () => void;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Inject an approved value into resume state.
|
|
109
|
+
* Use this when an external approval is granted and you want to resume the workflow.
|
|
110
|
+
*
|
|
111
|
+
* @param state - The resume state to update
|
|
112
|
+
* @param options - Object with stepKey and the approved value
|
|
113
|
+
* @returns A new ResumeState with the approval injected
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* // When approval is granted externally:
|
|
118
|
+
* const updatedState = injectApproval(savedState, {
|
|
119
|
+
* stepKey: 'deploy:prod',
|
|
120
|
+
* value: { approvedBy: 'admin', approvedAt: Date.now() }
|
|
121
|
+
* });
|
|
122
|
+
*
|
|
123
|
+
* // Resume workflow with the approval injected
|
|
124
|
+
* const workflow = createWorkflow({ ... }, { resumeState: updatedState });
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
declare function injectApproval<T>(state: ResumeState, options: {
|
|
128
|
+
stepKey: string;
|
|
129
|
+
value: T;
|
|
130
|
+
}): ResumeState;
|
|
131
|
+
/**
|
|
132
|
+
* Remove a step from resume state (e.g., to force re-execution).
|
|
133
|
+
* This is an immutable operation - returns a new ResumeState without modifying the original.
|
|
134
|
+
*
|
|
135
|
+
* @param state - The resume state to update
|
|
136
|
+
* @param stepKey - The key of the step to remove
|
|
137
|
+
* @returns A new ResumeState with the step removed (original is unchanged)
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* // Force a step to re-execute on resume
|
|
142
|
+
* const updatedState = clearStep(savedState, 'approval:123');
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
declare function clearStep(state: ResumeState, stepKey: string): ResumeState;
|
|
146
|
+
/**
|
|
147
|
+
* Check if a step in resume state has a pending approval error.
|
|
148
|
+
*
|
|
149
|
+
* @param state - The resume state to check
|
|
150
|
+
* @param stepKey - The key of the step to check
|
|
151
|
+
* @returns `true` if the step has a pending approval, `false` otherwise
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* if (hasPendingApproval(savedState, 'deploy:prod')) {
|
|
156
|
+
* // Show approval UI
|
|
157
|
+
* }
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
declare function hasPendingApproval(state: ResumeState, stepKey: string): boolean;
|
|
161
|
+
/**
|
|
162
|
+
* Get all pending approval step keys from resume state.
|
|
163
|
+
*
|
|
164
|
+
* @param state - The resume state to check
|
|
165
|
+
* @returns Array of step keys that have pending approvals
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* const pendingKeys = getPendingApprovals(savedState);
|
|
170
|
+
* // ['deploy:prod', 'deploy:staging']
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
declare function getPendingApprovals(state: ResumeState): string[];
|
|
174
|
+
/**
|
|
175
|
+
* Inject a hook callback value into resume state.
|
|
176
|
+
* Call this when the app receives the HTTP callback (e.g. POST /hook/:hookId) and pass the request body as value.
|
|
177
|
+
*
|
|
178
|
+
* @param state - The resume state to update
|
|
179
|
+
* @param options - hookId (from the callback URL) and value (e.g. request body)
|
|
180
|
+
* @returns A new ResumeState with the hook step set to ok(value)
|
|
181
|
+
*/
|
|
182
|
+
declare function injectHook<T>(state: ResumeState, options: {
|
|
183
|
+
hookId: string;
|
|
184
|
+
value: T;
|
|
185
|
+
}): ResumeState;
|
|
186
|
+
/**
|
|
187
|
+
* Check if a step in resume state has a pending hook error.
|
|
188
|
+
*
|
|
189
|
+
* @param state - The resume state to check
|
|
190
|
+
* @param hookId - The hook id (from createHook() or the callback URL)
|
|
191
|
+
* @returns `true` if that hook step is pending, `false` otherwise
|
|
192
|
+
*/
|
|
193
|
+
declare function hasPendingHook(state: ResumeState, hookId: string): boolean;
|
|
194
|
+
/**
|
|
195
|
+
* Get all pending hook hookIds from resume state.
|
|
196
|
+
*
|
|
197
|
+
* @param state - The resume state to check
|
|
198
|
+
* @returns Array of hookIds that have pending callbacks
|
|
199
|
+
*/
|
|
200
|
+
declare function getPendingHooks(state: ResumeState): string[];
|
|
201
|
+
/**
|
|
202
|
+
* Extended resume state collector that tracks pending approvals.
|
|
203
|
+
* Use this for human-in-the-loop workflows that need to track approval state.
|
|
204
|
+
*
|
|
205
|
+
* @returns An object with methods to handle events, get state, and manage approvals
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* const collector = createApprovalStateCollector();
|
|
210
|
+
*
|
|
211
|
+
* const workflow = createWorkflow({ fetchUser, requireApproval }, {
|
|
212
|
+
* onEvent: collector.handleEvent,
|
|
213
|
+
* });
|
|
214
|
+
*
|
|
215
|
+
* const result = await workflow(async ({ step }) => {
|
|
216
|
+
* const user = await step(() => fetchUser("1"), { key: "user:1" });
|
|
217
|
+
* const approval = await step(requireApproval, { key: "approval:1" });
|
|
218
|
+
* return { user, approval };
|
|
219
|
+
* });
|
|
220
|
+
*
|
|
221
|
+
* // Check for pending approvals
|
|
222
|
+
* if (collector.hasPendingApprovals()) {
|
|
223
|
+
* const pending = collector.getPendingApprovals();
|
|
224
|
+
* // pending: [{ stepKey: 'approval:1', error: PendingApproval }]
|
|
225
|
+
* await saveToDatabase(collector.getResumeState());
|
|
226
|
+
* }
|
|
227
|
+
*
|
|
228
|
+
* // Later, when approved:
|
|
229
|
+
* const resumeState = collector.injectApproval('approval:1', { approvedBy: 'admin' });
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
declare function createApprovalStateCollector(): {
|
|
233
|
+
/** Handle workflow events. Pass this to workflow's `onEvent` option. */
|
|
234
|
+
handleEvent: (event: WorkflowEvent<unknown>) => void;
|
|
235
|
+
/** Get the collected resume state. Call after workflow execution. */
|
|
236
|
+
getResumeState: () => ResumeState;
|
|
237
|
+
/** Clears the collector's internal recorded entries (does not mutate workflow state). */
|
|
238
|
+
clear: () => void;
|
|
239
|
+
/** Check if any steps have pending approvals */
|
|
240
|
+
hasPendingApprovals: () => boolean;
|
|
241
|
+
/** Get all pending approval entries with their errors */
|
|
242
|
+
getPendingApprovals: () => Array<{
|
|
243
|
+
stepKey: string;
|
|
244
|
+
error: PendingApproval;
|
|
245
|
+
}>;
|
|
246
|
+
/** Inject an approval result, updating the collector's internal state. Returns a copy for use as resumeState. */
|
|
247
|
+
injectApproval: <T>(stepKey: string, value: T) => ResumeState;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Human-in-the-Loop (HITL) helpers: pendingApproval, createApprovalStep, gatedStep.
|
|
252
|
+
*/
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Create a PendingApproval error result.
|
|
256
|
+
* Convenience helper for approval-gated steps.
|
|
257
|
+
*
|
|
258
|
+
* @param stepKey - Stable key for this approval step (used for resume)
|
|
259
|
+
* @param options - Optional reason and metadata for the pending approval
|
|
260
|
+
* @returns A Result with a PendingApproval error
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```typescript
|
|
264
|
+
* const requireApproval = async (userId: string) => {
|
|
265
|
+
* const status = await db.getApproval(userId);
|
|
266
|
+
* if (!status) return pendingApproval(`approval:${userId}`);
|
|
267
|
+
* return ok(status);
|
|
268
|
+
* };
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
declare function pendingApproval(stepKey: string, options?: {
|
|
272
|
+
reason?: string;
|
|
273
|
+
metadata?: Record<string, unknown>;
|
|
274
|
+
}): Err<PendingApproval>;
|
|
275
|
+
/**
|
|
276
|
+
* Create a Result-returning function that checks external approval status.
|
|
277
|
+
*
|
|
278
|
+
* ## When to Use
|
|
279
|
+
*
|
|
280
|
+
* Use `createApprovalStep` when you need:
|
|
281
|
+
* - **Human-in-the-loop workflows**: Steps that require human approval
|
|
282
|
+
* - **External approval systems**: Integrate with approval databases/APIs
|
|
283
|
+
* - **Workflow pausing**: Workflows that pause and resume after approval
|
|
284
|
+
* - **Approval tracking**: Track who approved what and when
|
|
285
|
+
*
|
|
286
|
+
* ## Why Use This Instead of Manual Approval Checks
|
|
287
|
+
*
|
|
288
|
+
* - **Standardized pattern**: Consistent approval step interface
|
|
289
|
+
* - **Type-safe**: Returns typed `PendingApproval` or `ApprovalRejected` errors
|
|
290
|
+
* - **Resume-friendly**: Works seamlessly with `injectApproval()` and resume state
|
|
291
|
+
* - **Metadata support**: Can include approval reason and metadata
|
|
292
|
+
*
|
|
293
|
+
* ## How It Works
|
|
294
|
+
*
|
|
295
|
+
* 1. Create approval step with `checkApproval` function
|
|
296
|
+
* 2. `checkApproval` returns one of:
|
|
297
|
+
* - `{ status: 'pending' }` - Approval not yet granted (workflow pauses)
|
|
298
|
+
* - `{ status: 'approved', value: T }` - Approval granted (workflow continues)
|
|
299
|
+
* - `{ status: 'rejected', reason: string }` - Approval rejected (workflow fails)
|
|
300
|
+
* 3. Use in workflow with `step()` - workflow pauses if pending
|
|
301
|
+
* 4. When approval granted externally, use `injectApproval()` to resume
|
|
302
|
+
*
|
|
303
|
+
* ## Typical Approval Flow
|
|
304
|
+
*
|
|
305
|
+
* 1. Workflow executes → reaches approval step
|
|
306
|
+
* 2. `checkApproval()` called → returns `{ status: 'pending' }`
|
|
307
|
+
* 3. Workflow returns `PendingApproval` error
|
|
308
|
+
* 4. Save workflow state → persist for later resume
|
|
309
|
+
* 5. Show approval UI → user sees pending approval
|
|
310
|
+
* 6. User grants/rejects → update approval system
|
|
311
|
+
* 7. Inject approval → call `injectApproval()` with approved value
|
|
312
|
+
* 8. Resume workflow → continue from approval step
|
|
313
|
+
*
|
|
314
|
+
* @param options - Configuration for the approval step:
|
|
315
|
+
* - `key`: Stable key for this approval (must match step key in workflow)
|
|
316
|
+
* - `checkApproval`: Async function that checks current approval status
|
|
317
|
+
* - `pendingReason`: Optional reason shown when approval is pending
|
|
318
|
+
* - `metadata`: Optional metadata attached to the approval request
|
|
319
|
+
*
|
|
320
|
+
* @returns A function that returns an AsyncResult checking approval status.
|
|
321
|
+
* The function can be used directly with `step()` in workflows.
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```typescript
|
|
325
|
+
* // Create approval step that checks database
|
|
326
|
+
* const requireManagerApproval = createApprovalStep<{ approvedBy: string }>({
|
|
327
|
+
* key: 'manager-approval',
|
|
328
|
+
* checkApproval: async () => {
|
|
329
|
+
* const approval = await db.getApproval('manager-approval');
|
|
330
|
+
* if (!approval) {
|
|
331
|
+
* return { status: 'pending' }; // Workflow pauses here
|
|
332
|
+
* }
|
|
333
|
+
* if (approval.rejected) {
|
|
334
|
+
* return { status: 'rejected', reason: approval.reason };
|
|
335
|
+
* }
|
|
336
|
+
* return {
|
|
337
|
+
* status: 'approved',
|
|
338
|
+
* value: { approvedBy: approval.approvedBy }
|
|
339
|
+
* };
|
|
340
|
+
* },
|
|
341
|
+
* pendingReason: 'Waiting for manager approval',
|
|
342
|
+
* });
|
|
343
|
+
*
|
|
344
|
+
* // Use in workflow
|
|
345
|
+
* const workflow = createWorkflow({ requireManagerApproval });
|
|
346
|
+
* const result = await workflow(async ({ step }) => {
|
|
347
|
+
* const approval = await step(requireManagerApproval, { key: 'manager-approval' });
|
|
348
|
+
* // If pending, workflow exits with PendingApproval error
|
|
349
|
+
* // If approved, continues with approval value
|
|
350
|
+
* return approval;
|
|
351
|
+
* });
|
|
352
|
+
*
|
|
353
|
+
* // Handle pending state
|
|
354
|
+
* if (!result.ok && isPendingApproval(result.error)) {
|
|
355
|
+
* // Workflow paused - show approval UI
|
|
356
|
+
* showApprovalUI(result.error.stepKey);
|
|
357
|
+
* }
|
|
358
|
+
* ```
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* ```typescript
|
|
362
|
+
* // With approval injection for resume
|
|
363
|
+
* const collector = createApprovalStateCollector();
|
|
364
|
+
* const workflow = createWorkflow({ requireApproval }, {
|
|
365
|
+
* onEvent: collector.handleEvent,
|
|
366
|
+
* });
|
|
367
|
+
*
|
|
368
|
+
* const result = await workflow(async ({ step }) => {
|
|
369
|
+
* const approval = await step(requireApproval, { key: 'approval:1' });
|
|
370
|
+
* return approval;
|
|
371
|
+
* });
|
|
372
|
+
*
|
|
373
|
+
* // When approval granted externally
|
|
374
|
+
* if (collector.hasPendingApprovals()) {
|
|
375
|
+
* const resumeState = collector.injectApproval('approval:1', {
|
|
376
|
+
* approvedBy: 'admin@example.com'
|
|
377
|
+
* });
|
|
378
|
+
*
|
|
379
|
+
* // Resume workflow
|
|
380
|
+
* const workflow2 = createWorkflow({ requireApproval }, { resumeState });
|
|
381
|
+
* const result2 = await workflow2(async ({ step }) => {
|
|
382
|
+
* const approval = await step(requireApproval, { key: 'approval:1' });
|
|
383
|
+
* return approval; // Now succeeds with injected value
|
|
384
|
+
* });
|
|
385
|
+
* }
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
declare function createApprovalStep<T>(options: ApprovalStepOptions<T>): () => AsyncResult<T, PendingApproval | ApprovalRejected>;
|
|
389
|
+
/**
|
|
390
|
+
* Create a gated step that requires approval before execution.
|
|
391
|
+
*
|
|
392
|
+
* This is the AI SDK / LangChain-style pattern where you intercept
|
|
393
|
+
* tool calls *before* they execute, allowing humans to see the args
|
|
394
|
+
* and approve, edit, or reject before the operation runs.
|
|
395
|
+
*
|
|
396
|
+
* ## When to Use
|
|
397
|
+
*
|
|
398
|
+
* Use `gatedStep` when you want to:
|
|
399
|
+
* - **Show args before execution**: Let humans see what the operation will do
|
|
400
|
+
* - **Allow editing args**: Humans can modify args before operation runs
|
|
401
|
+
* - **Conditional gating**: Only require approval for certain conditions
|
|
402
|
+
* - **AI safety**: Gate dangerous AI tool calls (send email, delete file, etc.)
|
|
403
|
+
*
|
|
404
|
+
* ## Difference from createApprovalStep
|
|
405
|
+
*
|
|
406
|
+
* - `createApprovalStep`: Checks external approval status, operation already defined
|
|
407
|
+
* - `gatedStep`: Gates before operation, shows args, allows editing, then executes
|
|
408
|
+
*
|
|
409
|
+
* ## Flow
|
|
410
|
+
*
|
|
411
|
+
* 1. Call gatedStep with args
|
|
412
|
+
* 2. Check if approval is required (based on requiresApproval condition)
|
|
413
|
+
* 3. If required and not approved:
|
|
414
|
+
* - Return PendingApproval with args visible in metadata
|
|
415
|
+
* - Human sees: "Send email to external@example.com with subject X"
|
|
416
|
+
* - Human can approve (run as-is), edit (modify args), or reject
|
|
417
|
+
* 4. If approved or not required:
|
|
418
|
+
* - Execute the operation with (potentially edited) args
|
|
419
|
+
*
|
|
420
|
+
* @param operation - The operation to gate (a function returning AsyncResult)
|
|
421
|
+
* @param options - Gating configuration
|
|
422
|
+
* @returns A gated function that checks approval before execution
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* ```typescript
|
|
426
|
+
* // Gate external email sends
|
|
427
|
+
* const sendEmail = async (to: string, subject: string, body: string) => { ... };
|
|
428
|
+
*
|
|
429
|
+
* const gatedSendEmail = gatedStep(
|
|
430
|
+
* sendEmail,
|
|
431
|
+
* {
|
|
432
|
+
* key: 'email',
|
|
433
|
+
* requiresApproval: (args) => !args.to.endsWith('@mycompany.com'),
|
|
434
|
+
* description: (args) => `Send email to ${args.to}: "${args.subject}"`,
|
|
435
|
+
* }
|
|
436
|
+
* );
|
|
437
|
+
*
|
|
438
|
+
* // In workflow:
|
|
439
|
+
* const result = await step(
|
|
440
|
+
* () => gatedSendEmail({ to: 'external@other.com', subject: 'Hello', body: '...' }),
|
|
441
|
+
* { key: 'send-welcome-email' }
|
|
442
|
+
* );
|
|
443
|
+
*
|
|
444
|
+
* // If gated, returns PendingApproval with:
|
|
445
|
+
* // {
|
|
446
|
+
* // stepKey: 'email',
|
|
447
|
+
* // reason: 'Send email to external@other.com: "Hello"',
|
|
448
|
+
* // metadata: { pendingArgs: { to: '...', subject: '...', body: '...' } }
|
|
449
|
+
* // }
|
|
450
|
+
* ```
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* ```typescript
|
|
454
|
+
* // Gate file deletion with explicit approval check
|
|
455
|
+
* const gatedDelete = gatedStep(
|
|
456
|
+
* (path: string) => deleteFile(path),
|
|
457
|
+
* {
|
|
458
|
+
* key: 'delete-file',
|
|
459
|
+
* requiresApproval: true, // Always require approval
|
|
460
|
+
* description: (args) => `Delete file: ${args.path}`,
|
|
461
|
+
* checkApproval: () => approvalStore.getApproval('delete-file'),
|
|
462
|
+
* }
|
|
463
|
+
* );
|
|
464
|
+
* ```
|
|
465
|
+
*/
|
|
466
|
+
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>;
|
|
467
|
+
|
|
468
|
+
export { createApprovalStateCollector as a, createApprovalStep as b, clearStep as c, getPendingApprovals as d, createResumeStateCollector as e, getPendingHooks as f, gatedStep as g, hasPendingApproval as h, injectApproval as i, hasPendingHook as j, injectHook as k, pendingApproval as p };
|