hierarchical-approval 4.0.6 → 4.1.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 (60) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/{ApprovalEngine-BnMpoQB2.d.cts → ApprovalEngine-CJQAHzxb.d.cts} +93 -20
  3. package/dist/{ApprovalEngine-BqKSm1gG.d.ts → ApprovalEngine-DevN2HQ6.d.ts} +93 -20
  4. package/dist/{IAuditAdapter-SNpV2hdt.d.ts → IAuditAdapter-Bg6e0_ME.d.ts} +1 -1
  5. package/dist/{IAuditAdapter-DR-WIAK6.d.cts → IAuditAdapter-Bv7jIph1.d.cts} +1 -1
  6. package/dist/{IAuthorizationPolicy-BwYBOQM3.d.cts → IAuthorizationPolicy-D0Phw1c_.d.cts} +1 -1
  7. package/dist/{IAuthorizationPolicy-BMl0xS-j.d.ts → IAuthorizationPolicy-abLdaLzR.d.ts} +1 -1
  8. package/dist/{IMetricsAdapter-WROxYSXd.d.cts → IMetricsAdapter-DN9rYsoq.d.cts} +1 -0
  9. package/dist/{IMetricsAdapter-WROxYSXd.d.ts → IMetricsAdapter-DN9rYsoq.d.ts} +1 -0
  10. package/dist/{INotificationAdapter-BO6kmkyC.d.cts → INotificationAdapter-D_b-H3bB.d.ts} +5 -1
  11. package/dist/{INotificationAdapter-DKLEDgAk.d.ts → INotificationAdapter-rBeUKnBa.d.cts} +5 -1
  12. package/dist/{IOperationMiddleware-CUI6VG6r.d.ts → IOperationMiddleware-BuRYpQdL.d.ts} +1 -1
  13. package/dist/{IOperationMiddleware-oYhWMKMn.d.cts → IOperationMiddleware-RGAqQ31Q.d.cts} +1 -1
  14. package/dist/{IStorageAdapter-DN78GD-1.d.ts → IStorageAdapter-Bje_7c62.d.ts} +2 -1
  15. package/dist/{IStorageAdapter-_PjAgjFA.d.cts → IStorageAdapter-DYFj85oL.d.cts} +2 -1
  16. package/dist/adapters/MemoryAdapter.d.cts +2 -2
  17. package/dist/adapters/MemoryAdapter.d.ts +2 -2
  18. package/dist/adapters/PostgresAdapter.cjs.map +1 -1
  19. package/dist/adapters/PostgresAdapter.d.cts +3 -2
  20. package/dist/adapters/PostgresAdapter.d.ts +3 -2
  21. package/dist/adapters/PostgresAdapter.js.map +1 -1
  22. package/dist/index.cjs +77 -16
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +9 -9
  25. package/dist/index.d.ts +9 -9
  26. package/dist/index.js +77 -16
  27. package/dist/index.js.map +1 -1
  28. package/dist/{instance-MWXPSsA6.d.cts → instance-Du-TGRH_.d.cts} +6 -0
  29. package/dist/{instance-MWXPSsA6.d.ts → instance-Du-TGRH_.d.ts} +6 -0
  30. package/dist/nestjs.cjs +77 -16
  31. package/dist/nestjs.cjs.map +1 -1
  32. package/dist/nestjs.d.cts +8 -8
  33. package/dist/nestjs.d.ts +8 -8
  34. package/dist/nestjs.js +77 -16
  35. package/dist/nestjs.js.map +1 -1
  36. package/dist/plugins/audit.d.cts +2 -2
  37. package/dist/plugins/audit.d.ts +2 -2
  38. package/dist/plugins/metrics.d.cts +1 -1
  39. package/dist/plugins/metrics.d.ts +1 -1
  40. package/dist/plugins/notify.d.cts +2 -2
  41. package/dist/plugins/notify.d.ts +2 -2
  42. package/dist/plugins/resilience.cjs +1 -1
  43. package/dist/plugins/resilience.cjs.map +1 -1
  44. package/dist/plugins/resilience.d.cts +3 -3
  45. package/dist/plugins/resilience.d.ts +3 -3
  46. package/dist/plugins/resilience.js +1 -1
  47. package/dist/plugins/resilience.js.map +1 -1
  48. package/dist/plugins/tracing.cjs.map +1 -1
  49. package/dist/plugins/tracing.d.cts +3 -2
  50. package/dist/plugins/tracing.d.ts +3 -2
  51. package/dist/plugins/tracing.js.map +1 -1
  52. package/dist/plugins/webhook.d.cts +2 -2
  53. package/dist/plugins/webhook.d.ts +2 -2
  54. package/dist/testing.cjs +77 -16
  55. package/dist/testing.cjs.map +1 -1
  56. package/dist/testing.d.cts +8 -8
  57. package/dist/testing.d.ts +8 -8
  58. package/dist/testing.js +77 -16
  59. package/dist/testing.js.map +1 -1
  60. package/package.json +10 -10
