hierarchical-approval 1.4.0 → 1.6.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 +79 -0
- package/README.md +30 -0
- package/dist/{ApprovalEngine-NN6Dqf6E.d.ts → ApprovalEngine-CyZJDbjP.d.ts} +54 -8
- package/dist/{ApprovalEngine-D8kKuapR.d.cts → ApprovalEngine-SI-IRUMY.d.cts} +54 -8
- package/dist/{IAuditAdapter-B2I3CjaJ.d.cts → IAuditAdapter-Cr0dLoVP.d.cts} +1 -1
- package/dist/{IAuditAdapter-GixC9zS1.d.ts → IAuditAdapter-wfOcN3Zf.d.ts} +1 -1
- package/dist/{IAuthorizationPolicy-BHDZK9QL.d.cts → IAuthorizationPolicy-BHybAsq5.d.cts} +2 -2
- package/dist/{IAuthorizationPolicy-4ge9Gnxj.d.ts → IAuthorizationPolicy-CdvoZsGC.d.ts} +2 -2
- package/dist/{IMetricsAdapter-QvyaKQCc.d.cts → IMetricsAdapter-JZVK3QUq.d.cts} +1 -1
- package/dist/{IMetricsAdapter-QvyaKQCc.d.ts → IMetricsAdapter-JZVK3QUq.d.ts} +1 -1
- package/dist/{INotificationAdapter-BmSeB7FZ.d.ts → INotificationAdapter-Cn-LNPtD.d.cts} +22 -2
- package/dist/{INotificationAdapter-BKMeGmaG.d.cts → INotificationAdapter-Cyo0cvXU.d.ts} +22 -2
- package/dist/{IOperationMiddleware-BLw9Oc-S.d.cts → IOperationMiddleware-DrJy9UtL.d.cts} +1 -1
- package/dist/{IOperationMiddleware-BW9t8wPn.d.ts → IOperationMiddleware-rx7XAPrW.d.ts} +1 -1
- package/dist/{IStorageAdapter-DVclp9cn.d.ts → IStorageAdapter-CXRyeUOK.d.ts} +1 -1
- package/dist/{IStorageAdapter-C7isBXJF.d.cts → IStorageAdapter-Dz8jrIAK.d.cts} +1 -1
- package/dist/adapters/MemoryAdapter.d.cts +2 -2
- package/dist/adapters/MemoryAdapter.d.ts +2 -2
- package/dist/adapters/PostgresAdapter.cjs +34 -9
- 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 +34 -9
- package/dist/adapters/PostgresAdapter.js.map +1 -1
- package/dist/index.cjs +213 -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 +213 -0
- package/dist/index.js.map +1 -1
- package/dist/{instance-BsM0DUPf.d.cts → instance-SA0BU-Gb.d.cts} +20 -2
- package/dist/{instance-BsM0DUPf.d.ts → instance-SA0BU-Gb.d.ts} +20 -2
- package/dist/nestjs.cjs +213 -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 +213 -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 +213 -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 +213 -0
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,85 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
_Nothing yet._
|
|
9
9
|
|
|
10
|
+
## [1.6.0] - 2026-09-04
|
|
11
|
+
|
|
12
|
+
### Fixed — PostgresAdapter silently dropped most instance updates
|
|
13
|
+
|
|
14
|
+
**Anyone running the PostgreSQL adapter should upgrade and run `migrate()`.**
|
|
15
|
+
`MemoryAdapter` stores whole objects and was always correct, so this reproduced
|
|
16
|
+
only against a real database — which is exactly where it mattered.
|
|
17
|
+
|
|
18
|
+
`updateInstance` wrote just six columns (`status`, `current_level`, `version`,
|
|
19
|
+
`levels`, `sla_breached_at`, `updated_at`). Every other field an operation
|
|
20
|
+
mutates was computed, logged, emitted as an event, and then thrown away on
|
|
21
|
+
write:
|
|
22
|
+
|
|
23
|
+
- **`updateData()` (0.9.0) did not change the stored document.** The engine
|
|
24
|
+
recomputed the chain and emitted `approval:data_updated`, but `data` was never
|
|
25
|
+
written, so the next read returned the old values — and any later
|
|
26
|
+
re-evaluation ran against them.
|
|
27
|
+
- **`requestInfo()` (1.5.0) lost the hold entirely.** `info_request` had no
|
|
28
|
+
column at all, so a held instance came back unheld: `provideInfo()` then threw
|
|
29
|
+
"No clarification request is open", and the scheduler escalated and expired an
|
|
30
|
+
approval that was supposed to be paused.
|
|
31
|
+
- **`provideInfo()`'s deadline give-back never landed** — `expires_at` and
|
|
32
|
+
`sla_deadline_at` were not written, so the time an instance spent on hold was
|
|
33
|
+
silently forfeited.
|
|
34
|
+
- `metadata`, `deadline_action` and `template_snapshot` were likewise never
|
|
35
|
+
updated after insert.
|
|
36
|
+
|
|
37
|
+
`updateInstance` now writes every mutable column, `saveInstance` and
|
|
38
|
+
`rowToInstance` carry `info_request` (reviving `askedAt` as a `Date`), and
|
|
39
|
+
`migrate()` adds the column to existing deployments via
|
|
40
|
+
`ADD COLUMN IF NOT EXISTS`. Optimistic concurrency is unchanged — the update
|
|
41
|
+
still guards on `version`.
|
|
42
|
+
|
|
43
|
+
Regression tests assert the generated SQL and parameters for each field, so a
|
|
44
|
+
future column cannot be added to the type and forgotten in the writer.
|
|
45
|
+
|
|
46
|
+
### Note on the roadmap
|
|
47
|
+
|
|
48
|
+
Attachment references were the planned 1.6.0. They are deferred: shipping a new
|
|
49
|
+
feature ahead of a data-loss fix in an already-published release would have been
|
|
50
|
+
the wrong order.
|
|
51
|
+
|
|
52
|
+
## [1.5.0] - 2026-09-04
|
|
53
|
+
|
|
54
|
+
### Added — request for information
|
|
55
|
+
|
|
56
|
+
- **`requestInfo()` / `provideInfo()` let an approver ask the submitter a
|
|
57
|
+
question without rejecting.** Approvers routinely need one fact before they
|
|
58
|
+
can decide, and there was no way to say so: rejecting throws away every
|
|
59
|
+
approval already collected and forces a resubmit, while chasing the question
|
|
60
|
+
by email leaves the request sitting and quietly burns the SLA the approver is
|
|
61
|
+
measured on.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
await engine.requestInfo(id, { approverId: 'mgr-1', question: 'Which cost centre?' });
|
|
65
|
+
await engine.provideInfo(id, { respondedBy: 'buyer-1', response: 'CC-42' });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The instance stays `pending` and keeps its approvers — this is a question, not
|
|
69
|
+
a decision.
|
|
70
|
+
|
|
71
|
+
- **Deadlines are paused for the duration of the hold.** The scheduler skips a
|
|
72
|
+
held instance entirely, so escalation, reminders, SLA breach and expiry cannot
|
|
73
|
+
fire while the submitter owes an answer; on answer, every deadline that was
|
|
74
|
+
set is pushed out by exactly how long the question was open. An approver gets
|
|
75
|
+
back the time they had before asking rather than being penalised for asking.
|
|
76
|
+
A deadline that was never configured is not invented by being held.
|
|
77
|
+
|
|
78
|
+
- **Wiring:** emits `approval:info_requested` (addressed to the submitter) and
|
|
79
|
+
`approval:info_provided` (addressed to the waiting approvers, carrying
|
|
80
|
+
`heldForMs`); records `info_requested` / `info_provided` audit entries;
|
|
81
|
+
increments `approval.info_requested` / `approval.info_provided`; and adds both
|
|
82
|
+
operations to the authorization-policy set. Only one question may be open at a
|
|
83
|
+
time.
|
|
84
|
+
|
|
85
|
+
New exports: `InfoRequest`, `InfoRequestedEvent`, `InfoProvidedEvent`,
|
|
86
|
+
`RequestInfoOptions`, `ProvideInfoOptions`. `ApprovalInstance` gains
|
|
87
|
+
`infoRequest`.
|
|
88
|
+
|
|
10
89
|
## [1.4.0] - 2026-09-04
|
|
11
90
|
|
|
12
91
|
### Added — filter instances by document data
|
package/README.md
CHANGED
|
@@ -399,6 +399,36 @@ engine.registerConditionOperator('between', (actual, expected) => {
|
|
|
399
399
|
});
|
|
400
400
|
```
|
|
401
401
|
|
|
402
|
+
### Asking the submitter a question
|
|
403
|
+
|
|
404
|
+
Approvers routinely need one fact before they can decide. `requestInfo()` puts
|
|
405
|
+
the approval on hold without rejecting it:
|
|
406
|
+
|
|
407
|
+
```ts
|
|
408
|
+
await engine.requestInfo(instance.id, {
|
|
409
|
+
approverId: 'mgr-1',
|
|
410
|
+
question: 'Which cost centre should this book to?',
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// ...later
|
|
414
|
+
await engine.provideInfo(instance.id, {
|
|
415
|
+
respondedBy: 'buyer-1',
|
|
416
|
+
response: 'CC-42',
|
|
417
|
+
});
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
The instance stays `pending` and keeps its approvers — this is a question, not a
|
|
421
|
+
decision. What changes is the clock: **escalation, SLA, reminder and expiry
|
|
422
|
+
deadlines stop advancing while the question is open**, and on answer every one
|
|
423
|
+
is pushed out by exactly how long the hold lasted. An approver gets back the
|
|
424
|
+
time they had before asking, rather than being penalised for asking.
|
|
425
|
+
|
|
426
|
+
Emits `approval:info_requested` (addressed to the submitter) and
|
|
427
|
+
`approval:info_provided` (addressed to the waiting approvers, carrying
|
|
428
|
+
`heldForMs`), and records `info_requested` / `info_provided` audit entries.
|
|
429
|
+
|
|
430
|
+
Only one question can be open at a time; answer it before asking another.
|
|
431
|
+
|
|
402
432
|
### Filtering by document data
|
|
403
433
|
|
|
404
434
|
Every query filter accepts `data`, matching against the document body itself —
|
|
@@ -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 { k as ConditionExpression, o as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, j as AuditContext, a as ApprovalInstance, e as ApprovalMode, 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-CXRyeUOK.js';
|
|
2
|
+
import { k as ConditionExpression, o as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, j as AuditContext, a as ApprovalInstance, e as ApprovalMode, b as AuditEntry } from './instance-SA0BU-Gb.js';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-Cyo0cvXU.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-rx7XAPrW.js';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-wfOcN3Zf.js';
|
|
9
|
+
import { I as IMetricsAdapter } from './IMetricsAdapter-JZVK3QUq.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-CdvoZsGC.js';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -69,6 +69,15 @@ declare const OverrideOptionsSchema: z.ZodObject<{
|
|
|
69
69
|
overriddenBy: z.ZodString;
|
|
70
70
|
justification: z.ZodString;
|
|
71
71
|
}, z.core.$strip>;
|
|
72
|
+
declare const RequestInfoOptionsSchema: z.ZodObject<{
|
|
73
|
+
approverId: z.ZodString;
|
|
74
|
+
question: z.ZodString;
|
|
75
|
+
level: z.ZodOptional<z.ZodNumber>;
|
|
76
|
+
}, z.core.$strip>;
|
|
77
|
+
declare const ProvideInfoOptionsSchema: z.ZodObject<{
|
|
78
|
+
respondedBy: z.ZodString;
|
|
79
|
+
response: z.ZodString;
|
|
80
|
+
}, z.core.$strip>;
|
|
72
81
|
declare const UpdateDataOptionsSchema: z.ZodObject<{
|
|
73
82
|
updatedBy: z.ZodString;
|
|
74
83
|
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -90,6 +99,8 @@ type ResubmitOptions = z.infer<typeof ResubmitOptionsSchema>;
|
|
|
90
99
|
type AddCommentOptions = z.infer<typeof AddCommentOptionsSchema>;
|
|
91
100
|
type OverrideOptions = z.infer<typeof OverrideOptionsSchema>;
|
|
92
101
|
type UpdateDataOptions = z.infer<typeof UpdateDataOptionsSchema>;
|
|
102
|
+
type RequestInfoOptions = z.infer<typeof RequestInfoOptionsSchema>;
|
|
103
|
+
type ProvideInfoOptions = z.infer<typeof ProvideInfoOptionsSchema>;
|
|
93
104
|
|
|
94
105
|
/**
|
|
95
106
|
* Computes deadline dates from a number of days. The default engine behaviour
|
|
@@ -444,6 +455,41 @@ declare class ApprovalEngine {
|
|
|
444
455
|
* {@link updateData} for why.
|
|
445
456
|
*/
|
|
446
457
|
private recomputeFutureChain;
|
|
458
|
+
/**
|
|
459
|
+
* Ask the submitter for clarification without rejecting.
|
|
460
|
+
*
|
|
461
|
+
* Approvers routinely need one fact before they can decide. The only ways to
|
|
462
|
+
* express that were to reject — which throws away every approval already
|
|
463
|
+
* collected and forces a resubmit — or to leave the request sitting while the
|
|
464
|
+
* question is chased by email, which quietly burns the SLA the approver is
|
|
465
|
+
* measured on.
|
|
466
|
+
*
|
|
467
|
+
* The instance stays `pending` and keeps its approvers: this is a question,
|
|
468
|
+
* not a decision. What changes is the clock — escalation, SLA and expiry
|
|
469
|
+
* deadlines stop advancing while the question is open, because time spent
|
|
470
|
+
* waiting on the submitter is not time the approver is sitting on their hands.
|
|
471
|
+
*
|
|
472
|
+
* @throws ApprovalError if the instance is not pending, or a question is already open.
|
|
473
|
+
*/
|
|
474
|
+
requestInfo(instanceId: string, raw: RequestInfoOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
|
|
475
|
+
/**
|
|
476
|
+
* Answer an open clarification request and take the instance off hold.
|
|
477
|
+
*
|
|
478
|
+
* Every deadline that was paused is pushed out by exactly how long the
|
|
479
|
+
* question was open, so an approver gets back the full remaining time they
|
|
480
|
+
* had before asking rather than being penalised for asking at all.
|
|
481
|
+
*
|
|
482
|
+
* @throws ApprovalError if no question is open.
|
|
483
|
+
*/
|
|
484
|
+
provideInfo(instanceId: string, raw: ProvideInfoOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
|
|
485
|
+
/**
|
|
486
|
+
* Push every pending deadline out by `ms`.
|
|
487
|
+
*
|
|
488
|
+
* Used to give back time an instance spent on hold. Deadlines that are not
|
|
489
|
+
* set stay unset — a level with no escalation configured does not acquire one
|
|
490
|
+
* by being held.
|
|
491
|
+
*/
|
|
492
|
+
private extendDeadlinesBy;
|
|
447
493
|
/** Add a comment to an instance without approving or rejecting. */
|
|
448
494
|
addComment(instanceId: string, raw: AddCommentOptions, auditCtx?: AuditContext): Promise<void>;
|
|
449
495
|
/** Resubmit a rejected instance, creating a new linked instance from level 1. */
|
|
@@ -568,4 +614,4 @@ declare class ApprovalEngine {
|
|
|
568
614
|
private runExternalAudit;
|
|
569
615
|
}
|
|
570
616
|
|
|
571
|
-
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E, type HealthResult as H, type IdGeneratorFn as I, type OrgProvider as O, type PreviewChainLevel as P, type ReassignOptions as R, type SubmitOptions as S, 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 BusinessCalendar as f, type CancelOptions as g, type ConditionOperatorFn as h, type CycleTimeStats as i, type IdempotencyKeyFn as j, type OutOfOfficeProvider as k, type OverrideOptions as l, type PreviewResult as m, type
|
|
617
|
+
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E, type HealthResult as H, type IdGeneratorFn as I, type OrgProvider as O, type PreviewChainLevel as P, type ReassignOptions as R, type SubmitOptions as S, 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 BusinessCalendar as f, type CancelOptions as g, type ConditionOperatorFn as h, type CycleTimeStats as i, type IdempotencyKeyFn as j, type OutOfOfficeProvider as k, type OverrideOptions as l, type PreviewResult as m, type ProvideInfoOptions as n, type RejectOptions as o, type RequestInfoOptions as p, type ResubmitOptions as q, type RetryPolicy as r, defaultIdGenerator as s, toComparableNumber as t, validateConditionExpression as v, weekendCalendar as w };
|
|
@@ -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 { k as ConditionExpression, o as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, j as AuditContext, a as ApprovalInstance, e as ApprovalMode, 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-Dz8jrIAK.cjs';
|
|
2
|
+
import { k as ConditionExpression, o as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, j as AuditContext, a as ApprovalInstance, e as ApprovalMode, b as AuditEntry } from './instance-SA0BU-Gb.cjs';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-Cn-LNPtD.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-DrJy9UtL.cjs';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-Cr0dLoVP.cjs';
|
|
9
|
+
import { I as IMetricsAdapter } from './IMetricsAdapter-JZVK3QUq.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-BHybAsq5.cjs';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -69,6 +69,15 @@ declare const OverrideOptionsSchema: z.ZodObject<{
|
|
|
69
69
|
overriddenBy: z.ZodString;
|
|
70
70
|
justification: z.ZodString;
|
|
71
71
|
}, z.core.$strip>;
|
|
72
|
+
declare const RequestInfoOptionsSchema: z.ZodObject<{
|
|
73
|
+
approverId: z.ZodString;
|
|
74
|
+
question: z.ZodString;
|
|
75
|
+
level: z.ZodOptional<z.ZodNumber>;
|
|
76
|
+
}, z.core.$strip>;
|
|
77
|
+
declare const ProvideInfoOptionsSchema: z.ZodObject<{
|
|
78
|
+
respondedBy: z.ZodString;
|
|
79
|
+
response: z.ZodString;
|
|
80
|
+
}, z.core.$strip>;
|
|
72
81
|
declare const UpdateDataOptionsSchema: z.ZodObject<{
|
|
73
82
|
updatedBy: z.ZodString;
|
|
74
83
|
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -90,6 +99,8 @@ type ResubmitOptions = z.infer<typeof ResubmitOptionsSchema>;
|
|
|
90
99
|
type AddCommentOptions = z.infer<typeof AddCommentOptionsSchema>;
|
|
91
100
|
type OverrideOptions = z.infer<typeof OverrideOptionsSchema>;
|
|
92
101
|
type UpdateDataOptions = z.infer<typeof UpdateDataOptionsSchema>;
|
|
102
|
+
type RequestInfoOptions = z.infer<typeof RequestInfoOptionsSchema>;
|
|
103
|
+
type ProvideInfoOptions = z.infer<typeof ProvideInfoOptionsSchema>;
|
|
93
104
|
|
|
94
105
|
/**
|
|
95
106
|
* Computes deadline dates from a number of days. The default engine behaviour
|
|
@@ -444,6 +455,41 @@ declare class ApprovalEngine {
|
|
|
444
455
|
* {@link updateData} for why.
|
|
445
456
|
*/
|
|
446
457
|
private recomputeFutureChain;
|
|
458
|
+
/**
|
|
459
|
+
* Ask the submitter for clarification without rejecting.
|
|
460
|
+
*
|
|
461
|
+
* Approvers routinely need one fact before they can decide. The only ways to
|
|
462
|
+
* express that were to reject — which throws away every approval already
|
|
463
|
+
* collected and forces a resubmit — or to leave the request sitting while the
|
|
464
|
+
* question is chased by email, which quietly burns the SLA the approver is
|
|
465
|
+
* measured on.
|
|
466
|
+
*
|
|
467
|
+
* The instance stays `pending` and keeps its approvers: this is a question,
|
|
468
|
+
* not a decision. What changes is the clock — escalation, SLA and expiry
|
|
469
|
+
* deadlines stop advancing while the question is open, because time spent
|
|
470
|
+
* waiting on the submitter is not time the approver is sitting on their hands.
|
|
471
|
+
*
|
|
472
|
+
* @throws ApprovalError if the instance is not pending, or a question is already open.
|
|
473
|
+
*/
|
|
474
|
+
requestInfo(instanceId: string, raw: RequestInfoOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
|
|
475
|
+
/**
|
|
476
|
+
* Answer an open clarification request and take the instance off hold.
|
|
477
|
+
*
|
|
478
|
+
* Every deadline that was paused is pushed out by exactly how long the
|
|
479
|
+
* question was open, so an approver gets back the full remaining time they
|
|
480
|
+
* had before asking rather than being penalised for asking at all.
|
|
481
|
+
*
|
|
482
|
+
* @throws ApprovalError if no question is open.
|
|
483
|
+
*/
|
|
484
|
+
provideInfo(instanceId: string, raw: ProvideInfoOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
|
|
485
|
+
/**
|
|
486
|
+
* Push every pending deadline out by `ms`.
|
|
487
|
+
*
|
|
488
|
+
* Used to give back time an instance spent on hold. Deadlines that are not
|
|
489
|
+
* set stay unset — a level with no escalation configured does not acquire one
|
|
490
|
+
* by being held.
|
|
491
|
+
*/
|
|
492
|
+
private extendDeadlinesBy;
|
|
447
493
|
/** Add a comment to an instance without approving or rejecting. */
|
|
448
494
|
addComment(instanceId: string, raw: AddCommentOptions, auditCtx?: AuditContext): Promise<void>;
|
|
449
495
|
/** Resubmit a rejected instance, creating a new linked instance from level 1. */
|
|
@@ -568,4 +614,4 @@ declare class ApprovalEngine {
|
|
|
568
614
|
private runExternalAudit;
|
|
569
615
|
}
|
|
570
616
|
|
|
571
|
-
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E, type HealthResult as H, type IdGeneratorFn as I, type OrgProvider as O, type PreviewChainLevel as P, type ReassignOptions as R, type SubmitOptions as S, 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 BusinessCalendar as f, type CancelOptions as g, type ConditionOperatorFn as h, type CycleTimeStats as i, type IdempotencyKeyFn as j, type OutOfOfficeProvider as k, type OverrideOptions as l, type PreviewResult as m, type
|
|
617
|
+
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E, type HealthResult as H, type IdGeneratorFn as I, type OrgProvider as O, type PreviewChainLevel as P, type ReassignOptions as R, type SubmitOptions as S, 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 BusinessCalendar as f, type CancelOptions as g, type ConditionOperatorFn as h, type CycleTimeStats as i, type IdempotencyKeyFn as j, type OutOfOfficeProvider as k, type OverrideOptions as l, type PreviewResult as m, type ProvideInfoOptions as n, type RejectOptions as o, type RequestInfoOptions as p, type ResubmitOptions as q, type RetryPolicy as r, defaultIdGenerator as s, toComparableNumber as t, validateConditionExpression as v, weekendCalendar as w };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-SA0BU-Gb.cjs';
|
|
2
2
|
|
|
3
3
|
interface AuthorizationContext {
|
|
4
|
-
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData';
|
|
4
|
+
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData' | 'requestInfo' | 'provideInfo';
|
|
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-SA0BU-Gb.js';
|
|
2
2
|
|
|
3
3
|
interface AuthorizationContext {
|
|
4
|
-
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData';
|
|
4
|
+
operation: 'submit' | 'approve' | 'reject' | 'delegate' | 'reassign' | 'cancel' | 'escalate' | 'override' | 'resubmit' | 'addComment' | 'updateData' | 'requestInfo' | 'provideInfo';
|
|
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.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.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.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.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-SA0BU-Gb.cjs';
|
|
2
2
|
|
|
3
3
|
interface ApprovalEvent {
|
|
4
4
|
instanceId: string;
|
|
@@ -86,6 +86,24 @@ interface ReminderEvent extends ApprovalEvent {
|
|
|
86
86
|
/** 1 for the first reminder on this level, 2 for the next, and so on. */
|
|
87
87
|
reminderNumber: number;
|
|
88
88
|
}
|
|
89
|
+
/** Emitted when an approver asks the submitter for clarification. */
|
|
90
|
+
interface InfoRequestedEvent extends ApprovalEvent {
|
|
91
|
+
askedBy: string;
|
|
92
|
+
question: string;
|
|
93
|
+
level: number;
|
|
94
|
+
/** Who is expected to answer — the submitter. */
|
|
95
|
+
recipients: string[];
|
|
96
|
+
}
|
|
97
|
+
/** Emitted when the question is answered and the instance comes off hold. */
|
|
98
|
+
interface InfoProvidedEvent extends ApprovalEvent {
|
|
99
|
+
respondedBy: string;
|
|
100
|
+
response: string;
|
|
101
|
+
level: number;
|
|
102
|
+
/** How long the instance spent on hold; deadlines were extended by this much. */
|
|
103
|
+
heldForMs: number;
|
|
104
|
+
/** The approvers waiting again now that the question is answered. */
|
|
105
|
+
recipients: string[];
|
|
106
|
+
}
|
|
89
107
|
interface ApprovalEventMap {
|
|
90
108
|
'approval:submitted': SubmittedEvent;
|
|
91
109
|
'approval:approved': ApprovedEvent;
|
|
@@ -102,6 +120,8 @@ interface ApprovalEventMap {
|
|
|
102
120
|
'approval:sla_breached': SlaBreachedEvent;
|
|
103
121
|
'approval:data_updated': DataUpdatedEvent;
|
|
104
122
|
'approval:reminder': ReminderEvent;
|
|
123
|
+
'approval:info_requested': InfoRequestedEvent;
|
|
124
|
+
'approval:info_provided': InfoProvidedEvent;
|
|
105
125
|
}
|
|
106
126
|
type ApprovalEventName = keyof ApprovalEventMap;
|
|
107
127
|
interface HistoryEntry extends AuditEntry {
|
|
@@ -125,4 +145,4 @@ interface INotificationAdapter {
|
|
|
125
145
|
notify(event: NotificationEvent): Promise<void>;
|
|
126
146
|
}
|
|
127
147
|
|
|
128
|
-
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, DelegatedEvent as d, ExpiredEvent as e,
|
|
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, DelegatedEvent as d, ExpiredEvent as e, InfoProvidedEvent as f, InfoRequestedEvent as g, RejectedEvent as h, ReminderEvent as i, ResubmittedEvent as j, SubmittedEvent as k };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance, b as AuditEntry } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, b as AuditEntry } from './instance-SA0BU-Gb.js';
|
|
2
2
|
|
|
3
3
|
interface ApprovalEvent {
|
|
4
4
|
instanceId: string;
|
|
@@ -86,6 +86,24 @@ interface ReminderEvent extends ApprovalEvent {
|
|
|
86
86
|
/** 1 for the first reminder on this level, 2 for the next, and so on. */
|
|
87
87
|
reminderNumber: number;
|
|
88
88
|
}
|
|
89
|
+
/** Emitted when an approver asks the submitter for clarification. */
|
|
90
|
+
interface InfoRequestedEvent extends ApprovalEvent {
|
|
91
|
+
askedBy: string;
|
|
92
|
+
question: string;
|
|
93
|
+
level: number;
|
|
94
|
+
/** Who is expected to answer — the submitter. */
|
|
95
|
+
recipients: string[];
|
|
96
|
+
}
|
|
97
|
+
/** Emitted when the question is answered and the instance comes off hold. */
|
|
98
|
+
interface InfoProvidedEvent extends ApprovalEvent {
|
|
99
|
+
respondedBy: string;
|
|
100
|
+
response: string;
|
|
101
|
+
level: number;
|
|
102
|
+
/** How long the instance spent on hold; deadlines were extended by this much. */
|
|
103
|
+
heldForMs: number;
|
|
104
|
+
/** The approvers waiting again now that the question is answered. */
|
|
105
|
+
recipients: string[];
|
|
106
|
+
}
|
|
89
107
|
interface ApprovalEventMap {
|
|
90
108
|
'approval:submitted': SubmittedEvent;
|
|
91
109
|
'approval:approved': ApprovedEvent;
|
|
@@ -102,6 +120,8 @@ interface ApprovalEventMap {
|
|
|
102
120
|
'approval:sla_breached': SlaBreachedEvent;
|
|
103
121
|
'approval:data_updated': DataUpdatedEvent;
|
|
104
122
|
'approval:reminder': ReminderEvent;
|
|
123
|
+
'approval:info_requested': InfoRequestedEvent;
|
|
124
|
+
'approval:info_provided': InfoProvidedEvent;
|
|
105
125
|
}
|
|
106
126
|
type ApprovalEventName = keyof ApprovalEventMap;
|
|
107
127
|
interface HistoryEntry extends AuditEntry {
|
|
@@ -125,4 +145,4 @@ interface INotificationAdapter {
|
|
|
125
145
|
notify(event: NotificationEvent): Promise<void>;
|
|
126
146
|
}
|
|
127
147
|
|
|
128
|
-
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, DelegatedEvent as d, ExpiredEvent as e,
|
|
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, DelegatedEvent as d, ExpiredEvent as e, InfoProvidedEvent as f, InfoRequestedEvent as g, RejectedEvent as h, ReminderEvent as i, ResubmittedEvent as j, SubmittedEvent as k };
|
|
@@ -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-SA0BU-Gb.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-SA0BU-Gb.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-Dz8jrIAK.cjs';
|
|
2
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-SA0BU-Gb.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-CXRyeUOK.js';
|
|
2
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-SA0BU-Gb.js';
|
|
3
3
|
|
|
4
4
|
declare class MemoryAdapter implements IStorageAdapter {
|
|
5
5
|
private templates;
|
|
@@ -115,6 +115,7 @@ var PostgresAdapter = class {
|
|
|
115
115
|
sla_deadline_at TIMESTAMPTZ,
|
|
116
116
|
sla_breached_at TIMESTAMPTZ,
|
|
117
117
|
template_snapshot JSONB,
|
|
118
|
+
info_request JSONB,
|
|
118
119
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
119
120
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
120
121
|
PRIMARY KEY (tenant_id, id),
|
|
@@ -157,6 +158,7 @@ var PostgresAdapter = class {
|
|
|
157
158
|
ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS sla_deadline_at TIMESTAMPTZ;
|
|
158
159
|
ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS sla_breached_at TIMESTAMPTZ;
|
|
159
160
|
ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS template_snapshot JSONB;
|
|
161
|
+
ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS info_request JSONB;
|
|
160
162
|
`);
|
|
161
163
|
}
|
|
162
164
|
/** Enable PostgreSQL Row Level Security for the instances and audit_log tables. */
|
|
@@ -216,9 +218,9 @@ var PostgresAdapter = class {
|
|
|
216
218
|
status, current_level, version, idempotency_key,
|
|
217
219
|
data, metadata, levels,
|
|
218
220
|
parent_instance_id, expires_at, deadline_action,
|
|
219
|
-
sla_deadline_at, sla_breached_at, template_snapshot,
|
|
221
|
+
sla_deadline_at, sla_breached_at, template_snapshot, info_request,
|
|
220
222
|
created_at, updated_at)
|
|
221
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22)
|
|
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)
|
|
222
224
|
ON CONFLICT (tenant_id, id) DO NOTHING`,
|
|
223
225
|
[
|
|
224
226
|
instance.id,
|
|
@@ -241,6 +243,7 @@ var PostgresAdapter = class {
|
|
|
241
243
|
instance.slaDeadlineAt?.toISOString() ?? null,
|
|
242
244
|
instance.slaBreachedAt?.toISOString() ?? null,
|
|
243
245
|
instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,
|
|
246
|
+
instance.infoRequest ? JSON.stringify(instance.infoRequest) : null,
|
|
244
247
|
instance.createdAt.toISOString(),
|
|
245
248
|
instance.updatedAt.toISOString()
|
|
246
249
|
]
|
|
@@ -249,13 +252,24 @@ var PostgresAdapter = class {
|
|
|
249
252
|
async updateInstance(instance, expectedVersion) {
|
|
250
253
|
const pool = await this.getPool();
|
|
251
254
|
const result = await pool.query(
|
|
255
|
+
// Every column an engine operation can mutate must be written here.
|
|
256
|
+
// Listing only a subset silently dropped writes: updateData() changed
|
|
257
|
+
// `data`, requestInfo() set `info_request`, and provideInfo() shifted the
|
|
258
|
+
// deadline columns — none of which reached the database.
|
|
252
259
|
`UPDATE ${this.p}_instances SET
|
|
253
|
-
status
|
|
254
|
-
current_level
|
|
255
|
-
version
|
|
256
|
-
levels
|
|
257
|
-
sla_breached_at
|
|
258
|
-
updated_at
|
|
260
|
+
status = $4,
|
|
261
|
+
current_level = $5,
|
|
262
|
+
version = $6 + 1,
|
|
263
|
+
levels = $7,
|
|
264
|
+
sla_breached_at = $8,
|
|
265
|
+
updated_at = $9,
|
|
266
|
+
data = $10,
|
|
267
|
+
metadata = $11,
|
|
268
|
+
expires_at = $12,
|
|
269
|
+
deadline_action = $13,
|
|
270
|
+
sla_deadline_at = $14,
|
|
271
|
+
template_snapshot = $15,
|
|
272
|
+
info_request = $16
|
|
259
273
|
WHERE tenant_id = $1 AND id = $2 AND version = $3
|
|
260
274
|
RETURNING id`,
|
|
261
275
|
[
|
|
@@ -267,7 +281,14 @@ var PostgresAdapter = class {
|
|
|
267
281
|
expectedVersion,
|
|
268
282
|
JSON.stringify(instance.levels),
|
|
269
283
|
instance.slaBreachedAt?.toISOString() ?? null,
|
|
270
|
-
instance.updatedAt.toISOString()
|
|
284
|
+
instance.updatedAt.toISOString(),
|
|
285
|
+
JSON.stringify(instance.data ?? {}),
|
|
286
|
+
JSON.stringify(instance.metadata ?? {}),
|
|
287
|
+
instance.expiresAt?.toISOString() ?? null,
|
|
288
|
+
instance.deadlineAction ?? null,
|
|
289
|
+
instance.slaDeadlineAt?.toISOString() ?? null,
|
|
290
|
+
instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,
|
|
291
|
+
instance.infoRequest ? JSON.stringify(instance.infoRequest) : null
|
|
271
292
|
]
|
|
272
293
|
);
|
|
273
294
|
if (result.rowCount === 0) throw new ApprovalConflictError(instance.id);
|
|
@@ -516,6 +537,10 @@ var PostgresAdapter = class {
|
|
|
516
537
|
slaDeadlineAt: row["sla_deadline_at"] ? new Date(row["sla_deadline_at"]) : void 0,
|
|
517
538
|
slaBreachedAt: row["sla_breached_at"] ? new Date(row["sla_breached_at"]) : void 0,
|
|
518
539
|
templateSnapshot: row["template_snapshot"] ?? void 0,
|
|
540
|
+
infoRequest: row["info_request"] ? {
|
|
541
|
+
...row["info_request"],
|
|
542
|
+
askedAt: new Date(row["info_request"].askedAt)
|
|
543
|
+
} : void 0,
|
|
519
544
|
createdAt: new Date(row["created_at"]),
|
|
520
545
|
updatedAt: new Date(row["updated_at"])
|
|
521
546
|
};
|