hierarchical-approval 2.3.0 → 2.5.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 (53) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +27 -0
  3. package/dist/{ApprovalEngine-Ci9urTDI.d.ts → ApprovalEngine-BHWls8-z.d.ts} +59 -7
  4. package/dist/{ApprovalEngine-C1dUPnLM.d.cts → ApprovalEngine-CW7Uu1bV.d.cts} +59 -7
  5. package/dist/{IAuditAdapter-C4OASS6u.d.ts → IAuditAdapter-CMCirvY-.d.ts} +1 -1
  6. package/dist/{IAuditAdapter-BHVKMMv6.d.cts → IAuditAdapter-Dwx8ZP4J.d.cts} +1 -1
  7. package/dist/{IAuthorizationPolicy-CtLkwQ8I.d.cts → IAuthorizationPolicy-B3vaV69v.d.cts} +1 -1
  8. package/dist/{IAuthorizationPolicy-BA_m2Dg9.d.ts → IAuthorizationPolicy-BryQIKko.d.ts} +1 -1
  9. package/dist/{INotificationAdapter-JJAiMyMd.d.cts → INotificationAdapter-AwbhZEqz.d.cts} +1 -1
  10. package/dist/{INotificationAdapter-BdgfUrIn.d.ts → INotificationAdapter-BBq2czpO.d.ts} +1 -1
  11. package/dist/{IOperationMiddleware-D0ksiFM3.d.cts → IOperationMiddleware-3J-_NSGq.d.cts} +1 -1
  12. package/dist/{IOperationMiddleware-BGYrAqep.d.ts → IOperationMiddleware-CqzFklMn.d.ts} +1 -1
  13. package/dist/{IStorageAdapter-BU3sau5W.d.ts → IStorageAdapter-Dw2xXP9B.d.ts} +1 -1
  14. package/dist/{IStorageAdapter-DdHO4Rf1.d.cts → IStorageAdapter-Pq9J60xv.d.cts} +1 -1
  15. package/dist/adapters/MemoryAdapter.d.cts +2 -2
  16. package/dist/adapters/MemoryAdapter.d.ts +2 -2
  17. package/dist/adapters/PostgresAdapter.d.cts +2 -2
  18. package/dist/adapters/PostgresAdapter.d.ts +2 -2
  19. package/dist/index.cjs +117 -6
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +8 -8
  22. package/dist/index.d.ts +8 -8
  23. package/dist/index.js +117 -7
  24. package/dist/index.js.map +1 -1
  25. package/dist/{instance-DUJY_Axf.d.cts → instance-BO-i9-nq.d.cts} +13 -0
  26. package/dist/{instance-DUJY_Axf.d.ts → instance-BO-i9-nq.d.ts} +13 -0
  27. package/dist/nestjs.cjs +49 -6
  28. package/dist/nestjs.cjs.map +1 -1
  29. package/dist/nestjs.d.cts +7 -7
  30. package/dist/nestjs.d.ts +7 -7
  31. package/dist/nestjs.js +49 -6
  32. package/dist/nestjs.js.map +1 -1
  33. package/dist/plugins/audit.d.cts +2 -2
  34. package/dist/plugins/audit.d.ts +2 -2
  35. package/dist/plugins/notify.cjs +105 -9
  36. package/dist/plugins/notify.cjs.map +1 -1
  37. package/dist/plugins/notify.d.cts +91 -3
  38. package/dist/plugins/notify.d.ts +91 -3
  39. package/dist/plugins/notify.js +105 -10
  40. package/dist/plugins/notify.js.map +1 -1
  41. package/dist/plugins/resilience.d.cts +3 -3
  42. package/dist/plugins/resilience.d.ts +3 -3
  43. package/dist/plugins/tracing.d.cts +2 -2
  44. package/dist/plugins/tracing.d.ts +2 -2
  45. package/dist/plugins/webhook.d.cts +2 -2
  46. package/dist/plugins/webhook.d.ts +2 -2
  47. package/dist/testing.cjs +49 -6
  48. package/dist/testing.cjs.map +1 -1
  49. package/dist/testing.d.cts +7 -7
  50. package/dist/testing.d.ts +7 -7
  51. package/dist/testing.js +49 -6
  52. package/dist/testing.js.map +1 -1
  53. package/package.json +1 -1