package/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [4.1.0] - 2026-10-12
9
+
10
+ ### Changed
11
+
12
+ - `SubmitOptions` now describes what `submit` accepts: `data` and `metadata` are
13
+ optional (they default to `{}`) and `expiresAt` may be a `Date`, ISO string or
14
+ epoch milliseconds.
15
+
16
+ ## [4.0.7] - 2026-09-28
17
+
18
+ ### Fixed
19
+
20
+ - `RbacAuthorizationPolicy`'s roleProvider-failure denial message now names
21
+ the offending actor id, instead of a message that gave no clue which actor
22
+ triggered the fail-closed denial.
23
+ - `ImportResult` and `PreviewResult`'s array fields are now typed as
24
+ `readonly T[]`, not just `readonly` properties holding a mutable array —
25
+ callers could previously still `push`/`splice` the returned arrays in
26
+ place even though the property itself couldn't be reassigned.
27
+
8
28
  ## [4.0.6] - 2026-09-26
9
29
 
10
30
  ### Fixed
@@ -1,14 +1,14 @@
1
- import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-_PjAgjFA.cjs';
2
- import { m as ConditionExpression, r as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, C as Comment, e as ApprovalMode, f as ApprovalStatus, L as LevelStatus, b as AuditEntry } from './instance-MWXPSsA6.cjs';
3
- import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-BO6kmkyC.cjs';
1
+ import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-DYFj85oL.cjs';
2
+ import { m as ConditionExpression, r as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, C as Comment, e as ApprovalMode, f as ApprovalStatus, L as LevelStatus, b as AuditEntry } from './instance-Du-TGRH_.cjs';
3
+ import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-rBeUKnBa.cjs';
4
4
  import { z } from 'zod';
5
5
  import { L as Logger } from './Logger-hqFbBCtB.cjs';
6
6
  import { C as Clock } from './Clock-D8Qp39q_.cjs';
7
- import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-oYhWMKMn.cjs';
8
- import { I as IAuditAdapter } from './IAuditAdapter-DR-WIAK6.cjs';
9
- import { I as IMetricsAdapter } from './IMetricsAdapter-WROxYSXd.cjs';
7
+ import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-RGAqQ31Q.cjs';
8
+ import { I as IAuditAdapter } from './IAuditAdapter-Bv7jIph1.cjs';
9
+ import { I as IMetricsAdapter } from './IMetricsAdapter-DN9rYsoq.cjs';
10
10
  import { I as ISchedulerAdapter } from './ISchedulerAdapter-CLMdBA-5.cjs';
11
- import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-BwYBOQM3.cjs';
11
+ import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-D0Phw1c_.cjs';
12
12
 
