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
package/dist/hitl.cjs ADDED
@@ -0,0 +1,646 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/hitl-entry.ts
21
+ var hitl_entry_exports = {};
22
+ __export(hitl_entry_exports, {
23
+ clearStep: () => clearStep,
24
+ createApprovalChecker: () => createApprovalChecker,
25
+ createApprovalStateCollector: () => createApprovalStateCollector,
26
+ createApprovalStep: () => createApprovalStep,
27
+ createApprovalWebhookHandler: () => createApprovalWebhookHandler,
28
+ createHITLOrchestrator: () => createHITLOrchestrator,
29
+ createMemoryApprovalStore: () => createMemoryApprovalStore,
30
+ createMemoryWorkflowStateStore: () => createMemoryWorkflowStateStore,
31
+ gatedStep: () => gatedStep,
32
+ getPendingApprovals: () => getPendingApprovals,
33
+ hasPendingApproval: () => hasPendingApproval,
34
+ injectApproval: () => injectApproval,
35
+ isApprovalRejected: () => isApprovalRejected,
36
+ isPendingApproval: () => isPendingApproval,
37
+ pendingApproval: () => pendingApproval
38
+ });
39
+ module.exports = __toCommonJS(hitl_entry_exports);
40
+
41
+ // src/core/index.ts
42
+ function ok(value) {
43
+ return { ok: true, value };
44
+ }
45
+ function err(error, options) {
46
+ const cause = options?.cause;
47
+ return { ok: false, error, ...cause !== void 0 ? { cause } : {} };
48
+ }
49
+
50
+ // src/workflow/guards.ts
51
+ function isStepComplete(event) {
52
+ return event.type === "step_complete";
53
+ }
54
+ function isPendingApproval(error) {
55
+ return typeof error === "object" && error !== null && error.type === "PENDING_APPROVAL";
56
+ }
57
+ function isApprovalRejected(error) {
58
+ return typeof error === "object" && error !== null && error.type === "APPROVAL_REJECTED";
59
+ }
60
+
61
+ // src/workflow/resume-state.ts
62
+ function injectApproval(state, options) {
63
+ const newSteps = new Map(state.steps);
64
+ newSteps.set(options.stepKey, {
65
+ result: ok(options.value)
66
+ });
67
+ return { steps: newSteps };
68
+ }
69
+ function clearStep(state, stepKey) {
70
+ const newSteps = new Map(state.steps);
71
+ newSteps.delete(stepKey);
72
+ return { steps: newSteps };
73
+ }
74
+ function hasPendingApproval(state, stepKey) {
75
+ const entry = state.steps.get(stepKey);
76
+ if (!entry || entry.result.ok) return false;
77
+ return isPendingApproval(entry.result.error);
78
+ }
79
+ function getPendingApprovals(state) {
80
+ const pending = [];
81
+ for (const [key, entry] of state.steps) {
82
+ if (!entry.result.ok && isPendingApproval(entry.result.error)) {
83
+ pending.push(key);
84
+ }
85
+ }
86
+ return pending;
87
+ }
88
+ function createApprovalStateCollector() {
89
+ const steps = /* @__PURE__ */ new Map();
90
+ return {
91
+ handleEvent: (event) => {
92
+ if (isStepComplete(event)) {
93
+ steps.set(event.stepKey, { result: event.result, meta: event.meta });
94
+ }
95
+ },
96
+ getResumeState: () => ({ steps: new Map(steps) }),
97
+ clear: () => steps.clear(),
98
+ hasPendingApprovals: () => {
99
+ for (const entry of steps.values()) {
100
+ if (!entry.result.ok && isPendingApproval(entry.result.error)) {
101
+ return true;
102
+ }
103
+ }
104
+ return false;
105
+ },
106
+ getPendingApprovals: () => {
107
+ const pending = [];
108
+ for (const [key, entry] of steps) {
109
+ if (!entry.result.ok && isPendingApproval(entry.result.error)) {
110
+ pending.push({ stepKey: key, error: entry.result.error });
111
+ }
112
+ }
113
+ return pending;
114
+ },
115
+ injectApproval: (stepKey, value) => {
116
+ steps.set(stepKey, { result: ok(value) });
117
+ return { steps: new Map(steps) };
118
+ }
119
+ };
120
+ }
121
+
122
+ // src/workflow/hitl.ts
123
+ function pendingApproval(stepKey, options) {
124
+ return err({
125
+ type: "PENDING_APPROVAL",
126
+ stepKey,
127
+ reason: options?.reason,
128
+ metadata: options?.metadata
129
+ });
130
+ }
131
+ function createApprovalStep(options) {
132
+ return async () => {
133
+ const result = await options.checkApproval();
134
+ switch (result.status) {
135
+ case "pending":
136
+ return err({
137
+ type: "PENDING_APPROVAL",
138
+ stepKey: options.key,
139
+ reason: options.pendingReason,
140
+ metadata: options.metadata
141
+ });
142
+ case "rejected":
143
+ return err({
144
+ type: "APPROVAL_REJECTED",
145
+ stepKey: options.key,
146
+ reason: result.reason
147
+ });
148
+ case "approved":
149
+ return ok(result.value);
150
+ }
151
+ };
152
+ }
153
+ function gatedStep(operation, options) {
154
+ return async (args) => {
155
+ const requiresApproval = typeof options.requiresApproval === "function" ? await options.requiresApproval(args) : options.requiresApproval;
156
+ if (!requiresApproval) {
157
+ return operation(args);
158
+ }
159
+ if (options.checkApproval) {
160
+ const approvalStatus = await options.checkApproval();
161
+ switch (approvalStatus.status) {
162
+ case "approved":
163
+ return operation(args);
164
+ case "rejected":
165
+ return err({
166
+ type: "APPROVAL_REJECTED",
167
+ stepKey: options.key,
168
+ reason: approvalStatus.reason
169
+ });
170
+ case "pending":
171
+ break;
172
+ }
173
+ }
174
+ const description = typeof options.description === "function" ? options.description(args) : options.description;
175
+ return err({
176
+ type: "PENDING_APPROVAL",
177
+ stepKey: options.key,
178
+ reason: description,
179
+ metadata: {
180
+ ...options.metadata,
181
+ pendingArgs: args,
182
+ gatedOperation: true
183
+ }
184
+ });
185
+ };
186
+ }
187
+
188
+ // src/hitl/index.ts
189
+ function createMemoryApprovalStore() {
190
+ const approvals = /* @__PURE__ */ new Map();
191
+ return {
192
+ async getApproval(key) {
193
+ const approval = approvals.get(key);
194
+ if (!approval) {
195
+ return { status: "pending" };
196
+ }
197
+ if (approval.expiresAt && Date.now() > approval.expiresAt) {
198
+ return { status: "expired", expiredAt: approval.expiresAt };
199
+ }
200
+ return approval;
201
+ },
202
+ async createApproval(key, options) {
203
+ approvals.set(key, {
204
+ status: "pending",
205
+ metadata: options?.metadata,
206
+ expiresAt: options?.expiresAt
207
+ });
208
+ },
209
+ async grantApproval(key, value, options) {
210
+ approvals.set(key, {
211
+ status: "approved",
212
+ value,
213
+ approvedBy: options?.approvedBy,
214
+ approvedAt: Date.now()
215
+ });
216
+ },
217
+ async rejectApproval(key, reason, options) {
218
+ approvals.set(key, {
219
+ status: "rejected",
220
+ reason,
221
+ rejectedBy: options?.rejectedBy,
222
+ rejectedAt: Date.now()
223
+ });
224
+ },
225
+ async editApproval(key, originalValue, editedValue, options) {
226
+ approvals.set(key, {
227
+ status: "edited",
228
+ originalValue,
229
+ editedValue,
230
+ editedBy: options?.editedBy,
231
+ editedAt: Date.now()
232
+ });
233
+ },
234
+ async cancelApproval(key) {
235
+ approvals.delete(key);
236
+ },
237
+ async listPending(options) {
238
+ const pending = [];
239
+ for (const [key, approval] of approvals) {
240
+ if (approval.status === "pending") {
241
+ if (!options?.prefix || key.startsWith(options.prefix)) {
242
+ pending.push(key);
243
+ }
244
+ }
245
+ }
246
+ return pending;
247
+ }
248
+ };
249
+ }
250
+ function createMemoryWorkflowStateStore() {
251
+ const states = /* @__PURE__ */ new Map();
252
+ return {
253
+ async save(state) {
254
+ states.set(state.runId, { ...state, updatedAt: Date.now() });
255
+ },
256
+ async load(runId) {
257
+ return states.get(runId);
258
+ },
259
+ async delete(runId) {
260
+ states.delete(runId);
261
+ },
262
+ async list(options) {
263
+ const results = [];
264
+ for (const [runId, state] of states) {
265
+ if (options?.workflowName && state.workflowName !== options.workflowName) {
266
+ continue;
267
+ }
268
+ if (options?.hasPendingApprovals !== void 0) {
269
+ const hasPending = state.pendingApprovals.length > 0;
270
+ if (options.hasPendingApprovals !== hasPending) {
271
+ continue;
272
+ }
273
+ }
274
+ results.push(runId);
275
+ }
276
+ return results;
277
+ },
278
+ async findByPendingApproval(approvalKey) {
279
+ const results = [];
280
+ for (const [runId, state] of states) {
281
+ if (state.pendingApprovals.includes(approvalKey)) {
282
+ results.push(runId);
283
+ }
284
+ }
285
+ return results;
286
+ }
287
+ };
288
+ }
289
+ function createHITLOrchestrator(options) {
290
+ const {
291
+ approvalStore,
292
+ workflowStateStore,
293
+ defaultExpirationMs = 7 * 24 * 60 * 60 * 1e3,
294
+ // 7 days
295
+ logger = () => {
296
+ },
297
+ notificationChannel
298
+ } = options;
299
+ async function execute(workflowName, workflowFactory, workflowFn, input, opts) {
300
+ const runId = opts?.runId ?? crypto.randomUUID();
301
+ const collector = createApprovalStateCollector();
302
+ const workflow = workflowFactory({
303
+ onEvent: collector.handleEvent
304
+ });
305
+ const args = input;
306
+ const result = await workflow.run(
307
+ async (context) => workflowFn({ ...context, args })
308
+ );
309
+ const pendingApprovals = collector.getPendingApprovals().map((p) => p.stepKey);
310
+ if (pendingApprovals.length > 0) {
311
+ const state = {
312
+ runId,
313
+ workflowName,
314
+ resumeState: collector.getResumeState(),
315
+ pendingApprovals,
316
+ input,
317
+ metadata: opts?.metadata,
318
+ startedAt: Date.now(),
319
+ updatedAt: Date.now()
320
+ };
321
+ await workflowStateStore.save(state);
322
+ let reason;
323
+ let pendingMetadata;
324
+ if (!result.ok && isPendingApproval(result.error)) {
325
+ reason = result.error.reason;
326
+ pendingMetadata = result.error.metadata;
327
+ }
328
+ const expiresAt = Date.now() + defaultExpirationMs;
329
+ for (const key of pendingApprovals) {
330
+ await approvalStore.createApproval(key, {
331
+ metadata: { ...pendingMetadata, runId, workflowName },
332
+ expiresAt
333
+ });
334
+ }
335
+ if (notificationChannel) {
336
+ for (const key of pendingApprovals) {
337
+ try {
338
+ const pendingInfo = collector.getPendingApprovals().find((p) => p.stepKey === key);
339
+ await notificationChannel.onApprovalNeeded({
340
+ approvalKey: key,
341
+ runId,
342
+ workflowName,
343
+ reason,
344
+ metadata: { ...opts?.metadata, ...pendingMetadata },
345
+ expiresAt,
346
+ summary: reason ?? `Approval needed for ${key}`,
347
+ pendingArgs: pendingInfo?.error.metadata
348
+ });
349
+ } catch (notifyError) {
350
+ logger(`Failed to notify for approval ${key}: ${notifyError}`);
351
+ }
352
+ }
353
+ }
354
+ logger(`Workflow ${runId} paused, waiting for: ${pendingApprovals.join(", ")}`);
355
+ return {
356
+ status: "paused",
357
+ runId,
358
+ pendingApprovals,
359
+ reason
360
+ };
361
+ }
362
+ logger(`Workflow ${runId} completed`);
363
+ return { status: "completed", result };
364
+ }
365
+ async function resume(runId, workflowFactory, workflowFn) {
366
+ const savedState = await workflowStateStore.load(runId);
367
+ if (!savedState) {
368
+ throw new Error(`Workflow run not found: ${runId}`);
369
+ }
370
+ for (const key of savedState.pendingApprovals) {
371
+ const status = await approvalStore.getApproval(key);
372
+ if (status.status === "pending") {
373
+ return {
374
+ status: "paused",
375
+ runId,
376
+ pendingApprovals: savedState.pendingApprovals
377
+ };
378
+ }
379
+ if (status.status === "rejected") {
380
+ }
381
+ if (status.status === "expired") {
382
+ throw new Error(`Approval ${key} has expired`);
383
+ }
384
+ }
385
+ let resumeState = savedState.resumeState;
386
+ for (const key of savedState.pendingApprovals) {
387
+ const status = await approvalStore.getApproval(key);
388
+ if (status.status === "approved") {
389
+ resumeState = injectApproval(resumeState, {
390
+ stepKey: key,
391
+ value: status.value
392
+ });
393
+ } else if (status.status === "edited") {
394
+ resumeState = injectApproval(resumeState, {
395
+ stepKey: key,
396
+ value: status.editedValue
397
+ });
398
+ }
399
+ }
400
+ const collector = createApprovalStateCollector();
401
+ const workflow = workflowFactory({
402
+ resumeState,
403
+ onEvent: collector.handleEvent
404
+ });
405
+ const args = savedState.input;
406
+ const result = await workflow.run(
407
+ async (context) => workflowFn({ ...context, args })
408
+ );
409
+ const newPendingApprovals = collector.getPendingApprovals().map((p) => p.stepKey);
410
+ if (newPendingApprovals.length > 0) {
411
+ const updatedState = {
412
+ ...savedState,
413
+ resumeState: collector.getResumeState(),
414
+ pendingApprovals: newPendingApprovals,
415
+ updatedAt: Date.now()
416
+ };
417
+ await workflowStateStore.save(updatedState);
418
+ for (const key of newPendingApprovals) {
419
+ await approvalStore.createApproval(key, {
420
+ metadata: { runId, workflowName: savedState.workflowName },
421
+ expiresAt: Date.now() + defaultExpirationMs
422
+ });
423
+ }
424
+ logger(`Workflow ${runId} paused again, waiting for: ${newPendingApprovals.join(", ")}`);
425
+ let reason;
426
+ if (!result.ok && isPendingApproval(result.error)) {
427
+ reason = result.error.reason;
428
+ }
429
+ return {
430
+ status: "paused",
431
+ runId,
432
+ pendingApprovals: newPendingApprovals,
433
+ reason
434
+ };
435
+ }
436
+ await workflowStateStore.delete(runId);
437
+ logger(`Workflow ${runId} resumed and completed`);
438
+ return { status: "resumed", runId, result };
439
+ }
440
+ async function grantApprovalFn(approvalKey, value, opts) {
441
+ await approvalStore.grantApproval(approvalKey, value, {
442
+ approvedBy: opts?.approvedBy
443
+ });
444
+ const grantedAt = Date.now();
445
+ const resumedWorkflows = [];
446
+ if (opts?.autoResume !== false) {
447
+ const waitingWorkflows = await workflowStateStore.findByPendingApproval(approvalKey);
448
+ for (const runId of waitingWorkflows) {
449
+ resumedWorkflows.push(runId);
450
+ }
451
+ }
452
+ if (notificationChannel?.onApprovalResolved) {
453
+ await notificationChannel.onApprovalResolved({
454
+ approvalKey,
455
+ action: "approved",
456
+ actorId: opts?.approvedBy,
457
+ resolvedAt: grantedAt,
458
+ value
459
+ });
460
+ }
461
+ logger(`Approval ${approvalKey} granted by ${opts?.approvedBy ?? "unknown"}`);
462
+ return { grantedAt, resumedWorkflows };
463
+ }
464
+ async function rejectApprovalFn(approvalKey, reason, opts) {
465
+ await approvalStore.rejectApproval(approvalKey, reason, {
466
+ rejectedBy: opts?.rejectedBy
467
+ });
468
+ if (notificationChannel?.onApprovalResolved) {
469
+ await notificationChannel.onApprovalResolved({
470
+ approvalKey,
471
+ action: "rejected",
472
+ actorId: opts?.rejectedBy,
473
+ resolvedAt: Date.now(),
474
+ reason
475
+ });
476
+ }
477
+ logger(`Approval ${approvalKey} rejected: ${reason}`);
478
+ }
479
+ async function editApprovalFn(approvalKey, originalValue, editedValue, opts) {
480
+ await approvalStore.editApproval(approvalKey, originalValue, editedValue, {
481
+ editedBy: opts?.editedBy
482
+ });
483
+ const editedAt = Date.now();
484
+ if (notificationChannel?.onApprovalResolved) {
485
+ await notificationChannel.onApprovalResolved({
486
+ approvalKey,
487
+ action: "edited",
488
+ actorId: opts?.editedBy,
489
+ resolvedAt: editedAt,
490
+ value: editedValue,
491
+ originalValue
492
+ });
493
+ }
494
+ logger(`Approval ${approvalKey} edited by ${opts?.editedBy ?? "unknown"}`);
495
+ return { editedAt };
496
+ }
497
+ async function pollApproval(approvalKey, opts) {
498
+ const {
499
+ intervalMs = 1e3,
500
+ maxPolls,
501
+ timeoutMs,
502
+ onPollStart,
503
+ onPollComplete
504
+ } = opts ?? {};
505
+ const startTime = Date.now();
506
+ let pollCount = 0;
507
+ while (true) {
508
+ onPollStart?.();
509
+ const status = await approvalStore.getApproval(approvalKey);
510
+ onPollComplete?.(status);
511
+ if (status.status !== "pending") {
512
+ return status;
513
+ }
514
+ pollCount++;
515
+ if (maxPolls !== void 0 && pollCount >= maxPolls) {
516
+ return { status: "pending" };
517
+ }
518
+ if (timeoutMs !== void 0 && Date.now() - startTime >= timeoutMs) {
519
+ return { status: "pending" };
520
+ }
521
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
522
+ }
523
+ }
524
+ async function getWorkflowStatus(runId) {
525
+ return workflowStateStore.load(runId);
526
+ }
527
+ async function listPendingWorkflows(workflowName) {
528
+ return workflowStateStore.list({
529
+ workflowName,
530
+ hasPendingApprovals: true
531
+ });
532
+ }
533
+ async function cleanup(maxAgeMs) {
534
+ const allWorkflows = await workflowStateStore.list();
535
+ let cleaned = 0;
536
+ const cutoff = Date.now() - maxAgeMs;
537
+ for (const runId of allWorkflows) {
538
+ const state = await workflowStateStore.load(runId);
539
+ if (state && state.updatedAt < cutoff && state.pendingApprovals.length === 0) {
540
+ await workflowStateStore.delete(runId);
541
+ cleaned++;
542
+ }
543
+ }
544
+ logger(`Cleaned up ${cleaned} old workflow states`);
545
+ return cleaned;
546
+ }
547
+ return {
548
+ execute,
549
+ resume,
550
+ grantApproval: grantApprovalFn,
551
+ rejectApproval: rejectApprovalFn,
552
+ editApproval: editApprovalFn,
553
+ pollApproval,
554
+ getWorkflowStatus,
555
+ listPendingWorkflows,
556
+ cleanup
557
+ };
558
+ }
559
+ function createApprovalWebhookHandler(store) {
560
+ return async (request) => {
561
+ const { key, action, value, originalValue, editedValue, reason, actorId } = request;
562
+ try {
563
+ switch (action) {
564
+ case "approve":
565
+ await store.grantApproval(key, value, { approvedBy: actorId });
566
+ return {
567
+ success: true,
568
+ message: `Approval ${key} granted`,
569
+ data: { key, action, timestamp: Date.now() }
570
+ };
571
+ case "reject":
572
+ if (!reason) {
573
+ return { success: false, message: "Reason is required for rejection" };
574
+ }
575
+ await store.rejectApproval(key, reason, { rejectedBy: actorId });
576
+ return {
577
+ success: true,
578
+ message: `Approval ${key} rejected`,
579
+ data: { key, action, timestamp: Date.now() }
580
+ };
581
+ case "edit":
582
+ if (originalValue === void 0 || editedValue === void 0) {
583
+ return { success: false, message: "Both originalValue and editedValue are required for edit" };
584
+ }
585
+ await store.editApproval(key, originalValue, editedValue, { editedBy: actorId });
586
+ return {
587
+ success: true,
588
+ message: `Approval ${key} edited`,
589
+ data: { key, action, timestamp: Date.now() }
590
+ };
591
+ case "cancel":
592
+ await store.cancelApproval(key);
593
+ return {
594
+ success: true,
595
+ message: `Approval ${key} cancelled`,
596
+ data: { key, action, timestamp: Date.now() }
597
+ };
598
+ default:
599
+ return { success: false, message: `Unknown action: ${action}` };
600
+ }
601
+ } catch (error) {
602
+ return {
603
+ success: false,
604
+ message: error instanceof Error ? error.message : String(error)
605
+ };
606
+ }
607
+ };
608
+ }
609
+ function createApprovalChecker(store) {
610
+ return (key) => async () => {
611
+ const status = await store.getApproval(key);
612
+ switch (status.status) {
613
+ case "pending":
614
+ return { status: "pending" };
615
+ case "approved":
616
+ return { status: "approved", value: status.value };
617
+ case "edited":
618
+ return { status: "approved", value: status.editedValue };
619
+ case "rejected":
620
+ return { status: "rejected", reason: status.reason };
621
+ case "expired":
622
+ return { status: "rejected", reason: "Approval request expired" };
623
+ default:
624
+ return { status: "pending" };
625
+ }
626
+ };
627
+ }
628
+ // Annotate the CommonJS export names for ESM import in node:
629
+ 0 && (module.exports = {
630
+ clearStep,
631
+ createApprovalChecker,
632
+ createApprovalStateCollector,
633
+ createApprovalStep,
634
+ createApprovalWebhookHandler,
635
+ createHITLOrchestrator,
636
+ createMemoryApprovalStore,
637
+ createMemoryWorkflowStateStore,
638
+ gatedStep,
639
+ getPendingApprovals,
640
+ hasPendingApproval,
641
+ injectApproval,
642
+ isApprovalRejected,
643
+ isPendingApproval,
644
+ pendingApproval
645
+ });
646
+ //# sourceMappingURL=hitl.cjs.map