package/dist/index.d.cts CHANGED
@@ -1,18 +1,18 @@
1
- export { A as AddCommentOptions, a as ApprovalEngine, b as ApprovalEngineOptions, c as ApprovalStatistics, d as ApproveOptions, e as ApproverResolverFn, f as ApproverWorkload, B as BulkResult, g as BusinessCalendar, C as CanApproveResult, h as CancelOptions, i as ConditionOperatorFn, j as CycleTimeStats, D as DelegateOptions, E as EscalateOptions, H as HealthResult, I as IdGeneratorFn, k as IdempotencyKeyFn, l as ImportResult, O as OrgProvider, m as OutOfOfficeProvider, n as OverrideOptions, P as PreviewChainLevel, o as PreviewResult, p as ProvideInfoOptions, q as PurgeResult, R as ReassignOptions, r as RejectOptions, s as RequestInfoOptions, t as ResubmitOptions, u as RetryPolicy, S as SubmitOptions, T as TEMPLATE_BUNDLE_VERSION, v as TemplateBundle, w as TransferResult, U as UpdateDataOptions, V as ValidationResult, W as WeekendCalendarOptions, x as defaultIdGenerator, y as toComparableNumber, z as validateConditionExpression, F as weekendCalendar } from './ApprovalEngine-C1dUPnLM.cjs';
2
- import { I as IStorageAdapter } from './IStorageAdapter-DdHO4Rf1.cjs';
3
- export { c as CursorPaginatedResult, C as CursorPaginationOpts, b as InstanceFilter, a as PaginatedResult, P as PaginationOpts } from './IStorageAdapter-DdHO4Rf1.cjs';
1
+ export { A as AddCommentOptions, a as ApprovalEngine, b as ApprovalEngineOptions, c as ApprovalStatistics, d as ApproveOptions, e as ApproverResolverFn, f as ApproverWorkload, B as BulkResult, g as BusinessCalendar, h as BusinessHoursCalendarOptions, C as CanApproveResult, i as CancelOptions, j as ConditionOperatorFn, k as CycleTimeStats, D as DelegateOptions, E as EscalateOptions, H as HealthResult, I as IdGeneratorFn, l as IdempotencyKeyFn, m as ImportResult, O as OrgProvider, n as OutOfOfficeProvider, o as OverrideOptions, P as PreviewChainLevel, p as PreviewResult, q as ProvideInfoOptions, r as PurgeResult, R as ReassignOptions, s as RejectOptions, t as RequestInfoOptions, u as ResubmitOptions, v as RetryPolicy, S as SubmitOptions, T as TEMPLATE_BUNDLE_VERSION, w as TemplateBundle, x as TransferResult, U as UpdateDataOptions, V as ValidationResult, W as WeekendCalendarOptions, y as businessHoursCalendar, z as defaultIdGenerator, F as toComparableNumber, G as validateConditionExpression, J as weekendCalendar } from './ApprovalEngine-CW7Uu1bV.cjs';
2
+ import { I as IStorageAdapter } from './IStorageAdapter-Pq9J60xv.cjs';
3
+ export { c as CursorPaginatedResult, C as CursorPaginationOpts, b as InstanceFilter, a as PaginatedResult, P as PaginationOpts } from './IStorageAdapter-Pq9J60xv.cjs';
4
4
  import { L as Logger } from './Logger-BplhlU7l.cjs';
5
5
  export { n as noopLogger } from './Logger-BplhlU7l.cjs';
6
6
  import { C as Clock } from './Clock-3FnOczFJ.cjs';
7
7
  export { s as systemClock } from './Clock-3FnOczFJ.cjs';