13
13
  declare const SubmitOptionsSchema: z.ZodObject<{
14
14
  templateName: z.ZodString;
@@ -113,7 +113,14 @@ declare const UpdateDataOptionsSchema: z.ZodObject<{
113
113
  reason: z.ZodOptional<z.ZodString>;
114
114
  recomputeChain: z.ZodDefault<z.ZodBoolean>;
115
115
  }, z.core.$strip>;
116
- type SubmitOptions = z.infer<typeof SubmitOptionsSchema>;
116
+ /**
117
+ * Input shape: `data` and `metadata` are optional because the schema defaults
118
+ * them. `expiresAt` is re-declared since zod types a coerced date's input as
119
+ * `unknown`, which would let any value through at compile time.
120
+ */
121
+ type SubmitOptions = Omit<z.input<typeof SubmitOptionsSchema>, 'expiresAt'> & {
122
+ expiresAt?: Date | string | number;
123
+ };
117
124
  type ApproveOptions = z.infer<typeof ApproveOptionsSchema>;
118
125
  type RejectOptions = z.infer<typeof RejectOptionsSchema>;
119
126
  type DelegateOptions = z.infer<typeof DelegateOptionsSchema>;
@@ -302,7 +309,7 @@ interface CanApproveResult {
302
309
  eligible: boolean;
303
310
  reason?: 'not_an_approver' | 'already_acted' | 'self_approval' | 'wrong_status' | 'delegated_away';
304
311
  }
305
- /** One entry in a {@link ApprovalEngine.preview} result, resolved against the given data without persisting anything. */
312
+ /** One entry in a {@link ApprovalEngine.previewApprovalChain} result, resolved against the given data without persisting anything. */
306
313
  interface PreviewChainLevel {
307
314
  level: number;
308
315
  name: string;
@@ -310,10 +317,12 @@ interface PreviewChainLevel {
310
317
  resolvedApprovers: string[];
311
318
  mode: ApprovalMode;
312
319
  }
320
+ /** Return value of {@link ApprovalEngine.previewApprovalChain}. */
313
321
  interface PreviewResult {
314
- levels: PreviewChainLevel[];
322
+ /** The resolved chain, in level order. */
323
+ readonly levels: readonly PreviewChainLevel[];
315
324
  /** Indices (0-based) of conditions that fired for this data. */
316
- conditionsApplied: number[];
325
+ readonly conditionsApplied: readonly number[];
317
326
  }
318
327
  /** Where one level in an explained chain came from. */
319
328
  interface ExplainedLevel {
@@ -468,14 +477,14 @@ interface TemplateBundle {
468
477
  }
469
478
  /** Outcome of {@link ApprovalEngine.importTemplates}. */
470
479
  interface ImportResult {
471
- created: string[];
472
- updated: string[];
473
- skipped: string[];
474
- errors: Array<{
480
+ readonly created: readonly string[];
481
+ readonly updated: readonly string[];
482
+ readonly skipped: readonly string[];
483
+ readonly errors: readonly Readonly<{
475
484
  name: string;
476
485
  message: string;
477
- }>;
478
- dryRun: boolean;
486
+ }>[];
487
+ readonly dryRun: boolean;
479
488
  }
480
489
  /** Outcome of a {@link ApprovalEngine.purgeInstances} sweep. */
481
490
  interface PurgeResult {
@@ -577,6 +586,7 @@ interface RetryPolicy {
577
586
  }
578
587
  /** Derives the idempotency key used to dedupe a {@link ApprovalEngine.submit} call from its tenant and document identity. */
579
588
  type IdempotencyKeyFn = (tenantId: string, documentType: string, documentId: string, templateName: string, data: Record<string, unknown>) => string;
589
+ /** Constructor options for {@link ApprovalEngine}. Only `adapter` is required. */
580
590
  interface ApprovalEngineOptions {
581
591
  adapter: IStorageAdapter;
582
592
  tenantId?: string;
@@ -631,6 +641,11 @@ interface ApprovalEngineOptions {
631
641
  /** Middleware chain: before/after/onError hooks for every operation. */
632
642
  middleware?: IOperationMiddleware[];
633
643
  }
644
+ /**
645
+ * Runs multi-level approval workflows against a pluggable {@link IStorageAdapter}:
646
+ * submitting instances against a template, resolving and notifying approvers,
647
+ * recording decisions, and driving escalation, expiry and sub-workflows.
648
+ */
634
649
  declare class ApprovalEngine {
635
650
  private readonly opts;
636
651
  private readonly bus;
@@ -715,6 +730,17 @@ declare class ApprovalEngine {
715
730
  * @throws {@link ApprovalTemplateNotFoundError} if `raw.templateName` is not defined.
716
731
  * @throws {@link ApprovalValidationError} if `raw` fails schema validation, or if
717
732
  * condition evaluation leaves no levels (or a duplicate level number) to open.
733
+ *
734
+ * @example
735
+ * ```ts
736
+ * const instance = await engine.submit({
737
+ * templateName: 'purchase-order',
738
+ * documentId: 'PO-1042',
739
+ * documentType: 'purchase_order',
740
+ * submittedBy: 'alice',
741
+ * data: { amount: 12_500 },
742
+ * });
743
+ * ```
718
744
  */
719
745
  submit(raw: SubmitOptions, auditCtx?: AuditContext,
720
746
  /**
@@ -744,6 +770,14 @@ declare class ApprovalEngine {
744
770
  * configured {@link IAuthorizationPolicy}.
745
771
  * @throws {@link ApprovalValidationError} if `opts.approverId` sits on more than one
746
772
  * open parallel level and `opts.level` was not supplied to disambiguate.
773
+ *
774
+ * @example
775
+ * ```ts
776
+ * const updated = await engine.approve(instance.id, {
777
+ * approverId: 'bob',
778
+ * comment: 'Within budget',
779
+ * });
780
+ * ```
747
781
  */
748
782
  approve(instanceId: string, raw: ApproveOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
749
783
  /**
@@ -766,6 +800,15 @@ declare class ApprovalEngine {
766
800
  * @throws {@link ApprovalValidationError} if `opts.approverId` sits on more than one
767
801
  * open parallel level with `opts.level` unset, or if `opts.returnTo === 'previous'`
768
802
  * but the failing level is already the first level in the chain.
803
+ *
804
+ * @example
805
+ * ```ts
806
+ * const updated = await engine.reject(instance.id, {
807
+ * approverId: 'bob',
808
+ * reason: 'Over budget',
809
+ * returnTo: 'previous',
810
+ * });
811
+ * ```
769
812
  */
770
813
  reject(instanceId: string, raw: RejectOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
771
814
  /**
@@ -784,6 +827,16 @@ declare class ApprovalEngine {
784
827
  * configured {@link IAuthorizationPolicy}.
785
828
  * @throws {@link ApprovalValidationError} if `opts.fromApprover` sits on more than one
786
829
  * open parallel level and `opts.level` was not supplied to disambiguate.
830
+ *
831
+ * @example
832
+ * ```ts
833
+ * await engine.delegate(instance.id, {
834
+ * fromApprover: 'bob',
835
+ * toApprover: 'carol',
836
+ * reason: 'On leave',
837
+ * until: new Date('2026-11-01'),
838
+ * });
839
+ * ```
787
840
  */
788
841
  delegate(instanceId: string, raw: DelegateOptions, auditCtx?: AuditContext): Promise<void>;
789
842
  /**
@@ -995,11 +1048,16 @@ declare class ApprovalEngine {
995
1048
  * theirs away), or they already voted on it.
996
1049
  *
997
1050
  * @returns `{ eligible: true }`, or `{ eligible: false, reason }`.
1051
+ *
1052
+ * @example
1053
+ * ```ts
1054
+ * const check = await engine.canApprove(instance.id, 'bob');
1055
+ * if (!check.eligible) console.log(check.reason);
1056
+ * ```
998
1057
  */
999
1058
  canApprove(instanceId: string, userId: string): Promise<CanApproveResult>;
1000
1059
  /** Emergency bypass — completes the instance as 'approved', skipping remaining levels. Requires template.allowOverride = true. */
1001
1060
  override(instanceId: string, raw: OverrideOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
1002
- /** Approve multiple instances in one call. Never throws — failures collected in result.failed. */
1003
1061
  /**
1004
1062
  * Move every pending approval assigned to one person over to another.
1005
1063
  *
@@ -1022,7 +1080,15 @@ declare class ApprovalEngine {
1022
1080
  * @returns What moved, what did not, and why. With `dryRun` nothing is written.
1023
1081
  */
1024
1082
  transferApprovals(raw: TransferApprovalsOptions, auditCtx?: AuditContext): Promise<TransferResult>;
1025
- /** Approve multiple instances in one call. Never throws — failures collected in result.failed. */
1083
+ /**
1084
+ * Approve multiple instances in one call. Never throws — failures collected in result.failed.
1085
+ *
1086
+ * @example
1087
+ * ```ts
1088
+ * const { succeeded, failed } = await engine.bulkApprove(ids, { approverId: 'bob' });
1089
+ * for (const { instanceId, error } of failed) console.warn(instanceId, error.code);
1090
+ * ```
1091
+ */
1026
1092
  bulkApprove(instanceIds: string[], raw: ApproveOptions, auditCtx?: AuditContext): Promise<BulkResult>;
1027
1093
  /** Reject multiple instances in one call. Never throws — failures collected in result.failed. */
1028
1094
  bulkReject(instanceIds: string[], raw: RejectOptions, auditCtx?: AuditContext): Promise<BulkResult>;
@@ -1032,7 +1098,14 @@ declare class ApprovalEngine {
1032
1098
  * @throws {@link ApprovalNotFoundError} if no instance exists with that id.
1033
1099
  */
1034
1100
  getInstance(instanceId: string): Promise<ApprovalInstance>;
1035
- /** Page through every instance with an open level assigned to one approver. */
1101
+ /**
1102
+ * Page through every instance with an open level assigned to one approver.
1103
+ *
1104
+ * @example
1105
+ * ```ts
1106
+ * const page = await engine.getPendingFor('bob', { limit: 20, offset: 0 });
1107
+ * ```
1108
+ */
1036
1109
  getPendingFor(approverId: string, opts?: PaginationOpts): Promise<PaginatedResult<ApprovalInstance>>;
1037
1110
  /** Page through instances matching an arbitrary filter (status, document type, date range, ...). */
1038
1111
  queryInstances(filter: InstanceFilter, opts?: PaginationOpts): Promise<PaginatedResult<ApprovalInstance>>;
@@ -1,14 +1,14 @@
1
- import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-DN78GD-1.js';
2
- import { m as ConditionExpression, r as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, C as Comment, e as ApprovalMode, f as ApprovalStatus, L as LevelStatus, b as AuditEntry } from './instance-MWXPSsA6.js';
3
- import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-DKLEDgAk.js';
1
+ import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-Bje_7c62.js';
2
+ import { m as ConditionExpression, r as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, C as Comment, e as ApprovalMode, f as ApprovalStatus, L as LevelStatus, b as AuditEntry } from './instance-Du-TGRH_.js';
3
+ import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-D_b-H3bB.js';
4
4
  import { z } from 'zod';
5
5
  import { L as Logger } from './Logger-hqFbBCtB.js';
6
6
  import { C as Clock } from './Clock-D8Qp39q_.js';
7
- import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-CUI6VG6r.js';
8
- import { I as IAuditAdapter } from './IAuditAdapter-SNpV2hdt.js';
9
- import { I as IMetricsAdapter } from './IMetricsAdapter-WROxYSXd.js';
7
+ import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-BuRYpQdL.js';
8
+ import { I as IAuditAdapter } from './IAuditAdapter-Bg6e0_ME.js';
9
+ import { I as IMetricsAdapter } from './IMetricsAdapter-DN9rYsoq.js';
10
10
  import { I as ISchedulerAdapter } from './ISchedulerAdapter-CLMdBA-5.js';
11
- import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-BMl0xS-j.js';
11
+ import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-abLdaLzR.js';
12
12
 
13
13
  declare const SubmitOptionsSchema: z.ZodObject<{
14
14
  templateName: z.ZodString;
@@ -113,7 +113,14 @@ declare const UpdateDataOptionsSchema: z.ZodObject<{
113
113
  reason: z.ZodOptional<z.ZodString>;
114
114
  recomputeChain: z.ZodDefault<z.ZodBoolean>;
115
115
  }, z.core.$strip>;
116
- type SubmitOptions = z.infer<typeof SubmitOptionsSchema>;
116
+ /**
117
+ * Input shape: `data` and `metadata` are optional because the schema defaults
118
+ * them. `expiresAt` is re-declared since zod types a coerced date's input as
119
+ * `unknown`, which would let any value through at compile time.
120
+ */
121
+ type SubmitOptions = Omit<z.input<typeof SubmitOptionsSchema>, 'expiresAt'> & {
122
+ expiresAt?: Date | string | number;
123
+ };
117
124
  type ApproveOptions = z.infer<typeof ApproveOptionsSchema>;
118
125
  type RejectOptions = z.infer<typeof RejectOptionsSchema>;
119
126
  type DelegateOptions = z.infer<typeof DelegateOptionsSchema>;
@@ -302,7 +309,7 @@ interface CanApproveResult {
302
309
  eligible: boolean;
303
310
  reason?: 'not_an_approver' | 'already_acted' | 'self_approval' | 'wrong_status' | 'delegated_away';
304
311
  }
305
- /** One entry in a {@link ApprovalEngine.preview} result, resolved against the given data without persisting anything. */
312
+ /** One entry in a {@link ApprovalEngine.previewApprovalChain} result, resolved against the given data without persisting anything. */
306
313
  interface PreviewChainLevel {
307
314
  level: number;
308
315
  name: string;
@@ -310,10 +317,12 @@ interface PreviewChainLevel {
310
317
  resolvedApprovers: string[];
311
318
  mode: ApprovalMode;
312
319
  }
320
+ /** Return value of {@link ApprovalEngine.previewApprovalChain}. */
313
321
  interface PreviewResult {
314
- levels: PreviewChainLevel[];
322
+ /** The resolved chain, in level order. */
323
+ readonly levels: readonly PreviewChainLevel[];
315
324
  /** Indices (0-based) of conditions that fired for this data. */
316
- conditionsApplied: number[];
325
+ readonly conditionsApplied: readonly number[];
317
326
  }
318
327
  /** Where one level in an explained chain came from. */
319
328
  interface ExplainedLevel {
@@ -468,14 +477,14 @@ interface TemplateBundle {
468
477
  }
469
478
  /** Outcome of {@link ApprovalEngine.importTemplates}. */
470
479
  interface ImportResult {
471
- created: string[];
472
- updated: string[];
473
- skipped: string[];
474
- errors: Array<{
480
+ readonly created: readonly string[];
481
+ readonly updated: readonly string[];
482
+ readonly skipped: readonly string[];
483
+ readonly errors: readonly Readonly<{
475
484
  name: string;
476
485
  message: string;
477
- }>;
478
- dryRun: boolean;
486
+ }>[];
487
+ readonly dryRun: boolean;
479
488
  }
480
489
  /** Outcome of a {@link ApprovalEngine.purgeInstances} sweep. */
481
490
  interface PurgeResult {
@@ -577,6 +586,7 @@ interface RetryPolicy {
577
586
  }
578
587
  /** Derives the idempotency key used to dedupe a {@link ApprovalEngine.submit} call from its tenant and document identity. */
579
588
  type IdempotencyKeyFn = (tenantId: string, documentType: string, documentId: string, templateName: string, data: Record<string, unknown>) => string;
589
+ /** Constructor options for {@link ApprovalEngine}. Only `adapter` is required. */
580
590
  interface ApprovalEngineOptions {
581
591
  adapter: IStorageAdapter;
582
592
  tenantId?: string;
@@ -631,6 +641,11 @@ interface ApprovalEngineOptions {
631
641
  /** Middleware chain: before/after/onError hooks for every operation. */
632
642
  middleware?: IOperationMiddleware[];
633
643
  }
644
+ /**
645
+ * Runs multi-level approval workflows against a pluggable {@link IStorageAdapter}:
646
+ * submitting instances against a template, resolving and notifying approvers,
647
+ * recording decisions, and driving escalation, expiry and sub-workflows.
648
+ */
634
649
  declare class ApprovalEngine {
635
650
  private readonly opts;
636
651
  private readonly bus;
@@ -715,6 +730,17 @@ declare class ApprovalEngine {
715
730
  * @throws {@link ApprovalTemplateNotFoundError} if `raw.templateName` is not defined.
716
731
  * @throws {@link ApprovalValidationError} if `raw` fails schema validation, or if
717
732
  * condition evaluation leaves no levels (or a duplicate level number) to open.
733
+ *
734
+ * @example
735
+ * ```ts
736
+ * const instance = await engine.submit({
737
+ * templateName: 'purchase-order',
738
+ * documentId: 'PO-1042',
739
+ * documentType: 'purchase_order',
740
+ * submittedBy: 'alice',
741
+ * data: { amount: 12_500 },
742
+ * });
743
+ * ```
718
744
  */
719
745
  submit(raw: SubmitOptions, auditCtx?: AuditContext,
720
746
  /**
@@ -744,6 +770,14 @@ declare class ApprovalEngine {
744
770
  * configured {@link IAuthorizationPolicy}.
745
771
  * @throws {@link ApprovalValidationError} if `opts.approverId` sits on more than one
746
772
  * open parallel level and `opts.level` was not supplied to disambiguate.
773
+ *
774
+ * @example
775
+ * ```ts
776
+ * const updated = await engine.approve(instance.id, {
777
+ * approverId: 'bob',
778
+ * comment: 'Within budget',
779
+ * });
780
+ * ```
747
781
  */
748
782
  approve(instanceId: string, raw: ApproveOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
749
783
  /**
@@ -766,6 +800,15 @@ declare class ApprovalEngine {
766
800
  * @throws {@link ApprovalValidationError} if `opts.approverId` sits on more than one
767
801
  * open parallel level with `opts.level` unset, or if `opts.returnTo === 'previous'`
768
802
  * but the failing level is already the first level in the chain.
803
+ *
804
+ * @example
805
+ * ```ts
806
+ * const updated = await engine.reject(instance.id, {
807
+ * approverId: 'bob',
808
+ * reason: 'Over budget',
809
+ * returnTo: 'previous',
810
+ * });
811
+ * ```
769
812
  */
770
813
  reject(instanceId: string, raw: RejectOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
771
814
  /**
@@ -784,6 +827,16 @@ declare class ApprovalEngine {
784
827
  * configured {@link IAuthorizationPolicy}.
785
828
  * @throws {@link ApprovalValidationError} if `opts.fromApprover` sits on more than one
786
829
  * open parallel level and `opts.level` was not supplied to disambiguate.
830
+ *
831
+ * @example
832
+ * ```ts
833
+ * await engine.delegate(instance.id, {
834
+ * fromApprover: 'bob',
835
+ * toApprover: 'carol',
836
+ * reason: 'On leave',
837
+ * until: new Date('2026-11-01'),
838
+ * });
839
+ * ```
787
840
  */
788
841
  delegate(instanceId: string, raw: DelegateOptions, auditCtx?: AuditContext): Promise<void>;
789
842
  /**
@@ -995,11 +1048,16 @@ declare class ApprovalEngine {
995
1048
  * theirs away), or they already voted on it.
996
1049
  *
997
1050
  * @returns `{ eligible: true }`, or `{ eligible: false, reason }`.
1051
+ *
1052
+ * @example
1053
+ * ```ts
1054
+ * const check = await engine.canApprove(instance.id, 'bob');
1055
+ * if (!check.eligible) console.log(check.reason);
1056
+ * ```
998
1057
  */
999
1058
  canApprove(instanceId: string, userId: string): Promise<CanApproveResult>;
1000
1059
  /** Emergency bypass — completes the instance as 'approved', skipping remaining levels. Requires template.allowOverride = true. */
1001
1060
  override(instanceId: string, raw: OverrideOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
1002
- /** Approve multiple instances in one call. Never throws — failures collected in result.failed. */
1003
1061
  /**
1004
1062
  * Move every pending approval assigned to one person over to another.
1005
1063
  *
@@ -1022,7 +1080,15 @@ declare class ApprovalEngine {
1022
1080
  * @returns What moved, what did not, and why. With `dryRun` nothing is written.
1023
1081
  */
1024
1082
  transferApprovals(raw: TransferApprovalsOptions, auditCtx?: AuditContext): Promise<TransferResult>;
1025
- /** Approve multiple instances in one call. Never throws — failures collected in result.failed. */
1083
+ /**
1084
+ * Approve multiple instances in one call. Never throws — failures collected in result.failed.
1085
+ *
1086
+ * @example
1087
+ * ```ts
1088
+ * const { succeeded, failed } = await engine.bulkApprove(ids, { approverId: 'bob' });
1089
+ * for (const { instanceId, error } of failed) console.warn(instanceId, error.code);
1090
+ * ```
1091
+ */
1026
1092
  bulkApprove(instanceIds: string[], raw: ApproveOptions, auditCtx?: AuditContext): Promise<BulkResult>;
1027
1093
  /** Reject multiple instances in one call. Never throws — failures collected in result.failed. */
1028
1094
  bulkReject(instanceIds: string[], raw: RejectOptions, auditCtx?: AuditContext): Promise<BulkResult>;
@@ -1032,7 +1098,14 @@ declare class ApprovalEngine {
1032
1098
  * @throws {@link ApprovalNotFoundError} if no instance exists with that id.
1033
1099
  */
1034
1100
  getInstance(instanceId: string): Promise<ApprovalInstance>;
1035
- /** Page through every instance with an open level assigned to one approver. */
1101
+ /**
1102
+ * Page through every instance with an open level assigned to one approver.
1103
+ *
1104
+ * @example
1105
+ * ```ts
1106
+ * const page = await engine.getPendingFor('bob', { limit: 20, offset: 0 });
1107
+ * ```
1108
+ */
1036
1109
  getPendingFor(approverId: string, opts?: PaginationOpts): Promise<PaginatedResult<ApprovalInstance>>;
1037
1110
  /** Page through instances matching an arbitrary filter (status, document type, date range, ...). */
1038
1111
  queryInstances(filter: InstanceFilter, opts?: PaginationOpts): Promise<PaginatedResult<ApprovalInstance>>;
@@ -1,4 +1,4 @@
1
- import { b as AuditEntry, a as ApprovalInstance } from './instance-MWXPSsA6.js';
1
+ import { b as AuditEntry, a as ApprovalInstance } from './instance-Du-TGRH_.js';
2
2
 
3
3
  /** Secondary write sink for audit entries, alongside the primary storage adapter. */
4
4
  interface IAuditAdapter {
@@ -1,4 +1,4 @@
1
- import { b as AuditEntry, a as ApprovalInstance } from './instance-MWXPSsA6.cjs';
1
+ import { b as AuditEntry, a as ApprovalInstance } from './instance-Du-TGRH_.cjs';
2
2
 
3
3
  /** Secondary write sink for audit entries, alongside the primary storage adapter. */
4
4
  interface IAuditAdapter {
@@ -1,4 +1,4 @@
1
- import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-MWXPSsA6.cjs';
1
+ import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-Du-TGRH_.cjs';
2
2
 
3
3
  interface AuthorizationContext {
4
4
  /** The engine method the actor is attempting to call. */
@@ -1,4 +1,4 @@
1
- import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-MWXPSsA6.js';
1
+ import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-Du-TGRH_.js';
2
2
 
3
3
  interface AuthorizationContext {
4
4
  /** The engine method the actor is attempting to call. */
@@ -1,5 +1,6 @@
1
1
  /** Closed set of metric names an {@link IMetricsAdapter} may be called with, namespaced `approval.<verb>`. */
2
2
  type MetricName = 'approval.submitted' | 'approval.approved' | 'approval.rejected' | 'approval.cancelled' | 'approval.expired' | 'approval.sla_breached' | 'approval.escalated' | 'approval.reassigned' | 'approval.overridden' | 'approval.data_updated' | 'approval.reminded' | 'approval.info_requested' | 'approval.info_provided' | 'approval.attachment_added' | 'approval.attachment_removed' | 'approval.conflict_retry' | 'approval.operation_duration_ms';
3
+ /** Sink for engine metrics. Implementations are called synchronously and must not throw. */
3
4
  interface IMetricsAdapter {
4
5
  /** Increment a counter. Synchronous — never awaited. */
5
6
  increment(metric: MetricName, labels?: Record<string, string>): void;
@@ -1,5 +1,6 @@
1
1
  /** Closed set of metric names an {@link IMetricsAdapter} may be called with, namespaced `approval.<verb>`. */
2
2
  type MetricName = 'approval.submitted' | 'approval.approved' | 'approval.rejected' | 'approval.cancelled' | 'approval.expired' | 'approval.sla_breached' | 'approval.escalated' | 'approval.reassigned' | 'approval.overridden' | 'approval.data_updated' | 'approval.reminded' | 'approval.info_requested' | 'approval.info_provided' | 'approval.attachment_added' | 'approval.attachment_removed' | 'approval.conflict_retry' | 'approval.operation_duration_ms';
3
+ /** Sink for engine metrics. Implementations are called synchronously and must not throw. */
3
4
  interface IMetricsAdapter {
4
5
  /** Increment a counter. Synchronous — never awaited. */
5
6
  increment(metric: MetricName, labels?: Record<string, string>): void;
@@ -1,4 +1,4 @@
1
- import { a as ApprovalInstance, b as AuditEntry } from './instance-MWXPSsA6.cjs';
1
+ import { a as ApprovalInstance, b as AuditEntry } from './instance-Du-TGRH_.js';
2
2
 
3
3
  /** Common fields carried by every lifecycle event emitted for an approval instance. */
4
4
  interface ApprovalEvent {
@@ -159,6 +159,7 @@ interface CommentedEvent extends ApprovalEvent {
159
159
  /** Users named in the comment. */
160
160
  recipients: string[];
161
161
  }
162
+ /** Maps each lifecycle event name to its payload type. Keys are what {@link ApprovalEngine.on} accepts; see {@link ApprovalEventName}. */
162
163
  interface ApprovalEventMap {
163
164
  'approval:submitted': SubmittedEvent;
164
165
  'approval:approved': ApprovedEvent;
@@ -184,10 +185,12 @@ interface ApprovalEventMap {
184
185
  'approval:commented': CommentedEvent;
185
186
  }
186
187
  type ApprovalEventName = keyof ApprovalEventMap;
188
+ /** An {@link AuditEntry} tagged with the instance it belongs to, for callers aggregating history across several instances. */
187
189
  interface HistoryEntry extends AuditEntry {
188
190
  instanceId: string;
189
191
  }
190
192
 
193
+ /** Normalized envelope passed to {@link INotificationAdapter.notify} for every approval event. */
191
194
  interface NotificationEvent {
192
195
  /** The event that fired, e.g. `'submitted'`, `'approved'`, `'escalated'`. */
193
196
  type: ApprovalEventName;
@@ -208,6 +211,7 @@ interface NotificationEvent {
208
211
  /** Event-specific detail, shaped by {@link type}. */
209
212
  payload: ApprovalEventMap[ApprovalEventName];
210
213
  }
214
+ /** Sink for approval-event notifications (email, Slack, webhooks, ...). */
211
215
  interface INotificationAdapter {
212
216
  /** Called after every emitted approval event. Must not throw — errors are logged and swallowed. */
213
217
  notify(event: NotificationEvent): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { a as ApprovalInstance, b as AuditEntry } from './instance-MWXPSsA6.js';
1
+ import { a as ApprovalInstance, b as AuditEntry } from './instance-Du-TGRH_.cjs';
2
2
 
3
3
  /** Common fields carried by every lifecycle event emitted for an approval instance. */
4
4
  interface ApprovalEvent {
@@ -159,6 +159,7 @@ interface CommentedEvent extends ApprovalEvent {
159
159
  /** Users named in the comment. */
160
160
  recipients: string[];
161
161
  }
162
+ /** Maps each lifecycle event name to its payload type. Keys are what {@link ApprovalEngine.on} accepts; see {@link ApprovalEventName}. */
162
163
  interface ApprovalEventMap {
163
164
  'approval:submitted': SubmittedEvent;
164
165
  'approval:approved': ApprovedEvent;
@@ -184,10 +185,12 @@ interface ApprovalEventMap {
184
185
  'approval:commented': CommentedEvent;
185
186
  }
186
187
  type ApprovalEventName = keyof ApprovalEventMap;
188
+ /** An {@link AuditEntry} tagged with the instance it belongs to, for callers aggregating history across several instances. */
187
189
  interface HistoryEntry extends AuditEntry {
188
190
  instanceId: string;
189
191
  }
190
192
 
193
+ /** Normalized envelope passed to {@link INotificationAdapter.notify} for every approval event. */
191
194
  interface NotificationEvent {
192
195
  /** The event that fired, e.g. `'submitted'`, `'approved'`, `'escalated'`. */
193
196
  type: ApprovalEventName;
@@ -208,6 +211,7 @@ interface NotificationEvent {
208
211
  /** Event-specific detail, shaped by {@link type}. */
209
212
  payload: ApprovalEventMap[ApprovalEventName];
210
213
  }
214
+ /** Sink for approval-event notifications (email, Slack, webhooks, ...). */
211
215
  interface INotificationAdapter {
212
216
  /** Called after every emitted approval event. Must not throw — errors are logged and swallowed. */
213
217
  notify(event: NotificationEvent): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { a as ApprovalInstance } from './instance-MWXPSsA6.js';
1
+ import { a as ApprovalInstance } from './instance-Du-TGRH_.js';
2
2
 
3
3
  /** Base class for all errors thrown by this library; carries a stable machine-readable `code`. */
4
4
  declare class ApprovalError extends Error {
@@ -1,4 +1,4 @@
1
- import { a as ApprovalInstance } from './instance-MWXPSsA6.cjs';
1
+ import { a as ApprovalInstance } from './instance-Du-TGRH_.cjs';
2
2
 
3
3
  /** Base class for all errors thrown by this library; carries a stable machine-readable `code`. */
4
4
  declare class ApprovalError extends Error {
@@ -1,4 +1,4 @@
1
- import { A as ApprovalTemplate, a as ApprovalInstance, f as ApprovalStatus, b as AuditEntry } from './instance-MWXPSsA6.js';
1
+ import { A as ApprovalTemplate, a as ApprovalInstance, f as ApprovalStatus, b as AuditEntry } from './instance-Du-TGRH_.js';
2
2
 
3
3
  /** Offset-based pagination request. */
4
4
  interface PaginationOpts {
@@ -34,6 +34,7 @@ interface CursorPaginatedResult<T> {
34
34
  /** Whether another page exists in the requested direction. */
35
35
  hasMore: boolean;
36
36
  }
37
+ /** Query filter for listing/counting instances; every set field narrows the match. */
37
38
  interface InstanceFilter {
38
39
  /** Match only instances in this status. */
39
40
  status?: ApprovalStatus;
@@ -1,4 +1,4 @@
1
- import { A as ApprovalTemplate, a as ApprovalInstance, f as ApprovalStatus, b as AuditEntry } from './instance-MWXPSsA6.cjs';
1
+ import { A as ApprovalTemplate, a as ApprovalInstance, f as ApprovalStatus, b as AuditEntry } from './instance-Du-TGRH_.cjs';
2
2
 
3
3
  /** Offset-based pagination request. */
4
4
  interface PaginationOpts {
@@ -34,6 +34,7 @@ interface CursorPaginatedResult<T> {
34
34
  /** Whether another page exists in the requested direction. */
35
35
  hasMore: boolean;
36
36
  }
37
+ /** Query filter for listing/counting instances; every set field narrows the match. */
37
38
  interface InstanceFilter {
38
39
  /** Match only instances in this status. */
39
40
  status?: ApprovalStatus;
@@ -1,5 +1,5 @@
1
- import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-_PjAgjFA.cjs';
2
- import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-MWXPSsA6.cjs';
1
+ import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-DYFj85oL.cjs';
2
+ import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-Du-TGRH_.cjs';
3
3
 
4
4
  /**
5
5
  * In-process {@link IStorageAdapter} backed by two `Map`s, keyed by
@@ -1,5 +1,5 @@
1
- import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-DN78GD-1.js';
2
- import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-MWXPSsA6.js';
1
+ import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-Bje_7c62.js';
2
+ import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-Du-TGRH_.js';
3
3
 
4
4
  /**
5
5
  * In-process {@link IStorageAdapter} backed by two `Map`s, keyed by