hierarchical-approval 1.6.0 → 1.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 +39 -0
- package/README.md +34 -0
- package/dist/{ApprovalEngine-SI-IRUMY.d.cts → ApprovalEngine-D16llfJv.d.cts} +46 -8
- package/dist/{ApprovalEngine-CyZJDbjP.d.ts → ApprovalEngine-gQdFy2CO.d.ts} +46 -8
- package/dist/{IAuditAdapter-wfOcN3Zf.d.ts → IAuditAdapter-DDV4Rf9F.d.ts} +1 -1
- package/dist/{IAuditAdapter-Cr0dLoVP.d.cts → IAuditAdapter-UJNdJanq.d.cts} +1 -1
- package/dist/{IAuthorizationPolicy-BHybAsq5.d.cts → IAuthorizationPolicy-CFJ-gXKl.d.ts} +2 -2
- package/dist/{IAuthorizationPolicy-CdvoZsGC.d.ts → IAuthorizationPolicy-CN6LAaKg.d.cts} +2 -2
- package/dist/{IMetricsAdapter-JZVK3QUq.d.cts → IMetricsAdapter-DWq8IFaf.d.cts} +1 -1
- package/dist/{IMetricsAdapter-JZVK3QUq.d.ts → IMetricsAdapter-DWq8IFaf.d.ts} +1 -1
- package/dist/{INotificationAdapter-Cn-LNPtD.d.cts → INotificationAdapter-Dv_MuYwS.d.ts} +12 -2
- package/dist/{INotificationAdapter-Cyo0cvXU.d.ts → INotificationAdapter-Dy2d0JKy.d.cts} +12 -2
- package/dist/{IOperationMiddleware-DrJy9UtL.d.cts → IOperationMiddleware-DGG-guxK.d.cts} +1 -1
- package/dist/{IOperationMiddleware-rx7XAPrW.d.ts → IOperationMiddleware-DWYXVuJD.d.ts} +1 -1
- package/dist/{IStorageAdapter-Dz8jrIAK.d.cts → IStorageAdapter-CRSfnH8U.d.cts} +1 -1
- package/dist/{IStorageAdapter-CXRyeUOK.d.ts → IStorageAdapter-DMa5VNNz.d.ts} +1 -1
- package/dist/adapters/MemoryAdapter.d.cts +2 -2
- package/dist/adapters/MemoryAdapter.d.ts +2 -2
- package/dist/adapters/PostgresAdapter.cjs +13 -4
- 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 +13 -4
- package/dist/adapters/PostgresAdapter.js.map +1 -1
- package/dist/index.cjs +187 -0
- 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 +187 -0
- package/dist/index.js.map +1 -1
- package/dist/{instance-SA0BU-Gb.d.cts → instance-BE0uJmg3.d.cts} +27 -2
- package/dist/{instance-SA0BU-Gb.d.ts → instance-BE0uJmg3.d.ts} +27 -2
- package/dist/nestjs.cjs +187 -0
- package/dist/nestjs.cjs.map +1 -1
- package/dist/nestjs.d.cts +8 -8
- package/dist/nestjs.d.ts +8 -8
- package/dist/nestjs.js +187 -0
- 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/metrics.cjs +2 -0
- package/dist/plugins/metrics.cjs.map +1 -1
- package/dist/plugins/metrics.d.cts +1 -1
- package/dist/plugins/metrics.d.ts +1 -1
- package/dist/plugins/metrics.js +2 -0
- package/dist/plugins/metrics.js.map +1 -1
- 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 +187 -0
- 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 +187 -0
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,45 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
_Nothing yet._
|
|
9
9
|
|
|
10
|
+
## [1.7.0] - 2026-09-04
|
|
11
|
+
|
|
12
|
+
### Added — attachment references
|
|
13
|
+
|
|
14
|
+
- **`addAttachment()` / `removeAttachment()` attach supporting evidence to an
|
|
15
|
+
approval** — a quote PDF, a signed contract, a screenshot of a system of
|
|
16
|
+
record. Approvers had nowhere to put the document their decision rested on, so
|
|
17
|
+
it lived in an email thread the audit trail never saw.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
await engine.addAttachment(id, {
|
|
21
|
+
actorId: 'buyer-1',
|
|
22
|
+
name: 'quote.pdf',
|
|
23
|
+
uri: 's3://procurement/quotes/q-1.pdf',
|
|
24
|
+
contentType: 'application/pdf',
|
|
25
|
+
sizeBytes: 48_120,
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- **References only, never bytes.** Approval documents belong in the object
|
|
30
|
+
store or DMS the organisation already runs, which handles retention, virus
|
|
31
|
+
scanning and access control far better than an approval table could; copying
|
|
32
|
+
them here would make the audit database the largest and least governed copy of
|
|
33
|
+
them. Removing detaches the reference and never deletes from the underlying
|
|
34
|
+
store.
|
|
35
|
+
|
|
36
|
+
- **The audit trail keeps what was removed** — name and URI are recorded on the
|
|
37
|
+
`attachment_removed` entry, so the record still shows an approver saw evidence
|
|
38
|
+
that is no longer listed. Dropping that would let the trail imply a decision
|
|
39
|
+
was made on less than it was.
|
|
40
|
+
|
|
41
|
+
- Persisted across all four PostgreSQL sites (schema, `migrate()`,
|
|
42
|
+
insert, update, and read-back), with `addedAt` revived as a `Date`. An
|
|
43
|
+
instance with no attachments reads back as `undefined` rather than `[]`,
|
|
44
|
+
keeping the round-trip shape identical to `MemoryAdapter`.
|
|
45
|
+
|
|
46
|
+
New exports: `Attachment`, `AttachmentEvent`, `AddAttachmentOptions`,
|
|
47
|
+
`RemoveAttachmentOptions`. `ApprovalInstance` gains `attachments`.
|
|
48
|
+
|
|
10
49
|
## [1.6.0] - 2026-09-04
|
|
11
50
|
|
|
12
51
|
### Fixed — PostgresAdapter silently dropped most instance updates
|
package/README.md
CHANGED
|
@@ -399,6 +399,40 @@ engine.registerConditionOperator('between', (actual, expected) => {
|
|
|
399
399
|
});
|
|
400
400
|
```
|
|
401
401
|
|
|
402
|
+
### Attachments
|
|
403
|
+
|
|
404
|
+
Attach supporting evidence — a quote PDF, a signed contract, a screenshot of a
|
|
405
|
+
system of record:
|
|
406
|
+
|
|
407
|
+
```ts
|
|
408
|
+
const withQuote = await engine.addAttachment(instance.id, {
|
|
409
|
+
actorId: 'buyer-1',
|
|
410
|
+
name: 'quote.pdf',
|
|
411
|
+
uri: 's3://procurement/quotes/q-1.pdf', // a reference, not bytes
|
|
412
|
+
contentType: 'application/pdf',
|
|
413
|
+
sizeBytes: 48_120,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
await engine.removeAttachment(instance.id, {
|
|
417
|
+
actorId: 'buyer-1',
|
|
418
|
+
attachmentId: withQuote.attachments[0].id,
|
|
419
|
+
reason: 'superseded by revised quote',
|
|
420
|
+
});
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**The engine stores references, never bytes.** Approval documents belong in the
|
|
424
|
+
object store or DMS you already run, which handles retention, virus scanning and
|
|
425
|
+
access control far better than an approval table could — copying them here would
|
|
426
|
+
make the audit database the largest and least governed copy of them.
|
|
427
|
+
|
|
428
|
+
Removing detaches the reference; it never deletes from the underlying store,
|
|
429
|
+
which is the DMS's call. The audit entry keeps the name and URI of what was
|
|
430
|
+
removed, so the trail still shows an approver saw evidence that is no longer
|
|
431
|
+
listed.
|
|
432
|
+
|
|
433
|
+
Emits `approval:attachment_added` / `approval:attachment_removed`. Allowed on
|
|
434
|
+
any non-terminal instance, subject to your authorization policy.
|
|
435
|
+
|
|
402
436
|
### Asking the submitter a question
|
|
403
437
|
|
|
404
438
|
Approvers routinely need one fact before they can decide. `requestInfo()` puts
|
|
@@ -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 {
|
|
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-CRSfnH8U.cjs';
|
|
2
|
+
import { l as ConditionExpression, p as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, e as ApprovalMode, b as AuditEntry } from './instance-BE0uJmg3.cjs';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-Dy2d0JKy.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-
|
|
9
|
-
import { I as IMetricsAdapter } from './IMetricsAdapter-
|
|
7
|
+
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-DGG-guxK.cjs';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-UJNdJanq.cjs';
|
|
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-CN6LAaKg.cjs';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -78,6 +78,18 @@ declare const ProvideInfoOptionsSchema: z.ZodObject<{
|
|
|
78
78
|
respondedBy: z.ZodString;
|
|
79
79
|
response: z.ZodString;
|
|
80
80
|
}, z.core.$strip>;
|
|
81
|
+
declare const AddAttachmentOptionsSchema: z.ZodObject<{
|
|
82
|
+
actorId: z.ZodString;
|
|
83
|
+
name: z.ZodString;
|
|
84
|
+
uri: z.ZodString;
|
|
85
|
+
contentType: z.ZodOptional<z.ZodString>;
|
|
86
|
+
sizeBytes: z.ZodOptional<z.ZodNumber>;
|
|
87
|
+
}, z.core.$strip>;
|
|
88
|
+
declare const RemoveAttachmentOptionsSchema: z.ZodObject<{
|
|
89
|
+
actorId: z.ZodString;
|
|
90
|
+
attachmentId: z.ZodString;
|
|
91
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
92
|
+
}, z.core.$strip>;
|
|
81
93
|
declare const UpdateDataOptionsSchema: z.ZodObject<{
|
|
82
94
|
updatedBy: z.ZodString;
|
|
83
95
|
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -101,6 +113,8 @@ type OverrideOptions = z.infer<typeof OverrideOptionsSchema>;
|
|
|
101
113
|
type UpdateDataOptions = z.infer<typeof UpdateDataOptionsSchema>;
|
|
102
114
|
type RequestInfoOptions = z.infer<typeof RequestInfoOptionsSchema>;
|
|
103
115
|
type ProvideInfoOptions = z.infer<typeof ProvideInfoOptionsSchema>;
|
|
116
|
+
type AddAttachmentOptions = z.infer<typeof AddAttachmentOptionsSchema>;
|
|
117
|
+
type RemoveAttachmentOptions = z.infer<typeof RemoveAttachmentOptionsSchema>;
|
|
104
118
|
|
|
105
119
|
/**
|
|
106
120
|
* Computes deadline dates from a number of days. The default engine behaviour
|
|
@@ -132,7 +146,7 @@ interface WeekendCalendarOptions {
|
|
|
132
146
|
*/
|
|
133
147
|
declare function weekendCalendar(options?: WeekendCalendarOptions): BusinessCalendar;
|
|
134
148
|
|
|
135
|
-
type IdGeneratorPrefix = 'inst' | 'tpl';
|
|
149
|
+
type IdGeneratorPrefix = 'inst' | 'tpl' | 'att';
|
|
136
150
|
type IdGeneratorFn = (prefix: IdGeneratorPrefix) => string;
|
|
137
151
|
declare const defaultIdGenerator: IdGeneratorFn;
|
|
138
152
|
|
|
@@ -490,6 +504,30 @@ declare class ApprovalEngine {
|
|
|
490
504
|
* by being held.
|
|
491
505
|
*/
|
|
492
506
|
private extendDeadlinesBy;
|
|
507
|
+
/**
|
|
508
|
+
* Attach supporting evidence to an approval — a quote PDF, a signed contract,
|
|
509
|
+
* a screenshot of a system of record.
|
|
510
|
+
*
|
|
511
|
+
* Stores a **reference**, never bytes. Approval documents belong in the object
|
|
512
|
+
* store or DMS the organisation already runs, which handles retention, virus
|
|
513
|
+
* scanning and access control far better than an approval table could;
|
|
514
|
+
* copying them here would make the audit database the largest and least
|
|
515
|
+
* governed copy of them.
|
|
516
|
+
*
|
|
517
|
+
* Allowed on any non-terminal instance, by anyone the authorization policy
|
|
518
|
+
* permits: the submitter adding a missing quote and an approver adding the
|
|
519
|
+
* note that justifies their decision are both ordinary.
|
|
520
|
+
*/
|
|
521
|
+
addAttachment(instanceId: string, raw: AddAttachmentOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
|
|
522
|
+
/**
|
|
523
|
+
* Detach a reference from an approval.
|
|
524
|
+
*
|
|
525
|
+
* The audit entry keeps the name and URI of what was removed, so the record
|
|
526
|
+
* still shows an approver saw evidence that is no longer listed — dropping
|
|
527
|
+
* that would let the trail imply a decision was made on less than it was.
|
|
528
|
+
* Nothing is deleted from the underlying store; that is the DMS's call.
|
|
529
|
+
*/
|
|
530
|
+
removeAttachment(instanceId: string, raw: RemoveAttachmentOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
|
|
493
531
|
/** Add a comment to an instance without approving or rejecting. */
|
|
494
532
|
addComment(instanceId: string, raw: AddCommentOptions, auditCtx?: AuditContext): Promise<void>;
|
|
495
533
|
/** Resubmit a rejected instance, creating a new linked instance from level 1. */
|
|
@@ -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 {
|
|
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-DMa5VNNz.js';
|
|
2
|
+
import { l as ConditionExpression, p as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, e as ApprovalMode, b as AuditEntry } from './instance-BE0uJmg3.js';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-Dv_MuYwS.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-
|
|
9
|
-
import { I as IMetricsAdapter } from './IMetricsAdapter-
|
|
7
|
+
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-DWYXVuJD.js';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-DDV4Rf9F.js';
|
|
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-CFJ-gXKl.js';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -78,6 +78,18 @@ declare const ProvideInfoOptionsSchema: z.ZodObject<{
|
|
|
78
78
|
respondedBy: z.ZodString;
|
|
79
79
|
response: z.ZodString;
|
|
80
80
|
}, z.core.$strip>;
|
|
81
|
+
declare const AddAttachmentOptionsSchema: z.ZodObject<{
|
|
82
|
+
actorId: z.ZodString;
|
|
83
|
+
name: z.ZodString;
|
|
84
|
+
uri: z.ZodString;
|
|
85
|
+
contentType: z.ZodOptional<z.ZodString>;
|
|
86
|
+
sizeBytes: z.ZodOptional<z.ZodNumber>;
|
|
87
|
+
}, z.core.$strip>;
|
|
88
|
+
declare const RemoveAttachmentOptionsSchema: z.ZodObject<{
|
|
89
|
+
actorId: z.ZodString;
|
|
90
|
+
attachmentId: z.ZodString;
|
|
91
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
92
|
+
}, z.core.$strip>;
|
|
81
93
|
declare const UpdateDataOptionsSchema: z.ZodObject<{
|
|
82
94
|
updatedBy: z.ZodString;
|
|
83
95
|
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -101,6 +113,8 @@ type OverrideOptions = z.infer<typeof OverrideOptionsSchema>;
|
|
|
101
113
|
type UpdateDataOptions = z.infer<typeof UpdateDataOptionsSchema>;
|
|
102
114
|
type RequestInfoOptions = z.infer<typeof RequestInfoOptionsSchema>;
|
|
103
115
|
type ProvideInfoOptions = z.infer<typeof ProvideInfoOptionsSchema>;
|
|
116
|
+
type AddAttachmentOptions = z.infer<typeof AddAttachmentOptionsSchema>;
|
|
117
|
+
type RemoveAttachmentOptions = z.infer<typeof RemoveAttachmentOptionsSchema>;
|
|
104
118
|
|
|
105
119
|
/**
|
|
106
120
|
* Computes deadline dates from a number of days. The default engine behaviour
|
|
@@ -132,7 +146,7 @@ interface WeekendCalendarOptions {
|
|
|
132
146
|
*/
|
|
133
147
|
declare function weekendCalendar(options?: WeekendCalendarOptions): BusinessCalendar;
|
|
134
148
|
|
|
135
|
-
type IdGeneratorPrefix = 'inst' | 'tpl';
|
|
149
|
+
type IdGeneratorPrefix = 'inst' | 'tpl' | 'att';
|
|
136
150
|
type IdGeneratorFn = (prefix: IdGeneratorPrefix) => string;
|
|
137
151
|
declare const defaultIdGenerator: IdGeneratorFn;
|
|
138
152
|
|
|
@@ -490,6 +504,30 @@ declare class ApprovalEngine {
|
|
|
490
504
|
* by being held.
|
|
491
505
|
*/
|
|
492
506
|
private extendDeadlinesBy;
|
|
507
|
+
/**
|
|
508
|
+
* Attach supporting evidence to an approval — a quote PDF, a signed contract,
|
|
509
|
+
* a screenshot of a system of record.
|
|
510
|
+
*
|
|
511
|
+
* Stores a **reference**, never bytes. Approval documents belong in the object
|
|
512
|
+
* store or DMS the organisation already runs, which handles retention, virus
|
|
513
|
+
* scanning and access control far better than an approval table could;
|
|
514
|
+
* copying them here would make the audit database the largest and least
|
|
515
|
+
* governed copy of them.
|
|
516
|
+
*
|
|
517
|
+
* Allowed on any non-terminal instance, by anyone the authorization policy
|
|
518
|
+
* permits: the submitter adding a missing quote and an approver adding the
|
|
519
|
+
* note that justifies their decision are both ordinary.
|
|
520
|
+
*/
|
|
521
|
+
addAttachment(instanceId: string, raw: AddAttachmentOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
|
|
522
|
+
/**
|
|
523
|
+
* Detach a reference from an approval.
|
|
524
|
+
*
|
|
525
|
+
* The audit entry keeps the name and URI of what was removed, so the record
|
|
526
|
+
* still shows an approver saw evidence that is no longer listed — dropping
|
|
527
|
+
* that would let the trail imply a decision was made on less than it was.
|
|
528
|
+
* Nothing is deleted from the underlying store; that is the DMS's call.
|
|
529
|
+
*/
|
|
530
|
+
removeAttachment(instanceId: string, raw: RemoveAttachmentOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
|
|
493
531
|
/** Add a comment to an instance without approving or rejecting. */
|
|
494
532
|
addComment(instanceId: string, raw: AddCommentOptions, auditCtx?: AuditContext): Promise<void>;
|
|
495
533
|
/** Resubmit a rejected instance, creating a new linked instance from level 1. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-BE0uJmg3.js';
|
|
2
2
|
|
|
3
3
|
interface AuthorizationContext {
|
|
4
|
-
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData' | 'requestInfo' | 'provideInfo';
|
|
4
|
+
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData' | 'requestInfo' | 'provideInfo' | 'addAttachment' | 'removeAttachment';
|
|
5
5
|
actorId: string;
|
|
6
6
|
instance: Readonly<ApprovalInstance>;
|
|
7
7
|
level?: Readonly<ApprovalLevelInstance>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-BE0uJmg3.cjs';
|
|
2
2
|
|
|
3
3
|
interface AuthorizationContext {
|
|
4
|
-
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData' | 'requestInfo' | 'provideInfo';
|
|
4
|
+
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData' | 'requestInfo' | 'provideInfo' | 'addAttachment' | 'removeAttachment';
|
|
5
5
|
actorId: string;
|
|
6
6
|
instance: Readonly<ApprovalInstance>;
|
|
7
7
|
level?: Readonly<ApprovalLevelInstance>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
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.conflict_retry' | 'approval.operation_duration_ms';
|
|
1
|
+
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
2
|
interface IMetricsAdapter {
|
|
3
3
|
/** Increment a counter. Synchronous — never awaited. */
|
|
4
4
|
increment(metric: MetricName, labels?: Record<string, string>): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
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.conflict_retry' | 'approval.operation_duration_ms';
|
|
1
|
+
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
2
|
interface IMetricsAdapter {
|
|
3
3
|
/** Increment a counter. Synchronous — never awaited. */
|
|
4
4
|
increment(metric: MetricName, labels?: Record<string, string>): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance, b as AuditEntry } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, b as AuditEntry } from './instance-BE0uJmg3.js';
|
|
2
2
|
|
|
3
3
|
interface ApprovalEvent {
|
|
4
4
|
instanceId: string;
|
|
@@ -104,6 +104,14 @@ interface InfoProvidedEvent extends ApprovalEvent {
|
|
|
104
104
|
/** The approvers waiting again now that the question is answered. */
|
|
105
105
|
recipients: string[];
|
|
106
106
|
}
|
|
107
|
+
/** Emitted when supporting evidence is attached to or removed from an approval. */
|
|
108
|
+
interface AttachmentEvent extends ApprovalEvent {
|
|
109
|
+
actorId: string;
|
|
110
|
+
attachmentId: string;
|
|
111
|
+
name: string;
|
|
112
|
+
uri: string;
|
|
113
|
+
level?: number;
|
|
114
|
+
}
|
|
107
115
|
interface ApprovalEventMap {
|
|
108
116
|
'approval:submitted': SubmittedEvent;
|
|
109
117
|
'approval:approved': ApprovedEvent;
|
|
@@ -122,6 +130,8 @@ interface ApprovalEventMap {
|
|
|
122
130
|
'approval:reminder': ReminderEvent;
|
|
123
131
|
'approval:info_requested': InfoRequestedEvent;
|
|
124
132
|
'approval:info_provided': InfoProvidedEvent;
|
|
133
|
+
'approval:attachment_added': AttachmentEvent;
|
|
134
|
+
'approval:attachment_removed': AttachmentEvent;
|
|
125
135
|
}
|
|
126
136
|
type ApprovalEventName = keyof ApprovalEventMap;
|
|
127
137
|
interface HistoryEntry extends AuditEntry {
|
|
@@ -145,4 +155,4 @@ interface INotificationAdapter {
|
|
|
145
155
|
notify(event: NotificationEvent): Promise<void>;
|
|
146
156
|
}
|
|
147
157
|
|
|
148
|
-
export type { ApprovalEvent as A, CancelledEvent as C, DataUpdatedEvent as D, EscalatedEvent as E, HistoryEntry as H, INotificationAdapter as I, LevelAdvancedEvent as L, NotificationEvent as N, OverriddenEvent as O, ReassignedEvent as R, SlaBreachedEvent as S, ApprovalEventMap as a, ApprovalEventName as b, ApprovedEvent as c,
|
|
158
|
+
export type { ApprovalEvent as A, CancelledEvent as C, DataUpdatedEvent as D, EscalatedEvent as E, HistoryEntry as H, INotificationAdapter as I, LevelAdvancedEvent as L, NotificationEvent as N, OverriddenEvent as O, ReassignedEvent as R, SlaBreachedEvent as S, ApprovalEventMap as a, ApprovalEventName as b, ApprovedEvent as c, AttachmentEvent as d, DelegatedEvent as e, ExpiredEvent as f, InfoProvidedEvent as g, InfoRequestedEvent as h, RejectedEvent as i, ReminderEvent as j, ResubmittedEvent as k, SubmittedEvent as l };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance, b as AuditEntry } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, b as AuditEntry } from './instance-BE0uJmg3.cjs';
|
|
2
2
|
|
|
3
3
|
interface ApprovalEvent {
|
|
4
4
|
instanceId: string;
|
|
@@ -104,6 +104,14 @@ interface InfoProvidedEvent extends ApprovalEvent {
|
|
|
104
104
|
/** The approvers waiting again now that the question is answered. */
|
|
105
105
|
recipients: string[];
|
|
106
106
|
}
|
|
107
|
+
/** Emitted when supporting evidence is attached to or removed from an approval. */
|
|
108
|
+
interface AttachmentEvent extends ApprovalEvent {
|
|
109
|
+
actorId: string;
|
|
110
|
+
attachmentId: string;
|
|
111
|
+
name: string;
|
|
112
|
+
uri: string;
|
|
113
|
+
level?: number;
|
|
114
|
+
}
|
|
107
115
|
interface ApprovalEventMap {
|
|
108
116
|
'approval:submitted': SubmittedEvent;
|
|
109
117
|
'approval:approved': ApprovedEvent;
|
|
@@ -122,6 +130,8 @@ interface ApprovalEventMap {
|
|
|
122
130
|
'approval:reminder': ReminderEvent;
|
|
123
131
|
'approval:info_requested': InfoRequestedEvent;
|
|
124
132
|
'approval:info_provided': InfoProvidedEvent;
|
|
133
|
+
'approval:attachment_added': AttachmentEvent;
|
|
134
|
+
'approval:attachment_removed': AttachmentEvent;
|
|
125
135
|
}
|
|
126
136
|
type ApprovalEventName = keyof ApprovalEventMap;
|
|
127
137
|
interface HistoryEntry extends AuditEntry {
|
|
@@ -145,4 +155,4 @@ interface INotificationAdapter {
|
|
|
145
155
|
notify(event: NotificationEvent): Promise<void>;
|
|
146
156
|
}
|
|
147
157
|
|
|
148
|
-
export type { ApprovalEvent as A, CancelledEvent as C, DataUpdatedEvent as D, EscalatedEvent as E, HistoryEntry as H, INotificationAdapter as I, LevelAdvancedEvent as L, NotificationEvent as N, OverriddenEvent as O, ReassignedEvent as R, SlaBreachedEvent as S, ApprovalEventMap as a, ApprovalEventName as b, ApprovedEvent as c,
|
|
158
|
+
export type { ApprovalEvent as A, CancelledEvent as C, DataUpdatedEvent as D, EscalatedEvent as E, HistoryEntry as H, INotificationAdapter as I, LevelAdvancedEvent as L, NotificationEvent as N, OverriddenEvent as O, ReassignedEvent as R, SlaBreachedEvent as S, ApprovalEventMap as a, ApprovalEventName as b, ApprovedEvent as c, AttachmentEvent as d, DelegatedEvent as e, ExpiredEvent as f, InfoProvidedEvent as g, InfoRequestedEvent as h, RejectedEvent as i, ReminderEvent as j, ResubmittedEvent as k, SubmittedEvent as l };
|
|
@@ -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-BE0uJmg3.cjs';
|
|
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-BE0uJmg3.js';
|
|
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-CRSfnH8U.cjs';
|
|
2
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-BE0uJmg3.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-DMa5VNNz.js';
|
|
2
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-BE0uJmg3.js';
|
|
3
3
|
|
|
4
4
|
declare class MemoryAdapter implements IStorageAdapter {
|
|
5
5
|
private templates;
|
|
@@ -116,6 +116,7 @@ var PostgresAdapter = class {
|
|
|
116
116
|
sla_breached_at TIMESTAMPTZ,
|
|
117
117
|
template_snapshot JSONB,
|
|
118
118
|
info_request JSONB,
|
|
119
|
+
attachments JSONB NOT NULL DEFAULT '[]',
|
|
119
120
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
120
121
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
121
122
|
PRIMARY KEY (tenant_id, id),
|
|
@@ -159,6 +160,7 @@ var PostgresAdapter = class {
|
|
|
159
160
|
ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS sla_breached_at TIMESTAMPTZ;
|
|
160
161
|
ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS template_snapshot JSONB;
|
|
161
162
|
ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS info_request JSONB;
|
|
163
|
+
ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS attachments JSONB NOT NULL DEFAULT '[]';
|
|
162
164
|
`);
|
|
163
165
|
}
|
|
164
166
|
/** Enable PostgreSQL Row Level Security for the instances and audit_log tables. */
|
|
@@ -218,9 +220,9 @@ var PostgresAdapter = class {
|
|
|
218
220
|
status, current_level, version, idempotency_key,
|
|
219
221
|
data, metadata, levels,
|
|
220
222
|
parent_instance_id, expires_at, deadline_action,
|
|
221
|
-
sla_deadline_at, sla_breached_at, template_snapshot, info_request,
|
|
223
|
+
sla_deadline_at, sla_breached_at, template_snapshot, info_request, attachments,
|
|
222
224
|
created_at, updated_at)
|
|
223
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23)
|
|
225
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24)
|
|
224
226
|
ON CONFLICT (tenant_id, id) DO NOTHING`,
|
|
225
227
|
[
|
|
226
228
|
instance.id,
|
|
@@ -244,6 +246,7 @@ var PostgresAdapter = class {
|
|
|
244
246
|
instance.slaBreachedAt?.toISOString() ?? null,
|
|
245
247
|
instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,
|
|
246
248
|
instance.infoRequest ? JSON.stringify(instance.infoRequest) : null,
|
|
249
|
+
JSON.stringify(instance.attachments ?? []),
|
|
247
250
|
instance.createdAt.toISOString(),
|
|
248
251
|
instance.updatedAt.toISOString()
|
|
249
252
|
]
|
|
@@ -269,7 +272,8 @@ var PostgresAdapter = class {
|
|
|
269
272
|
deadline_action = $13,
|
|
270
273
|
sla_deadline_at = $14,
|
|
271
274
|
template_snapshot = $15,
|
|
272
|
-
info_request = $16
|
|
275
|
+
info_request = $16,
|
|
276
|
+
attachments = $17
|
|
273
277
|
WHERE tenant_id = $1 AND id = $2 AND version = $3
|
|
274
278
|
RETURNING id`,
|
|
275
279
|
[
|
|
@@ -288,7 +292,8 @@ var PostgresAdapter = class {
|
|
|
288
292
|
instance.deadlineAction ?? null,
|
|
289
293
|
instance.slaDeadlineAt?.toISOString() ?? null,
|
|
290
294
|
instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,
|
|
291
|
-
instance.infoRequest ? JSON.stringify(instance.infoRequest) : null
|
|
295
|
+
instance.infoRequest ? JSON.stringify(instance.infoRequest) : null,
|
|
296
|
+
JSON.stringify(instance.attachments ?? [])
|
|
292
297
|
]
|
|
293
298
|
);
|
|
294
299
|
if (result.rowCount === 0) throw new ApprovalConflictError(instance.id);
|
|
@@ -537,6 +542,10 @@ var PostgresAdapter = class {
|
|
|
537
542
|
slaDeadlineAt: row["sla_deadline_at"] ? new Date(row["sla_deadline_at"]) : void 0,
|
|
538
543
|
slaBreachedAt: row["sla_breached_at"] ? new Date(row["sla_breached_at"]) : void 0,
|
|
539
544
|
templateSnapshot: row["template_snapshot"] ?? void 0,
|
|
545
|
+
// Absent attachments map to undefined, not [], so a Postgres round trip
|
|
546
|
+
// returns the same shape MemoryAdapter does for an instance that never
|
|
547
|
+
// had any — the parity the round-trip test pins down.
|
|
548
|
+
attachments: (row["attachments"] ?? []).length ? row["attachments"].map((a) => ({ ...a, addedAt: new Date(a.addedAt) })) : void 0,
|
|
540
549
|
infoRequest: row["info_request"] ? {
|
|
541
550
|
...row["info_request"],
|
|
542
551
|
askedAt: new Date(row["info_request"].askedAt)
|