hierarchical-approval 2.5.0 → 2.7.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 +72 -0
- package/README.md +58 -0
- package/dist/{ApprovalEngine-CW7Uu1bV.d.cts → ApprovalEngine-BH9GvzMI.d.cts} +97 -7
- package/dist/{ApprovalEngine-BHWls8-z.d.ts → ApprovalEngine-DkQcLaeT.d.ts} +97 -7
- package/dist/{IAuditAdapter-Dwx8ZP4J.d.cts → IAuditAdapter-2UU5edKG.d.cts} +1 -1
- package/dist/{IAuditAdapter-CMCirvY-.d.ts → IAuditAdapter-DSor54iY.d.ts} +1 -1
- package/dist/{IAuthorizationPolicy-BryQIKko.d.ts → IAuthorizationPolicy-BOWg-3AH.d.ts} +1 -1
- package/dist/{IAuthorizationPolicy-B3vaV69v.d.cts → IAuthorizationPolicy-DPpiKzyh.d.cts} +1 -1
- package/dist/{INotificationAdapter-BBq2czpO.d.ts → INotificationAdapter-BNGivLgU.d.ts} +1 -1
- package/dist/{INotificationAdapter-AwbhZEqz.d.cts → INotificationAdapter-C6HPrzh6.d.cts} +1 -1
- package/dist/{IOperationMiddleware-CqzFklMn.d.ts → IOperationMiddleware-CXtg2wzF.d.ts} +1 -1
- package/dist/{IOperationMiddleware-3J-_NSGq.d.cts → IOperationMiddleware-CxnPzpqL.d.cts} +1 -1
- package/dist/{IStorageAdapter-Dw2xXP9B.d.ts → IStorageAdapter-ChPT7ZDp.d.ts} +1 -1
- package/dist/{IStorageAdapter-Pq9J60xv.d.cts → IStorageAdapter-yX9ERfQE.d.cts} +1 -1
- package/dist/adapters/MemoryAdapter.d.cts +2 -2
- package/dist/adapters/MemoryAdapter.d.ts +2 -2
- package/dist/adapters/PostgresAdapter.d.cts +2 -2
- package/dist/adapters/PostgresAdapter.d.ts +2 -2
- package/dist/index.cjs +157 -8
- 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 +157 -8
- package/dist/index.js.map +1 -1
- package/dist/{instance-BO-i9-nq.d.cts → instance-weh2w7Ji.d.cts} +26 -1
- package/dist/{instance-BO-i9-nq.d.ts → instance-weh2w7Ji.d.ts} +26 -1
- package/dist/nestjs.cjs +157 -8
- 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 +157 -8
- package/dist/nestjs.js.map +1 -1
- package/dist/plugins/audit.d.cts +2 -2
- package/dist/plugins/audit.d.ts +2 -2
- package/dist/plugins/notify.d.cts +2 -2
- package/dist/plugins/notify.d.ts +2 -2
- package/dist/plugins/resilience.d.cts +3 -3
- package/dist/plugins/resilience.d.ts +3 -3
- package/dist/plugins/tracing.d.cts +2 -2
- package/dist/plugins/tracing.d.ts +2 -2
- package/dist/plugins/webhook.d.cts +2 -2
- package/dist/plugins/webhook.d.ts +2 -2
- package/dist/testing.cjs +157 -8
- 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 +157 -8
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,78 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
_Nothing yet._
|
|
9
9
|
|
|
10
|
+
## [2.7.0] - 2026-09-04
|
|
11
|
+
|
|
12
|
+
### Added — `explainChain()`
|
|
13
|
+
|
|
14
|
+
- **Explains why a chain resolves the way it does.** `previewApprovalChain()`
|
|
15
|
+
answers *what* the chain will be; nothing answered *why*, so "why does this
|
|
16
|
+
purchase order have a CFO level?" meant reading the template and
|
|
17
|
+
re-evaluating its conditions by hand — the most common support question about
|
|
18
|
+
an approval engine, and the one it was worst at answering.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
await engine.explainChain('purchase-order', data, 'buyer-1');
|
|
22
|
+
// levels: [{ level: 3, name: 'CFO', source: 'condition', addedByRule: 0, … }]
|
|
23
|
+
// skipped: [{ level: 2, name: 'Finance', skippedByRule: 1 }]
|
|
24
|
+
// rules: [{ index: 0, matched: true, addsLevels: [3], skipsLevels: [] }, …]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- **Every rule is reported, matched or not**, along with what it *would* add or
|
|
28
|
+
skip — which is how you find the rule that was supposed to fire and didn't,
|
|
29
|
+
not just the ones that did.
|
|
30
|
+
|
|
31
|
+
- **Failures are described rather than thrown.** A level whose approvers cannot
|
|
32
|
+
be resolved is still listed, carrying `resolutionError`; a rule that throws —
|
|
33
|
+
an unregistered operator, a malformed group — is reported against that rule
|
|
34
|
+
and the rest of the explanation still returns. A diagnostic is least useful at
|
|
35
|
+
exactly the moment a broken rule would make it throw.
|
|
36
|
+
|
|
37
|
+
- Reads nothing and writes nothing, so it is safe to expose to a support UI.
|
|
38
|
+
Sub-workflow levels are marked with their child template and skip approver
|
|
39
|
+
resolution, since nobody approves them directly.
|
|
40
|
+
|
|
41
|
+
New exports: `ChainExplanation`, `ExplainedLevel`, `ExplainedSkip`,
|
|
42
|
+
`ExplainedRule`.
|
|
43
|
+
|
|
44
|
+
## [2.6.0] - 2026-09-04
|
|
45
|
+
|
|
46
|
+
### Added — escalation ladders
|
|
47
|
+
|
|
48
|
+
- **`escalationSteps` escalates repeatedly, up a chain.** A single `escalation`
|
|
49
|
+
could only ever fire once, so a request that stalled past its second deadline
|
|
50
|
+
had nowhere further to go — the usual "chase the manager, then the director,
|
|
51
|
+
then the VP" pattern had to be built outside the engine, on top of the events.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
escalationSteps: [
|
|
55
|
+
{ afterDays: 2, escalateTo: { type: 'user', userId: 'director' } },
|
|
56
|
+
{ afterDays: 4, escalateTo: { type: 'user', userId: 'vp' } },
|
|
57
|
+
{ afterDays: 7, escalateTo: { type: 'role', role: 'exec' } },
|
|
58
|
+
]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Rungs are sorted by delay and fire in order, each **adding** an approver rather
|
|
62
|
+
than replacing one: escalation widens the pool, it does not hand the work over.
|
|
63
|
+
|
|
64
|
+
- **Delays are measured from when the level opened, not from the previous
|
|
65
|
+
escalation**, so a ladder reads the way it is written. The level's opening
|
|
66
|
+
time is recovered from its `submitted` / `level_advanced` audit entry.
|
|
67
|
+
|
|
68
|
+
- Rungs accept `afterHours` as well as `afterDays`, counted through the
|
|
69
|
+
working-hours calendar from 2.5.0 when one is configured. A per-level
|
|
70
|
+
`escalationAfterDays`/`escalationAfterHours` still decides *when* the first
|
|
71
|
+
rung fires — it is the more specific statement about that level — while the
|
|
72
|
+
ladder supplies *who*.
|
|
73
|
+
|
|
74
|
+
- Templates carrying only the single-step `escalation` behave exactly as before;
|
|
75
|
+
the ladder takes precedence when both are set. Levels track progress in
|
|
76
|
+
`escalationStep`, and the ladder is captured in the template snapshot, so an
|
|
77
|
+
in-flight approval keeps the ladder it was submitted under.
|
|
78
|
+
|
|
79
|
+
New exports: `EscalationStep`. `ApprovalTemplateConfig` gains
|
|
80
|
+
`escalationSteps`; levels gain `escalationStep`.
|
|
81
|
+
|
|
10
82
|
## [2.5.0] - 2026-09-04
|
|
11
83
|
|
|
12
84
|
### Added — deadlines in working hours
|
package/README.md
CHANGED
|
@@ -468,6 +468,64 @@ The sweep is **not atomic**: it reports per-instance failures rather than rollin
|
|
|
468
468
|
back. A partial transfer is the useful outcome — the approvals that can move
|
|
469
469
|
should move, and the ones that cannot are named so a human can look at them.
|
|
470
470
|
|
|
471
|
+
### Why does this chain look like this?
|
|
472
|
+
|
|
473
|
+
`previewApprovalChain()` answers *what* the chain will be. `explainChain()`
|
|
474
|
+
answers *why* — the question behind "why does this purchase order have a CFO
|
|
475
|
+
level?":
|
|
476
|
+
|
|
477
|
+
```ts
|
|
478
|
+
const explanation = await engine.explainChain('purchase-order', data, 'buyer-1');
|
|
479
|
+
// {
|
|
480
|
+
// templateName: 'purchase-order',
|
|
481
|
+
// levels: [
|
|
482
|
+
// { level: 1, name: 'Manager', source: 'template', resolvedApprovers: ['mgr-1'], … },
|
|
483
|
+
// { level: 3, name: 'CFO', source: 'condition', addedByRule: 0, resolvedApprovers: ['cfo'], … },
|
|
484
|
+
// ],
|
|
485
|
+
// skipped: [{ level: 2, name: 'Finance', skippedByRule: 1 }],
|
|
486
|
+
// rules: [
|
|
487
|
+
// { index: 0, matched: true, addsLevels: [3], skipsLevels: [] },
|
|
488
|
+
// { index: 1, matched: true, addsLevels: [], skipsLevels: [2] },
|
|
489
|
+
// ],
|
|
490
|
+
// }
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
Every rule is reported, matched or not, along with what it *would* do — which is
|
|
494
|
+
usually how you find the rule that was supposed to fire and didn't. A level
|
|
495
|
+
whose approvers cannot be resolved is still listed, with `resolutionError`
|
|
496
|
+
naming the reason; a rule that throws is reported against that rule rather than
|
|
497
|
+
failing the whole explanation, since the explanation is least useful at exactly
|
|
498
|
+
the moment a broken rule would make it throw.
|
|
499
|
+
|
|
500
|
+
Reads nothing and writes nothing, so it is safe to expose to a support UI.
|
|
501
|
+
|
|
502
|
+
### Escalation ladders
|
|
503
|
+
|
|
504
|
+
A single `escalation` fires once, so a request that stalls past its second
|
|
505
|
+
deadline has nowhere further to go. `escalationSteps` is a ladder:
|
|
506
|
+
|
|
507
|
+
```ts
|
|
508
|
+
escalationSteps: [
|
|
509
|
+
{ afterDays: 2, escalateTo: { type: 'user', userId: 'director' } },
|
|
510
|
+
{ afterDays: 4, escalateTo: { type: 'user', userId: 'vp' } },
|
|
511
|
+
{ afterDays: 7, escalateTo: { type: 'role', role: 'exec' } },
|
|
512
|
+
]
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Rungs are sorted by delay and fire in order, each **adding** an approver without
|
|
516
|
+
removing anyone already assigned — escalation widens the pool rather than
|
|
517
|
+
handing the work over.
|
|
518
|
+
|
|
519
|
+
Every delay is measured **from the moment the level opened**, not from the
|
|
520
|
+
previous escalation, so the ladder reads the way it is written: late at day 2,
|
|
521
|
+
later at day 4, latest at day 7. Rungs accept `afterHours` too, counted through
|
|
522
|
+
the working-hours calendar if one is configured.
|
|
523
|
+
|
|
524
|
+
A per-level `escalationAfterDays`/`escalationAfterHours` still wins for *when*
|
|
525
|
+
the first rung fires — it is the more specific statement about that level — while
|
|
526
|
+
the ladder supplies *who*. Templates with only the single-step `escalation`
|
|
527
|
+
behave exactly as before.
|
|
528
|
+
|
|
471
529
|
### SLAs in working hours
|
|
472
530
|
|
|
473
531
|
Deadlines were whole days, so "respond within four working hours" could only be
|
|
@@ -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 { l as ConditionExpression,
|
|
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-yX9ERfQE.cjs';
|
|
2
|
+
import { l as ConditionExpression, q as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, e as ApprovalMode, b as AuditEntry } from './instance-weh2w7Ji.cjs';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-C6HPrzh6.cjs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { L as Logger } from './Logger-BplhlU7l.cjs';
|
|
6
6
|
import { C as Clock } from './Clock-3FnOczFJ.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-CxnPzpqL.cjs';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-2UU5edKG.cjs';
|
|
9
9
|
import { I as IMetricsAdapter } from './IMetricsAdapter-DWq8IFaf.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-DPpiKzyh.cjs';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -299,6 +299,52 @@ interface PreviewResult {
|
|
|
299
299
|
/** Indices (0-based) of conditions that fired for this data. */
|
|
300
300
|
conditionsApplied: number[];
|
|
301
301
|
}
|
|
302
|
+
/** Where one level in an explained chain came from. */
|
|
303
|
+
interface ExplainedLevel {
|
|
304
|
+
level: number;
|
|
305
|
+
name: string;
|
|
306
|
+
mode: ApprovalMode;
|
|
307
|
+
/** `'template'` for a statically declared level, `'condition'` for one a rule added. */
|
|
308
|
+
source: 'template' | 'condition';
|
|
309
|
+
/** Index of the condition rule that added it, when `source` is `'condition'`. */
|
|
310
|
+
addedByRule?: number;
|
|
311
|
+
resolvedApprovers: string[];
|
|
312
|
+
/** Why approver resolution failed, when it did. The level is still listed. */
|
|
313
|
+
resolutionError?: string;
|
|
314
|
+
/** Set when this level hands off to a child approval. */
|
|
315
|
+
subWorkflowTemplate?: string;
|
|
316
|
+
}
|
|
317
|
+
/** A level the template declares that will not run, and the rule that removed it. */
|
|
318
|
+
interface ExplainedSkip {
|
|
319
|
+
level: number;
|
|
320
|
+
name: string;
|
|
321
|
+
/** Index of the condition rule whose `skipLevels` removed it. */
|
|
322
|
+
skippedByRule: number;
|
|
323
|
+
}
|
|
324
|
+
/** How one condition rule evaluated against the data. */
|
|
325
|
+
interface ExplainedRule {
|
|
326
|
+
index: number;
|
|
327
|
+
matched: boolean;
|
|
328
|
+
/** Levels this rule would add. Present whether or not it matched. */
|
|
329
|
+
addsLevels: number[];
|
|
330
|
+
/** Levels this rule would skip. Present whether or not it matched. */
|
|
331
|
+
skipsLevels: number[];
|
|
332
|
+
/** Why the rule could not be evaluated, e.g. an unregistered operator. */
|
|
333
|
+
error?: string;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* A full account of why a chain looks the way it does.
|
|
337
|
+
*
|
|
338
|
+
* `previewApprovalChain()` answers *what* the chain will be; this answers *why*,
|
|
339
|
+
* which is the question a support engineer actually has when a purchase order
|
|
340
|
+
* arrives with a level nobody expected.
|
|
341
|
+
*/
|
|
342
|
+
interface ChainExplanation {
|
|
343
|
+
templateName: string;
|
|
344
|
+
levels: ExplainedLevel[];
|
|
345
|
+
skipped: ExplainedSkip[];
|
|
346
|
+
rules: ExplainedRule[];
|
|
347
|
+
}
|
|
302
348
|
interface BulkResult {
|
|
303
349
|
succeeded: ApprovalInstance[];
|
|
304
350
|
failed: Array<{
|
|
@@ -677,6 +723,28 @@ declare class ApprovalEngine {
|
|
|
677
723
|
/** Preview the resolved approval chain for a template and document data, without creating an instance. */
|
|
678
724
|
previewApprovalChain(templateName: string, data: Record<string, unknown>, submittedBy: string): Promise<PreviewResult>;
|
|
679
725
|
/** Check whether a user is eligible to approve a specific instance. Never throws. */
|
|
726
|
+
/**
|
|
727
|
+
* Explain why a chain resolves the way it does for a given document.
|
|
728
|
+
*
|
|
729
|
+
* `previewApprovalChain()` answers *what* the chain will be. This answers
|
|
730
|
+
* *why*: which rule added a level, which rule removed one, which rules were
|
|
731
|
+
* evaluated and did not match, and where each level's approvers came from —
|
|
732
|
+
* the question behind "why does this purchase order have a CFO level?", which
|
|
733
|
+
* previously meant reading the template and re-evaluating the conditions by
|
|
734
|
+
* hand.
|
|
735
|
+
*
|
|
736
|
+
* A rule that throws — an operator nobody registered, a malformed group — is
|
|
737
|
+
* reported against that rule rather than failing the whole explanation. The
|
|
738
|
+
* explanation is a diagnostic tool, and it is least useful at exactly the
|
|
739
|
+
* moment a broken rule makes it throw.
|
|
740
|
+
*
|
|
741
|
+
* Reads nothing and writes nothing; safe to expose to a support UI.
|
|
742
|
+
*
|
|
743
|
+
* @param templateName - Template to explain.
|
|
744
|
+
* @param data - Document data the conditions are evaluated against.
|
|
745
|
+
* @param submittedBy - Submitter, used for approver resolution.
|
|
746
|
+
*/
|
|
747
|
+
explainChain(templateName: string, data: Record<string, unknown>, submittedBy: string): Promise<ChainExplanation>;
|
|
680
748
|
canApprove(instanceId: string, userId: string): Promise<CanApproveResult>;
|
|
681
749
|
/** Emergency bypass — completes the instance as 'approved', skipping remaining levels. Requires template.allowOverride = true. */
|
|
682
750
|
override(instanceId: string, raw: OverrideOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
|
|
@@ -852,6 +920,28 @@ declare class ApprovalEngine {
|
|
|
852
920
|
private deadlineFromHours;
|
|
853
921
|
/** Level deadline from whichever of days/hours the template configured. */
|
|
854
922
|
private levelEscalationDue;
|
|
923
|
+
/** First rung of a ladder, sorted by delay, or undefined when there is none. */
|
|
924
|
+
private firstRungOf;
|
|
925
|
+
/**
|
|
926
|
+
* The escalation ladder for an instance, sorted by delay.
|
|
927
|
+
*
|
|
928
|
+
* Read from the instance's template snapshot so an in-flight approval keeps
|
|
929
|
+
* the ladder it was submitted under, exactly as the single-step
|
|
930
|
+
* {@link EscalationConfig} already did.
|
|
931
|
+
*/
|
|
932
|
+
private escalationLadder;
|
|
933
|
+
/**
|
|
934
|
+
* When a level started collecting decisions.
|
|
935
|
+
*
|
|
936
|
+
* Escalation rungs are measured from this, not from the previous rung, so a
|
|
937
|
+
* ladder reads the way it is written. Recovered from the audit trail — the
|
|
938
|
+
* `submitted` entry for the opening level, the `level_advanced` entry
|
|
939
|
+
* otherwise — and falls back to `now` when no entry exists, which only leaves
|
|
940
|
+
* the ladder no worse off than the single-step behaviour it replaces.
|
|
941
|
+
*/
|
|
942
|
+
private levelOpenedAt;
|
|
943
|
+
/** Deadline for one rung, measured from when the level opened. */
|
|
944
|
+
private stepDueAt;
|
|
855
945
|
private requireInstance;
|
|
856
946
|
/**
|
|
857
947
|
* Identity of the parallel branch group a level belongs to.
|
|
@@ -942,4 +1032,4 @@ declare class ApprovalEngine {
|
|
|
942
1032
|
private runExternalAudit;
|
|
943
1033
|
}
|
|
944
1034
|
|
|
945
|
-
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E,
|
|
1035
|
+
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E, type TemplateBundle as F, type TransferResult as G, type HealthResult as H, type IdGeneratorFn as I, businessHoursCalendar as J, defaultIdGenerator as K, toComparableNumber as L, validateConditionExpression as M, weekendCalendar as N, type OrgProvider as O, type PreviewChainLevel as P, type ReassignOptions as R, type SubmitOptions as S, TEMPLATE_BUNDLE_VERSION as T, type UpdateDataOptions as U, type ValidationResult as V, type WeekendCalendarOptions as W, ApprovalEngine as a, type ApprovalEngineOptions as b, type ApprovalStatistics as c, type ApproveOptions as d, type ApproverResolverFn as e, type ApproverWorkload as f, type BusinessCalendar as g, type BusinessHoursCalendarOptions as h, type CancelOptions as i, type ChainExplanation as j, type ConditionOperatorFn as k, type CycleTimeStats as l, type ExplainedLevel as m, type ExplainedRule as n, type ExplainedSkip as o, type IdempotencyKeyFn as p, type ImportResult as q, type OutOfOfficeProvider as r, type OverrideOptions as s, type PreviewResult as t, type ProvideInfoOptions as u, type PurgeResult as v, type RejectOptions as w, type RequestInfoOptions as x, type ResubmitOptions as y, type RetryPolicy as z };
|
|
@@ -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 { l as ConditionExpression,
|
|
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-ChPT7ZDp.js';
|
|
2
|
+
import { l as ConditionExpression, q as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, e as ApprovalMode, b as AuditEntry } from './instance-weh2w7Ji.js';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-BNGivLgU.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { L as Logger } from './Logger-BplhlU7l.js';
|
|
6
6
|
import { C as Clock } from './Clock-3FnOczFJ.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-CXtg2wzF.js';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-DSor54iY.js';
|
|
9
9
|
import { I as IMetricsAdapter } from './IMetricsAdapter-DWq8IFaf.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-BOWg-3AH.js';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -299,6 +299,52 @@ interface PreviewResult {
|
|
|
299
299
|
/** Indices (0-based) of conditions that fired for this data. */
|
|
300
300
|
conditionsApplied: number[];
|
|
301
301
|
}
|
|
302
|
+
/** Where one level in an explained chain came from. */
|
|
303
|
+
interface ExplainedLevel {
|
|
304
|
+
level: number;
|
|
305
|
+
name: string;
|
|
306
|
+
mode: ApprovalMode;
|
|
307
|
+
/** `'template'` for a statically declared level, `'condition'` for one a rule added. */
|
|
308
|
+
source: 'template' | 'condition';
|
|
309
|
+
/** Index of the condition rule that added it, when `source` is `'condition'`. */
|
|
310
|
+
addedByRule?: number;
|
|
311
|
+
resolvedApprovers: string[];
|
|
312
|
+
/** Why approver resolution failed, when it did. The level is still listed. */
|
|
313
|
+
resolutionError?: string;
|
|
314
|
+
/** Set when this level hands off to a child approval. */
|
|
315
|
+
subWorkflowTemplate?: string;
|
|
316
|
+
}
|
|
317
|
+
/** A level the template declares that will not run, and the rule that removed it. */
|
|
318
|
+
interface ExplainedSkip {
|
|
319
|
+
level: number;
|
|
320
|
+
name: string;
|
|
321
|
+
/** Index of the condition rule whose `skipLevels` removed it. */
|
|
322
|
+
skippedByRule: number;
|
|
323
|
+
}
|
|
324
|
+
/** How one condition rule evaluated against the data. */
|
|
325
|
+
interface ExplainedRule {
|
|
326
|
+
index: number;
|
|
327
|
+
matched: boolean;
|
|
328
|
+
/** Levels this rule would add. Present whether or not it matched. */
|
|
329
|
+
addsLevels: number[];
|
|
330
|
+
/** Levels this rule would skip. Present whether or not it matched. */
|
|
331
|
+
skipsLevels: number[];
|
|
332
|
+
/** Why the rule could not be evaluated, e.g. an unregistered operator. */
|
|
333
|
+
error?: string;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* A full account of why a chain looks the way it does.
|
|
337
|
+
*
|
|
338
|
+
* `previewApprovalChain()` answers *what* the chain will be; this answers *why*,
|
|
339
|
+
* which is the question a support engineer actually has when a purchase order
|
|
340
|
+
* arrives with a level nobody expected.
|
|
341
|
+
*/
|
|
342
|
+
interface ChainExplanation {
|
|
343
|
+
templateName: string;
|
|
344
|
+
levels: ExplainedLevel[];
|
|
345
|
+
skipped: ExplainedSkip[];
|
|
346
|
+
rules: ExplainedRule[];
|
|
347
|
+
}
|
|
302
348
|
interface BulkResult {
|
|
303
349
|
succeeded: ApprovalInstance[];
|
|
304
350
|
failed: Array<{
|
|
@@ -677,6 +723,28 @@ declare class ApprovalEngine {
|
|
|
677
723
|
/** Preview the resolved approval chain for a template and document data, without creating an instance. */
|
|
678
724
|
previewApprovalChain(templateName: string, data: Record<string, unknown>, submittedBy: string): Promise<PreviewResult>;
|
|
679
725
|
/** Check whether a user is eligible to approve a specific instance. Never throws. */
|
|
726
|
+
/**
|
|
727
|
+
* Explain why a chain resolves the way it does for a given document.
|
|
728
|
+
*
|
|
729
|
+
* `previewApprovalChain()` answers *what* the chain will be. This answers
|
|
730
|
+
* *why*: which rule added a level, which rule removed one, which rules were
|
|
731
|
+
* evaluated and did not match, and where each level's approvers came from —
|
|
732
|
+
* the question behind "why does this purchase order have a CFO level?", which
|
|
733
|
+
* previously meant reading the template and re-evaluating the conditions by
|
|
734
|
+
* hand.
|
|
735
|
+
*
|
|
736
|
+
* A rule that throws — an operator nobody registered, a malformed group — is
|
|
737
|
+
* reported against that rule rather than failing the whole explanation. The
|
|
738
|
+
* explanation is a diagnostic tool, and it is least useful at exactly the
|
|
739
|
+
* moment a broken rule makes it throw.
|
|
740
|
+
*
|
|
741
|
+
* Reads nothing and writes nothing; safe to expose to a support UI.
|
|
742
|
+
*
|
|
743
|
+
* @param templateName - Template to explain.
|
|
744
|
+
* @param data - Document data the conditions are evaluated against.
|
|
745
|
+
* @param submittedBy - Submitter, used for approver resolution.
|
|
746
|
+
*/
|
|
747
|
+
explainChain(templateName: string, data: Record<string, unknown>, submittedBy: string): Promise<ChainExplanation>;
|
|
680
748
|
canApprove(instanceId: string, userId: string): Promise<CanApproveResult>;
|
|
681
749
|
/** Emergency bypass — completes the instance as 'approved', skipping remaining levels. Requires template.allowOverride = true. */
|
|
682
750
|
override(instanceId: string, raw: OverrideOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
|
|
@@ -852,6 +920,28 @@ declare class ApprovalEngine {
|
|
|
852
920
|
private deadlineFromHours;
|
|
853
921
|
/** Level deadline from whichever of days/hours the template configured. */
|
|
854
922
|
private levelEscalationDue;
|
|
923
|
+
/** First rung of a ladder, sorted by delay, or undefined when there is none. */
|
|
924
|
+
private firstRungOf;
|
|
925
|
+
/**
|
|
926
|
+
* The escalation ladder for an instance, sorted by delay.
|
|
927
|
+
*
|
|
928
|
+
* Read from the instance's template snapshot so an in-flight approval keeps
|
|
929
|
+
* the ladder it was submitted under, exactly as the single-step
|
|
930
|
+
* {@link EscalationConfig} already did.
|
|
931
|
+
*/
|
|
932
|
+
private escalationLadder;
|
|
933
|
+
/**
|
|
934
|
+
* When a level started collecting decisions.
|
|
935
|
+
*
|
|
936
|
+
* Escalation rungs are measured from this, not from the previous rung, so a
|
|
937
|
+
* ladder reads the way it is written. Recovered from the audit trail — the
|
|
938
|
+
* `submitted` entry for the opening level, the `level_advanced` entry
|
|
939
|
+
* otherwise — and falls back to `now` when no entry exists, which only leaves
|
|
940
|
+
* the ladder no worse off than the single-step behaviour it replaces.
|
|
941
|
+
*/
|
|
942
|
+
private levelOpenedAt;
|
|
943
|
+
/** Deadline for one rung, measured from when the level opened. */
|
|
944
|
+
private stepDueAt;
|
|
855
945
|
private requireInstance;
|
|
856
946
|
/**
|
|
857
947
|
* Identity of the parallel branch group a level belongs to.
|
|
@@ -942,4 +1032,4 @@ declare class ApprovalEngine {
|
|
|
942
1032
|
private runExternalAudit;
|
|
943
1033
|
}
|
|
944
1034
|
|
|
945
|
-
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E,
|
|
1035
|
+
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E, type TemplateBundle as F, type TransferResult as G, type HealthResult as H, type IdGeneratorFn as I, businessHoursCalendar as J, defaultIdGenerator as K, toComparableNumber as L, validateConditionExpression as M, weekendCalendar as N, type OrgProvider as O, type PreviewChainLevel as P, type ReassignOptions as R, type SubmitOptions as S, TEMPLATE_BUNDLE_VERSION as T, type UpdateDataOptions as U, type ValidationResult as V, type WeekendCalendarOptions as W, ApprovalEngine as a, type ApprovalEngineOptions as b, type ApprovalStatistics as c, type ApproveOptions as d, type ApproverResolverFn as e, type ApproverWorkload as f, type BusinessCalendar as g, type BusinessHoursCalendarOptions as h, type CancelOptions as i, type ChainExplanation as j, type ConditionOperatorFn as k, type CycleTimeStats as l, type ExplainedLevel as m, type ExplainedRule as n, type ExplainedSkip as o, type IdempotencyKeyFn as p, type ImportResult as q, type OutOfOfficeProvider as r, type OverrideOptions as s, type PreviewResult as t, type ProvideInfoOptions as u, type PurgeResult as v, type RejectOptions as w, type RequestInfoOptions as x, type ResubmitOptions as y, type RetryPolicy as z };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-weh2w7Ji.js';
|
|
2
2
|
|
|
3
3
|
interface AuthorizationContext {
|
|
4
4
|
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData' | 'requestInfo' | 'provideInfo' | 'addAttachment' | 'removeAttachment';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-weh2w7Ji.cjs';
|
|
2
2
|
|
|
3
3
|
interface AuthorizationContext {
|
|
4
4
|
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData' | 'requestInfo' | 'provideInfo' | 'addAttachment' | 'removeAttachment';
|
|
@@ -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-weh2w7Ji.js';
|
|
2
2
|
|
|
3
3
|
interface PaginationOpts {
|
|
4
4
|
limit: number;
|
|
@@ -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-weh2w7Ji.cjs';
|
|
2
2
|
|
|
3
3
|
interface PaginationOpts {
|
|
4
4
|
limit: number;
|
|
@@ -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-yX9ERfQE.cjs';
|
|
2
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-weh2w7Ji.cjs';
|
|
3
3
|
|
|
4
4
|
declare class MemoryAdapter implements IStorageAdapter {
|
|
5
5
|
private templates;
|
|
@@ -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-ChPT7ZDp.js';
|
|
2
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-weh2w7Ji.js';
|
|
3
3
|
|
|
4
4
|
declare class MemoryAdapter implements IStorageAdapter {
|
|
5
5
|
private templates;
|
|
@@ -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-yX9ERfQE.cjs';
|
|
4
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-weh2w7Ji.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-ChPT7ZDp.js';
|
|
4
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-weh2w7Ji.js';
|
|
5
5
|
|
|
6
6
|
interface PostgresAdapterOptions {
|
|
7
7
|
connectionString?: string;
|