hierarchical-approval 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -0
- package/README.md +54 -0
- package/dist/{ApprovalEngine-C1dUPnLM.d.cts → ApprovalEngine-BWrblNQ0.d.cts} +81 -7
- package/dist/{ApprovalEngine-Ci9urTDI.d.ts → ApprovalEngine-D4DdSx-M.d.ts} +81 -7
- package/dist/{IAuditAdapter-BHVKMMv6.d.cts → IAuditAdapter-2UU5edKG.d.cts} +1 -1
- package/dist/{IAuditAdapter-C4OASS6u.d.ts → IAuditAdapter-DSor54iY.d.ts} +1 -1
- package/dist/{IAuthorizationPolicy-BA_m2Dg9.d.ts → IAuthorizationPolicy-BOWg-3AH.d.ts} +1 -1
- package/dist/{IAuthorizationPolicy-CtLkwQ8I.d.cts → IAuthorizationPolicy-DPpiKzyh.d.cts} +1 -1
- package/dist/{INotificationAdapter-BdgfUrIn.d.ts → INotificationAdapter-BNGivLgU.d.ts} +1 -1
- package/dist/{INotificationAdapter-JJAiMyMd.d.cts → INotificationAdapter-C6HPrzh6.d.cts} +1 -1
- package/dist/{IOperationMiddleware-BGYrAqep.d.ts → IOperationMiddleware-CXtg2wzF.d.ts} +1 -1
- package/dist/{IOperationMiddleware-D0ksiFM3.d.cts → IOperationMiddleware-CxnPzpqL.d.cts} +1 -1
- package/dist/{IStorageAdapter-BU3sau5W.d.ts → IStorageAdapter-ChPT7ZDp.d.ts} +1 -1
- package/dist/{IStorageAdapter-DdHO4Rf1.d.cts → IStorageAdapter-yX9ERfQE.d.cts} +1 -1
- package/dist/adapters/MemoryAdapter.d.cts +2 -2
- package/dist/adapters/MemoryAdapter.d.ts +2 -2
- package/dist/adapters/PostgresAdapter.d.cts +2 -2
- package/dist/adapters/PostgresAdapter.d.ts +2 -2
- package/dist/index.cjs +179 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +179 -12
- package/dist/index.js.map +1 -1
- package/dist/{instance-DUJY_Axf.d.cts → instance-weh2w7Ji.d.cts} +39 -1
- package/dist/{instance-DUJY_Axf.d.ts → instance-weh2w7Ji.d.ts} +39 -1
- package/dist/nestjs.cjs +111 -11
- package/dist/nestjs.cjs.map +1 -1
- package/dist/nestjs.d.cts +7 -7
- package/dist/nestjs.d.ts +7 -7
- package/dist/nestjs.js +111 -11
- package/dist/nestjs.js.map +1 -1
- package/dist/plugins/audit.d.cts +2 -2
- package/dist/plugins/audit.d.ts +2 -2
- package/dist/plugins/notify.d.cts +2 -2
- package/dist/plugins/notify.d.ts +2 -2
- package/dist/plugins/resilience.d.cts +3 -3
- package/dist/plugins/resilience.d.ts +3 -3
- package/dist/plugins/tracing.d.cts +2 -2
- package/dist/plugins/tracing.d.ts +2 -2
- package/dist/plugins/webhook.d.cts +2 -2
- package/dist/plugins/webhook.d.ts +2 -2
- package/dist/testing.cjs +111 -11
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +7 -7
- package/dist/testing.d.ts +7 -7
- package/dist/testing.js +111 -11
- package/dist/testing.js.map +1 -1
- 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,
|
|
2
|
-
import { I as IStorageAdapter } from './IStorageAdapter-
|
|
3
|
-
export { c as CursorPaginatedResult, C as CursorPaginationOpts, b as InstanceFilter, a as PaginatedResult, P as PaginationOpts } from './IStorageAdapter-
|
|
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-BWrblNQ0.cjs';
|
|
2
|
+
import { I as IStorageAdapter } from './IStorageAdapter-yX9ERfQE.cjs';
|
|
3
|
+
export { c as CursorPaginatedResult, C as CursorPaginationOpts, b as InstanceFilter, a as PaginatedResult, P as PaginationOpts } from './IStorageAdapter-yX9ERfQE.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-
|
|
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-
|
|
8
|
+
export { A as AuthorizationContext, I as IAuthorizationPolicy } from './IAuthorizationPolicy-DPpiKzyh.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-CxnPzpqL.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-
|
|
12
|
-
export { I as IAuditAdapter } from './IAuditAdapter-
|
|
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-C6HPrzh6.cjs';
|
|
12
|
+
export { I as IAuditAdapter } from './IAuditAdapter-2UU5edKG.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,
|
|
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, p as EscalationStep, I as InfoRequest, L as LevelStatus, R as ResolvedApprover, q as ResolverFn, S as SubWorkflowConfig, T as TemplateSnapshot } from './instance-weh2w7Ji.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,
|
|
2
|
-
import { I as IStorageAdapter } from './IStorageAdapter-
|
|
3
|
-
export { c as CursorPaginatedResult, C as CursorPaginationOpts, b as InstanceFilter, a as PaginatedResult, P as PaginationOpts } from './IStorageAdapter-
|
|
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-D4DdSx-M.js';
|
|
2
|
+
import { I as IStorageAdapter } from './IStorageAdapter-ChPT7ZDp.js';
|
|
3
|
+
export { c as CursorPaginatedResult, C as CursorPaginationOpts, b as InstanceFilter, a as PaginatedResult, P as PaginationOpts } from './IStorageAdapter-ChPT7ZDp.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-
|
|
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-
|
|
8
|
+
export { A as AuthorizationContext, I as IAuthorizationPolicy } from './IAuthorizationPolicy-BOWg-3AH.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-CXtg2wzF.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-
|
|
12
|
-
export { I as IAuditAdapter } from './IAuditAdapter-
|
|
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-BNGivLgU.js';
|
|
12
|
+
export { I as IAuditAdapter } from './IAuditAdapter-DSor54iY.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,
|
|
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, p as EscalationStep, I as InfoRequest, L as LevelStatus, R as ResolvedApprover, q as ResolverFn, S as SubWorkflowConfig, T as TemplateSnapshot } from './instance-weh2w7Ji.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,9 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1242
1266
|
threshold: cfg.threshold,
|
|
1243
1267
|
weights: cfg.weights,
|
|
1244
1268
|
escalationAfterDays: cfg.escalationAfterDays,
|
|
1245
|
-
|
|
1269
|
+
escalationAfterHours: cfg.escalationAfterHours,
|
|
1270
|
+
escalationStep: 0,
|
|
1271
|
+
escalationDueAt: inFirstGroup ? this.levelEscalationDue(now, cfg, this.firstRungOf(template.escalationSteps)) : void 0,
|
|
1246
1272
|
subWorkflowTemplate: cfg.subWorkflow?.templateName,
|
|
1247
1273
|
reminderAfterDays: cfg.reminderAfterDays,
|
|
1248
1274
|
reminderEveryDays: cfg.reminderEveryDays,
|
|
@@ -1272,7 +1298,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1272
1298
|
timestamp: now,
|
|
1273
1299
|
...auditCtx
|
|
1274
1300
|
};
|
|
1275
|
-
const slaDeadlineAt = template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
1301
|
+
const slaDeadlineAt = template.slaDeadlineHours ? this.deadlineFromHours(now, template.slaDeadlineHours) : template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
1276
1302
|
const instance = {
|
|
1277
1303
|
id: instanceId,
|
|
1278
1304
|
tenantId: this.tenantId,
|
|
@@ -1299,7 +1325,9 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1299
1325
|
slaDeadlineAt,
|
|
1300
1326
|
templateSnapshot: {
|
|
1301
1327
|
escalation: template.escalation,
|
|
1328
|
+
escalationSteps: template.escalationSteps,
|
|
1302
1329
|
slaDeadlineDays: template.slaDeadlineDays,
|
|
1330
|
+
slaDeadlineHours: template.slaDeadlineHours,
|
|
1303
1331
|
allowOverride: template.allowOverride
|
|
1304
1332
|
}
|
|
1305
1333
|
};
|
|
@@ -2571,7 +2599,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
2571
2599
|
reason: opts.reason,
|
|
2572
2600
|
...auditCtx
|
|
2573
2601
|
};
|
|
2574
|
-
const slaDeadlineAt = template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
2602
|
+
const slaDeadlineAt = template.slaDeadlineHours ? this.deadlineFromHours(now, template.slaDeadlineHours) : template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
2575
2603
|
const newInstance = {
|
|
2576
2604
|
id: newInstanceId,
|
|
2577
2605
|
tenantId: this.tenantId,
|
|
@@ -2593,7 +2621,9 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
2593
2621
|
slaDeadlineAt,
|
|
2594
2622
|
templateSnapshot: {
|
|
2595
2623
|
escalation: template.escalation,
|
|
2624
|
+
escalationSteps: template.escalationSteps,
|
|
2596
2625
|
slaDeadlineDays: template.slaDeadlineDays,
|
|
2626
|
+
slaDeadlineHours: template.slaDeadlineHours,
|
|
2597
2627
|
allowOverride: template.allowOverride
|
|
2598
2628
|
}
|
|
2599
2629
|
};
|
|
@@ -3340,10 +3370,14 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
3340
3370
|
async escalateInternal(instanceId, escalatedBy = "system", auditCtx, levelNumber) {
|
|
3341
3371
|
return this.withOptimisticRetry(instanceId, async (instance) => {
|
|
3342
3372
|
if (instance.status !== "pending") return instance;
|
|
3343
|
-
const
|
|
3344
|
-
|
|
3373
|
+
const ladder = await this.escalationLadder(instance);
|
|
3374
|
+
const targetLevel = levelNumber === void 0 ? this.currentLevelInstance(instance) : instance.levels.find((l) => l.level === levelNumber) ?? this.currentLevelInstance(instance);
|
|
3375
|
+
const rungIndex = targetLevel.escalationStep ?? 0;
|
|
3376
|
+
const rung = ladder[rungIndex];
|
|
3377
|
+
const escalateTo = rung?.escalateTo ?? (instance.templateSnapshot?.escalation ?? (await this.registry.get(instance.templateName)).escalation)?.escalateTo;
|
|
3378
|
+
if (!escalateTo) return instance;
|
|
3345
3379
|
const newApprovers = await this.resolver.resolveApprovers(
|
|
3346
|
-
[
|
|
3380
|
+
[escalateTo],
|
|
3347
3381
|
instance.submittedBy,
|
|
3348
3382
|
instance.data,
|
|
3349
3383
|
this.opts.orgProvider,
|
|
@@ -3361,10 +3395,13 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
3361
3395
|
);
|
|
3362
3396
|
return instance;
|
|
3363
3397
|
}
|
|
3364
|
-
const level =
|
|
3398
|
+
const level = targetLevel;
|
|
3365
3399
|
level.approverIds = [.../* @__PURE__ */ new Set([...level.approverIds, ...filteredApprovers])];
|
|
3366
|
-
level.escalationDueAt = void 0;
|
|
3367
3400
|
const now = this.clock.now();
|
|
3401
|
+
level.escalationStep = rungIndex + 1;
|
|
3402
|
+
const nextRung = ladder[rungIndex + 1];
|
|
3403
|
+
const levelOpenedAt = this.levelOpenedAt(instance, level, now);
|
|
3404
|
+
level.escalationDueAt = nextRung ? this.stepDueAt(levelOpenedAt, nextRung) : void 0;
|
|
3368
3405
|
const escalatedTo = filteredApprovers[0] ?? "unknown";
|
|
3369
3406
|
const auditEntry = {
|
|
3370
3407
|
action: "escalated",
|
|
@@ -3536,6 +3573,69 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
3536
3573
|
deadlineFrom(from, days) {
|
|
3537
3574
|
return this.calendar ? this.calendar.addBusinessDays(from, days) : new Date(from.getTime() + days * 864e5);
|
|
3538
3575
|
}
|
|
3576
|
+
/**
|
|
3577
|
+
* Resolve an hour-based deadline.
|
|
3578
|
+
*
|
|
3579
|
+
* A calendar that only knows whole days — `weekendCalendar`, say — has no
|
|
3580
|
+
* `addBusinessHours`, and this falls back to elapsed clock time rather than
|
|
3581
|
+
* quietly pretending the calendar was applied. Configure
|
|
3582
|
+
* `businessHoursCalendar` to have hours skip evenings and weekends.
|
|
3583
|
+
*/
|
|
3584
|
+
deadlineFromHours(from, hours) {
|
|
3585
|
+
const addHours = this.calendar?.addBusinessHours?.bind(this.calendar);
|
|
3586
|
+
return addHours ? addHours(from, hours) : new Date(from.getTime() + hours * 36e5);
|
|
3587
|
+
}
|
|
3588
|
+
/** Level deadline from whichever of days/hours the template configured. */
|
|
3589
|
+
levelEscalationDue(from, level, firstRung) {
|
|
3590
|
+
if (level.escalationAfterHours) return this.deadlineFromHours(from, level.escalationAfterHours);
|
|
3591
|
+
if (level.escalationAfterDays) return this.deadlineFrom(from, level.escalationAfterDays);
|
|
3592
|
+
return firstRung ? this.stepDueAt(from, firstRung) : void 0;
|
|
3593
|
+
}
|
|
3594
|
+
/** First rung of a ladder, sorted by delay, or undefined when there is none. */
|
|
3595
|
+
firstRungOf(steps) {
|
|
3596
|
+
if (!steps || steps.length === 0) return void 0;
|
|
3597
|
+
return [...steps].sort(
|
|
3598
|
+
(a, b) => (a.afterHours ?? (a.afterDays ?? 0) * 24) - (b.afterHours ?? (b.afterDays ?? 0) * 24)
|
|
3599
|
+
)[0];
|
|
3600
|
+
}
|
|
3601
|
+
/**
|
|
3602
|
+
* The escalation ladder for an instance, sorted by delay.
|
|
3603
|
+
*
|
|
3604
|
+
* Read from the instance's template snapshot so an in-flight approval keeps
|
|
3605
|
+
* the ladder it was submitted under, exactly as the single-step
|
|
3606
|
+
* {@link EscalationConfig} already did.
|
|
3607
|
+
*/
|
|
3608
|
+
async escalationLadder(instance) {
|
|
3609
|
+
const snapshot = instance.templateSnapshot;
|
|
3610
|
+
const steps = snapshot?.escalationSteps ?? (await this.registry.get(instance.templateName)).escalationSteps ?? [];
|
|
3611
|
+
return [...steps].sort(
|
|
3612
|
+
(a, b) => (a.afterHours ?? (a.afterDays ?? 0) * 24) - (b.afterHours ?? (b.afterDays ?? 0) * 24)
|
|
3613
|
+
);
|
|
3614
|
+
}
|
|
3615
|
+
/**
|
|
3616
|
+
* When a level started collecting decisions.
|
|
3617
|
+
*
|
|
3618
|
+
* Escalation rungs are measured from this, not from the previous rung, so a
|
|
3619
|
+
* ladder reads the way it is written. Recovered from the audit trail — the
|
|
3620
|
+
* `submitted` entry for the opening level, the `level_advanced` entry
|
|
3621
|
+
* otherwise — and falls back to `now` when no entry exists, which only leaves
|
|
3622
|
+
* the ladder no worse off than the single-step behaviour it replaces.
|
|
3623
|
+
*/
|
|
3624
|
+
levelOpenedAt(instance, level, fallback) {
|
|
3625
|
+
for (let i = instance.auditLog.length - 1; i >= 0; i--) {
|
|
3626
|
+
const entry = instance.auditLog[i];
|
|
3627
|
+
if (!entry) continue;
|
|
3628
|
+
const opensThisLevel = (entry.action === "level_advanced" || entry.action === "submitted") && entry.level === level.level;
|
|
3629
|
+
if (opensThisLevel) return new Date(entry.timestamp);
|
|
3630
|
+
}
|
|
3631
|
+
return fallback;
|
|
3632
|
+
}
|
|
3633
|
+
/** Deadline for one rung, measured from when the level opened. */
|
|
3634
|
+
stepDueAt(from, step) {
|
|
3635
|
+
if (step.afterHours) return this.deadlineFromHours(from, step.afterHours);
|
|
3636
|
+
if (step.afterDays) return this.deadlineFrom(from, step.afterDays);
|
|
3637
|
+
return void 0;
|
|
3638
|
+
}
|
|
3539
3639
|
async requireInstance(id) {
|
|
3540
3640
|
const instance = await this.opts.adapter.getInstance(this.tenantId, id);
|
|
3541
3641
|
if (!instance) throw new ApprovalNotFoundError("Instance", id);
|
|
@@ -3628,6 +3728,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
3628
3728
|
}
|
|
3629
3729
|
/** Resolve approvers for a group, set its deadlines, and mark it pending. */
|
|
3630
3730
|
async activateGroup(instance, group, now) {
|
|
3731
|
+
const firstRung = this.firstRungOf(instance.templateSnapshot?.escalationSteps);
|
|
3631
3732
|
for (const lvl of group) {
|
|
3632
3733
|
if (lvl.subWorkflowTemplate) {
|
|
3633
3734
|
lvl.approverIds = [];
|
|
@@ -3641,9 +3742,8 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
3641
3742
|
now
|
|
3642
3743
|
);
|
|
3643
3744
|
}
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
}
|
|
3745
|
+
lvl.escalationDueAt = this.levelEscalationDue(now, lvl, firstRung);
|
|
3746
|
+
lvl.escalationStep = 0;
|
|
3647
3747
|
this.scheduleReminder(lvl, now);
|
|
3648
3748
|
lvl.status = "pending";
|
|
3649
3749
|
}
|
|
@@ -4284,7 +4384,74 @@ function weekendCalendar(options = {}) {
|
|
|
4284
4384
|
}
|
|
4285
4385
|
};
|
|
4286
4386
|
}
|
|
4387
|
+
var HOUR_MS = 36e5;
|
|
4388
|
+
function businessHoursCalendar(options = {}) {
|
|
4389
|
+
const startHour = options.workdayStartHour ?? 9;
|
|
4390
|
+
const endHour = options.workdayEndHour ?? 17;
|
|
4391
|
+
if (!(endHour > startHour)) {
|
|
4392
|
+
throw new Error(
|
|
4393
|
+
`businessHoursCalendar: workdayEndHour (${endHour}) must be greater than workdayStartHour (${startHour}).`
|
|
4394
|
+
);
|
|
4395
|
+
}
|
|
4396
|
+
const weekend = new Set(options.weekendDays ?? [0, 6]);
|
|
4397
|
+
const holidays = new Set((options.holidays ?? []).map(dayKey));
|
|
4398
|
+
const isBusinessDay = (d) => !weekend.has(d.getDay()) && !holidays.has(dayKey(d));
|
|
4399
|
+
const startOfWorkday = (d) => {
|
|
4400
|
+
const out = new Date(d.getTime());
|
|
4401
|
+
out.setHours(startHour, 0, 0, 0);
|
|
4402
|
+
return out;
|
|
4403
|
+
};
|
|
4404
|
+
const endOfWorkday = (d) => {
|
|
4405
|
+
const out = new Date(d.getTime());
|
|
4406
|
+
out.setHours(endHour, 0, 0, 0);
|
|
4407
|
+
return out;
|
|
4408
|
+
};
|
|
4409
|
+
const toWorkingMoment = (from) => {
|
|
4410
|
+
const cursor = new Date(from.getTime());
|
|
4411
|
+
for (let guard = 0; guard < 3660; guard++) {
|
|
4412
|
+
if (!isBusinessDay(cursor)) {
|
|
4413
|
+
cursor.setDate(cursor.getDate() + 1);
|
|
4414
|
+
cursor.setHours(startHour, 0, 0, 0);
|
|
4415
|
+
continue;
|
|
4416
|
+
}
|
|
4417
|
+
if (cursor.getTime() < startOfWorkday(cursor).getTime()) return startOfWorkday(cursor);
|
|
4418
|
+
if (cursor.getTime() >= endOfWorkday(cursor).getTime()) {
|
|
4419
|
+
cursor.setDate(cursor.getDate() + 1);
|
|
4420
|
+
cursor.setHours(startHour, 0, 0, 0);
|
|
4421
|
+
continue;
|
|
4422
|
+
}
|
|
4423
|
+
return cursor;
|
|
4424
|
+
}
|
|
4425
|
+
throw new Error(
|
|
4426
|
+
"businessHoursCalendar: no working day found within 10 years of the start date."
|
|
4427
|
+
);
|
|
4428
|
+
};
|
|
4429
|
+
const base = weekendCalendar(options);
|
|
4430
|
+
return {
|
|
4431
|
+
addBusinessDays: base.addBusinessDays,
|
|
4432
|
+
addBusinessHours(from, hours) {
|
|
4433
|
+
if (hours <= 0 || Number.isNaN(hours)) return new Date(from.getTime());
|
|
4434
|
+
let cursor = toWorkingMoment(from);
|
|
4435
|
+
let remainingMs = Math.round(hours * HOUR_MS);
|
|
4436
|
+
for (let guard = 0; remainingMs > 0 && guard < 3660; guard++) {
|
|
4437
|
+
const dayEnd = endOfWorkday(cursor);
|
|
4438
|
+
const availableMs = dayEnd.getTime() - cursor.getTime();
|
|
4439
|
+
if (remainingMs <= availableMs) {
|
|
4440
|
+
return new Date(cursor.getTime() + remainingMs);
|
|
4441
|
+
}
|
|
4442
|
+
remainingMs -= availableMs;
|
|
4443
|
+
const next = new Date(cursor.getTime());
|
|
4444
|
+
next.setDate(next.getDate() + 1);
|
|
4445
|
+
next.setHours(startHour, 0, 0, 0);
|
|
4446
|
+
cursor = toWorkingMoment(next);
|
|
4447
|
+
}
|
|
4448
|
+
throw new Error(
|
|
4449
|
+
`businessHoursCalendar: ${hours} working hours could not be scheduled within 10 years \u2014 check weekendDays and holidays.`
|
|
4450
|
+
);
|
|
4451
|
+
}
|
|
4452
|
+
};
|
|
4453
|
+
}
|
|
4287
4454
|
|
|
4288
|
-
export { ApprovalConflictError, ApprovalEngine, ApprovalError, ApprovalForbiddenError, ApprovalNotFoundError, ApprovalTemplateNotFoundError, ApprovalValidationError, EscalationScheduler, MemoryAdapter, TEMPLATE_BUNDLE_VERSION, defaultIdGenerator, noopLogger, systemClock, toComparableNumber, validateConditionExpression, weekendCalendar };
|
|
4455
|
+
export { ApprovalConflictError, ApprovalEngine, ApprovalError, ApprovalForbiddenError, ApprovalNotFoundError, ApprovalTemplateNotFoundError, ApprovalValidationError, EscalationScheduler, MemoryAdapter, TEMPLATE_BUNDLE_VERSION, businessHoursCalendar, defaultIdGenerator, noopLogger, systemClock, toComparableNumber, validateConditionExpression, weekendCalendar };
|
|
4289
4456
|
//# sourceMappingURL=index.js.map
|
|
4290
4457
|
//# sourceMappingURL=index.js.map
|