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
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
## 4.0.
|
|
1
|
+
## 4.0.3 - 2026-09-22
|
|
2
2
|
|
|
3
|
-
- fix(
|
|
4
|
-
- fix(
|
|
5
|
-
- fix(
|
|
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
6
|
|
|
7
7
|
# Changelog
|
|
8
8
|
|
|
@@ -13,6 +13,19 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
13
13
|
|
|
14
14
|
### Fixed
|
|
15
15
|
|
|
16
|
+
- `assertStatus`'s `INVALID_STATUS` error now names the instance that failed
|
|
17
|
+
the check, instead of a message that gave no clue which instance it was.
|
|
18
|
+
- `businessHoursCalendar`'s "no working day found" error now names the start
|
|
19
|
+
date that triggered it, instead of a message that gave no clue which call
|
|
20
|
+
misconfigured the calendar.
|
|
21
|
+
- `assertApproverOnLevel`'s forbidden-approver error now names the level,
|
|
22
|
+
matching every sibling guard in `StateMachine.ts`, instead of a bare level
|
|
23
|
+
number with no human-readable name.
|
|
24
|
+
|
|
25
|
+
## [4.0.2] - 2026-09-20
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
16
29
|
- `EventBus` (and therefore `engine.on()`/`off()`) no longer leaks a listener
|
|
17
30
|
when the same function is registered twice with `on()`: each registration
|
|
18
31
|
now needs its own `off()` call to undo, instead of the second registration
|
|
@@ -20,6 +33,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
20
33
|
attached.
|
|
21
34
|
- `DigestNotificationAdapter`'s `intervalMs` validation error now names the
|
|
22
35
|
offending value, instead of a message that gave no clue which value failed.
|
|
36
|
+
- The default-sleep regression test for `WebhookNotificationAdapter` no longer
|
|
37
|
+
flakes under CI's timer coalescing, which could report elapsed time a
|
|
38
|
+
millisecond or two short of the asserted floor.
|
|
23
39
|
|
|
24
40
|
## [4.0.1] - 2026-09-14
|
|
25
41
|
|
|
@@ -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-CVvNsnnD.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-Dd1jQizG.js';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-BcUskJ6z.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { L as Logger } from './Logger-
|
|
6
|
-
import { C as Clock } from './Clock-
|
|
7
|
-
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-
|
|
8
|
-
import { I as IAuditAdapter } from './IAuditAdapter-
|
|
9
|
-
import { I as IMetricsAdapter } from './IMetricsAdapter-
|
|
5
|
+
import { L as Logger } from './Logger-hqFbBCtB.js';
|
|
6
|
+
import { C as Clock } from './Clock-D8Qp39q_.js';
|
|
7
|
+
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-C7ciNnJp.js';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-C-04ar9A.js';
|
|
9
|
+
import { I as IMetricsAdapter } from './IMetricsAdapter-WROxYSXd.js';
|
|
10
10
|
import { I as ISchedulerAdapter } from './ISchedulerAdapter-DKv_QjVN.js';
|
|
11
|
-
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-
|
|
11
|
+
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-wIE1mpTy.js';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -201,11 +201,15 @@ interface BusinessHoursCalendarOptions extends WeekendCalendarOptions {
|
|
|
201
201
|
*/
|
|
202
202
|
declare function businessHoursCalendar(options?: BusinessHoursCalendarOptions): Required<Pick<BusinessCalendar, 'addBusinessDays' | 'addBusinessHours'>>;
|
|
203
203
|
|
|
204
|
+
/** Entity kind an id is generated for: instance, template, attachment, or comment. */
|
|
204
205
|
type IdGeneratorPrefix = 'inst' | 'tpl' | 'att' | 'cmt';
|
|
206
|
+
/** Produces a unique id for the given entity kind. */
|
|
205
207
|
type IdGeneratorFn = (prefix: IdGeneratorPrefix) => string;
|
|
208
|
+
/** {@link IdGeneratorFn} used when no custom generator is configured. */
|
|
206
209
|
declare const defaultIdGenerator: IdGeneratorFn;
|
|
207
210
|
|
|
208
211
|
interface OrgProvider {
|
|
212
|
+
/** Required: resolve users holding a named role. */
|
|
209
213
|
getUsersByRole(role: string, tenantId?: string): Promise<string[]> | string[];
|
|
210
214
|
/** Optional: resolve users by department name. */
|
|
211
215
|
getUsersByDepartment?(dept: string, tenantId?: string): Promise<string[]> | string[];
|
|
@@ -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-D7cy20uD.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-Dd1jQizG.cjs';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-CBsJDk-Y.cjs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { L as Logger } from './Logger-
|
|
6
|
-
import { C as Clock } from './Clock-
|
|
7
|
-
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-
|
|
8
|
-
import { I as IAuditAdapter } from './IAuditAdapter-
|
|
9
|
-
import { I as IMetricsAdapter } from './IMetricsAdapter-
|
|
5
|
+
import { L as Logger } from './Logger-hqFbBCtB.cjs';
|
|
6
|
+
import { C as Clock } from './Clock-D8Qp39q_.cjs';
|
|
7
|
+
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-D3Yelw9R.cjs';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-TT2Xhtu6.cjs';
|
|
9
|
+
import { I as IMetricsAdapter } from './IMetricsAdapter-WROxYSXd.cjs';
|
|
10
10
|
import { I as ISchedulerAdapter } from './ISchedulerAdapter-DKv_QjVN.cjs';
|
|
11
|
-
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-
|
|
11
|
+
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-D6tTT3gv.cjs';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -201,11 +201,15 @@ interface BusinessHoursCalendarOptions extends WeekendCalendarOptions {
|
|
|
201
201
|
*/
|
|
202
202
|
declare function businessHoursCalendar(options?: BusinessHoursCalendarOptions): Required<Pick<BusinessCalendar, 'addBusinessDays' | 'addBusinessHours'>>;
|
|
203
203
|
|
|
204
|
+
/** Entity kind an id is generated for: instance, template, attachment, or comment. */
|
|
204
205
|
type IdGeneratorPrefix = 'inst' | 'tpl' | 'att' | 'cmt';
|
|
206
|
+
/** Produces a unique id for the given entity kind. */
|
|
205
207
|
type IdGeneratorFn = (prefix: IdGeneratorPrefix) => string;
|
|
208
|
+
/** {@link IdGeneratorFn} used when no custom generator is configured. */
|
|
206
209
|
declare const defaultIdGenerator: IdGeneratorFn;
|
|
207
210
|
|
|
208
211
|
interface OrgProvider {
|
|
212
|
+
/** Required: resolve users holding a named role. */
|
|
209
213
|
getUsersByRole(role: string, tenantId?: string): Promise<string[]> | string[];
|
|
210
214
|
/** Optional: resolve users by department name. */
|
|
211
215
|
getUsersByDepartment?(dept: string, tenantId?: string): Promise<string[]> | string[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Time source injected into engine and plugin components, so tests can control the clock. */
|
|
2
|
+
interface Clock {
|
|
3
|
+
now(): Date;
|
|
4
|
+
}
|
|
5
|
+
/** {@link Clock} backed by the real wall clock — the default when no clock is configured. */
|
|
6
|
+
declare const systemClock: Clock;
|
|
7
|
+
|
|
8
|
+
export { type Clock as C, systemClock as s };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Time source injected into engine and plugin components, so tests can control the clock. */
|
|
2
|
+
interface Clock {
|
|
3
|
+
now(): Date;
|
|
4
|
+
}
|
|
5
|
+
/** {@link Clock} backed by the real wall clock — the default when no clock is configured. */
|
|
6
|
+
declare const systemClock: Clock;
|
|
7
|
+
|
|
8
|
+
export { type Clock as C, systemClock as s };
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-Dd1jQizG.cjs';
|
|
2
2
|
|
|
3
3
|
interface AuthorizationContext {
|
|
4
|
+
/** The engine method the actor is attempting to call. */
|
|
4
5
|
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData' | 'requestInfo' | 'provideInfo' | 'addAttachment' | 'removeAttachment';
|
|
6
|
+
/** Id of the user or system attempting the operation. */
|
|
5
7
|
actorId: string;
|
|
8
|
+
/** The instance the operation targets, as it stood before the operation ran. */
|
|
6
9
|
instance: Readonly<ApprovalInstance>;
|
|
10
|
+
/** The current level, for level-scoped operations (approve, reject, delegate, etc.); absent otherwise. */
|
|
7
11
|
level?: Readonly<ApprovalLevelInstance>;
|
|
12
|
+
/** The caller's own options object for this operation, passed through unmodified. */
|
|
8
13
|
opts: Record<string, unknown>;
|
|
9
14
|
}
|
|
10
15
|
interface IAuthorizationPolicy {
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-Dd1jQizG.js';
|
|
2
2
|
|
|
3
3
|
interface AuthorizationContext {
|
|
4
|
+
/** The engine method the actor is attempting to call. */
|
|
4
5
|
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData' | 'requestInfo' | 'provideInfo' | 'addAttachment' | 'removeAttachment';
|
|
6
|
+
/** Id of the user or system attempting the operation. */
|
|
5
7
|
actorId: string;
|
|
8
|
+
/** The instance the operation targets, as it stood before the operation ran. */
|
|
6
9
|
instance: Readonly<ApprovalInstance>;
|
|
10
|
+
/** The current level, for level-scoped operations (approve, reject, delegate, etc.); absent otherwise. */
|
|
7
11
|
level?: Readonly<ApprovalLevelInstance>;
|
|
12
|
+
/** The caller's own options object for this operation, passed through unmodified. */
|
|
8
13
|
opts: Record<string, unknown>;
|
|
9
14
|
}
|
|
10
15
|
interface IAuthorizationPolicy {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Closed set of metric names an {@link IMetricsAdapter} may be called with, namespaced `approval.<verb>`. */
|
|
1
2
|
type MetricName = 'approval.submitted' | 'approval.approved' | 'approval.rejected' | 'approval.cancelled' | 'approval.expired' | 'approval.sla_breached' | 'approval.escalated' | 'approval.reassigned' | 'approval.overridden' | 'approval.data_updated' | 'approval.reminded' | 'approval.info_requested' | 'approval.info_provided' | 'approval.attachment_added' | 'approval.attachment_removed' | 'approval.conflict_retry' | 'approval.operation_duration_ms';
|
|
2
3
|
interface IMetricsAdapter {
|
|
3
4
|
/** Increment a counter. Synchronous — never awaited. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Closed set of metric names an {@link IMetricsAdapter} may be called with, namespaced `approval.<verb>`. */
|
|
1
2
|
type MetricName = 'approval.submitted' | 'approval.approved' | 'approval.rejected' | 'approval.cancelled' | 'approval.expired' | 'approval.sla_breached' | 'approval.escalated' | 'approval.reassigned' | 'approval.overridden' | 'approval.data_updated' | 'approval.reminded' | 'approval.info_requested' | 'approval.info_provided' | 'approval.attachment_added' | 'approval.attachment_removed' | 'approval.conflict_retry' | 'approval.operation_duration_ms';
|
|
2
3
|
interface IMetricsAdapter {
|
|
3
4
|
/** Increment a counter. Synchronous — never awaited. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance, b as AuditEntry } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, b as AuditEntry } from './instance-Dd1jQizG.js';
|
|
2
2
|
|
|
3
3
|
interface ApprovalEvent {
|
|
4
4
|
instanceId: string;
|
|
@@ -160,15 +160,23 @@ interface HistoryEntry extends AuditEntry {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
interface NotificationEvent {
|
|
163
|
+
/** The event that fired, e.g. `'submitted'`, `'approved'`, `'escalated'`. */
|
|
163
164
|
type: ApprovalEventName;
|
|
165
|
+
/** Id of the approval instance the event fired for. */
|
|
164
166
|
instanceId: string;
|
|
167
|
+
/** Id of the document under approval, as submitted by the caller. */
|
|
165
168
|
documentId: string;
|
|
169
|
+
/** Caller-defined document category (e.g. `'purchase_order'`). */
|
|
166
170
|
documentType: string;
|
|
171
|
+
/** When the underlying transition happened. */
|
|
167
172
|
timestamp: Date;
|
|
168
173
|
/** Current-level approver IDs; empty for non-level events (cancelled, expired, etc.). */
|
|
169
174
|
recipients: string[];
|
|
175
|
+
/** Name of the template governing the instance. */
|
|
170
176
|
templateName: string;
|
|
177
|
+
/** Tenant the instance belongs to, for multi-tenant deployments. */
|
|
171
178
|
tenantId: string;
|
|
179
|
+
/** Event-specific detail, shaped by {@link type}. */
|
|
172
180
|
payload: ApprovalEventMap[ApprovalEventName];
|
|
173
181
|
}
|
|
174
182
|
interface INotificationAdapter {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance, b as AuditEntry } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, b as AuditEntry } from './instance-Dd1jQizG.cjs';
|
|
2
2
|
|
|
3
3
|
interface ApprovalEvent {
|
|
4
4
|
instanceId: string;
|
|
@@ -160,15 +160,23 @@ interface HistoryEntry extends AuditEntry {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
interface NotificationEvent {
|
|
163
|
+
/** The event that fired, e.g. `'submitted'`, `'approved'`, `'escalated'`. */
|
|
163
164
|
type: ApprovalEventName;
|
|
165
|
+
/** Id of the approval instance the event fired for. */
|
|
164
166
|
instanceId: string;
|
|
167
|
+
/** Id of the document under approval, as submitted by the caller. */
|
|
165
168
|
documentId: string;
|
|
169
|
+
/** Caller-defined document category (e.g. `'purchase_order'`). */
|
|
166
170
|
documentType: string;
|
|
171
|
+
/** When the underlying transition happened. */
|
|
167
172
|
timestamp: Date;
|
|
168
173
|
/** Current-level approver IDs; empty for non-level events (cancelled, expired, etc.). */
|
|
169
174
|
recipients: string[];
|
|
175
|
+
/** Name of the template governing the instance. */
|
|
170
176
|
templateName: string;
|
|
177
|
+
/** Tenant the instance belongs to, for multi-tenant deployments. */
|
|
171
178
|
tenantId: string;
|
|
179
|
+
/** Event-specific detail, shaped by {@link type}. */
|
|
172
180
|
payload: ApprovalEventMap[ApprovalEventName];
|
|
173
181
|
}
|
|
174
182
|
interface INotificationAdapter {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance } from './instance-Dd1jQizG.js';
|
|
2
2
|
|
|
3
3
|
declare class ApprovalError extends Error {
|
|
4
4
|
readonly code: string;
|
|
@@ -27,8 +27,9 @@ declare class ApprovalTemplateNotFoundError extends ApprovalError {
|
|
|
27
27
|
constructor(name: string);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
type EngineOperation = 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'updateData' | 'requestInfo' | 'provideInfo' | 'addAttachment' | 'removeAttachment' | 'addComment' | 'resubmit' | 'override';
|
|
30
31
|
interface OperationContext<T = unknown> {
|
|
31
|
-
operation:
|
|
32
|
+
operation: EngineOperation;
|
|
32
33
|
instanceId?: string;
|
|
33
34
|
actorId?: string;
|
|
34
35
|
tenantId: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance } from './instance-Dd1jQizG.cjs';
|
|
2
2
|
|
|
3
3
|
declare class ApprovalError extends Error {
|
|
4
4
|
readonly code: string;
|
|
@@ -27,8 +27,9 @@ declare class ApprovalTemplateNotFoundError extends ApprovalError {
|
|
|
27
27
|
constructor(name: string);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
type EngineOperation = 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'updateData' | 'requestInfo' | 'provideInfo' | 'addAttachment' | 'removeAttachment' | 'addComment' | 'resubmit' | 'override';
|
|
30
31
|
interface OperationContext<T = unknown> {
|
|
31
|
-
operation:
|
|
32
|
+
operation: EngineOperation;
|
|
32
33
|
instanceId?: string;
|
|
33
34
|
actorId?: string;
|
|
34
35
|
tenantId: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
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-Dd1jQizG.js';
|
|
2
2
|
|
|
3
3
|
interface PaginationOpts {
|
|
4
4
|
limit: number;
|
|
@@ -42,14 +42,19 @@ interface IStorageAdapter {
|
|
|
42
42
|
saveTemplate(template: ApprovalTemplate): Promise<void>;
|
|
43
43
|
getTemplate(tenantId: string, name: string): Promise<ApprovalTemplate | null>;
|
|
44
44
|
listTemplates(tenantId: string): Promise<ApprovalTemplate[]>;
|
|
45
|
+
/** Create a new instance. Callers own uniqueness of `instance.id`. */
|
|
45
46
|
saveInstance(instance: ApprovalInstance): Promise<void>;
|
|
46
47
|
/** Conditional update — throws ApprovalConflictError if stored version !== expectedVersion. */
|
|
47
48
|
updateInstance(instance: ApprovalInstance, expectedVersion: number): Promise<void>;
|
|
49
|
+
/** Fetch one instance by id, scoped to a tenant. Returns null if absent. */
|
|
48
50
|
getInstance(tenantId: string, id: string): Promise<ApprovalInstance | null>;
|
|
51
|
+
/** Instances where `approverId` is a current approver on any pending level. */
|
|
49
52
|
getInstancesByApprover(tenantId: string, approverId: string, opts?: PaginationOpts): Promise<PaginatedResult<ApprovalInstance>>;
|
|
53
|
+
/** Instances matching an {@link InstanceFilter}. Ordering is adapter-specific. */
|
|
50
54
|
getInstancesByFilter(tenantId: string, filter: InstanceFilter, opts?: PaginationOpts): Promise<PaginatedResult<ApprovalInstance>>;
|
|
51
55
|
/** Optional cursor-based pagination — more efficient than offset at scale. */
|
|
52
56
|
getInstancesByCursor?(tenantId: string, filter: InstanceFilter, opts: CursorPaginationOpts): Promise<CursorPaginatedResult<ApprovalInstance>>;
|
|
57
|
+
/** Pending instances overdue as of `asOf` on any open level, not just the current one. */
|
|
53
58
|
getOverdueInstances(tenantId: string, asOf: Date, filter?: InstanceFilter): Promise<ApprovalInstance[]>;
|
|
54
59
|
/**
|
|
55
60
|
* Count instances matching a filter, without fetching any of them.
|
|
@@ -85,6 +90,7 @@ interface IStorageAdapter {
|
|
|
85
90
|
* @since 2.3.0
|
|
86
91
|
*/
|
|
87
92
|
deleteInstance?(tenantId: string, id: string): Promise<boolean>;
|
|
93
|
+
/** Look up an instance by `idempotencyKey`, unique within a tenant, not globally. */
|
|
88
94
|
getIdempotentInstance(tenantId: string, idempotencyKey: string): Promise<ApprovalInstance | null>;
|
|
89
95
|
appendAuditEntry(tenantId: string, instanceId: string, entry: AuditEntry): Promise<void>;
|
|
90
96
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
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-Dd1jQizG.cjs';
|
|
2
2
|
|
|
3
3
|
interface PaginationOpts {
|
|
4
4
|
limit: number;
|
|
@@ -42,14 +42,19 @@ interface IStorageAdapter {
|
|
|
42
42
|
saveTemplate(template: ApprovalTemplate): Promise<void>;
|
|
43
43
|
getTemplate(tenantId: string, name: string): Promise<ApprovalTemplate | null>;
|
|
44
44
|
listTemplates(tenantId: string): Promise<ApprovalTemplate[]>;
|
|
45
|
+
/** Create a new instance. Callers own uniqueness of `instance.id`. */
|
|
45
46
|
saveInstance(instance: ApprovalInstance): Promise<void>;
|
|
46
47
|
/** Conditional update — throws ApprovalConflictError if stored version !== expectedVersion. */
|
|
47
48
|
updateInstance(instance: ApprovalInstance, expectedVersion: number): Promise<void>;
|
|
49
|
+
/** Fetch one instance by id, scoped to a tenant. Returns null if absent. */
|
|
48
50
|
getInstance(tenantId: string, id: string): Promise<ApprovalInstance | null>;
|
|
51
|
+
/** Instances where `approverId` is a current approver on any pending level. */
|
|
49
52
|
getInstancesByApprover(tenantId: string, approverId: string, opts?: PaginationOpts): Promise<PaginatedResult<ApprovalInstance>>;
|
|
53
|
+
/** Instances matching an {@link InstanceFilter}. Ordering is adapter-specific. */
|
|
50
54
|
getInstancesByFilter(tenantId: string, filter: InstanceFilter, opts?: PaginationOpts): Promise<PaginatedResult<ApprovalInstance>>;
|
|
51
55
|
/** Optional cursor-based pagination — more efficient than offset at scale. */
|
|
52
56
|
getInstancesByCursor?(tenantId: string, filter: InstanceFilter, opts: CursorPaginationOpts): Promise<CursorPaginatedResult<ApprovalInstance>>;
|
|
57
|
+
/** Pending instances overdue as of `asOf` on any open level, not just the current one. */
|
|
53
58
|
getOverdueInstances(tenantId: string, asOf: Date, filter?: InstanceFilter): Promise<ApprovalInstance[]>;
|
|
54
59
|
/**
|
|
55
60
|
* Count instances matching a filter, without fetching any of them.
|
|
@@ -85,6 +90,7 @@ interface IStorageAdapter {
|
|
|
85
90
|
* @since 2.3.0
|
|
86
91
|
*/
|
|
87
92
|
deleteInstance?(tenantId: string, id: string): Promise<boolean>;
|
|
93
|
+
/** Look up an instance by `idempotencyKey`, unique within a tenant, not globally. */
|
|
88
94
|
getIdempotentInstance(tenantId: string, idempotencyKey: string): Promise<ApprovalInstance | null>;
|
|
89
95
|
appendAuditEntry(tenantId: string, instanceId: string, entry: AuditEntry): Promise<void>;
|
|
90
96
|
}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
/** Structured logger injected into engine and plugin components. */
|
|
1
2
|
interface Logger {
|
|
3
|
+
/** Routine, expected activity. */
|
|
2
4
|
info(msg: string, context?: Record<string, unknown>): void;
|
|
5
|
+
/** Unexpected but recoverable condition. */
|
|
3
6
|
warn(msg: string, context?: Record<string, unknown>): void;
|
|
7
|
+
/** A caught error that did not stop the calling operation. */
|
|
4
8
|
error(msg: string, err?: unknown, context?: Record<string, unknown>): void;
|
|
9
|
+
/** A caught error severe enough that the calling operation could not continue. */
|
|
5
10
|
fatal(msg: string, err?: unknown, context?: Record<string, unknown>): void;
|
|
11
|
+
/** Verbose, developer-facing detail. */
|
|
6
12
|
debug(msg: string, context?: Record<string, unknown>): void;
|
|
7
13
|
}
|
|
14
|
+
/** {@link Logger} that discards every call — the default when no logger is configured. */
|
|
8
15
|
declare const noopLogger: Logger;
|
|
9
16
|
|
|
10
17
|
export { type Logger as L, noopLogger as n };
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
/** Structured logger injected into engine and plugin components. */
|
|
1
2
|
interface Logger {
|
|
3
|
+
/** Routine, expected activity. */
|
|
2
4
|
info(msg: string, context?: Record<string, unknown>): void;
|
|
5
|
+
/** Unexpected but recoverable condition. */
|
|
3
6
|
warn(msg: string, context?: Record<string, unknown>): void;
|
|
7
|
+
/** A caught error that did not stop the calling operation. */
|
|
4
8
|
error(msg: string, err?: unknown, context?: Record<string, unknown>): void;
|
|
9
|
+
/** A caught error severe enough that the calling operation could not continue. */
|
|
5
10
|
fatal(msg: string, err?: unknown, context?: Record<string, unknown>): void;
|
|
11
|
+
/** Verbose, developer-facing detail. */
|
|
6
12
|
debug(msg: string, context?: Record<string, unknown>): void;
|
|
7
13
|
}
|
|
14
|
+
/** {@link Logger} that discards every call — the default when no logger is configured. */
|
|
8
15
|
declare const noopLogger: Logger;
|
|
9
16
|
|
|
10
17
|
export { type Logger as L, noopLogger as n };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-
|
|
2
|
-
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-
|
|
1
|
+
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-D7cy20uD.cjs';
|
|
2
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-Dd1jQizG.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* In-process {@link IStorageAdapter} backed by two `Map`s, keyed by
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-
|
|
2
|
-
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-
|
|
1
|
+
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-CVvNsnnD.js';
|
|
2
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-Dd1jQizG.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* In-process {@link IStorageAdapter} backed by two `Map`s, keyed by
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as tls from 'tls';
|
|
2
2
|
import * as pg from 'pg';
|
|
3
|
-
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-
|
|
4
|
-
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-
|
|
3
|
+
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-D7cy20uD.cjs';
|
|
4
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-Dd1jQizG.cjs';
|
|
5
5
|
|
|
6
6
|
interface PostgresAdapterOptions {
|
|
7
7
|
connectionString?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as tls from 'tls';
|
|
2
2
|
import * as pg from 'pg';
|
|
3
|
-
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-
|
|
4
|
-
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-
|
|
3
|
+
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-CVvNsnnD.js';
|
|
4
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-Dd1jQizG.js';
|
|
5
5
|
|
|
6
6
|
interface PostgresAdapterOptions {
|
|
7
7
|
connectionString?: string;
|
package/dist/index.cjs
CHANGED
|
@@ -556,6 +556,7 @@ var TemplateRegistry = class {
|
|
|
556
556
|
delete resolved.removeLevels;
|
|
557
557
|
return resolved;
|
|
558
558
|
}
|
|
559
|
+
/** Register a new template. Throws if a template with this name already exists for the tenant. */
|
|
559
560
|
async define(config) {
|
|
560
561
|
const existing = await this.adapter.getTemplate(this.tenantId, config.name);
|
|
561
562
|
if (existing) {
|
|
@@ -592,11 +593,13 @@ var TemplateRegistry = class {
|
|
|
592
593
|
await this.adapter.saveTemplate(updated);
|
|
593
594
|
return newId;
|
|
594
595
|
}
|
|
596
|
+
/** Fetch a template by name. Throws if no template with this name exists for the tenant. */
|
|
595
597
|
async get(name) {
|
|
596
598
|
const template = await this.adapter.getTemplate(this.tenantId, name);
|
|
597
599
|
if (!template) throw new ApprovalTemplateNotFoundError(name);
|
|
598
600
|
return template;
|
|
599
601
|
}
|
|
602
|
+
/** List every template registered for the tenant. */
|
|
600
603
|
async list() {
|
|
601
604
|
return this.adapter.listTemplates(this.tenantId);
|
|
602
605
|
}
|
|
@@ -636,6 +639,32 @@ var LevelResolver = class {
|
|
|
636
639
|
for (const [name, fn] of this.resolvers) target.register(name, fn);
|
|
637
640
|
for (const [name, fn] of this.approverTypes) target.registerApproverType(name, fn);
|
|
638
641
|
}
|
|
642
|
+
/**
|
|
643
|
+
* Resolve a level's `approvers` config down to a deduplicated list of user
|
|
644
|
+
* ids, applying out-of-office cover last so a substitute never sees a stale
|
|
645
|
+
* approver id.
|
|
646
|
+
*
|
|
647
|
+
* @param approvers - The level's approver configs (`user`, `role`,
|
|
648
|
+
* `dynamic`, or a custom type registered via {@link registerApproverType}).
|
|
649
|
+
* @param submittedBy - Passed through to `dynamic` resolvers and custom
|
|
650
|
+
* approver-type functions as routing context.
|
|
651
|
+
* @param data - The instance's document data, passed through the same way.
|
|
652
|
+
* @param orgProvider - Required to resolve `role` approvers; omit only when
|
|
653
|
+
* the template uses no `role` approvers.
|
|
654
|
+
* @param outOfOffice - Optional cover lookup, consulted after every other
|
|
655
|
+
* approver type has resolved.
|
|
656
|
+
* @param at - The moment out-of-office cover is resolved for. Defaults to
|
|
657
|
+
* now.
|
|
658
|
+
* @param substitutions - Filled with `original -> stand-in` for each
|
|
659
|
+
* approver replaced by out-of-office cover, so the caller can carry
|
|
660
|
+
* anything keyed by the original id (such as a weighted level's vote
|
|
661
|
+
* weight) across to the substitute.
|
|
662
|
+
* @returns The resolved, deduplicated approver ids (post-substitution).
|
|
663
|
+
* @throws {ApprovalValidationError} If no approvers resolve at all, a
|
|
664
|
+
* `role` approver is configured without an `orgProvider`, a `dynamic`
|
|
665
|
+
* approver names a resolver that was never registered, or a custom
|
|
666
|
+
* approver type was never registered.
|
|
667
|
+
*/
|
|
639
668
|
async resolveApprovers(approvers, submittedBy, data, orgProvider, outOfOffice, at, substitutions) {
|
|
640
669
|
const resolved = [];
|
|
641
670
|
for (const approver of approvers) {
|
|
@@ -1047,7 +1076,7 @@ function evaluateConditions(conditions, data) {
|
|
|
1047
1076
|
function assertStatus(instance, expected) {
|
|
1048
1077
|
if (instance.status !== expected) {
|
|
1049
1078
|
throw new ApprovalError(
|
|
1050
|
-
`
|
|
1079
|
+
`Instance "${instance.id}": expected status "${expected}" but got "${instance.status}".`,
|
|
1051
1080
|
"INVALID_STATUS"
|
|
1052
1081
|
);
|
|
1053
1082
|
}
|
|
@@ -1055,7 +1084,7 @@ function assertStatus(instance, expected) {
|
|
|
1055
1084
|
function assertApproverOnLevel(level, approverId) {
|
|
1056
1085
|
if (!level.approverIds.includes(approverId)) {
|
|
1057
1086
|
throw new ApprovalForbiddenError(
|
|
1058
|
-
`User "${approverId}" is not an approver for level ${level.level}.`
|
|
1087
|
+
`User "${approverId}" is not an approver for level ${level.level} ("${level.name}").`
|
|
1059
1088
|
);
|
|
1060
1089
|
}
|
|
1061
1090
|
}
|
|
@@ -5170,7 +5199,7 @@ function businessHoursCalendar(options = {}) {
|
|
|
5170
5199
|
return cursor;
|
|
5171
5200
|
}
|
|
5172
5201
|
throw new Error(
|
|
5173
|
-
|
|
5202
|
+
`businessHoursCalendar: no working day found within 10 years of ${from.toISOString()} \u2014 check weekendDays and holidays.`
|
|
5174
5203
|
);
|
|
5175
5204
|
};
|
|
5176
5205
|
const base = weekendCalendar(options);
|