8
- export { A as AuthorizationContext, I as IAuthorizationPolicy } from './IAuthorizationPolicy-CtLkwQ8I.cjs';
9
- export { A as ApprovalConflictError, a as ApprovalError, b as ApprovalForbiddenError, c as ApprovalNotFoundError, d as ApprovalTemplateNotFoundError, e as ApprovalValidationError, I as IOperationMiddleware, O as OperationContext } from './IOperationMiddleware-D0ksiFM3.cjs';
8
+ export { A as AuthorizationContext, I as IAuthorizationPolicy } from './IAuthorizationPolicy-B3vaV69v.cjs';
9
+ export { A as ApprovalConflictError, a as ApprovalError, b as ApprovalForbiddenError, c as ApprovalNotFoundError, d as ApprovalTemplateNotFoundError, e as ApprovalValidationError, I as IOperationMiddleware, O as OperationContext } from './IOperationMiddleware-3J-_NSGq.cjs';
10
10
  export { MemoryAdapter } from './adapters/MemoryAdapter.cjs';
11
- export { A as ApprovalEvent, a as ApprovalEventMap, b as ApprovalEventName, c as ApprovedEvent, d as AttachmentEvent, C as CancelledEvent, D as DataUpdatedEvent, e as DelegatedEvent, E as EscalatedEvent, f as ExpiredEvent, H as HistoryEntry, I as INotificationAdapter, g as InfoProvidedEvent, h as InfoRequestedEvent, L as LevelAdvancedEvent, N as NotificationEvent, O as OverriddenEvent, R as ReassignedEvent, i as RejectedEvent, j as ReminderEvent, k as ResubmittedEvent, S as SlaBreachedEvent, l as SubWorkflowEvent, m as SubmittedEvent } from './INotificationAdapter-JJAiMyMd.cjs';
12
- export { I as IAuditAdapter } from './IAuditAdapter-BHVKMMv6.cjs';
11
+ export { A as ApprovalEvent, a as ApprovalEventMap, b as ApprovalEventName, c as ApprovedEvent, d as AttachmentEvent, C as CancelledEvent, D as DataUpdatedEvent, e as DelegatedEvent, E as EscalatedEvent, f as ExpiredEvent, H as HistoryEntry, I as INotificationAdapter, g as InfoProvidedEvent, h as InfoRequestedEvent, L as LevelAdvancedEvent, N as NotificationEvent, O as OverriddenEvent, R as ReassignedEvent, i as RejectedEvent, j as ReminderEvent, k as ResubmittedEvent, S as SlaBreachedEvent, l as SubWorkflowEvent, m as SubmittedEvent } from './INotificationAdapter-AwbhZEqz.cjs';
12
+ export { I as IAuditAdapter } from './IAuditAdapter-Dwx8ZP4J.cjs';
13
13
  export { I as IMetricsAdapter, M as MetricName } from './IMetricsAdapter-DWq8IFaf.cjs';
14
14
  export { I as ISchedulerAdapter } from './ISchedulerAdapter-DKv_QjVN.cjs';
15
- export { a as ApprovalInstance, c as ApprovalLevelConfig, d as ApprovalLevelInstance, e as ApprovalMode, f as ApprovalStatus, A as ApprovalTemplate, g as ApprovalTemplateConfig, h as ApproverConfig, i as Attachment, j as AuditAction, k as AuditContext, b as AuditEntry, C as Condition, l as ConditionExpression, m as ConditionGroup, n as ConditionOperator, o as ConditionRule, E as EscalationConfig, I as InfoRequest, L as LevelStatus, R as ResolvedApprover, p as ResolverFn, S as SubWorkflowConfig, T as TemplateSnapshot } from './instance-DUJY_Axf.cjs';
15
+ export { a as ApprovalInstance, c as ApprovalLevelConfig, d as ApprovalLevelInstance, e as ApprovalMode, f as ApprovalStatus, A as ApprovalTemplate, g as ApprovalTemplateConfig, h as ApproverConfig, i as Attachment, j as AuditAction, k as AuditContext, b as AuditEntry, C as Condition, l as ConditionExpression, m as ConditionGroup, n as ConditionOperator, o as ConditionRule, E as EscalationConfig, I as InfoRequest, L as LevelStatus, R as ResolvedApprover, p as ResolverFn, S as SubWorkflowConfig, T as TemplateSnapshot } from './instance-BO-i9-nq.cjs';
16
16
  import 'zod';
