hierarchical-approval 4.0.2 → 4.0.3

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 (72) hide show
  1. package/CHANGELOG.md +20 -4
  2. package/dist/{ApprovalEngine-Dqe-rC6K.d.ts → ApprovalEngine-BtgQDr_8.d.ts} +13 -9
  3. package/dist/{ApprovalEngine-XgGBQLC_.d.cts → ApprovalEngine-Djdw-JzA.d.cts} +13 -9
  4. package/dist/Clock-D8Qp39q_.d.cts +8 -0
  5. package/dist/Clock-D8Qp39q_.d.ts +8 -0
  6. package/dist/{IAuditAdapter-DkbyFFjb.d.ts → IAuditAdapter-C-04ar9A.d.ts} +1 -1
  7. package/dist/{IAuditAdapter-Dt8qjkQL.d.cts → IAuditAdapter-TT2Xhtu6.d.cts} +1 -1
  8. package/dist/{IAuthorizationPolicy-eo6glbpE.d.ts → IAuthorizationPolicy-D6tTT3gv.d.cts} +6 -1
  9. package/dist/{IAuthorizationPolicy-YJ_08Mof.d.cts → IAuthorizationPolicy-wIE1mpTy.d.ts} +6 -1
  10. package/dist/{IMetricsAdapter-DWq8IFaf.d.cts → IMetricsAdapter-WROxYSXd.d.cts} +1 -0
  11. package/dist/{IMetricsAdapter-DWq8IFaf.d.ts → IMetricsAdapter-WROxYSXd.d.ts} +1 -0
  12. package/dist/{INotificationAdapter-BTO9FSKy.d.ts → INotificationAdapter-BcUskJ6z.d.ts} +9 -1
  13. package/dist/{INotificationAdapter-XbD69gss.d.cts → INotificationAdapter-CBsJDk-Y.d.cts} +9 -1
  14. package/dist/{IOperationMiddleware-CX7lo4lp.d.ts → IOperationMiddleware-C7ciNnJp.d.ts} +3 -2
  15. package/dist/{IOperationMiddleware-BBLk8MIz.d.cts → IOperationMiddleware-D3Yelw9R.d.cts} +3 -2
  16. package/dist/{IStorageAdapter-CdXMggPK.d.ts → IStorageAdapter-CVvNsnnD.d.ts} +7 -1
  17. package/dist/{IStorageAdapter-Cjw5zDwQ.d.cts → IStorageAdapter-D7cy20uD.d.cts} +7 -1
  18. package/dist/{Logger-BplhlU7l.d.cts → Logger-hqFbBCtB.d.cts} +7 -0
  19. package/dist/{Logger-BplhlU7l.d.ts → Logger-hqFbBCtB.d.ts} +7 -0
  20. package/dist/adapters/MemoryAdapter.d.cts +2 -2
  21. package/dist/adapters/MemoryAdapter.d.ts +2 -2
  22. package/dist/adapters/PostgresAdapter.d.cts +2 -2
  23. package/dist/adapters/PostgresAdapter.d.ts +2 -2
  24. package/dist/index.cjs +32 -3
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +13 -13
  27. package/dist/index.d.ts +13 -13
  28. package/dist/index.js +32 -3
  29. package/dist/index.js.map +1 -1
  30. package/dist/{instance-uUQh5XW3.d.cts → instance-Dd1jQizG.d.cts} +19 -0
  31. package/dist/{instance-uUQh5XW3.d.ts → instance-Dd1jQizG.d.ts} +19 -0
  32. package/dist/nestjs.cjs +31 -2
  33. package/dist/nestjs.cjs.map +1 -1
  34. package/dist/nestjs.d.cts +10 -10
  35. package/dist/nestjs.d.ts +10 -10
  36. package/dist/nestjs.js +31 -2
  37. package/dist/nestjs.js.map +1 -1
  38. package/dist/plugins/audit.cjs.map +1 -1
  39. package/dist/plugins/audit.d.cts +3 -3
  40. package/dist/plugins/audit.d.ts +3 -3
  41. package/dist/plugins/audit.js.map +1 -1
  42. package/dist/plugins/metrics.cjs.map +1 -1
  43. package/dist/plugins/metrics.d.cts +2 -2
  44. package/dist/plugins/metrics.d.ts +2 -2
  45. package/dist/plugins/metrics.js.map +1 -1
  46. package/dist/plugins/notify.cjs.map +1 -1
  47. package/dist/plugins/notify.d.cts +4 -4
  48. package/dist/plugins/notify.d.ts +4 -4
  49. package/dist/plugins/notify.js.map +1 -1
  50. package/dist/plugins/resilience.cjs.map +1 -1
  51. package/dist/plugins/resilience.d.cts +5 -5
  52. package/dist/plugins/resilience.d.ts +5 -5
  53. package/dist/plugins/resilience.js.map +1 -1
  54. package/dist/plugins/scheduler.cjs.map +1 -1
  55. package/dist/plugins/scheduler.d.cts +2 -2
  56. package/dist/plugins/scheduler.d.ts +2 -2
  57. package/dist/plugins/scheduler.js.map +1 -1
  58. package/dist/plugins/tracing.d.cts +2 -2
  59. package/dist/plugins/tracing.d.ts +2 -2
  60. package/dist/plugins/webhook.cjs.map +1 -1
  61. package/dist/plugins/webhook.d.cts +4 -4
  62. package/dist/plugins/webhook.d.ts +4 -4
  63. package/dist/plugins/webhook.js.map +1 -1
  64. package/dist/testing.cjs +31 -2
  65. package/dist/testing.cjs.map +1 -1
  66. package/dist/testing.d.cts +10 -10
  67. package/dist/testing.d.ts +10 -10
  68. package/dist/testing.js +31 -2
  69. package/dist/testing.js.map +1 -1
  70. package/package.json +1 -1
  71. package/dist/Clock-3FnOczFJ.d.cts +0 -6
  72. package/dist/Clock-3FnOczFJ.d.ts +0 -6
