hierarchical-approval 4.0.3 → 4.0.5
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 +28 -6
- package/README.md +1 -1
- package/dist/{ApprovalEngine-Djdw-JzA.d.cts → ApprovalEngine-BaJ2PNB7.d.cts} +29 -7
- package/dist/{ApprovalEngine-BtgQDr_8.d.ts → ApprovalEngine-CeBKYLz4.d.ts} +29 -7
- package/dist/{IAuditAdapter-TT2Xhtu6.d.cts → IAuditAdapter-Dqpkg-Tz.d.cts} +2 -1
- package/dist/{IAuditAdapter-C-04ar9A.d.ts → IAuditAdapter-ggCTPf02.d.ts} +2 -1
- package/dist/{IAuthorizationPolicy-wIE1mpTy.d.ts → IAuthorizationPolicy-BUHKvalu.d.ts} +1 -1
- package/dist/{IAuthorizationPolicy-D6tTT3gv.d.cts → IAuthorizationPolicy-D5mAFBTC.d.cts} +1 -1
- package/dist/{INotificationAdapter-BcUskJ6z.d.ts → INotificationAdapter-B_9JFH1i.d.ts} +18 -1
- package/dist/{INotificationAdapter-CBsJDk-Y.d.cts → INotificationAdapter-D9IOl9G_.d.cts} +18 -1
- package/dist/{IOperationMiddleware-C7ciNnJp.d.ts → IOperationMiddleware-C-N8MpHV.d.ts} +13 -1
- package/dist/{IOperationMiddleware-D3Yelw9R.d.cts → IOperationMiddleware-CsF_xD4b.d.cts} +13 -1
- package/dist/{ISchedulerAdapter-DKv_QjVN.d.cts → ISchedulerAdapter-CLMdBA-5.d.cts} +1 -0
- package/dist/{ISchedulerAdapter-DKv_QjVN.d.ts → ISchedulerAdapter-CLMdBA-5.d.ts} +1 -0
- package/dist/{IStorageAdapter-D7cy20uD.d.cts → IStorageAdapter-CrbVtEE-.d.cts} +8 -1
- package/dist/{IStorageAdapter-CVvNsnnD.d.ts → IStorageAdapter-DDOW9z0L.d.ts} +8 -1
- package/dist/adapters/MemoryAdapter.cjs.map +1 -1
- package/dist/adapters/MemoryAdapter.d.cts +2 -2
- package/dist/adapters/MemoryAdapter.d.ts +2 -2
- package/dist/adapters/MemoryAdapter.js.map +1 -1
- package/dist/adapters/PostgresAdapter.cjs.map +1 -1
- package/dist/adapters/PostgresAdapter.d.cts +2 -2
- package/dist/adapters/PostgresAdapter.d.ts +2 -2
- package/dist/adapters/PostgresAdapter.js.map +1 -1
- package/dist/index.cjs +27 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.js +27 -13
- package/dist/index.js.map +1 -1
- package/dist/{instance-Dd1jQizG.d.cts → instance-Cu6-qPxs.d.cts} +27 -0
- package/dist/{instance-Dd1jQizG.d.ts → instance-Cu6-qPxs.d.ts} +27 -0
- package/dist/nestjs.cjs +35 -10
- package/dist/nestjs.cjs.map +1 -1
- package/dist/nestjs.d.cts +19 -8
- package/dist/nestjs.d.ts +19 -8
- package/dist/nestjs.js +35 -10
- package/dist/nestjs.js.map +1 -1
- package/dist/plugins/audit.cjs +30 -1
- package/dist/plugins/audit.cjs.map +1 -1
- package/dist/plugins/audit.d.cts +4 -4
- package/dist/plugins/audit.d.ts +4 -4
- package/dist/plugins/audit.js +30 -1
- package/dist/plugins/audit.js.map +1 -1
- package/dist/plugins/notify.cjs +30 -1
- package/dist/plugins/notify.cjs.map +1 -1
- package/dist/plugins/notify.d.cts +2 -2
- package/dist/plugins/notify.d.ts +2 -2
- package/dist/plugins/notify.js +30 -1
- package/dist/plugins/notify.js.map +1 -1
- package/dist/plugins/resilience.cjs +11 -4
- package/dist/plugins/resilience.cjs.map +1 -1
- package/dist/plugins/resilience.d.cts +3 -3
- package/dist/plugins/resilience.d.ts +3 -3
- package/dist/plugins/resilience.js +11 -4
- package/dist/plugins/resilience.js.map +1 -1
- package/dist/plugins/scheduler.d.cts +1 -1
- package/dist/plugins/scheduler.d.ts +1 -1
- package/dist/plugins/tracing.cjs.map +1 -1
- package/dist/plugins/tracing.d.cts +4 -2
- package/dist/plugins/tracing.d.ts +4 -2
- package/dist/plugins/tracing.js.map +1 -1
- package/dist/plugins/webhook.d.cts +2 -2
- package/dist/plugins/webhook.d.ts +2 -2
- package/dist/testing.cjs +24 -10
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +8 -8
- package/dist/testing.d.ts +8 -8
- package/dist/testing.js +24 -10
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
## 4.0.3 - 2026-09-22
|
|
2
|
-
|
|
3
|
-
- fix(engine): name the level in assertApproverOnLevel's error
|
|
4
|
-
- fix(utils): name the offending start date in businessHoursCalendar's error
|
|
5
|
-
- fix(engine): name the offending instance id in assertStatus's error
|
|
6
|
-
|
|
7
1
|
# Changelog
|
|
8
2
|
|
|
9
3
|
All notable changes to `hierarchical-approval` are documented here. This project
|
|
@@ -11,6 +5,34 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
11
5
|
|
|
12
6
|
## [Unreleased]
|
|
13
7
|
|
|
8
|
+
## [4.0.5] - 2026-09-25
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- `RateLimitMiddleware`'s constructor validation now throws
|
|
13
|
+
`ApprovalValidationError`, instead of a plain `Error` a caller catching
|
|
14
|
+
`ApprovalValidationError` to distinguish config problems would miss.
|
|
15
|
+
- `DigestNotificationAdapter`'s `intervalMs` validation now throws
|
|
16
|
+
`ApprovalValidationError` for the same reason.
|
|
17
|
+
- `HashChainAuditAdapter`'s missing-`reader` validation now throws
|
|
18
|
+
`ApprovalValidationError` for the same reason.
|
|
19
|
+
- `businessHoursCalendar`'s three configuration guards now throw
|
|
20
|
+
`ApprovalValidationError` for the same reason.
|
|
21
|
+
|
|
22
|
+
## [4.0.4] - 2026-09-24
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- `resubmit`'s duplicate-level error now names the offending level numbers,
|
|
27
|
+
instead of a message that gave no clue which levels collided.
|
|
28
|
+
- `purgeInstances`'s date-validation error now names the offending value,
|
|
29
|
+
instead of a message that gave no clue which value failed.
|
|
30
|
+
- `delegate`'s, `reassign`'s, and `override`'s self-action guards now name
|
|
31
|
+
the offending id, matching every sibling guard in the same methods that
|
|
32
|
+
already named theirs.
|
|
33
|
+
|
|
34
|
+
## [4.0.3] - 2026-09-22
|
|
35
|
+
|
|
14
36
|
### Fixed
|
|
15
37
|
|
|
16
38
|
- `assertStatus`'s `INVALID_STATUS` error now names the instance that failed
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Multi-tenant · audit-ready · fully pluggable · zero runtime dependencies you
|
|
|
11
11
|
[](https://www.typescriptlang.org/)
|
|
12
12
|
[](https://bundlephobia.com/package/hierarchical-approval)
|
|
13
13
|
[](./LICENSE)
|
|
14
|
-
[](./tests)
|
|
15
15
|
|
|
16
16
|
[**Documentation**](https://hierarchical-approval.matthewswong.com) ·
|
|
17
17
|
[**npm**](https://www.npmjs.com/package/hierarchical-approval) ·
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-
|
|
2
|
-
import { m as ConditionExpression, r as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, C as Comment, e as ApprovalMode, f as ApprovalStatus, L as LevelStatus, b as AuditEntry } from './instance-
|
|
3
|
-
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-
|
|
1
|
+
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-CrbVtEE-.cjs';
|
|
2
|
+
import { m as ConditionExpression, r as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, C as Comment, e as ApprovalMode, f as ApprovalStatus, L as LevelStatus, b as AuditEntry } from './instance-Cu6-qPxs.cjs';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-D9IOl9G_.cjs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { L as Logger } from './Logger-hqFbBCtB.cjs';
|
|
6
6
|
import { C as Clock } from './Clock-D8Qp39q_.cjs';
|
|
7
|
-
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-
|
|
8
|
-
import { I as IAuditAdapter } from './IAuditAdapter-
|
|
7
|
+
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-CsF_xD4b.cjs';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-Dqpkg-Tz.cjs';
|
|
9
9
|
import { I as IMetricsAdapter } from './IMetricsAdapter-WROxYSXd.cjs';
|
|
10
|
-
import { I as ISchedulerAdapter } from './ISchedulerAdapter-
|
|
11
|
-
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-
|
|
10
|
+
import { I as ISchedulerAdapter } from './ISchedulerAdapter-CLMdBA-5.cjs';
|
|
11
|
+
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-D5mAFBTC.cjs';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -208,6 +208,7 @@ type IdGeneratorFn = (prefix: IdGeneratorPrefix) => string;
|
|
|
208
208
|
/** {@link IdGeneratorFn} used when no custom generator is configured. */
|
|
209
209
|
declare const defaultIdGenerator: IdGeneratorFn;
|
|
210
210
|
|
|
211
|
+
/** Backs `{ type: 'role' }` approvers, so a level can name an org role instead of a fixed user id. */
|
|
211
212
|
interface OrgProvider {
|
|
212
213
|
/** Required: resolve users holding a named role. */
|
|
213
214
|
getUsersByRole(role: string, tenantId?: string): Promise<string[]> | string[];
|
|
@@ -237,6 +238,11 @@ interface OutOfOfficeProvider {
|
|
|
237
238
|
*/
|
|
238
239
|
getDelegateFor(userId: string, at: Date): Promise<string | null | undefined> | string | null | undefined;
|
|
239
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Resolves a custom approver type's config to concrete user ids. Registered
|
|
243
|
+
* via {@link ApprovalEngine.registerApproverType} to back a level whose
|
|
244
|
+
* approver config carries an app-defined `type`.
|
|
245
|
+
*/
|
|
240
246
|
type ApproverResolverFn = (config: Record<string, unknown>, ctx: {
|
|
241
247
|
submittedBy: string;
|
|
242
248
|
data: Record<string, unknown>;
|
|
@@ -283,6 +289,7 @@ declare function validateConditionExpression(expression: ConditionExpression, pa
|
|
|
283
289
|
message: string;
|
|
284
290
|
}>;
|
|
285
291
|
|
|
292
|
+
/** Outcome of {@link ApprovalEngine.validateTemplate}. */
|
|
286
293
|
interface ValidationResult {
|
|
287
294
|
valid: boolean;
|
|
288
295
|
errors: Array<{
|
|
@@ -290,13 +297,16 @@ interface ValidationResult {
|
|
|
290
297
|
message: string;
|
|
291
298
|
}>;
|
|
292
299
|
}
|
|
300
|
+
/** Outcome of {@link ApprovalEngine.canApprove}. */
|
|
293
301
|
interface CanApproveResult {
|
|
294
302
|
eligible: boolean;
|
|
295
303
|
reason?: 'not_an_approver' | 'already_acted' | 'self_approval' | 'wrong_status' | 'delegated_away';
|
|
296
304
|
}
|
|
305
|
+
/** One entry in a {@link ApprovalEngine.preview} result, resolved against the given data without persisting anything. */
|
|
297
306
|
interface PreviewChainLevel {
|
|
298
307
|
level: number;
|
|
299
308
|
name: string;
|
|
309
|
+
/** Approver ids resolved as of preview time; dynamic/role approvers may resolve differently at submit time. */
|
|
300
310
|
resolvedApprovers: string[];
|
|
301
311
|
mode: ApprovalMode;
|
|
302
312
|
}
|
|
@@ -390,12 +400,16 @@ interface SimulationResult {
|
|
|
390
400
|
/** True when the script ran out before the approval finished. */
|
|
391
401
|
incomplete: boolean;
|
|
392
402
|
}
|
|
403
|
+
/** Outcome of a {@link ApprovalEngine.bulkApprove}/{@link ApprovalEngine.bulkReject} sweep. */
|
|
393
404
|
interface BulkResult {
|
|
405
|
+
/** Instances that reached the requested decision. */
|
|
394
406
|
succeeded: ApprovalInstance[];
|
|
407
|
+
/** Instances that could not be decided, with the reason. */
|
|
395
408
|
failed: Array<{
|
|
396
409
|
instanceId: string;
|
|
397
410
|
error: ApprovalError;
|
|
398
411
|
}>;
|
|
412
|
+
/** Number of instance ids passed in, equal to `succeeded.length + failed.length`. */
|
|
399
413
|
total: number;
|
|
400
414
|
}
|
|
401
415
|
/** Outcome of a {@link ApprovalEngine.transferApprovals} sweep. */
|
|
@@ -541,6 +555,7 @@ interface CycleTimeStats {
|
|
|
541
555
|
/** Largest observed time-to-decision. `0` when {@link count} is `0`. */
|
|
542
556
|
maxMs: number;
|
|
543
557
|
}
|
|
558
|
+
/** Outcome of {@link ApprovalEngine.healthCheck}. */
|
|
544
559
|
interface HealthResult {
|
|
545
560
|
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
546
561
|
adapter: 'connected' | 'error';
|
|
@@ -549,12 +564,18 @@ interface HealthResult {
|
|
|
549
564
|
escalationRunning: boolean;
|
|
550
565
|
lastTickAt?: Date;
|
|
551
566
|
}
|
|
567
|
+
/** Controls how {@link ApprovalEngine}'s optimistic-lock retry backs off on write conflicts. Defaults to 3 attempts with a 50ms base delay. */
|
|
552
568
|
interface RetryPolicy {
|
|
569
|
+
/** Number of attempts before giving up and rethrowing the conflict. */
|
|
553
570
|
maxAttempts: number;
|
|
571
|
+
/** Linear backoff unit; attempt N waits roughly `baseDelayMs * N` before retrying. */
|
|
554
572
|
baseDelayMs: number;
|
|
573
|
+
/** Upper bound on the computed delay. Defaults to unbounded. */
|
|
555
574
|
maxDelayMs?: number;
|
|
575
|
+
/** Adds up to one `baseDelayMs` of random jitter to each delay. Defaults to `true`. */
|
|
556
576
|
jitter?: boolean;
|
|
557
577
|
}
|
|
578
|
+
/** Derives the idempotency key used to dedupe a {@link ApprovalEngine.submit} call from its tenant and document identity. */
|
|
558
579
|
type IdempotencyKeyFn = (tenantId: string, documentType: string, documentId: string, templateName: string, data: Record<string, unknown>) => string;
|
|
559
580
|
interface ApprovalEngineOptions {
|
|
560
581
|
adapter: IStorageAdapter;
|
|
@@ -566,6 +587,7 @@ interface ApprovalEngineOptions {
|
|
|
566
587
|
*/
|
|
567
588
|
outOfOfficeProvider?: OutOfOfficeProvider;
|
|
568
589
|
logger?: Logger;
|
|
590
|
+
/** Interval, in milliseconds, between automatic escalation sweeps. Default: 60000 (1 minute). */
|
|
569
591
|
escalationPollIntervalMs?: number;
|
|
570
592
|
/** Maximum number of instances allowed in a single bulk operation. Default: 200. */
|
|
571
593
|
maxBulkItems?: number;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-
|
|
2
|
-
import { m as ConditionExpression, r as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, C as Comment, e as ApprovalMode, f as ApprovalStatus, L as LevelStatus, b as AuditEntry } from './instance-
|
|
3
|
-
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-
|
|
1
|
+
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-DDOW9z0L.js';
|
|
2
|
+
import { m as ConditionExpression, r as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, C as Comment, e as ApprovalMode, f as ApprovalStatus, L as LevelStatus, b as AuditEntry } from './instance-Cu6-qPxs.js';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-B_9JFH1i.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { L as Logger } from './Logger-hqFbBCtB.js';
|
|
6
6
|
import { C as Clock } from './Clock-D8Qp39q_.js';
|
|
7
|
-
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-
|
|
8
|
-
import { I as IAuditAdapter } from './IAuditAdapter-
|
|
7
|
+
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-C-N8MpHV.js';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-ggCTPf02.js';
|
|
9
9
|
import { I as IMetricsAdapter } from './IMetricsAdapter-WROxYSXd.js';
|
|
10
|
-
import { I as ISchedulerAdapter } from './ISchedulerAdapter-
|
|
11
|
-
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-
|
|
10
|
+
import { I as ISchedulerAdapter } from './ISchedulerAdapter-CLMdBA-5.js';
|
|
11
|
+
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-BUHKvalu.js';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -208,6 +208,7 @@ type IdGeneratorFn = (prefix: IdGeneratorPrefix) => string;
|
|
|
208
208
|
/** {@link IdGeneratorFn} used when no custom generator is configured. */
|
|
209
209
|
declare const defaultIdGenerator: IdGeneratorFn;
|
|
210
210
|
|
|
211
|
+
/** Backs `{ type: 'role' }` approvers, so a level can name an org role instead of a fixed user id. */
|
|
211
212
|
interface OrgProvider {
|
|
212
213
|
/** Required: resolve users holding a named role. */
|
|
213
214
|
getUsersByRole(role: string, tenantId?: string): Promise<string[]> | string[];
|
|
@@ -237,6 +238,11 @@ interface OutOfOfficeProvider {
|
|
|
237
238
|
*/
|
|
238
239
|
getDelegateFor(userId: string, at: Date): Promise<string | null | undefined> | string | null | undefined;
|
|
239
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Resolves a custom approver type's config to concrete user ids. Registered
|
|
243
|
+
* via {@link ApprovalEngine.registerApproverType} to back a level whose
|
|
244
|
+
* approver config carries an app-defined `type`.
|
|
245
|
+
*/
|
|
240
246
|
type ApproverResolverFn = (config: Record<string, unknown>, ctx: {
|
|
241
247
|
submittedBy: string;
|
|
242
248
|
data: Record<string, unknown>;
|
|
@@ -283,6 +289,7 @@ declare function validateConditionExpression(expression: ConditionExpression, pa
|
|
|
283
289
|
message: string;
|
|
284
290
|
}>;
|
|
285
291
|
|
|
292
|
+
/** Outcome of {@link ApprovalEngine.validateTemplate}. */
|
|
286
293
|
interface ValidationResult {
|
|
287
294
|
valid: boolean;
|
|
288
295
|
errors: Array<{
|
|
@@ -290,13 +297,16 @@ interface ValidationResult {
|
|
|
290
297
|
message: string;
|
|
291
298
|
}>;
|
|
292
299
|
}
|
|
300
|
+
/** Outcome of {@link ApprovalEngine.canApprove}. */
|
|
293
301
|
interface CanApproveResult {
|
|
294
302
|
eligible: boolean;
|
|
295
303
|
reason?: 'not_an_approver' | 'already_acted' | 'self_approval' | 'wrong_status' | 'delegated_away';
|
|
296
304
|
}
|
|
305
|
+
/** One entry in a {@link ApprovalEngine.preview} result, resolved against the given data without persisting anything. */
|
|
297
306
|
interface PreviewChainLevel {
|
|
298
307
|
level: number;
|
|
299
308
|
name: string;
|
|
309
|
+
/** Approver ids resolved as of preview time; dynamic/role approvers may resolve differently at submit time. */
|
|
300
310
|
resolvedApprovers: string[];
|
|
301
311
|
mode: ApprovalMode;
|
|
302
312
|
}
|
|
@@ -390,12 +400,16 @@ interface SimulationResult {
|
|
|
390
400
|
/** True when the script ran out before the approval finished. */
|
|
391
401
|
incomplete: boolean;
|
|
392
402
|
}
|
|
403
|
+
/** Outcome of a {@link ApprovalEngine.bulkApprove}/{@link ApprovalEngine.bulkReject} sweep. */
|
|
393
404
|
interface BulkResult {
|
|
405
|
+
/** Instances that reached the requested decision. */
|
|
394
406
|
succeeded: ApprovalInstance[];
|
|
407
|
+
/** Instances that could not be decided, with the reason. */
|
|
395
408
|
failed: Array<{
|
|
396
409
|
instanceId: string;
|
|
397
410
|
error: ApprovalError;
|
|
398
411
|
}>;
|
|
412
|
+
/** Number of instance ids passed in, equal to `succeeded.length + failed.length`. */
|
|
399
413
|
total: number;
|
|
400
414
|
}
|
|
401
415
|
/** Outcome of a {@link ApprovalEngine.transferApprovals} sweep. */
|
|
@@ -541,6 +555,7 @@ interface CycleTimeStats {
|
|
|
541
555
|
/** Largest observed time-to-decision. `0` when {@link count} is `0`. */
|
|
542
556
|
maxMs: number;
|
|
543
557
|
}
|
|
558
|
+
/** Outcome of {@link ApprovalEngine.healthCheck}. */
|
|
544
559
|
interface HealthResult {
|
|
545
560
|
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
546
561
|
adapter: 'connected' | 'error';
|
|
@@ -549,12 +564,18 @@ interface HealthResult {
|
|
|
549
564
|
escalationRunning: boolean;
|
|
550
565
|
lastTickAt?: Date;
|
|
551
566
|
}
|
|
567
|
+
/** Controls how {@link ApprovalEngine}'s optimistic-lock retry backs off on write conflicts. Defaults to 3 attempts with a 50ms base delay. */
|
|
552
568
|
interface RetryPolicy {
|
|
569
|
+
/** Number of attempts before giving up and rethrowing the conflict. */
|
|
553
570
|
maxAttempts: number;
|
|
571
|
+
/** Linear backoff unit; attempt N waits roughly `baseDelayMs * N` before retrying. */
|
|
554
572
|
baseDelayMs: number;
|
|
573
|
+
/** Upper bound on the computed delay. Defaults to unbounded. */
|
|
555
574
|
maxDelayMs?: number;
|
|
575
|
+
/** Adds up to one `baseDelayMs` of random jitter to each delay. Defaults to `true`. */
|
|
556
576
|
jitter?: boolean;
|
|
557
577
|
}
|
|
578
|
+
/** Derives the idempotency key used to dedupe a {@link ApprovalEngine.submit} call from its tenant and document identity. */
|
|
558
579
|
type IdempotencyKeyFn = (tenantId: string, documentType: string, documentId: string, templateName: string, data: Record<string, unknown>) => string;
|
|
559
580
|
interface ApprovalEngineOptions {
|
|
560
581
|
adapter: IStorageAdapter;
|
|
@@ -566,6 +587,7 @@ interface ApprovalEngineOptions {
|
|
|
566
587
|
*/
|
|
567
588
|
outOfOfficeProvider?: OutOfOfficeProvider;
|
|
568
589
|
logger?: Logger;
|
|
590
|
+
/** Interval, in milliseconds, between automatic escalation sweeps. Default: 60000 (1 minute). */
|
|
569
591
|
escalationPollIntervalMs?: number;
|
|
570
592
|
/** Maximum number of instances allowed in a single bulk operation. Default: 200. */
|
|
571
593
|
maxBulkItems?: number;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { b as AuditEntry, a as ApprovalInstance } from './instance-
|
|
1
|
+
import { b as AuditEntry, a as ApprovalInstance } from './instance-Cu6-qPxs.cjs';
|
|
2
2
|
|
|
3
|
+
/** Secondary write sink for audit entries, alongside the primary storage adapter. */
|
|
3
4
|
interface IAuditAdapter {
|
|
4
5
|
/**
|
|
5
6
|
* Called after every state-mutating operation, in addition to the primary storage adapter.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { b as AuditEntry, a as ApprovalInstance } from './instance-
|
|
1
|
+
import { b as AuditEntry, a as ApprovalInstance } from './instance-Cu6-qPxs.js';
|
|
2
2
|
|
|
3
|
+
/** Secondary write sink for audit entries, alongside the primary storage adapter. */
|
|
3
4
|
interface IAuditAdapter {
|
|
4
5
|
/**
|
|
5
6
|
* Called after every state-mutating operation, in addition to the primary storage adapter.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-Cu6-qPxs.js';
|
|
2
2
|
|
|
3
3
|
interface AuthorizationContext {
|
|
4
4
|
/** The engine method the actor is attempting to call. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-Cu6-qPxs.cjs';
|
|
2
2
|
|
|
3
3
|
interface AuthorizationContext {
|
|
4
4
|
/** The engine method the actor is attempting to call. */
|
|
@@ -1,33 +1,43 @@
|
|
|
1
|
-
import { a as ApprovalInstance, b as AuditEntry } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, b as AuditEntry } from './instance-Cu6-qPxs.js';
|
|
2
2
|
|
|
3
|
+
/** Common fields carried by every lifecycle event emitted for an approval instance. */
|
|
3
4
|
interface ApprovalEvent {
|
|
5
|
+
/** The `ApprovalInstance.id` this event pertains to. */
|
|
4
6
|
instanceId: string;
|
|
7
|
+
/** The id of the business document the instance was raised against. */
|
|
5
8
|
documentId: string;
|
|
9
|
+
/** The document type, matching the template used to raise the instance. */
|
|
6
10
|
documentType: string;
|
|
11
|
+
/** When the event occurred. */
|
|
7
12
|
timestamp: Date;
|
|
8
13
|
}
|
|
14
|
+
/** Emitted as `approval:submitted` when a new instance is raised. */
|
|
9
15
|
interface SubmittedEvent extends ApprovalEvent {
|
|
10
16
|
submittedBy: string;
|
|
11
17
|
currentApprovers: string[];
|
|
12
18
|
}
|
|
19
|
+
/** Emitted as `approval:approved` when an approver accepts a level. */
|
|
13
20
|
interface ApprovedEvent extends ApprovalEvent {
|
|
14
21
|
approverId: string;
|
|
15
22
|
level: number;
|
|
16
23
|
comment?: string;
|
|
17
24
|
isFinal: boolean;
|
|
18
25
|
}
|
|
26
|
+
/** Emitted as `approval:rejected` when an approver rejects a level. */
|
|
19
27
|
interface RejectedEvent extends ApprovalEvent {
|
|
20
28
|
approverId: string;
|
|
21
29
|
level: number;
|
|
22
30
|
reason: string;
|
|
23
31
|
returnTo: 'originator' | 'previous' | null;
|
|
24
32
|
}
|
|
33
|
+
/** Emitted as `approval:delegated` when an approver hands their decision to someone else. */
|
|
25
34
|
interface DelegatedEvent extends ApprovalEvent {
|
|
26
35
|
fromApprover: string;
|
|
27
36
|
toApprover: string;
|
|
28
37
|
level: number;
|
|
29
38
|
reason: string;
|
|
30
39
|
}
|
|
40
|
+
/** Emitted as `approval:reassigned` when an admin moves a level to a different approver. */
|
|
31
41
|
interface ReassignedEvent extends ApprovalEvent {
|
|
32
42
|
reassignedBy: string;
|
|
33
43
|
fromApprover: string;
|
|
@@ -35,30 +45,37 @@ interface ReassignedEvent extends ApprovalEvent {
|
|
|
35
45
|
level: number;
|
|
36
46
|
reason: string;
|
|
37
47
|
}
|
|
48
|
+
/** Emitted as `approval:escalated` when a level's SLA rung hands off to a new approver. */
|
|
38
49
|
interface EscalatedEvent extends ApprovalEvent {
|
|
39
50
|
level: number;
|
|
40
51
|
escalatedTo: string;
|
|
41
52
|
}
|
|
53
|
+
/** Emitted as `approval:cancelled` when the instance is withdrawn before completion. */
|
|
42
54
|
interface CancelledEvent extends ApprovalEvent {
|
|
43
55
|
cancelledBy: string;
|
|
44
56
|
reason: string;
|
|
45
57
|
}
|
|
58
|
+
/** Emitted as `approval:level_advanced` when the chain moves to its next open level. */
|
|
46
59
|
interface LevelAdvancedEvent extends ApprovalEvent {
|
|
47
60
|
fromLevel: number;
|
|
48
61
|
toLevel: number;
|
|
49
62
|
newApprovers: string[];
|
|
50
63
|
}
|
|
64
|
+
/** Emitted as `approval:resubmitted` when a rejected instance is raised again. */
|
|
51
65
|
interface ResubmittedEvent extends ApprovalEvent {
|
|
52
66
|
resubmittedBy: string;
|
|
53
67
|
originalInstanceId: string;
|
|
54
68
|
}
|
|
69
|
+
/** Emitted as `approval:overridden` when an admin force-approves past the normal chain. */
|
|
55
70
|
interface OverriddenEvent extends ApprovalEvent {
|
|
56
71
|
overriddenBy: string;
|
|
57
72
|
justification: string;
|
|
58
73
|
}
|
|
74
|
+
/** Emitted as `approval:expired` when the instance's deadline passes unresolved. */
|
|
59
75
|
interface ExpiredEvent extends ApprovalEvent {
|
|
60
76
|
deadlineAction: 'cancel' | 'reject';
|
|
61
77
|
}
|
|
78
|
+
/** Emitted as `approval:sla_breached` when a level's deadline passes without expiring the instance. */
|
|
62
79
|
interface SlaBreachedEvent extends ApprovalEvent {
|
|
63
80
|
slaDeadlineAt: Date;
|
|
64
81
|
}
|
|
@@ -1,33 +1,43 @@
|
|
|
1
|
-
import { a as ApprovalInstance, b as AuditEntry } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, b as AuditEntry } from './instance-Cu6-qPxs.cjs';
|
|
2
2
|
|
|
3
|
+
/** Common fields carried by every lifecycle event emitted for an approval instance. */
|
|
3
4
|
interface ApprovalEvent {
|
|
5
|
+
/** The `ApprovalInstance.id` this event pertains to. */
|
|
4
6
|
instanceId: string;
|
|
7
|
+
/** The id of the business document the instance was raised against. */
|
|
5
8
|
documentId: string;
|
|
9
|
+
/** The document type, matching the template used to raise the instance. */
|
|
6
10
|
documentType: string;
|
|
11
|
+
/** When the event occurred. */
|
|
7
12
|
timestamp: Date;
|
|
8
13
|
}
|
|
14
|
+
/** Emitted as `approval:submitted` when a new instance is raised. */
|
|
9
15
|
interface SubmittedEvent extends ApprovalEvent {
|
|
10
16
|
submittedBy: string;
|
|
11
17
|
currentApprovers: string[];
|
|
12
18
|
}
|
|
19
|
+
/** Emitted as `approval:approved` when an approver accepts a level. */
|
|
13
20
|
interface ApprovedEvent extends ApprovalEvent {
|
|
14
21
|
approverId: string;
|
|
15
22
|
level: number;
|
|
16
23
|
comment?: string;
|
|
17
24
|
isFinal: boolean;
|
|
18
25
|
}
|
|
26
|
+
/** Emitted as `approval:rejected` when an approver rejects a level. */
|
|
19
27
|
interface RejectedEvent extends ApprovalEvent {
|
|
20
28
|
approverId: string;
|
|
21
29
|
level: number;
|
|
22
30
|
reason: string;
|
|
23
31
|
returnTo: 'originator' | 'previous' | null;
|
|
24
32
|
}
|
|
33
|
+
/** Emitted as `approval:delegated` when an approver hands their decision to someone else. */
|
|
25
34
|
interface DelegatedEvent extends ApprovalEvent {
|
|
26
35
|
fromApprover: string;
|
|
27
36
|
toApprover: string;
|
|
28
37
|
level: number;
|
|
29
38
|
reason: string;
|
|
30
39
|
}
|
|
40
|
+
/** Emitted as `approval:reassigned` when an admin moves a level to a different approver. */
|
|
31
41
|
interface ReassignedEvent extends ApprovalEvent {
|
|
32
42
|
reassignedBy: string;
|
|
33
43
|
fromApprover: string;
|
|
@@ -35,30 +45,37 @@ interface ReassignedEvent extends ApprovalEvent {
|
|
|
35
45
|
level: number;
|
|
36
46
|
reason: string;
|
|
37
47
|
}
|
|
48
|
+
/** Emitted as `approval:escalated` when a level's SLA rung hands off to a new approver. */
|
|
38
49
|
interface EscalatedEvent extends ApprovalEvent {
|
|
39
50
|
level: number;
|
|
40
51
|
escalatedTo: string;
|
|
41
52
|
}
|
|
53
|
+
/** Emitted as `approval:cancelled` when the instance is withdrawn before completion. */
|
|
42
54
|
interface CancelledEvent extends ApprovalEvent {
|
|
43
55
|
cancelledBy: string;
|
|
44
56
|
reason: string;
|
|
45
57
|
}
|
|
58
|
+
/** Emitted as `approval:level_advanced` when the chain moves to its next open level. */
|
|
46
59
|
interface LevelAdvancedEvent extends ApprovalEvent {
|
|
47
60
|
fromLevel: number;
|
|
48
61
|
toLevel: number;
|
|
49
62
|
newApprovers: string[];
|
|
50
63
|
}
|
|
64
|
+
/** Emitted as `approval:resubmitted` when a rejected instance is raised again. */
|
|
51
65
|
interface ResubmittedEvent extends ApprovalEvent {
|
|
52
66
|
resubmittedBy: string;
|
|
53
67
|
originalInstanceId: string;
|
|
54
68
|
}
|
|
69
|
+
/** Emitted as `approval:overridden` when an admin force-approves past the normal chain. */
|
|
55
70
|
interface OverriddenEvent extends ApprovalEvent {
|
|
56
71
|
overriddenBy: string;
|
|
57
72
|
justification: string;
|
|
58
73
|
}
|
|
74
|
+
/** Emitted as `approval:expired` when the instance's deadline passes unresolved. */
|
|
59
75
|
interface ExpiredEvent extends ApprovalEvent {
|
|
60
76
|
deadlineAction: 'cancel' | 'reject';
|
|
61
77
|
}
|
|
78
|
+
/** Emitted as `approval:sla_breached` when a level's deadline passes without expiring the instance. */
|
|
62
79
|
interface SlaBreachedEvent extends ApprovalEvent {
|
|
63
80
|
slaDeadlineAt: Date;
|
|
64
81
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { a as ApprovalInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance } from './instance-Cu6-qPxs.js';
|
|
2
2
|
|
|
3
|
+
/** Base class for all errors thrown by this library; carries a stable machine-readable `code`. */
|
|
3
4
|
declare class ApprovalError extends Error {
|
|
4
5
|
readonly code: string;
|
|
5
6
|
constructor(message: string, code: string);
|
|
@@ -10,29 +11,40 @@ declare class ApprovalError extends Error {
|
|
|
10
11
|
};
|
|
11
12
|
toHttpStatus(): number;
|
|
12
13
|
}
|
|
14
|
+
/** Thrown when a requested resource (instance, template, etc.) does not exist. */
|
|
13
15
|
declare class ApprovalNotFoundError extends ApprovalError {
|
|
14
16
|
constructor(resource: string, id: string);
|
|
15
17
|
}
|
|
18
|
+
/** Thrown when an instance was modified by another process since it was last read. */
|
|
16
19
|
declare class ApprovalConflictError extends ApprovalError {
|
|
17
20
|
constructor(instanceId: string);
|
|
18
21
|
}
|
|
22
|
+
/** Thrown when the caller is not authorized to perform the requested action. */
|
|
19
23
|
declare class ApprovalForbiddenError extends ApprovalError {
|
|
20
24
|
constructor(message: string);
|
|
21
25
|
}
|
|
26
|
+
/** Thrown when input or configuration fails validation; `cause` may hold the originating error. */
|
|
22
27
|
declare class ApprovalValidationError extends ApprovalError {
|
|
23
28
|
readonly cause?: unknown | undefined;
|
|
24
29
|
constructor(message: string, cause?: unknown | undefined);
|
|
25
30
|
}
|
|
31
|
+
/** Thrown when a template lookup by name finds no registered template. */
|
|
26
32
|
declare class ApprovalTemplateNotFoundError extends ApprovalError {
|
|
27
33
|
constructor(name: string);
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
/** The engine method a middleware hook is running around. */
|
|
30
37
|
type EngineOperation = 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'updateData' | 'requestInfo' | 'provideInfo' | 'addAttachment' | 'removeAttachment' | 'addComment' | 'resubmit' | 'override';
|
|
38
|
+
/** Context passed to an {@link IOperationMiddleware} hook for one engine call. */
|
|
31
39
|
interface OperationContext<T = unknown> {
|
|
32
40
|
operation: EngineOperation;
|
|
41
|
+
/** The target instance's id, for operations that act on an existing instance. */
|
|
33
42
|
instanceId?: string;
|
|
43
|
+
/** Id of the user or system performing the operation, when known. */
|
|
34
44
|
actorId?: string;
|
|
45
|
+
/** The engine's configured tenant id (defaults to `'default'`). */
|
|
35
46
|
tenantId: string;
|
|
47
|
+
/** The caller's own input for this operation, e.g. the submit payload or decision comment. */
|
|
36
48
|
input: T;
|
|
37
49
|
}
|
|
38
50
|
interface IOperationMiddleware {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { a as ApprovalInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance } from './instance-Cu6-qPxs.cjs';
|
|
2
2
|
|
|
3
|
+
/** Base class for all errors thrown by this library; carries a stable machine-readable `code`. */
|
|
3
4
|
declare class ApprovalError extends Error {
|
|
4
5
|
readonly code: string;
|
|
5
6
|
constructor(message: string, code: string);
|
|
@@ -10,29 +11,40 @@ declare class ApprovalError extends Error {
|
|
|
10
11
|
};
|
|
11
12
|
toHttpStatus(): number;
|
|
12
13
|
}
|
|
14
|
+
/** Thrown when a requested resource (instance, template, etc.) does not exist. */
|
|
13
15
|
declare class ApprovalNotFoundError extends ApprovalError {
|
|
14
16
|
constructor(resource: string, id: string);
|
|
15
17
|
}
|
|
18
|
+
/** Thrown when an instance was modified by another process since it was last read. */
|
|
16
19
|
declare class ApprovalConflictError extends ApprovalError {
|
|
17
20
|
constructor(instanceId: string);
|
|
18
21
|
}
|
|
22
|
+
/** Thrown when the caller is not authorized to perform the requested action. */
|
|
19
23
|
declare class ApprovalForbiddenError extends ApprovalError {
|
|
20
24
|
constructor(message: string);
|
|
21
25
|
}
|
|
26
|
+
/** Thrown when input or configuration fails validation; `cause` may hold the originating error. */
|
|
22
27
|
declare class ApprovalValidationError extends ApprovalError {
|
|
23
28
|
readonly cause?: unknown | undefined;
|
|
24
29
|
constructor(message: string, cause?: unknown | undefined);
|
|
25
30
|
}
|
|
31
|
+
/** Thrown when a template lookup by name finds no registered template. */
|
|
26
32
|
declare class ApprovalTemplateNotFoundError extends ApprovalError {
|
|
27
33
|
constructor(name: string);
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
/** The engine method a middleware hook is running around. */
|
|
30
37
|
type EngineOperation = 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'updateData' | 'requestInfo' | 'provideInfo' | 'addAttachment' | 'removeAttachment' | 'addComment' | 'resubmit' | 'override';
|
|
38
|
+
/** Context passed to an {@link IOperationMiddleware} hook for one engine call. */
|
|
31
39
|
interface OperationContext<T = unknown> {
|
|
32
40
|
operation: EngineOperation;
|
|
41
|
+
/** The target instance's id, for operations that act on an existing instance. */
|
|
33
42
|
instanceId?: string;
|
|
43
|
+
/** Id of the user or system performing the operation, when known. */
|
|
34
44
|
actorId?: string;
|
|
45
|
+
/** The engine's configured tenant id (defaults to `'default'`). */
|
|
35
46
|
tenantId: string;
|
|
47
|
+
/** The caller's own input for this operation, e.g. the submit payload or decision comment. */
|
|
36
48
|
input: T;
|
|
37
49
|
}
|
|
38
50
|
interface IOperationMiddleware {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { A as ApprovalTemplate, a as ApprovalInstance, f as ApprovalStatus, b as AuditEntry } from './instance-
|
|
1
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, f as ApprovalStatus, b as AuditEntry } from './instance-Cu6-qPxs.cjs';
|
|
2
2
|
|
|
3
|
+
/** Offset-based pagination request. */
|
|
3
4
|
interface PaginationOpts {
|
|
4
5
|
limit: number;
|
|
5
6
|
offset: number;
|
|
6
7
|
}
|
|
8
|
+
/** Offset-based pagination response, including the total matching count. */
|
|
7
9
|
interface PaginatedResult<T> {
|
|
8
10
|
items: T[];
|
|
9
11
|
total: number;
|
|
@@ -14,10 +16,14 @@ interface CursorPaginationOpts {
|
|
|
14
16
|
limit: number;
|
|
15
17
|
direction?: 'forward' | 'backward';
|
|
16
18
|
}
|
|
19
|
+
/** Cursor-based pagination response. */
|
|
17
20
|
interface CursorPaginatedResult<T> {
|
|
18
21
|
items: T[];
|
|
22
|
+
/** Pass as `cursor` with `direction: 'forward'` to fetch the next page, if any. */
|
|
19
23
|
nextCursor?: string;
|
|
24
|
+
/** Pass as `cursor` with `direction: 'backward'` to fetch the previous page, if any. */
|
|
20
25
|
prevCursor?: string;
|
|
26
|
+
/** Whether another page exists in the requested direction. */
|
|
21
27
|
hasMore: boolean;
|
|
22
28
|
}
|
|
23
29
|
interface InstanceFilter {
|
|
@@ -38,6 +44,7 @@ interface InstanceFilter {
|
|
|
38
44
|
*/
|
|
39
45
|
data?: Record<string, unknown>;
|
|
40
46
|
}
|
|
47
|
+
/** The primary persistence contract `ApprovalEngine` reads and writes through — implement this to bring your own store. */
|
|
41
48
|
interface IStorageAdapter {
|
|
42
49
|
saveTemplate(template: ApprovalTemplate): Promise<void>;
|
|
43
50
|
getTemplate(tenantId: string, name: string): Promise<ApprovalTemplate | null>;
|