17
17
 
18
18
  interface EscalationSchedulerOpts {
package/dist/index.d.ts CHANGED
@@ -1,18 +1,18 @@
1
- export { A as AddCommentOptions, a as ApprovalEngine, b as ApprovalEngineOptions, c as ApprovalStatistics, d as ApproveOptions, e as ApproverResolverFn, f as ApproverWorkload, B as BulkResult, g as BusinessCalendar, C as CanApproveResult, h as CancelOptions, i as ConditionOperatorFn, j as CycleTimeStats, D as DelegateOptions, E as EscalateOptions, H as HealthResult, I as IdGeneratorFn, k as IdempotencyKeyFn, l as ImportResult, O as OrgProvider, m as OutOfOfficeProvider, n as OverrideOptions, P as PreviewChainLevel, o as PreviewResult, p as ProvideInfoOptions, q as PurgeResult, R as ReassignOptions, r as RejectOptions, s as RequestInfoOptions, t as ResubmitOptions, u as RetryPolicy, S as SubmitOptions, T as TEMPLATE_BUNDLE_VERSION, v as TemplateBundle, w as TransferResult, U as UpdateDataOptions, V as ValidationResult, W as WeekendCalendarOptions, x as defaultIdGenerator, y as toComparableNumber, z as validateConditionExpression, F as weekendCalendar } from './ApprovalEngine-Ci9urTDI.js';
2
- import { I as IStorageAdapter } from './IStorageAdapter-BU3sau5W.js';
3
- export { c as CursorPaginatedResult, C as CursorPaginationOpts, b as InstanceFilter, a as PaginatedResult, P as PaginationOpts } from './IStorageAdapter-BU3sau5W.js';
1
+ export { A as AddCommentOptions, a as ApprovalEngine, b as ApprovalEngineOptions, c as ApprovalStatistics, d as ApproveOptions, e as ApproverResolverFn, f as ApproverWorkload, B as BulkResult, g as BusinessCalendar, h as BusinessHoursCalendarOptions, C as CanApproveResult, i as CancelOptions, j as ConditionOperatorFn, k as CycleTimeStats, D as DelegateOptions, E as EscalateOptions, H as HealthResult, I as IdGeneratorFn, l as IdempotencyKeyFn, m as ImportResult, O as OrgProvider, n as OutOfOfficeProvider, o as OverrideOptions, P as PreviewChainLevel, p as PreviewResult, q as ProvideInfoOptions, r as PurgeResult, R as ReassignOptions, s as RejectOptions, t as RequestInfoOptions, u as ResubmitOptions, v as RetryPolicy, S as SubmitOptions, T as TEMPLATE_BUNDLE_VERSION, w as TemplateBundle, x as TransferResult, U as UpdateDataOptions, V as ValidationResult, W as WeekendCalendarOptions, y as businessHoursCalendar, z as defaultIdGenerator, F as toComparableNumber, G as validateConditionExpression, J as weekendCalendar } from './ApprovalEngine-BHWls8-z.js';
2
+ import { I as IStorageAdapter } from './IStorageAdapter-Dw2xXP9B.js';
3
+ export { c as CursorPaginatedResult, C as CursorPaginationOpts, b as InstanceFilter, a as PaginatedResult, P as PaginationOpts } from './IStorageAdapter-Dw2xXP9B.js';
4
4
  import { L as Logger } from './Logger-BplhlU7l.js';
5
5
  export { n as noopLogger } from './Logger-BplhlU7l.js';
6
6
  import { C as Clock } from './Clock-3FnOczFJ.js';
7
7
  export { s as systemClock } from './Clock-3FnOczFJ.js';
8
- export { A as AuthorizationContext, I as IAuthorizationPolicy } from './IAuthorizationPolicy-BA_m2Dg9.js';
9
- export { A as ApprovalConflictError, a as ApprovalError, b as ApprovalForbiddenError, c as ApprovalNotFoundError, d as ApprovalTemplateNotFoundError, e as ApprovalValidationError, I as IOperationMiddleware, O as OperationContext } from './IOperationMiddleware-BGYrAqep.js';
8
+ export { A as AuthorizationContext, I as IAuthorizationPolicy } from './IAuthorizationPolicy-BryQIKko.js';
9
+ export { A as ApprovalConflictError, a as ApprovalError, b as ApprovalForbiddenError, c as ApprovalNotFoundError, d as ApprovalTemplateNotFoundError, e as ApprovalValidationError, I as IOperationMiddleware, O as OperationContext } from './IOperationMiddleware-CqzFklMn.js';
10
10
  export { MemoryAdapter } from './adapters/MemoryAdapter.js';
11
- export { A as ApprovalEvent, a as ApprovalEventMap, b as ApprovalEventName, c as ApprovedEvent, d as AttachmentEvent, C as CancelledEvent, D as DataUpdatedEvent, e as DelegatedEvent, E as EscalatedEvent, f as ExpiredEvent, H as HistoryEntry, I as INotificationAdapter, g as InfoProvidedEvent, h as InfoRequestedEvent, L as LevelAdvancedEvent, N as NotificationEvent, O as OverriddenEvent, R as ReassignedEvent, i as RejectedEvent, j as ReminderEvent, k as ResubmittedEvent, S as SlaBreachedEvent, l as SubWorkflowEvent, m as SubmittedEvent } from './INotificationAdapter-BdgfUrIn.js';
12
- export { I as IAuditAdapter } from './IAuditAdapter-C4OASS6u.js';
11
+ export { A as ApprovalEvent, a as ApprovalEventMap, b as ApprovalEventName, c as ApprovedEvent, d as AttachmentEvent, C as CancelledEvent, D as DataUpdatedEvent, e as DelegatedEvent, E as EscalatedEvent, f as ExpiredEvent, H as HistoryEntry, I as INotificationAdapter, g as InfoProvidedEvent, h as InfoRequestedEvent, L as LevelAdvancedEvent, N as NotificationEvent, O as OverriddenEvent, R as ReassignedEvent, i as RejectedEvent, j as ReminderEvent, k as ResubmittedEvent, S as SlaBreachedEvent, l as SubWorkflowEvent, m as SubmittedEvent } from './INotificationAdapter-BBq2czpO.js';
12
+ export { I as IAuditAdapter } from './IAuditAdapter-CMCirvY-.js';
13
13
  export { I as IMetricsAdapter, M as MetricName } from './IMetricsAdapter-DWq8IFaf.js';
14
14
  export { I as ISchedulerAdapter } from './ISchedulerAdapter-DKv_QjVN.js';
15
- export { a as ApprovalInstance, c as ApprovalLevelConfig, d as ApprovalLevelInstance, e as ApprovalMode, f as ApprovalStatus, A as ApprovalTemplate, g as ApprovalTemplateConfig, h as ApproverConfig, i as Attachment, j as AuditAction, k as AuditContext, b as AuditEntry, C as Condition, l as ConditionExpression, m as ConditionGroup, n as ConditionOperator, o as ConditionRule, E as EscalationConfig, I as InfoRequest, L as LevelStatus, R as ResolvedApprover, p as ResolverFn, S as SubWorkflowConfig, T as TemplateSnapshot } from './instance-DUJY_Axf.js';
15
+ export { a as ApprovalInstance, c as ApprovalLevelConfig, d as ApprovalLevelInstance, e as ApprovalMode, f as ApprovalStatus, A as ApprovalTemplate, g as ApprovalTemplateConfig, h as ApproverConfig, i as Attachment, j as AuditAction, k as AuditContext, b as AuditEntry, C as Condition, l as ConditionExpression, m as ConditionGroup, n as ConditionOperator, o as ConditionRule, E as EscalationConfig, I as InfoRequest, L as LevelStatus, R as ResolvedApprover, p as ResolverFn, S as SubWorkflowConfig, T as TemplateSnapshot } from './instance-BO-i9-nq.js';
16
16
  import 'zod';
17
17
 
18
18
  interface EscalationSchedulerOpts {
package/dist/index.js CHANGED
@@ -1065,6 +1065,18 @@ var ApprovalEngine = class _ApprovalEngine {
1065
1065
  message: `Level ${l.level} sets reminderEveryDays without reminderAfterDays, so no reminder would ever be sent.`
1066
1066
  });
1067
1067
  }