@@ -13,8 +13,10 @@ type ApproverConfig = {
13
13
  type: string;
14
14
  [key: string]: unknown;
15
15
  };
16
+ /** An {@link ApproverConfig} resolved down to a concrete user. */
16
17
  interface ResolvedApprover {
17
18
  userId: string;
19
+ /** The config entry this approver was resolved from. */
18
20
  source: ApproverConfig;
19
21
  }
20
22
  type ResolverFn = (submittedBy: string, data: Record<string, unknown>) => Promise<string> | string;
@@ -145,8 +147,11 @@ interface ConditionRule {
145
147
  addLevels?: ApprovalLevelConfig[];
146
148
  skipLevels?: number[];
147
149
  }
150
+ /** A single-rung escalation. See {@link EscalationStep} for a multi-rung ladder. */
148
151
  interface EscalationConfig {
152
+ /** Delay from the level opening, in calendar or business days. */
149
153
  afterDays: number;
154
+ /** Who is added to the level once `afterDays` elapses. */
150
155
  escalateTo: ApproverConfig;
151
156
  }
152
157
  /** One rung of an escalation ladder. See {@link ApprovalTemplateConfig.escalationSteps}. */
@@ -215,19 +220,33 @@ interface ApprovalTemplate extends ApprovalTemplateConfig {
215
220
  type ApprovalStatus = 'pending' | 'approved' | 'rejected' | 'cancelled' | 'expired';
216
221
  type LevelStatus = 'waiting' | 'pending' | 'approved' | 'rejected' | 'skipped';
217
222
  type AuditAction = 'submitted' | 'approved' | 'rejected' | 'delegated' | 'reassigned' | 'escalated' | 'cancelled' | 'level_advanced' | 'commented' | 'resubmitted' | 'overridden' | 'data_updated' | 'reminded' | 'info_requested' | 'info_provided' | 'attachment_added' | 'attachment_removed' | 'subworkflow_started' | 'subworkflow_completed' | 'expired';
223
+ /** One row of an {@link ApprovalInstance}'s audit log, recording a single state change. */
218
224
  interface AuditEntry {
225
+ /** What happened. */
219
226
  action: AuditAction;
227
+ /** Id of the user or system that performed the action. */
220
228
  actorId: string;
229
+ /** Role the actor held at the time, when supplied via {@link AuditContext}. */
221
230
  actorRole?: string;
231
+ /** IP address the action originated from, when supplied via {@link AuditContext}. */
222
232
  actorIp?: string;
233
+ /** User agent the action originated from, when supplied via {@link AuditContext}. */
223
234
  actorUserAgent?: string;
235
+ /** Level the action applied to, or the current level for instance-wide actions. */
224
236
  level: number;
237
+ /** When the action happened. */
225
238
  timestamp: Date;
239
+ /** Caller-supplied trace id for correlating this entry with external logs. */
226
240
  traceId?: string;
241
+ /** Free-text comment attached to the action, if any. */
227
242
  comment?: string;
243
+ /** Reason given for the action (e.g. a rejection or override reason), if any. */
228
244
  reason?: string;
245
+ /** Id of the user this action routed to, for `'delegated'`/`'reassigned'`/`'escalated'` actions. */
229
246
  delegateTo?: string;
247
+ /** State the action changed from, shaped differently per {@link action}. */
230
248
  oldValue?: Record<string, unknown>;
249
+ /** State the action changed to, shaped differently per {@link action}. */
231
250
  newValue?: Record<string, unknown>;
232
251
  }
233
252
  /** Context injected by the caller on each mutating operation (for SOX/SOC2 compliance). */
@@ -13,8 +13,10 @@ type ApproverConfig = {
13
13
  type: string;
14
14
  [key: string]: unknown;
15
15
  };
16
+ /** An {@link ApproverConfig} resolved down to a concrete user. */
16
17
  interface ResolvedApprover {
17
18
  userId: string;
19
+ /** The config entry this approver was resolved from. */
18
20
  source: ApproverConfig;
19
21
  }
20
22
  type ResolverFn = (submittedBy: string, data: Record<string, unknown>) => Promise<string> | string;
@@ -145,8 +147,11 @@ interface ConditionRule {
145
147
  addLevels?: ApprovalLevelConfig[];
146
148
  skipLevels?: number[];
147
149
  }
150
+ /** A single-rung escalation. See {@link EscalationStep} for a multi-rung ladder. */
148
151
  interface EscalationConfig {
152
+ /** Delay from the level opening, in calendar or business days. */
149
153
  afterDays: number;
154
+ /** Who is added to the level once `afterDays` elapses. */
150
155
  escalateTo: ApproverConfig;
151
156
  }
152
157
  /** One rung of an escalation ladder. See {@link ApprovalTemplateConfig.escalationSteps}. */
@@ -215,19 +220,33 @@ interface ApprovalTemplate extends ApprovalTemplateConfig {
215
220
  type ApprovalStatus = 'pending' | 'approved' | 'rejected' | 'cancelled' | 'expired';
216
221
  type LevelStatus = 'waiting' | 'pending' | 'approved' | 'rejected' | 'skipped';
217
222
  type AuditAction = 'submitted' | 'approved' | 'rejected' | 'delegated' | 'reassigned' | 'escalated' | 'cancelled' | 'level_advanced' | 'commented' | 'resubmitted' | 'overridden' | 'data_updated' | 'reminded' | 'info_requested' | 'info_provided' | 'attachment_added' | 'attachment_removed' | 'subworkflow_started' | 'subworkflow_completed' | 'expired';
223
+ /** One row of an {@link ApprovalInstance}'s audit log, recording a single state change. */
218
224
  interface AuditEntry {
225
+ /** What happened. */
219
226
  action: AuditAction;
227
+ /** Id of the user or system that performed the action. */
220
228
  actorId: string;
229
+ /** Role the actor held at the time, when supplied via {@link AuditContext}. */
221
230
  actorRole?: string;
231
+ /** IP address the action originated from, when supplied via {@link AuditContext}. */
222
232
  actorIp?: string;
233
+ /** User agent the action originated from, when supplied via {@link AuditContext}. */
223
234
  actorUserAgent?: string;
235
+ /** Level the action applied to, or the current level for instance-wide actions. */
224
236
  level: number;
237
+ /** When the action happened. */
225
238
  timestamp: Date;
239
+ /** Caller-supplied trace id for correlating this entry with external logs. */
226
240
  traceId?: string;
241
+ /** Free-text comment attached to the action, if any. */
227
242
  comment?: string;
243
+ /** Reason given for the action (e.g. a rejection or override reason), if any. */
228
244
  reason?: string;
245
+ /** Id of the user this action routed to, for `'delegated'`/`'reassigned'`/`'escalated'` actions. */
229
246
  delegateTo?: string;
247
+ /** State the action changed from, shaped differently per {@link action}. */
230
248
  oldValue?: Record<string, unknown>;
249
+ /** State the action changed to, shaped differently per {@link action}. */
231
250
  newValue?: Record<string, unknown>;
232
251
  }
233
252
  /** Context injected by the caller on each mutating operation (for SOX/SOC2 compliance). */
package/dist/nestjs.cjs CHANGED
@@ -557,6 +557,7 @@ var TemplateRegistry = class {
557
557
  delete resolved.removeLevels;
558
558
  return resolved;
559
559
  }
560
+ /** Register a new template. Throws if a template with this name already exists for the tenant. */
560
561
  async define(config) {
561
562
  const existing = await this.adapter.getTemplate(this.tenantId, config.name);
562
563
  if (existing) {
@@ -593,11 +594,13 @@ var TemplateRegistry = class {
593
594
  await this.adapter.saveTemplate(updated);
594
595
  return newId;
595
596
  }
597
+ /** Fetch a template by name. Throws if no template with this name exists for the tenant. */
596
598
  async get(name) {
597
599
  const template = await this.adapter.getTemplate(this.tenantId, name);
598
600
  if (!template) throw new ApprovalTemplateNotFoundError(name);
599
601
  return template;
600
602
  }
603
+ /** List every template registered for the tenant. */
601
604
  async list() {
602
605
  return this.adapter.listTemplates(this.tenantId);
603
606
  }
@@ -637,6 +640,32 @@ var LevelResolver = class {
637
640
  for (const [name, fn] of this.resolvers) target.register(name, fn);
638
641
  for (const [name, fn] of this.approverTypes) target.registerApproverType(name, fn);
639
642
  }
643
+ /**
644
+ * Resolve a level's `approvers` config down to a deduplicated list of user
645
+ * ids, applying out-of-office cover last so a substitute never sees a stale
646
+ * approver id.
647
+ *
648
+ * @param approvers - The level's approver configs (`user`, `role`,
649
+ * `dynamic`, or a custom type registered via {@link registerApproverType}).
650
+ * @param submittedBy - Passed through to `dynamic` resolvers and custom
651
+ * approver-type functions as routing context.
652
+ * @param data - The instance's document data, passed through the same way.
653
+ * @param orgProvider - Required to resolve `role` approvers; omit only when
654
+ * the template uses no `role` approvers.
655
+ * @param outOfOffice - Optional cover lookup, consulted after every other
656
+ * approver type has resolved.
657
+ * @param at - The moment out-of-office cover is resolved for. Defaults to
658
+ * now.
659
+ * @param substitutions - Filled with `original -> stand-in` for each
660
+ * approver replaced by out-of-office cover, so the caller can carry
661
+ * anything keyed by the original id (such as a weighted level's vote
662
+ * weight) across to the substitute.
663
+ * @returns The resolved, deduplicated approver ids (post-substitution).
664
+ * @throws {ApprovalValidationError} If no approvers resolve at all, a
665
+ * `role` approver is configured without an `orgProvider`, a `dynamic`
666
+ * approver names a resolver that was never registered, or a custom
667
+ * approver type was never registered.
668
+ */
640
669
  async resolveApprovers(approvers, submittedBy, data, orgProvider, outOfOffice, at, substitutions) {
641
670
  const resolved = [];
642
671
  for (const approver of approvers) {
@@ -1048,7 +1077,7 @@ function evaluateConditions(conditions, data) {
1048
1077
  function assertStatus(instance, expected) {
1049
1078
  if (instance.status !== expected) {
1050
1079
  throw new ApprovalError(
1051
- `Expected instance status "${expected}" but got "${instance.status}".`,
1080
+ `Instance "${instance.id}": expected status "${expected}" but got "${instance.status}".`,
1052
1081
  "INVALID_STATUS"
1053
1082
  );
1054
1083
  }
@@ -1056,7 +1085,7 @@ function assertStatus(instance, expected) {
1056
1085
  function assertApproverOnLevel(level, approverId) {
1057
1086
  if (!level.approverIds.includes(approverId)) {
1058
1087
  throw new ApprovalForbiddenError(
1059
- `User "${approverId}" is not an approver for level ${level.level}.`
1088
+ `User "${approverId}" is not an approver for level ${level.level} ("${level.name}").`
1060
1089
  );
1061
1090
  }
1062
1091
  }