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.
- package/CHANGELOG.md +20 -4
- package/dist/{ApprovalEngine-Dqe-rC6K.d.ts → ApprovalEngine-BtgQDr_8.d.ts} +13 -9
- package/dist/{ApprovalEngine-XgGBQLC_.d.cts → ApprovalEngine-Djdw-JzA.d.cts} +13 -9
- package/dist/Clock-D8Qp39q_.d.cts +8 -0
- package/dist/Clock-D8Qp39q_.d.ts +8 -0
- package/dist/{IAuditAdapter-DkbyFFjb.d.ts → IAuditAdapter-C-04ar9A.d.ts} +1 -1
- package/dist/{IAuditAdapter-Dt8qjkQL.d.cts → IAuditAdapter-TT2Xhtu6.d.cts} +1 -1
- package/dist/{IAuthorizationPolicy-eo6glbpE.d.ts → IAuthorizationPolicy-D6tTT3gv.d.cts} +6 -1
- package/dist/{IAuthorizationPolicy-YJ_08Mof.d.cts → IAuthorizationPolicy-wIE1mpTy.d.ts} +6 -1
- package/dist/{IMetricsAdapter-DWq8IFaf.d.cts → IMetricsAdapter-WROxYSXd.d.cts} +1 -0
- package/dist/{IMetricsAdapter-DWq8IFaf.d.ts → IMetricsAdapter-WROxYSXd.d.ts} +1 -0
- package/dist/{INotificationAdapter-BTO9FSKy.d.ts → INotificationAdapter-BcUskJ6z.d.ts} +9 -1
- package/dist/{INotificationAdapter-XbD69gss.d.cts → INotificationAdapter-CBsJDk-Y.d.cts} +9 -1
- package/dist/{IOperationMiddleware-CX7lo4lp.d.ts → IOperationMiddleware-C7ciNnJp.d.ts} +3 -2
- package/dist/{IOperationMiddleware-BBLk8MIz.d.cts → IOperationMiddleware-D3Yelw9R.d.cts} +3 -2
- package/dist/{IStorageAdapter-CdXMggPK.d.ts → IStorageAdapter-CVvNsnnD.d.ts} +7 -1
- package/dist/{IStorageAdapter-Cjw5zDwQ.d.cts → IStorageAdapter-D7cy20uD.d.cts} +7 -1
- package/dist/{Logger-BplhlU7l.d.cts → Logger-hqFbBCtB.d.cts} +7 -0
- package/dist/{Logger-BplhlU7l.d.ts → Logger-hqFbBCtB.d.ts} +7 -0
- 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 +32 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -13
- package/dist/index.d.ts +13 -13
- package/dist/index.js +32 -3
- package/dist/index.js.map +1 -1
- package/dist/{instance-uUQh5XW3.d.cts → instance-Dd1jQizG.d.cts} +19 -0
- package/dist/{instance-uUQh5XW3.d.ts → instance-Dd1jQizG.d.ts} +19 -0
- package/dist/nestjs.cjs +31 -2
- package/dist/nestjs.cjs.map +1 -1
- package/dist/nestjs.d.cts +10 -10
- package/dist/nestjs.d.ts +10 -10
- package/dist/nestjs.js +31 -2
- package/dist/nestjs.js.map +1 -1
- package/dist/plugins/audit.cjs.map +1 -1
- package/dist/plugins/audit.d.cts +3 -3
- package/dist/plugins/audit.d.ts +3 -3
- package/dist/plugins/audit.js.map +1 -1
- package/dist/plugins/metrics.cjs.map +1 -1
- package/dist/plugins/metrics.d.cts +2 -2
- package/dist/plugins/metrics.d.ts +2 -2
- package/dist/plugins/metrics.js.map +1 -1
- package/dist/plugins/notify.cjs.map +1 -1
- package/dist/plugins/notify.d.cts +4 -4
- package/dist/plugins/notify.d.ts +4 -4
- package/dist/plugins/notify.js.map +1 -1
- package/dist/plugins/resilience.cjs.map +1 -1
- package/dist/plugins/resilience.d.cts +5 -5
- package/dist/plugins/resilience.d.ts +5 -5
- package/dist/plugins/resilience.js.map +1 -1
- package/dist/plugins/scheduler.cjs.map +1 -1
- package/dist/plugins/scheduler.d.cts +2 -2
- package/dist/plugins/scheduler.d.ts +2 -2
- package/dist/plugins/scheduler.js.map +1 -1
- package/dist/plugins/tracing.d.cts +2 -2
- package/dist/plugins/tracing.d.ts +2 -2
- package/dist/plugins/webhook.cjs.map +1 -1
- package/dist/plugins/webhook.d.cts +4 -4
- package/dist/plugins/webhook.d.ts +4 -4
- package/dist/plugins/webhook.js.map +1 -1
- package/dist/testing.cjs +31 -2
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +10 -10
- package/dist/testing.d.ts +10 -10
- package/dist/testing.js +31 -2
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
- package/dist/Clock-3FnOczFJ.d.cts +0 -6
- 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
|
-
`
|
|
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
|
}
|