1068
+ if (l.escalationAfterHours !== void 0 && l.escalationAfterHours <= 0) {
1069
+ errors.push({
1070
+ field: `levels[${i}].escalationAfterHours`,
1071
+ message: `Level ${l.level} escalationAfterHours must be a positive number.`
1072
+ });
1073
+ }
1074
+ if (l.escalationAfterDays !== void 0 && l.escalationAfterHours !== void 0) {
1075
+ errors.push({
1076
+ field: `levels[${i}].escalationAfterHours`,
1077
+ message: `Level ${l.level} sets both escalationAfterDays and escalationAfterHours; pick one so the deadline is unambiguous.`
1078
+ });
1079
+ }
1068
1080
  if (l.escalationAfterDays !== void 0 && l.escalationAfterDays <= 0) {
1069
1081
  errors.push({
1070
1082
  field: `levels[${i}].escalationAfterDays`,
@@ -1129,6 +1141,18 @@ var ApprovalEngine = class _ApprovalEngine {
1129
1141
  }
1130
1142
  });
1131
1143
  }
1144
+ if (config.slaDeadlineDays !== void 0 && config.slaDeadlineHours !== void 0) {
1145
+ errors.push({
1146
+ field: "slaDeadlineHours",
1147
+ message: "Template sets both slaDeadlineDays and slaDeadlineHours; pick one so the SLA is unambiguous."
1148
+ });
1149
+ }
1150
+ if (config.slaDeadlineHours !== void 0 && config.slaDeadlineHours <= 0) {
1151
+ errors.push({
1152
+ field: "slaDeadlineHours",
1153
+ message: "slaDeadlineHours must be a positive number."
1154
+ });
1155
+ }
1132
1156
  if (config.conditions) {
1133
1157
  config.conditions.forEach((rule, ruleIdx) => {
1134
1158
  errors.push(...validateConditionExpression(rule.when, `conditions[${ruleIdx}].when`));
@@ -1242,7 +1266,8 @@ var ApprovalEngine = class _ApprovalEngine {
1242
1266
  threshold: cfg.threshold,
1243
1267
  weights: cfg.weights,
1244
1268
  escalationAfterDays: cfg.escalationAfterDays,
1245
- escalationDueAt: inFirstGroup && cfg.escalationAfterDays ? this.deadlineFrom(now, cfg.escalationAfterDays) : void 0,
1269
+ escalationAfterHours: cfg.escalationAfterHours,
1270
+ escalationDueAt: inFirstGroup ? this.levelEscalationDue(now, cfg) : void 0,
1246
1271
  subWorkflowTemplate: cfg.subWorkflow?.templateName,
1247
1272
  reminderAfterDays: cfg.reminderAfterDays,
1248
1273
  reminderEveryDays: cfg.reminderEveryDays,
@@ -1272,7 +1297,7 @@ var ApprovalEngine = class _ApprovalEngine {
1272
1297
  timestamp: now,
1273
1298
  ...auditCtx
1274
1299
  };
1275
- const slaDeadlineAt = template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
1300
+ const slaDeadlineAt = template.slaDeadlineHours ? this.deadlineFromHours(now, template.slaDeadlineHours) : template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
1276
1301
  const instance = {
1277
1302
  id: instanceId,
1278
1303
  tenantId: this.tenantId,
@@ -1300,6 +1325,7 @@ var ApprovalEngine = class _ApprovalEngine {
1300
1325
  templateSnapshot: {
1301
1326
  escalation: template.escalation,
1302
1327
  slaDeadlineDays: template.slaDeadlineDays,
1328
+ slaDeadlineHours: template.slaDeadlineHours,
1303
1329
  allowOverride: template.allowOverride
1304
1330
  }
1305
1331
  };
@@ -2571,7 +2597,7 @@ var ApprovalEngine = class _ApprovalEngine {
2571
2597
  reason: opts.reason,
2572
2598
  ...auditCtx
2573
2599
  };
2574
- const slaDeadlineAt = template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
2600
+ const slaDeadlineAt = template.slaDeadlineHours ? this.deadlineFromHours(now, template.slaDeadlineHours) : template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
2575
2601
  const newInstance = {
2576
2602
  id: newInstanceId,
2577
2603
  tenantId: this.tenantId,
@@ -2594,6 +2620,7 @@ var ApprovalEngine = class _ApprovalEngine {
2594
2620
  templateSnapshot: {
2595
2621
  escalation: template.escalation,
2596
2622
  slaDeadlineDays: template.slaDeadlineDays,
2623
+ slaDeadlineHours: template.slaDeadlineHours,
2597
2624
  allowOverride: template.allowOverride
2598
2625
  }
2599
2626
  };
@@ -3536,6 +3563,24 @@ var ApprovalEngine = class _ApprovalEngine {
3536
3563
  deadlineFrom(from, days) {
3537
3564
  return this.calendar ? this.calendar.addBusinessDays(from, days) : new Date(from.getTime() + days * 864e5);
3538
3565
  }
3566
+ /**
3567
+ * Resolve an hour-based deadline.
3568
+ *
3569
+ * A calendar that only knows whole days — `weekendCalendar`, say — has no
3570
+ * `addBusinessHours`, and this falls back to elapsed clock time rather than
3571
+ * quietly pretending the calendar was applied. Configure
3572
+ * `businessHoursCalendar` to have hours skip evenings and weekends.
3573
+ */
3574
+ deadlineFromHours(from, hours) {
3575
+ const addHours = this.calendar?.addBusinessHours?.bind(this.calendar);
3576
+ return addHours ? addHours(from, hours) : new Date(from.getTime() + hours * 36e5);
3577
+ }
3578
+ /** Level deadline from whichever of days/hours the template configured. */
3579
+ levelEscalationDue(from, level) {
3580
+ if (level.escalationAfterHours) return this.deadlineFromHours(from, level.escalationAfterHours);
3581
+ if (level.escalationAfterDays) return this.deadlineFrom(from, level.escalationAfterDays);
3582
+ return void 0;
3583
+ }
3539
3584
  async requireInstance(id) {
3540
3585
  const instance = await this.opts.adapter.getInstance(this.tenantId, id);
3541
3586
  if (!instance) throw new ApprovalNotFoundError("Instance", id);
@@ -3641,9 +3686,7 @@ var ApprovalEngine = class _ApprovalEngine {
3641
3686
  now
3642
3687
  );
3643
3688
  }
3644
- if (lvl.escalationAfterDays) {
3645
- lvl.escalationDueAt = this.deadlineFrom(now, lvl.escalationAfterDays);
3646
- }
3689
+ lvl.escalationDueAt = this.levelEscalationDue(now, lvl);
3647
3690
  this.scheduleReminder(lvl, now);
3648
3691
  lvl.status = "pending";
3649
3692
  }
@@ -4284,7 +4327,74 @@ function weekendCalendar(options = {}) {
4284
4327
  }
4285
4328
  };
4286
4329
  }
4330
+ var HOUR_MS = 36e5;
4331
+ function businessHoursCalendar(options = {}) {
4332
+ const startHour = options.workdayStartHour ?? 9;
4333
+ const endHour = options.workdayEndHour ?? 17;
4334
+ if (!(endHour > startHour)) {
4335
+ throw new Error(
4336
+ `businessHoursCalendar: workdayEndHour (${endHour}) must be greater than workdayStartHour (${startHour}).`
4337
+ );
4338
+ }
4339
+ const weekend = new Set(options.weekendDays ?? [0, 6]);
4340
+ const holidays = new Set((options.holidays ?? []).map(dayKey));
4341
+ const isBusinessDay = (d) => !weekend.has(d.getDay()) && !holidays.has(dayKey(d));
4342
+ const startOfWorkday = (d) => {
4343
+ const out = new Date(d.getTime());
4344
+ out.setHours(startHour, 0, 0, 0);
4345
+ return out;
4346
+ };
4347
+ const endOfWorkday = (d) => {
4348
+ const out = new Date(d.getTime());
4349
+ out.setHours(endHour, 0, 0, 0);
4350
+ return out;
4351
+ };
4352
+ const toWorkingMoment = (from) => {
4353
+ const cursor = new Date(from.getTime());
4354
+ for (let guard = 0; guard < 3660; guard++) {
4355
+ if (!isBusinessDay(cursor)) {
4356
+ cursor.setDate(cursor.getDate() + 1);
4357
+ cursor.setHours(startHour, 0, 0, 0);
4358
+ continue;
4359
+ }
4360
+ if (cursor.getTime() < startOfWorkday(cursor).getTime()) return startOfWorkday(cursor);
4361
+ if (cursor.getTime() >= endOfWorkday(cursor).getTime()) {
4362
+ cursor.setDate(cursor.getDate() + 1);
4363
+ cursor.setHours(startHour, 0, 0, 0);
4364
+ continue;
4365
+ }
4366
+ return cursor;
4367
+ }
4368
+ throw new Error(
4369
+ "businessHoursCalendar: no working day found within 10 years of the start date."
4370
+ );
4371
+ };
4372
+ const base = weekendCalendar(options);
4373
+ return {
4374
+ addBusinessDays: base.addBusinessDays,
4375
+ addBusinessHours(from, hours) {
4376
+ if (hours <= 0 || Number.isNaN(hours)) return new Date(from.getTime());
4377
+ let cursor = toWorkingMoment(from);
4378
+ let remainingMs = Math.round(hours * HOUR_MS);
4379
+ for (let guard = 0; remainingMs > 0 && guard < 3660; guard++) {
4380
+ const dayEnd = endOfWorkday(cursor);
4381
+ const availableMs = dayEnd.getTime() - cursor.getTime();
4382
+ if (remainingMs <= availableMs) {
4383
+ return new Date(cursor.getTime() + remainingMs);
4384
+ }
4385
+ remainingMs -= availableMs;
4386
+ const next = new Date(cursor.getTime());
4387
+ next.setDate(next.getDate() + 1);
4388
+ next.setHours(startHour, 0, 0, 0);
4389
+ cursor = toWorkingMoment(next);
4390
+ }
4391
+ throw new Error(
4392
+ `businessHoursCalendar: ${hours} working hours could not be scheduled within 10 years \u2014 check weekendDays and holidays.`
4393
+ );
4394
+ }
4395
+ };
4396
+ }
4287
4397
 
4288
- export { ApprovalConflictError, ApprovalEngine, ApprovalError, ApprovalForbiddenError, ApprovalNotFoundError, ApprovalTemplateNotFoundError, ApprovalValidationError, EscalationScheduler, MemoryAdapter, TEMPLATE_BUNDLE_VERSION, defaultIdGenerator, noopLogger, systemClock, toComparableNumber, validateConditionExpression, weekendCalendar };
4398
+ export { ApprovalConflictError, ApprovalEngine, ApprovalError, ApprovalForbiddenError, ApprovalNotFoundError, ApprovalTemplateNotFoundError, ApprovalValidationError, EscalationScheduler, MemoryAdapter, TEMPLATE_BUNDLE_VERSION, businessHoursCalendar, defaultIdGenerator, noopLogger, systemClock, toComparableNumber, validateConditionExpression, weekendCalendar };
4289
4399
  //# sourceMappingURL=index.js.map
4290
4400
  //# sourceMappingURL=index.js.map