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.
- package/CHANGELOG.md +74 -0
- package/README.md +27 -0
- package/dist/{ApprovalEngine-Ci9urTDI.d.ts → ApprovalEngine-BHWls8-z.d.ts} +59 -7
- package/dist/{ApprovalEngine-C1dUPnLM.d.cts → ApprovalEngine-CW7Uu1bV.d.cts} +59 -7
- package/dist/{IAuditAdapter-C4OASS6u.d.ts → IAuditAdapter-CMCirvY-.d.ts} +1 -1
- package/dist/{IAuditAdapter-BHVKMMv6.d.cts → IAuditAdapter-Dwx8ZP4J.d.cts} +1 -1
- package/dist/{IAuthorizationPolicy-CtLkwQ8I.d.cts → IAuthorizationPolicy-B3vaV69v.d.cts} +1 -1
- package/dist/{IAuthorizationPolicy-BA_m2Dg9.d.ts → IAuthorizationPolicy-BryQIKko.d.ts} +1 -1
- package/dist/{INotificationAdapter-JJAiMyMd.d.cts → INotificationAdapter-AwbhZEqz.d.cts} +1 -1
- package/dist/{INotificationAdapter-BdgfUrIn.d.ts → INotificationAdapter-BBq2czpO.d.ts} +1 -1
- package/dist/{IOperationMiddleware-D0ksiFM3.d.cts → IOperationMiddleware-3J-_NSGq.d.cts} +1 -1
- package/dist/{IOperationMiddleware-BGYrAqep.d.ts → IOperationMiddleware-CqzFklMn.d.ts} +1 -1
- package/dist/{IStorageAdapter-BU3sau5W.d.ts → IStorageAdapter-Dw2xXP9B.d.ts} +1 -1
- package/dist/{IStorageAdapter-DdHO4Rf1.d.cts → IStorageAdapter-Pq9J60xv.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 +117 -6
- 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 +117 -7
- package/dist/index.js.map +1 -1
- package/dist/{instance-DUJY_Axf.d.cts → instance-BO-i9-nq.d.cts} +13 -0
- package/dist/{instance-DUJY_Axf.d.ts → instance-BO-i9-nq.d.ts} +13 -0
- package/dist/nestjs.cjs +49 -6
- 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 +49 -6
- 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.cjs +105 -9
- package/dist/plugins/notify.cjs.map +1 -1
- package/dist/plugins/notify.d.cts +91 -3
- package/dist/plugins/notify.d.ts +91 -3
- package/dist/plugins/notify.js +105 -10
- package/dist/plugins/notify.js.map +1 -1
- 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 +49 -6
- 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 +49 -6
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
|
@@ -59,6 +59,12 @@ interface ApprovalLevelConfig {
|
|
|
59
59
|
*/
|
|
60
60
|
subWorkflow?: SubWorkflowConfig;
|
|
61
61
|
escalationAfterDays?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Escalate this level after this many **working hours**, for SLAs quoted in
|
|
64
|
+
* hours rather than days. Mutually exclusive with
|
|
65
|
+
* {@link escalationAfterDays}.
|
|
66
|
+
*/
|
|
67
|
+
escalationAfterHours?: number;
|
|
62
68
|
/**
|
|
63
69
|
* Send a reminder to this level's pending approvers this many days after the
|
|
64
70
|
* level opens. Escalation reassigns work; a reminder only nudges, which is
|
|
@@ -166,6 +172,11 @@ interface ApprovalTemplateConfig {
|
|
|
166
172
|
escalation?: EscalationConfig;
|
|
167
173
|
/** Overall SLA for the entire workflow in days. Emits approval:sla_breached when elapsed. */
|
|
168
174
|
slaDeadlineDays?: number;
|
|
175
|
+
/**
|
|
176
|
+
* Overall SLA in **working hours**. Mutually exclusive with
|
|
177
|
+
* {@link slaDeadlineDays}.
|
|
178
|
+
*/
|
|
179
|
+
slaDeadlineHours?: number;
|
|
169
180
|
/** Allow emergency override (bypass remaining levels). Must be true to use engine.override(). */
|
|
170
181
|
allowOverride?: boolean;
|
|
171
182
|
}
|
|
@@ -227,6 +238,7 @@ interface ApprovalLevelInstance {
|
|
|
227
238
|
weights?: Record<string, number>;
|
|
228
239
|
escalationDueAt?: Date;
|
|
229
240
|
escalationAfterDays?: number;
|
|
241
|
+
escalationAfterHours?: number;
|
|
230
242
|
/** When the next reminder for this level is due; cleared when the level closes. */
|
|
231
243
|
reminderDueAt?: Date;
|
|
232
244
|
/** How many reminders have already been sent for this level. */
|
|
@@ -281,6 +293,7 @@ interface InfoRequest {
|
|
|
281
293
|
interface TemplateSnapshot {
|
|
282
294
|
escalation?: EscalationConfig;
|
|
283
295
|
slaDeadlineDays?: number;
|
|
296
|
+
slaDeadlineHours?: number;
|
|
284
297
|
allowOverride?: boolean;
|
|
285
298
|
}
|
|
286
299
|
interface ApprovalInstance {
|
|
@@ -59,6 +59,12 @@ interface ApprovalLevelConfig {
|
|
|
59
59
|
*/
|
|
60
60
|
subWorkflow?: SubWorkflowConfig;
|
|
61
61
|
escalationAfterDays?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Escalate this level after this many **working hours**, for SLAs quoted in
|
|
64
|
+
* hours rather than days. Mutually exclusive with
|
|
65
|
+
* {@link escalationAfterDays}.
|
|
66
|
+
*/
|
|
67
|
+
escalationAfterHours?: number;
|
|
62
68
|
/**
|
|
63
69
|
* Send a reminder to this level's pending approvers this many days after the
|
|
64
70
|
* level opens. Escalation reassigns work; a reminder only nudges, which is
|
|
@@ -166,6 +172,11 @@ interface ApprovalTemplateConfig {
|
|
|
166
172
|
escalation?: EscalationConfig;
|
|
167
173
|
/** Overall SLA for the entire workflow in days. Emits approval:sla_breached when elapsed. */
|
|
168
174
|
slaDeadlineDays?: number;
|
|
175
|
+
/**
|
|
176
|
+
* Overall SLA in **working hours**. Mutually exclusive with
|
|
177
|
+
* {@link slaDeadlineDays}.
|
|
178
|
+
*/
|
|
179
|
+
slaDeadlineHours?: number;
|
|
169
180
|
/** Allow emergency override (bypass remaining levels). Must be true to use engine.override(). */
|
|
170
181
|
allowOverride?: boolean;
|
|
171
182
|
}
|
|
@@ -227,6 +238,7 @@ interface ApprovalLevelInstance {
|
|
|
227
238
|
weights?: Record<string, number>;
|
|
228
239
|
escalationDueAt?: Date;
|
|
229
240
|
escalationAfterDays?: number;
|
|
241
|
+
escalationAfterHours?: number;
|
|
230
242
|
/** When the next reminder for this level is due; cleared when the level closes. */
|
|
231
243
|
reminderDueAt?: Date;
|
|
232
244
|
/** How many reminders have already been sent for this level. */
|
|
@@ -281,6 +293,7 @@ interface InfoRequest {
|
|
|
281
293
|
interface TemplateSnapshot {
|
|
282
294
|
escalation?: EscalationConfig;
|
|
283
295
|
slaDeadlineDays?: number;
|
|
296
|
+
slaDeadlineHours?: number;
|
|
284
297
|
allowOverride?: boolean;
|
|
285
298
|
}
|
|
286
299
|
interface ApprovalInstance {
|
package/dist/nestjs.cjs
CHANGED
|
@@ -1072,6 +1072,18 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1072
1072
|
message: `Level ${l.level} sets reminderEveryDays without reminderAfterDays, so no reminder would ever be sent.`
|
|
1073
1073
|
});
|
|
1074
1074
|
}
|
|
1075
|
+
if (l.escalationAfterHours !== void 0 && l.escalationAfterHours <= 0) {
|
|
1076
|
+
errors.push({
|
|
1077
|
+
field: `levels[${i}].escalationAfterHours`,
|
|
1078
|
+
message: `Level ${l.level} escalationAfterHours must be a positive number.`
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
if (l.escalationAfterDays !== void 0 && l.escalationAfterHours !== void 0) {
|
|
1082
|
+
errors.push({
|
|
1083
|
+
field: `levels[${i}].escalationAfterHours`,
|
|
1084
|
+
message: `Level ${l.level} sets both escalationAfterDays and escalationAfterHours; pick one so the deadline is unambiguous.`
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1075
1087
|
if (l.escalationAfterDays !== void 0 && l.escalationAfterDays <= 0) {
|
|
1076
1088
|
errors.push({
|
|
1077
1089
|
field: `levels[${i}].escalationAfterDays`,
|
|
@@ -1136,6 +1148,18 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1136
1148
|
}
|
|
1137
1149
|
});
|
|
1138
1150
|
}
|
|
1151
|
+
if (config.slaDeadlineDays !== void 0 && config.slaDeadlineHours !== void 0) {
|
|
1152
|
+
errors.push({
|
|
1153
|
+
field: "slaDeadlineHours",
|
|
1154
|
+
message: "Template sets both slaDeadlineDays and slaDeadlineHours; pick one so the SLA is unambiguous."
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
if (config.slaDeadlineHours !== void 0 && config.slaDeadlineHours <= 0) {
|
|
1158
|
+
errors.push({
|
|
1159
|
+
field: "slaDeadlineHours",
|
|
1160
|
+
message: "slaDeadlineHours must be a positive number."
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1139
1163
|
if (config.conditions) {
|
|
1140
1164
|
config.conditions.forEach((rule, ruleIdx) => {
|
|
1141
1165
|
errors.push(...validateConditionExpression(rule.when, `conditions[${ruleIdx}].when`));
|
|
@@ -1249,7 +1273,8 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1249
1273
|
threshold: cfg.threshold,
|
|
1250
1274
|
weights: cfg.weights,
|
|
1251
1275
|
escalationAfterDays: cfg.escalationAfterDays,
|
|
1252
|
-
|
|
1276
|
+
escalationAfterHours: cfg.escalationAfterHours,
|
|
1277
|
+
escalationDueAt: inFirstGroup ? this.levelEscalationDue(now, cfg) : void 0,
|
|
1253
1278
|
subWorkflowTemplate: cfg.subWorkflow?.templateName,
|
|
1254
1279
|
reminderAfterDays: cfg.reminderAfterDays,
|
|
1255
1280
|
reminderEveryDays: cfg.reminderEveryDays,
|
|
@@ -1279,7 +1304,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1279
1304
|
timestamp: now,
|
|
1280
1305
|
...auditCtx
|
|
1281
1306
|
};
|
|
1282
|
-
const slaDeadlineAt = template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
1307
|
+
const slaDeadlineAt = template.slaDeadlineHours ? this.deadlineFromHours(now, template.slaDeadlineHours) : template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
1283
1308
|
const instance = {
|
|
1284
1309
|
id: instanceId,
|
|
1285
1310
|
tenantId: this.tenantId,
|
|
@@ -1307,6 +1332,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1307
1332
|
templateSnapshot: {
|
|
1308
1333
|
escalation: template.escalation,
|
|
1309
1334
|
slaDeadlineDays: template.slaDeadlineDays,
|
|
1335
|
+
slaDeadlineHours: template.slaDeadlineHours,
|
|
1310
1336
|
allowOverride: template.allowOverride
|
|
1311
1337
|
}
|
|
1312
1338
|
};
|
|
@@ -2578,7 +2604,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
2578
2604
|
reason: opts.reason,
|
|
2579
2605
|
...auditCtx
|
|
2580
2606
|
};
|
|
2581
|
-
const slaDeadlineAt = template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
2607
|
+
const slaDeadlineAt = template.slaDeadlineHours ? this.deadlineFromHours(now, template.slaDeadlineHours) : template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
2582
2608
|
const newInstance = {
|
|
2583
2609
|
id: newInstanceId,
|
|
2584
2610
|
tenantId: this.tenantId,
|
|
@@ -2601,6 +2627,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
2601
2627
|
templateSnapshot: {
|
|
2602
2628
|
escalation: template.escalation,
|
|
2603
2629
|
slaDeadlineDays: template.slaDeadlineDays,
|
|
2630
|
+
slaDeadlineHours: template.slaDeadlineHours,
|
|
2604
2631
|
allowOverride: template.allowOverride
|
|
2605
2632
|
}
|
|
2606
2633
|
};
|
|
@@ -3543,6 +3570,24 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
3543
3570
|
deadlineFrom(from, days) {
|
|
3544
3571
|
return this.calendar ? this.calendar.addBusinessDays(from, days) : new Date(from.getTime() + days * 864e5);
|
|
3545
3572
|
}
|
|
3573
|
+
/**
|
|
3574
|
+
* Resolve an hour-based deadline.
|
|
3575
|
+
*
|
|
3576
|
+
* A calendar that only knows whole days — `weekendCalendar`, say — has no
|
|
3577
|
+
* `addBusinessHours`, and this falls back to elapsed clock time rather than
|
|
3578
|
+
* quietly pretending the calendar was applied. Configure
|
|
3579
|
+
* `businessHoursCalendar` to have hours skip evenings and weekends.
|
|
3580
|
+
*/
|
|
3581
|
+
deadlineFromHours(from, hours) {
|
|
3582
|
+
const addHours = this.calendar?.addBusinessHours?.bind(this.calendar);
|
|
3583
|
+
return addHours ? addHours(from, hours) : new Date(from.getTime() + hours * 36e5);
|
|
3584
|
+
}
|
|
3585
|
+
/** Level deadline from whichever of days/hours the template configured. */
|
|
3586
|
+
levelEscalationDue(from, level) {
|
|
3587
|
+
if (level.escalationAfterHours) return this.deadlineFromHours(from, level.escalationAfterHours);
|
|
3588
|
+
if (level.escalationAfterDays) return this.deadlineFrom(from, level.escalationAfterDays);
|
|
3589
|
+
return void 0;
|
|
3590
|
+
}
|
|
3546
3591
|
async requireInstance(id) {
|
|
3547
3592
|
const instance = await this.opts.adapter.getInstance(this.tenantId, id);
|
|
3548
3593
|
if (!instance) throw new ApprovalNotFoundError("Instance", id);
|
|
@@ -3648,9 +3693,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
3648
3693
|
now
|
|
3649
3694
|
);
|
|
3650
3695
|
}
|
|
3651
|
-
|
|
3652
|
-
lvl.escalationDueAt = this.deadlineFrom(now, lvl.escalationAfterDays);
|
|
3653
|
-
}
|
|
3696
|
+
lvl.escalationDueAt = this.levelEscalationDue(now, lvl);
|
|
3654
3697
|
this.scheduleReminder(lvl, now);
|
|
3655
3698
|
lvl.status = "pending";
|
|
3656
3699
|
}
|