hierarchical-approval 2.7.0 → 2.8.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +27 -0
  3. package/dist/{ApprovalEngine-BH9GvzMI.d.cts → ApprovalEngine-D6BmYI8B.d.cts} +18 -7
  4. package/dist/{ApprovalEngine-DkQcLaeT.d.ts → ApprovalEngine-DKn7KymS.d.ts} +18 -7
  5. package/dist/{IAuditAdapter-DSor54iY.d.ts → IAuditAdapter-B33n15YP.d.ts} +1 -1
  6. package/dist/{IAuditAdapter-2UU5edKG.d.cts → IAuditAdapter-DxdK4UVt.d.cts} +1 -1
  7. package/dist/{IAuthorizationPolicy-DPpiKzyh.d.cts → IAuthorizationPolicy-BzGVzkLJ.d.cts} +1 -1
  8. package/dist/{IAuthorizationPolicy-BOWg-3AH.d.ts → IAuthorizationPolicy-DOm6KCwI.d.ts} +1 -1
  9. package/dist/{INotificationAdapter-C6HPrzh6.d.cts → INotificationAdapter-DFKvGpOp.d.cts} +13 -2
  10. package/dist/{INotificationAdapter-BNGivLgU.d.ts → INotificationAdapter-DMvA4R-c.d.ts} +13 -2
  11. package/dist/{IOperationMiddleware-CxnPzpqL.d.cts → IOperationMiddleware-C8i06wq4.d.cts} +1 -1
  12. package/dist/{IOperationMiddleware-CXtg2wzF.d.ts → IOperationMiddleware-Nk8Q1Z8o.d.ts} +1 -1
  13. package/dist/{IStorageAdapter-yX9ERfQE.d.cts → IStorageAdapter-C0qeZgs4.d.cts} +1 -1
  14. package/dist/{IStorageAdapter-ChPT7ZDp.d.ts → IStorageAdapter-C2QxUwbX.d.ts} +1 -1
  15. package/dist/adapters/MemoryAdapter.cjs +4 -0
  16. package/dist/adapters/MemoryAdapter.cjs.map +1 -1
  17. package/dist/adapters/MemoryAdapter.d.cts +2 -2
  18. package/dist/adapters/MemoryAdapter.d.ts +2 -2
  19. package/dist/adapters/MemoryAdapter.js +4 -0
  20. package/dist/adapters/MemoryAdapter.js.map +1 -1
  21. package/dist/adapters/PostgresAdapter.cjs +10 -4
  22. package/dist/adapters/PostgresAdapter.cjs.map +1 -1
  23. package/dist/adapters/PostgresAdapter.d.cts +2 -2
  24. package/dist/adapters/PostgresAdapter.d.ts +2 -2
  25. package/dist/adapters/PostgresAdapter.js +10 -4
  26. package/dist/adapters/PostgresAdapter.js.map +1 -1
  27. package/dist/index.cjs +57 -1
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +8 -8
  30. package/dist/index.d.ts +8 -8
  31. package/dist/index.js +57 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/{instance-weh2w7Ji.d.cts → instance-BvOyT00S.d.cts} +23 -1
  34. package/dist/{instance-weh2w7Ji.d.ts → instance-BvOyT00S.d.ts} +23 -1
  35. package/dist/nestjs.cjs +53 -1
  36. package/dist/nestjs.cjs.map +1 -1
  37. package/dist/nestjs.d.cts +7 -7
  38. package/dist/nestjs.d.ts +7 -7
  39. package/dist/nestjs.js +53 -1
  40. package/dist/nestjs.js.map +1 -1
  41. package/dist/plugins/audit.d.cts +2 -2
  42. package/dist/plugins/audit.d.ts +2 -2
  43. package/dist/plugins/notify.d.cts +2 -2
  44. package/dist/plugins/notify.d.ts +2 -2
  45. package/dist/plugins/resilience.d.cts +3 -3
  46. package/dist/plugins/resilience.d.ts +3 -3
  47. package/dist/plugins/tracing.d.cts +2 -2
  48. package/dist/plugins/tracing.d.ts +2 -2
  49. package/dist/plugins/webhook.d.cts +2 -2
  50. package/dist/plugins/webhook.d.ts +2 -2
  51. package/dist/testing.cjs +57 -1
  52. package/dist/testing.cjs.map +1 -1
  53. package/dist/testing.d.cts +7 -7
  54. package/dist/testing.d.ts +7 -7
  55. package/dist/testing.js +57 -1
  56. package/dist/testing.js.map +1 -1
  57. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,50 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  _Nothing yet._
9
9
 
10
+ ## [2.8.0] - 2026-09-04
11
+
12
+ ### Added — comment threads
13
+
14
+ - **Comments are addressable objects rather than audit entries alone.** There
15
+ was no id to point at, so no way to reply to a comment, and no way to tell
16
+ somebody a remark was aimed at them — discussion moved to email, where the
17
+ approval record could not see it.
18
+
19
+ ```ts
20
+ await engine.addComment(id, { actorId: 'mgr-1', comment: 'Need the quote.' });
21
+ const [question] = await engine.getComments(id);
22
+ await engine.addComment(id, {
23
+ actorId: 'buyer-1',
24
+ comment: 'Attached now.',
25
+ parentCommentId: question.id,
26
+ mentions: ['mgr-1'],
27
+ });
28
+ ```
29
+
30
+ - **`approval:commented` is addressed to the people the comment mentions**, not
31
+ to the current approvers. A remark aimed at somebody should reach them, and
32
+ one aimed at nobody should not page the whole level.
33
+
34
+ - **`getComments()` returns a flat list carrying `parentCommentId`**, oldest
35
+ first, rather than a nested tree: a UI that wants threads can build them, and
36
+ one that wants a chronological feed does not have to flatten a structure it
37
+ never wanted. Replying to a comment that is not on the approval is rejected.
38
+
39
+ Comments are still written to the audit trail — the record of who said what
40
+ belongs there. `addComment()` keeps its `Promise<void>` signature, so nothing
41
+ calling it needs to change.
42
+
43
+ New exports: `Comment`, `CommentedEvent`. `ApprovalInstance` gains `comments`.
44
+
45
+ ### Fixed — MemoryAdapter left some timestamps as strings
46
+
47
+ - **`attachments[].addedAt`, `infoRequest.askedAt` and `levels[].reminderDueAt`
48
+ read back as strings, not `Date`s.** `MemoryAdapter` clones through JSON and
49
+ revives date fields by an explicit list, which these were missing from —
50
+ while `PostgresAdapter` revived them correctly. The two adapters therefore
51
+ disagreed, and any code trusting the declared `Date` type broke under one of
52
+ them only. Affected `addedAt` since 1.7.0 and `askedAt` since 1.5.0.
53
+
10
54
  ## [2.7.0] - 2026-09-04
11
55
 
12
56
  ### Added — `explainChain()`
package/README.md CHANGED
@@ -468,6 +468,33 @@ 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
+ ### Comment threads
472
+
473
+ Comments are addressable objects, so approvers can reply to each other and name
474
+ the person a remark is aimed at:
475
+
476
+ ```ts
477
+ await engine.addComment(id, { actorId: 'mgr-1', comment: 'Need the quote.' });
478
+ const [question] = await engine.getComments(id);
479
+
480
+ await engine.addComment(id, {
481
+ actorId: 'buyer-1',
482
+ comment: 'Attached now, @mgr-1.',
483
+ parentCommentId: question.id,
484
+ mentions: ['mgr-1'],
485
+ });
486
+ ```
487
+
488
+ `getComments()` returns a flat list carrying `parentCommentId`, oldest first — a
489
+ UI that wants threads can build them, and one that wants a chronological feed
490
+ (what most approval screens show) does not have to flatten a structure it never
491
+ wanted.
492
+
493
+ `approval:commented` is addressed to the people the comment **mentions**, not to
494
+ the current approvers: a remark aimed at somebody should reach them, and one
495
+ aimed at nobody should not page the whole level. Comments are still written to
496
+ the audit trail, since the record of who said what belongs there.
497
+
471
498
  ### Why does this chain look like this?
472
499
 
473
500
  `previewApprovalChain()` answers *what* the chain will be. `explainChain()`
@@ -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-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';
1
+ import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-C0qeZgs4.cjs';
2
+ import { m as ConditionExpression, r as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, C as Comment, e as ApprovalMode, b as AuditEntry } from './instance-BvOyT00S.cjs';
3
+ import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-DFKvGpOp.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-CxnPzpqL.cjs';
8
- import { I as IAuditAdapter } from './IAuditAdapter-2UU5edKG.cjs';
7
+ import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-C8i06wq4.cjs';
8
+ import { I as IAuditAdapter } from './IAuditAdapter-DxdK4UVt.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-DPpiKzyh.cjs';
11
+ import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-BzGVzkLJ.cjs';
12
12
 
13
13
  declare const SubmitOptionsSchema: z.ZodObject<{
14
14
  templateName: z.ZodString;
@@ -66,6 +66,8 @@ declare const ResubmitOptionsSchema: z.ZodObject<{
66
66
  declare const AddCommentOptionsSchema: z.ZodObject<{
67
67
  actorId: z.ZodString;
68
68
  comment: z.ZodString;
69
+ parentCommentId: z.ZodOptional<z.ZodString>;
70
+ mentions: z.ZodOptional<z.ZodArray<z.ZodString>>;
69
71
  }, z.core.$strip>;
70
72
  declare const OverrideOptionsSchema: z.ZodObject<{
71
73
  overriddenBy: z.ZodString;
@@ -199,7 +201,7 @@ interface BusinessHoursCalendarOptions extends WeekendCalendarOptions {
199
201
  */
200
202
  declare function businessHoursCalendar(options?: BusinessHoursCalendarOptions): Required<Pick<BusinessCalendar, 'addBusinessDays' | 'addBusinessHours'>>;
201
203
 
202
- type IdGeneratorPrefix = 'inst' | 'tpl' | 'att';
204
+ type IdGeneratorPrefix = 'inst' | 'tpl' | 'att' | 'cmt';
203
205
  type IdGeneratorFn = (prefix: IdGeneratorPrefix) => string;
204
206
  declare const defaultIdGenerator: IdGeneratorFn;
205
207
 
@@ -718,6 +720,15 @@ declare class ApprovalEngine {
718
720
  removeAttachment(instanceId: string, raw: RemoveAttachmentOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
719
721
  /** Add a comment to an instance without approving or rejecting. */
720
722
  addComment(instanceId: string, raw: AddCommentOptions, auditCtx?: AuditContext): Promise<void>;
723
+ /**
724
+ * Comments on an approval, oldest first.
725
+ *
726
+ * Returns a flat list carrying `parentCommentId`, rather than a nested tree:
727
+ * a UI that wants threads can build them, and one that wants a chronological
728
+ * feed — which is what most approval screens actually show — does not have to
729
+ * flatten a structure it never wanted.
730
+ */
731
+ getComments(instanceId: string): Promise<Comment[]>;
721
732
  /** Resubmit a rejected instance, creating a new linked instance from level 1. */
722
733
  resubmit(instanceId: string, raw: ResubmitOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
723
734
  /** Preview the resolved approval chain for a template and document data, without creating an instance. */
@@ -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-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';
1
+ import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-C2QxUwbX.js';
2
+ import { m as ConditionExpression, r as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, C as Comment, e as ApprovalMode, b as AuditEntry } from './instance-BvOyT00S.js';
3
+ import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-DMvA4R-c.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-CXtg2wzF.js';
8
- import { I as IAuditAdapter } from './IAuditAdapter-DSor54iY.js';
7
+ import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-Nk8Q1Z8o.js';
8
+ import { I as IAuditAdapter } from './IAuditAdapter-B33n15YP.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-BOWg-3AH.js';
11
+ import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-DOm6KCwI.js';
12
12
 
13
13
  declare const SubmitOptionsSchema: z.ZodObject<{
14
14
  templateName: z.ZodString;
@@ -66,6 +66,8 @@ declare const ResubmitOptionsSchema: z.ZodObject<{
66
66
  declare const AddCommentOptionsSchema: z.ZodObject<{
67
67
  actorId: z.ZodString;
68
68
  comment: z.ZodString;
69
+ parentCommentId: z.ZodOptional<z.ZodString>;
70
+ mentions: z.ZodOptional<z.ZodArray<z.ZodString>>;
69
71
  }, z.core.$strip>;
70
72
  declare const OverrideOptionsSchema: z.ZodObject<{
71
73
  overriddenBy: z.ZodString;
@@ -199,7 +201,7 @@ interface BusinessHoursCalendarOptions extends WeekendCalendarOptions {
199
201
  */
200
202
  declare function businessHoursCalendar(options?: BusinessHoursCalendarOptions): Required<Pick<BusinessCalendar, 'addBusinessDays' | 'addBusinessHours'>>;
201
203
 
202
- type IdGeneratorPrefix = 'inst' | 'tpl' | 'att';
204
+ type IdGeneratorPrefix = 'inst' | 'tpl' | 'att' | 'cmt';
203
205
  type IdGeneratorFn = (prefix: IdGeneratorPrefix) => string;
204
206
  declare const defaultIdGenerator: IdGeneratorFn;
205
207
 
@@ -718,6 +720,15 @@ declare class ApprovalEngine {
718
720
  removeAttachment(instanceId: string, raw: RemoveAttachmentOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
719
721
  /** Add a comment to an instance without approving or rejecting. */
720
722
  addComment(instanceId: string, raw: AddCommentOptions, auditCtx?: AuditContext): Promise<void>;
723
+ /**
724
+ * Comments on an approval, oldest first.
725
+ *
726
+ * Returns a flat list carrying `parentCommentId`, rather than a nested tree:
727
+ * a UI that wants threads can build them, and one that wants a chronological
728
+ * feed — which is what most approval screens actually show — does not have to
729
+ * flatten a structure it never wanted.
730
+ */
731
+ getComments(instanceId: string): Promise<Comment[]>;
721
732
  /** Resubmit a rejected instance, creating a new linked instance from level 1. */
722
733
  resubmit(instanceId: string, raw: ResubmitOptions, auditCtx?: AuditContext): Promise<ApprovalInstance>;
723
734
  /** Preview the resolved approval chain for a template and document data, without creating an instance. */
@@ -1,4 +1,4 @@
1
- import { b as AuditEntry, a as ApprovalInstance } from './instance-weh2w7Ji.js';
1
+ import { b as AuditEntry, a as ApprovalInstance } from './instance-BvOyT00S.js';
2
2
 
3
3
  interface IAuditAdapter {
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { b as AuditEntry, a as ApprovalInstance } from './instance-weh2w7Ji.cjs';
1
+ import { b as AuditEntry, a as ApprovalInstance } from './instance-BvOyT00S.cjs';
2
2
 
3
3
  interface IAuditAdapter {
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-weh2w7Ji.cjs';
1
+ import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-BvOyT00S.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 ApprovalInstance, d as ApprovalLevelInstance } from './instance-weh2w7Ji.js';
1
+ import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-BvOyT00S.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, b as AuditEntry } from './instance-weh2w7Ji.cjs';
1
+ import { a as ApprovalInstance, b as AuditEntry } from './instance-BvOyT00S.cjs';
2
2
 
3
3
  interface ApprovalEvent {
4
4
  instanceId: string;
@@ -120,6 +120,16 @@ interface SubWorkflowEvent extends ApprovalEvent {
120
120
  /** Set on completion: the outcome the child returned. */
121
121
  outcome?: 'approved' | 'rejected' | 'cancelled' | 'expired';
122
122
  }
123
+ /** Emitted when a comment is posted. Recipients are the users it mentions. */
124
+ interface CommentedEvent extends ApprovalEvent {
125
+ commentId: string;
126
+ authorId: string;
127
+ body: string;
128
+ level?: number;
129
+ parentCommentId?: string;
130
+ /** Users named in the comment. */
131
+ recipients: string[];
132
+ }
123
133
  interface ApprovalEventMap {
124
134
  'approval:submitted': SubmittedEvent;
125
135
  'approval:approved': ApprovedEvent;
@@ -142,6 +152,7 @@ interface ApprovalEventMap {
142
152
  'approval:attachment_removed': AttachmentEvent;
143
153
  'approval:subworkflow_started': SubWorkflowEvent;
144
154
  'approval:subworkflow_completed': SubWorkflowEvent;
155
+ 'approval:commented': CommentedEvent;
145
156
  }
146
157
  type ApprovalEventName = keyof ApprovalEventMap;
147
158
  interface HistoryEntry extends AuditEntry {
@@ -165,4 +176,4 @@ interface INotificationAdapter {
165
176
  notify(event: NotificationEvent): Promise<void>;
166
177
  }
167
178
 
168
- 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, SubWorkflowEvent as l, SubmittedEvent as m };
179
+ 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, CommentedEvent as e, DelegatedEvent as f, ExpiredEvent as g, InfoProvidedEvent as h, InfoRequestedEvent as i, RejectedEvent as j, ReminderEvent as k, ResubmittedEvent as l, SubWorkflowEvent as m, SubmittedEvent as n };
@@ -1,4 +1,4 @@
1
- import { a as ApprovalInstance, b as AuditEntry } from './instance-weh2w7Ji.js';
1
+ import { a as ApprovalInstance, b as AuditEntry } from './instance-BvOyT00S.js';
2
2
 
3
3
  interface ApprovalEvent {
4
4
  instanceId: string;
@@ -120,6 +120,16 @@ interface SubWorkflowEvent extends ApprovalEvent {
120
120
  /** Set on completion: the outcome the child returned. */
121
121
  outcome?: 'approved' | 'rejected' | 'cancelled' | 'expired';
122
122
  }
123
+ /** Emitted when a comment is posted. Recipients are the users it mentions. */
124
+ interface CommentedEvent extends ApprovalEvent {
125
+ commentId: string;
126
+ authorId: string;
127
+ body: string;
128
+ level?: number;
129
+ parentCommentId?: string;
130
+ /** Users named in the comment. */
131
+ recipients: string[];
132
+ }
123
133
  interface ApprovalEventMap {
124
134
  'approval:submitted': SubmittedEvent;
125
135
  'approval:approved': ApprovedEvent;
@@ -142,6 +152,7 @@ interface ApprovalEventMap {
142
152
  'approval:attachment_removed': AttachmentEvent;
143
153
  'approval:subworkflow_started': SubWorkflowEvent;
144
154
  'approval:subworkflow_completed': SubWorkflowEvent;
155
+ 'approval:commented': CommentedEvent;
145
156
  }
146
157
  type ApprovalEventName = keyof ApprovalEventMap;
147
158
  interface HistoryEntry extends AuditEntry {
@@ -165,4 +176,4 @@ interface INotificationAdapter {
165
176
  notify(event: NotificationEvent): Promise<void>;
166
177
  }
167
178
 
168
- 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, SubWorkflowEvent as l, SubmittedEvent as m };
179
+ 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, CommentedEvent as e, DelegatedEvent as f, ExpiredEvent as g, InfoProvidedEvent as h, InfoRequestedEvent as i, RejectedEvent as j, ReminderEvent as k, ResubmittedEvent as l, SubWorkflowEvent as m, SubmittedEvent as n };
@@ -1,4 +1,4 @@
1
- import { a as ApprovalInstance } from './instance-weh2w7Ji.cjs';
1
+ import { a as ApprovalInstance } from './instance-BvOyT00S.cjs';
2
2
 
3
3
  declare class ApprovalError extends Error {
4
4
  readonly code: string;
@@ -1,4 +1,4 @@
1
- import { a as ApprovalInstance } from './instance-weh2w7Ji.js';
1
+ import { a as ApprovalInstance } from './instance-BvOyT00S.js';
2
2
 
3
3
  declare class ApprovalError extends Error {
4
4
  readonly code: string;
@@ -1,4 +1,4 @@
1
- import { A as ApprovalTemplate, a as ApprovalInstance, f as ApprovalStatus, b as AuditEntry } from './instance-weh2w7Ji.cjs';
1
+ import { A as ApprovalTemplate, a as ApprovalInstance, f as ApprovalStatus, b as AuditEntry } from './instance-BvOyT00S.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-weh2w7Ji.js';
1
+ import { A as ApprovalTemplate, a as ApprovalInstance, f as ApprovalStatus, b as AuditEntry } from './instance-BvOyT00S.js';
2
2
 
3
3
  interface PaginationOpts {
4
4
  limit: number;
@@ -44,10 +44,14 @@ function reviveDates(instance) {
44
44
  slaDeadlineAt: instance.slaDeadlineAt ? new Date(instance.slaDeadlineAt) : void 0,
45
45
  slaBreachedAt: instance.slaBreachedAt ? new Date(instance.slaBreachedAt) : void 0,
46
46
  auditLog: instance.auditLog.map((e) => ({ ...e, timestamp: new Date(e.timestamp) })),
47
+ infoRequest: instance.infoRequest ? { ...instance.infoRequest, askedAt: new Date(instance.infoRequest.askedAt) } : void 0,
48
+ attachments: instance.attachments ? instance.attachments.map((a) => ({ ...a, addedAt: new Date(a.addedAt) })) : void 0,
49
+ comments: instance.comments ? instance.comments.map((c) => ({ ...c, createdAt: new Date(c.createdAt) })) : void 0,
47
50
  levels: instance.levels.map((l) => {
48
51
  const level = { ...l };
49
52
  if (l.escalationDueAt) level.escalationDueAt = new Date(l.escalationDueAt);
50
53
  if (l.delegatedUntil) level.delegatedUntil = new Date(l.delegatedUntil);
54
+ if (l.reminderDueAt) level.reminderDueAt = new Date(l.reminderDueAt);
51
55
  return level;
52
56
  })
53
57
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/errors.ts","../../src/adapters/MemoryAdapter.ts"],"names":[],"mappings":"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","file":"MemoryAdapter.cjs","sourcesContent":["export class ApprovalError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = 'ApprovalError';\n }\n\n toJSON(): { code: string; message: string; name: string } {\n return { code: this.code, message: this.message, name: this.name };\n }\n\n toHttpStatus(): number {\n const map: Record<string, number> = {\n NOT_FOUND: 404,\n CONFLICT: 409,\n FORBIDDEN: 403,\n VALIDATION: 422,\n TEMPLATE_NOT_FOUND: 404,\n };\n return map[this.code] ?? 500;\n }\n}\n\nexport class ApprovalNotFoundError extends ApprovalError {\n constructor(resource: string, id: string) {\n super(`${resource} \"${id}\" not found.`, 'NOT_FOUND');\n this.name = 'ApprovalNotFoundError';\n }\n}\n\nexport class ApprovalConflictError extends ApprovalError {\n constructor(instanceId: string) {\n super(\n `Concurrent modification detected on instance \"${instanceId}\". The record was updated by another process. Please retry.`,\n 'CONFLICT',\n );\n this.name = 'ApprovalConflictError';\n }\n}\n\nexport class ApprovalForbiddenError extends ApprovalError {\n constructor(message: string) {\n super(message, 'FORBIDDEN');\n this.name = 'ApprovalForbiddenError';\n }\n}\n\nexport class ApprovalValidationError extends ApprovalError {\n constructor(\n message: string,\n public readonly cause?: unknown,\n ) {\n super(message, 'VALIDATION');\n this.name = 'ApprovalValidationError';\n }\n}\n\nexport class ApprovalTemplateNotFoundError extends ApprovalError {\n constructor(name: string) {\n super(`Template \"${name}\" not found.`, 'TEMPLATE_NOT_FOUND');\n this.name = 'ApprovalTemplateNotFoundError';\n }\n}\n","import type {\n IStorageAdapter,\n PaginationOpts,\n PaginatedResult,\n InstanceFilter,\n CursorPaginationOpts,\n CursorPaginatedResult,\n} from './IStorageAdapter.js';\nimport type { ApprovalTemplate, ApprovalInstance, AuditEntry } from '../types/index.js';\nimport { ApprovalConflictError } from '../errors.js';\n\nfunction deepClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nfunction reviveDates(instance: ApprovalInstance): ApprovalInstance {\n return {\n ...instance,\n createdAt: new Date(instance.createdAt),\n updatedAt: new Date(instance.updatedAt),\n expiresAt: instance.expiresAt ? new Date(instance.expiresAt) : undefined,\n slaDeadlineAt: instance.slaDeadlineAt ? new Date(instance.slaDeadlineAt) : undefined,\n slaBreachedAt: instance.slaBreachedAt ? new Date(instance.slaBreachedAt) : undefined,\n auditLog: instance.auditLog.map((e) => ({ ...e, timestamp: new Date(e.timestamp) })),\n levels: instance.levels.map((l) => {\n const level: typeof l = { ...l };\n if (l.escalationDueAt) level.escalationDueAt = new Date(l.escalationDueAt);\n if (l.delegatedUntil) level.delegatedUntil = new Date(l.delegatedUntil);\n return level;\n }),\n };\n}\n\nfunction reviveTemplateDates(template: ApprovalTemplate): ApprovalTemplate {\n return { ...template, createdAt: new Date(template.createdAt) };\n}\n\n/**\n * Read a dot-path from a document, over **own** properties only.\n *\n * Mirrors how conditions resolve field paths, so a filter and a condition\n * written against the same path agree about what that path means — and an\n * inherited prototype member can never make an instance match a query.\n */\nfunction readPath(data: Record<string, unknown>, path: string): unknown {\n return path.split('.').reduce<unknown>((obj, key) => {\n if (obj !== null && typeof obj === 'object' && Object.prototype.hasOwnProperty.call(obj, key)) {\n return (obj as Record<string, unknown>)[key];\n }\n return undefined;\n }, data);\n}\n\n/** Structural equality, so a filter can match an object or array value. */\nfunction deepEquals(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false;\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n const aKeys = Object.keys(a as Record<string, unknown>);\n const bKeys = Object.keys(b as Record<string, unknown>);\n if (aKeys.length !== bKeys.length) return false;\n return aKeys.every(\n (k) =>\n Object.prototype.hasOwnProperty.call(b, k) &&\n deepEquals((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]),\n );\n}\n\nfunction applyFilter(instance: ApprovalInstance, filter: InstanceFilter): boolean {\n if (filter.status && instance.status !== filter.status) return false;\n if (filter.documentType && instance.documentType !== filter.documentType) return false;\n if (filter.submittedBy && instance.submittedBy !== filter.submittedBy) return false;\n if (filter.templateName && instance.templateName !== filter.templateName) return false;\n // Stored instances carry string dates (deepClone JSON round-trip), so wrap before comparing.\n if (filter.fromDate && new Date(instance.createdAt) < filter.fromDate) return false;\n if (filter.toDate && new Date(instance.createdAt) > filter.toDate) return false;\n if (filter.data) {\n for (const [path, expected] of Object.entries(filter.data)) {\n if (!deepEquals(readPath(instance.data ?? {}, path), expected)) return false;\n }\n }\n return true;\n}\n\nexport class MemoryAdapter implements IStorageAdapter {\n // keyed by `${tenantId}:${template.name}`\n private templates = new Map<string, ApprovalTemplate>();\n // keyed by `${tenantId}:${instance.id}`\n private instances = new Map<string, ApprovalInstance>();\n\n async saveTemplate(template: ApprovalTemplate): Promise<void> {\n this.templates.set(`${template.tenantId}:${template.name}`, deepClone(template));\n }\n\n async getTemplate(tenantId: string, name: string): Promise<ApprovalTemplate | null> {\n const template = this.templates.get(`${tenantId}:${name}`);\n return template ? reviveTemplateDates(deepClone(template)) : null;\n }\n\n async listTemplates(tenantId: string): Promise<ApprovalTemplate[]> {\n const result: ApprovalTemplate[] = [];\n for (const [key, template] of this.templates) {\n if (key.startsWith(`${tenantId}:`)) {\n result.push(reviveTemplateDates(deepClone(template)));\n }\n }\n return result;\n }\n\n async saveInstance(instance: ApprovalInstance): Promise<void> {\n this.instances.set(`${instance.tenantId}:${instance.id}`, deepClone(instance));\n }\n\n async updateInstance(instance: ApprovalInstance, expectedVersion: number): Promise<void> {\n const key = `${instance.tenantId}:${instance.id}`;\n const stored = this.instances.get(key);\n if (!stored) throw new ApprovalConflictError(instance.id);\n if (stored.version !== expectedVersion) throw new ApprovalConflictError(instance.id);\n const updated = deepClone(instance);\n updated.version = expectedVersion + 1;\n this.instances.set(key, updated);\n }\n\n async getInstance(tenantId: string, id: string): Promise<ApprovalInstance | null> {\n const raw = this.instances.get(`${tenantId}:${id}`);\n if (!raw) return null;\n return reviveDates(deepClone(raw));\n }\n\n async getInstancesByApprover(\n tenantId: string,\n approverId: string,\n opts?: PaginationOpts,\n ): Promise<PaginatedResult<ApprovalInstance>> {\n const all = [...this.instances.values()].filter((i) => {\n if (i.tenantId !== tenantId || i.status !== 'pending') return false;\n // Use .find() by level number, not array index (level numbers may not be consecutive)\n const currentLevel = i.levels.find((l) => l.level === i.currentLevel);\n return currentLevel?.approverIds.includes(approverId) ?? false;\n });\n return paginate(\n all.map((i) => reviveDates(deepClone(i))),\n opts,\n );\n }\n\n async getInstancesByFilter(\n tenantId: string,\n filter: InstanceFilter,\n opts?: PaginationOpts,\n ): Promise<PaginatedResult<ApprovalInstance>> {\n const all = [...this.instances.values()].filter(\n (i) => i.tenantId === tenantId && applyFilter(i, filter),\n );\n return paginate(\n all.map((i) => reviveDates(deepClone(i))),\n opts,\n );\n }\n\n async getOverdueInstances(\n tenantId: string,\n asOf: Date,\n filter: InstanceFilter = {},\n ): Promise<ApprovalInstance[]> {\n return [...this.instances.values()]\n .filter((i) => {\n if (i.tenantId !== tenantId || i.status !== 'pending') return false;\n if (filter.documentType && i.documentType !== filter.documentType) return false;\n if (filter.submittedBy && i.submittedBy !== filter.submittedBy) return false;\n // Escalation overdue on ANY open branch. Filtering to i.currentLevel\n // would miss the upper branches of a parallel group entirely, and left\n // this adapter disagreeing with PostgresAdapter, which already scans\n // every level.\n const hasOverdueEscalation = i.levels.some(\n (l) => l.escalationDueAt != null && new Date(l.escalationDueAt) <= asOf,\n );\n // Instance deadline expired\n const isExpired = i.expiresAt != null && new Date(i.expiresAt) <= asOf;\n // SLA breach (not yet recorded)\n const hasSLABreach =\n i.slaDeadlineAt != null && new Date(i.slaDeadlineAt) <= asOf && !i.slaBreachedAt;\n // Delegation expiry on any pending level\n const hasDelegationExpiry = i.levels.some(\n (l) =>\n l.status === 'pending' &&\n l.delegatedUntil != null &&\n new Date(l.delegatedUntil) <= asOf &&\n l.delegatedFrom != null,\n );\n // Reminder due on any open branch. Without this the scheduler never\n // sees the instance and no reminder is ever sent.\n const hasDueReminder = i.levels.some(\n (l) =>\n l.status === 'pending' && l.reminderDueAt != null && new Date(l.reminderDueAt) <= asOf,\n );\n return (\n hasOverdueEscalation || isExpired || hasSLABreach || hasDelegationExpiry || hasDueReminder\n );\n })\n .map((i) => reviveDates(deepClone(i)));\n }\n\n async deleteInstance(tenantId: string, id: string): Promise<boolean> {\n const key = `${tenantId}:${id}`;\n // The audit trail lives on the instance in this adapter, so removing the\n // instance removes it too — matching PostgresAdapter, which cascades.\n return this.instances.delete(key);\n }\n\n async countInstances(tenantId: string, filter: InstanceFilter): Promise<number> {\n let count = 0;\n for (const instance of this.instances.values()) {\n if (instance.tenantId === tenantId && applyFilter(instance, filter)) count++;\n }\n return count;\n }\n\n async getInstancesByCursor(\n tenantId: string,\n filter: InstanceFilter,\n opts: CursorPaginationOpts,\n ): Promise<CursorPaginatedResult<ApprovalInstance>> {\n const all = [...this.instances.values()]\n .filter((i) => i.tenantId === tenantId && applyFilter(i, filter))\n .map((i) => reviveDates(deepClone(i)))\n .sort((a, b) => {\n const ta = a.updatedAt.getTime();\n const tb = b.updatedAt.getTime();\n return ta !== tb ? ta - tb : a.id.localeCompare(b.id);\n });\n\n const { cursor, limit, direction = 'forward' } = opts;\n let startIdx = 0;\n\n if (cursor) {\n const [ts, id] = decodeCursor(cursor);\n const idx = all.findIndex(\n (i) => i.updatedAt.getTime() > ts || (i.updatedAt.getTime() === ts && i.id > id),\n );\n startIdx = idx === -1 ? all.length : idx;\n }\n\n if (direction === 'backward' && startIdx > 0) {\n startIdx = Math.max(0, startIdx - limit - 1);\n }\n\n const slice = all.slice(startIdx, startIdx + limit);\n const hasMore = startIdx + limit < all.length;\n const nextCursor = hasMore ? encodeCursor(slice[slice.length - 1]!) : undefined;\n const prevCursor = startIdx > 0 ? encodeCursor(all[startIdx - 1]!) : undefined;\n\n return { items: slice, nextCursor, prevCursor, hasMore };\n }\n\n async getIdempotentInstance(\n tenantId: string,\n idempotencyKey: string,\n ): Promise<ApprovalInstance | null> {\n for (const instance of this.instances.values()) {\n if (instance.tenantId === tenantId && instance.idempotencyKey === idempotencyKey) {\n return reviveDates(deepClone(instance));\n }\n }\n return null;\n }\n\n async appendAuditEntry(\n _tenantId: string,\n _instanceId: string,\n _entry: AuditEntry,\n ): Promise<void> {\n // No-op by design. The engine already pushes each entry onto instance.auditLog\n // and persists the whole instance via saveInstance()/updateInstance(), so the\n // embedded log is the source of truth for this adapter. Pushing here too would\n // duplicate every entry (as a dedicated, append-only audit table would in\n // PostgresAdapter, this method exists to satisfy that separate-sink contract).\n }\n\n /** Test helper — total stored instances across all tenants. */\n get size(): number {\n return this.instances.size;\n }\n}\n\nfunction paginate<T>(items: T[], opts?: PaginationOpts): PaginatedResult<T> {\n const total = items.length;\n if (!opts) return { items, total };\n return { items: items.slice(opts.offset, opts.offset + opts.limit), total };\n}\n\nfunction encodeCursor(instance: ApprovalInstance): string {\n return Buffer.from(`${instance.updatedAt.getTime()}|${instance.id}`).toString('base64');\n}\n\nfunction decodeCursor(cursor: string): [number, string] {\n const decoded = Buffer.from(cursor, 'base64').toString('utf8');\n const pipeIdx = decoded.indexOf('|');\n return [Number(decoded.slice(0, pipeIdx)), decoded.slice(pipeIdx + 1)];\n}\n"]}
1
+ {"version":3,"sources":["../../src/errors.ts","../../src/adapters/MemoryAdapter.ts"],"names":[],"mappings":"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","file":"MemoryAdapter.cjs","sourcesContent":["export class ApprovalError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = 'ApprovalError';\n }\n\n toJSON(): { code: string; message: string; name: string } {\n return { code: this.code, message: this.message, name: this.name };\n }\n\n toHttpStatus(): number {\n const map: Record<string, number> = {\n NOT_FOUND: 404,\n CONFLICT: 409,\n FORBIDDEN: 403,\n VALIDATION: 422,\n TEMPLATE_NOT_FOUND: 404,\n };\n return map[this.code] ?? 500;\n }\n}\n\nexport class ApprovalNotFoundError extends ApprovalError {\n constructor(resource: string, id: string) {\n super(`${resource} \"${id}\" not found.`, 'NOT_FOUND');\n this.name = 'ApprovalNotFoundError';\n }\n}\n\nexport class ApprovalConflictError extends ApprovalError {\n constructor(instanceId: string) {\n super(\n `Concurrent modification detected on instance \"${instanceId}\". The record was updated by another process. Please retry.`,\n 'CONFLICT',\n );\n this.name = 'ApprovalConflictError';\n }\n}\n\nexport class ApprovalForbiddenError extends ApprovalError {\n constructor(message: string) {\n super(message, 'FORBIDDEN');\n this.name = 'ApprovalForbiddenError';\n }\n}\n\nexport class ApprovalValidationError extends ApprovalError {\n constructor(\n message: string,\n public readonly cause?: unknown,\n ) {\n super(message, 'VALIDATION');\n this.name = 'ApprovalValidationError';\n }\n}\n\nexport class ApprovalTemplateNotFoundError extends ApprovalError {\n constructor(name: string) {\n super(`Template \"${name}\" not found.`, 'TEMPLATE_NOT_FOUND');\n this.name = 'ApprovalTemplateNotFoundError';\n }\n}\n","import type {\n IStorageAdapter,\n PaginationOpts,\n PaginatedResult,\n InstanceFilter,\n CursorPaginationOpts,\n CursorPaginatedResult,\n} from './IStorageAdapter.js';\nimport type { ApprovalTemplate, ApprovalInstance, AuditEntry } from '../types/index.js';\nimport { ApprovalConflictError } from '../errors.js';\n\nfunction deepClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\n/**\n * Restore every `Date` the JSON clone flattened to a string.\n *\n * Every date-bearing field must be listed here. Missing one leaves a value the\n * type says is a `Date` holding a string, which `PostgresAdapter` revives\n * correctly — so the two adapters disagree and the bug only shows up under one\n * of them. That is how `attachments[].addedAt` and `infoRequest.askedAt` came\n * to read back as strings.\n */\nfunction reviveDates(instance: ApprovalInstance): ApprovalInstance {\n return {\n ...instance,\n createdAt: new Date(instance.createdAt),\n updatedAt: new Date(instance.updatedAt),\n expiresAt: instance.expiresAt ? new Date(instance.expiresAt) : undefined,\n slaDeadlineAt: instance.slaDeadlineAt ? new Date(instance.slaDeadlineAt) : undefined,\n slaBreachedAt: instance.slaBreachedAt ? new Date(instance.slaBreachedAt) : undefined,\n auditLog: instance.auditLog.map((e) => ({ ...e, timestamp: new Date(e.timestamp) })),\n infoRequest: instance.infoRequest\n ? { ...instance.infoRequest, askedAt: new Date(instance.infoRequest.askedAt) }\n : undefined,\n attachments: instance.attachments\n ? instance.attachments.map((a) => ({ ...a, addedAt: new Date(a.addedAt) }))\n : undefined,\n comments: instance.comments\n ? instance.comments.map((c) => ({ ...c, createdAt: new Date(c.createdAt) }))\n : undefined,\n levels: instance.levels.map((l) => {\n const level: typeof l = { ...l };\n if (l.escalationDueAt) level.escalationDueAt = new Date(l.escalationDueAt);\n if (l.delegatedUntil) level.delegatedUntil = new Date(l.delegatedUntil);\n if (l.reminderDueAt) level.reminderDueAt = new Date(l.reminderDueAt);\n return level;\n }),\n };\n}\n\nfunction reviveTemplateDates(template: ApprovalTemplate): ApprovalTemplate {\n return { ...template, createdAt: new Date(template.createdAt) };\n}\n\n/**\n * Read a dot-path from a document, over **own** properties only.\n *\n * Mirrors how conditions resolve field paths, so a filter and a condition\n * written against the same path agree about what that path means — and an\n * inherited prototype member can never make an instance match a query.\n */\nfunction readPath(data: Record<string, unknown>, path: string): unknown {\n return path.split('.').reduce<unknown>((obj, key) => {\n if (obj !== null && typeof obj === 'object' && Object.prototype.hasOwnProperty.call(obj, key)) {\n return (obj as Record<string, unknown>)[key];\n }\n return undefined;\n }, data);\n}\n\n/** Structural equality, so a filter can match an object or array value. */\nfunction deepEquals(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false;\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n const aKeys = Object.keys(a as Record<string, unknown>);\n const bKeys = Object.keys(b as Record<string, unknown>);\n if (aKeys.length !== bKeys.length) return false;\n return aKeys.every(\n (k) =>\n Object.prototype.hasOwnProperty.call(b, k) &&\n deepEquals((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]),\n );\n}\n\nfunction applyFilter(instance: ApprovalInstance, filter: InstanceFilter): boolean {\n if (filter.status && instance.status !== filter.status) return false;\n if (filter.documentType && instance.documentType !== filter.documentType) return false;\n if (filter.submittedBy && instance.submittedBy !== filter.submittedBy) return false;\n if (filter.templateName && instance.templateName !== filter.templateName) return false;\n // Stored instances carry string dates (deepClone JSON round-trip), so wrap before comparing.\n if (filter.fromDate && new Date(instance.createdAt) < filter.fromDate) return false;\n if (filter.toDate && new Date(instance.createdAt) > filter.toDate) return false;\n if (filter.data) {\n for (const [path, expected] of Object.entries(filter.data)) {\n if (!deepEquals(readPath(instance.data ?? {}, path), expected)) return false;\n }\n }\n return true;\n}\n\nexport class MemoryAdapter implements IStorageAdapter {\n // keyed by `${tenantId}:${template.name}`\n private templates = new Map<string, ApprovalTemplate>();\n // keyed by `${tenantId}:${instance.id}`\n private instances = new Map<string, ApprovalInstance>();\n\n async saveTemplate(template: ApprovalTemplate): Promise<void> {\n this.templates.set(`${template.tenantId}:${template.name}`, deepClone(template));\n }\n\n async getTemplate(tenantId: string, name: string): Promise<ApprovalTemplate | null> {\n const template = this.templates.get(`${tenantId}:${name}`);\n return template ? reviveTemplateDates(deepClone(template)) : null;\n }\n\n async listTemplates(tenantId: string): Promise<ApprovalTemplate[]> {\n const result: ApprovalTemplate[] = [];\n for (const [key, template] of this.templates) {\n if (key.startsWith(`${tenantId}:`)) {\n result.push(reviveTemplateDates(deepClone(template)));\n }\n }\n return result;\n }\n\n async saveInstance(instance: ApprovalInstance): Promise<void> {\n this.instances.set(`${instance.tenantId}:${instance.id}`, deepClone(instance));\n }\n\n async updateInstance(instance: ApprovalInstance, expectedVersion: number): Promise<void> {\n const key = `${instance.tenantId}:${instance.id}`;\n const stored = this.instances.get(key);\n if (!stored) throw new ApprovalConflictError(instance.id);\n if (stored.version !== expectedVersion) throw new ApprovalConflictError(instance.id);\n const updated = deepClone(instance);\n updated.version = expectedVersion + 1;\n this.instances.set(key, updated);\n }\n\n async getInstance(tenantId: string, id: string): Promise<ApprovalInstance | null> {\n const raw = this.instances.get(`${tenantId}:${id}`);\n if (!raw) return null;\n return reviveDates(deepClone(raw));\n }\n\n async getInstancesByApprover(\n tenantId: string,\n approverId: string,\n opts?: PaginationOpts,\n ): Promise<PaginatedResult<ApprovalInstance>> {\n const all = [...this.instances.values()].filter((i) => {\n if (i.tenantId !== tenantId || i.status !== 'pending') return false;\n // Use .find() by level number, not array index (level numbers may not be consecutive)\n const currentLevel = i.levels.find((l) => l.level === i.currentLevel);\n return currentLevel?.approverIds.includes(approverId) ?? false;\n });\n return paginate(\n all.map((i) => reviveDates(deepClone(i))),\n opts,\n );\n }\n\n async getInstancesByFilter(\n tenantId: string,\n filter: InstanceFilter,\n opts?: PaginationOpts,\n ): Promise<PaginatedResult<ApprovalInstance>> {\n const all = [...this.instances.values()].filter(\n (i) => i.tenantId === tenantId && applyFilter(i, filter),\n );\n return paginate(\n all.map((i) => reviveDates(deepClone(i))),\n opts,\n );\n }\n\n async getOverdueInstances(\n tenantId: string,\n asOf: Date,\n filter: InstanceFilter = {},\n ): Promise<ApprovalInstance[]> {\n return [...this.instances.values()]\n .filter((i) => {\n if (i.tenantId !== tenantId || i.status !== 'pending') return false;\n if (filter.documentType && i.documentType !== filter.documentType) return false;\n if (filter.submittedBy && i.submittedBy !== filter.submittedBy) return false;\n // Escalation overdue on ANY open branch. Filtering to i.currentLevel\n // would miss the upper branches of a parallel group entirely, and left\n // this adapter disagreeing with PostgresAdapter, which already scans\n // every level.\n const hasOverdueEscalation = i.levels.some(\n (l) => l.escalationDueAt != null && new Date(l.escalationDueAt) <= asOf,\n );\n // Instance deadline expired\n const isExpired = i.expiresAt != null && new Date(i.expiresAt) <= asOf;\n // SLA breach (not yet recorded)\n const hasSLABreach =\n i.slaDeadlineAt != null && new Date(i.slaDeadlineAt) <= asOf && !i.slaBreachedAt;\n // Delegation expiry on any pending level\n const hasDelegationExpiry = i.levels.some(\n (l) =>\n l.status === 'pending' &&\n l.delegatedUntil != null &&\n new Date(l.delegatedUntil) <= asOf &&\n l.delegatedFrom != null,\n );\n // Reminder due on any open branch. Without this the scheduler never\n // sees the instance and no reminder is ever sent.\n const hasDueReminder = i.levels.some(\n (l) =>\n l.status === 'pending' && l.reminderDueAt != null && new Date(l.reminderDueAt) <= asOf,\n );\n return (\n hasOverdueEscalation || isExpired || hasSLABreach || hasDelegationExpiry || hasDueReminder\n );\n })\n .map((i) => reviveDates(deepClone(i)));\n }\n\n async deleteInstance(tenantId: string, id: string): Promise<boolean> {\n const key = `${tenantId}:${id}`;\n // The audit trail lives on the instance in this adapter, so removing the\n // instance removes it too — matching PostgresAdapter, which cascades.\n return this.instances.delete(key);\n }\n\n async countInstances(tenantId: string, filter: InstanceFilter): Promise<number> {\n let count = 0;\n for (const instance of this.instances.values()) {\n if (instance.tenantId === tenantId && applyFilter(instance, filter)) count++;\n }\n return count;\n }\n\n async getInstancesByCursor(\n tenantId: string,\n filter: InstanceFilter,\n opts: CursorPaginationOpts,\n ): Promise<CursorPaginatedResult<ApprovalInstance>> {\n const all = [...this.instances.values()]\n .filter((i) => i.tenantId === tenantId && applyFilter(i, filter))\n .map((i) => reviveDates(deepClone(i)))\n .sort((a, b) => {\n const ta = a.updatedAt.getTime();\n const tb = b.updatedAt.getTime();\n return ta !== tb ? ta - tb : a.id.localeCompare(b.id);\n });\n\n const { cursor, limit, direction = 'forward' } = opts;\n let startIdx = 0;\n\n if (cursor) {\n const [ts, id] = decodeCursor(cursor);\n const idx = all.findIndex(\n (i) => i.updatedAt.getTime() > ts || (i.updatedAt.getTime() === ts && i.id > id),\n );\n startIdx = idx === -1 ? all.length : idx;\n }\n\n if (direction === 'backward' && startIdx > 0) {\n startIdx = Math.max(0, startIdx - limit - 1);\n }\n\n const slice = all.slice(startIdx, startIdx + limit);\n const hasMore = startIdx + limit < all.length;\n const nextCursor = hasMore ? encodeCursor(slice[slice.length - 1]!) : undefined;\n const prevCursor = startIdx > 0 ? encodeCursor(all[startIdx - 1]!) : undefined;\n\n return { items: slice, nextCursor, prevCursor, hasMore };\n }\n\n async getIdempotentInstance(\n tenantId: string,\n idempotencyKey: string,\n ): Promise<ApprovalInstance | null> {\n for (const instance of this.instances.values()) {\n if (instance.tenantId === tenantId && instance.idempotencyKey === idempotencyKey) {\n return reviveDates(deepClone(instance));\n }\n }\n return null;\n }\n\n async appendAuditEntry(\n _tenantId: string,\n _instanceId: string,\n _entry: AuditEntry,\n ): Promise<void> {\n // No-op by design. The engine already pushes each entry onto instance.auditLog\n // and persists the whole instance via saveInstance()/updateInstance(), so the\n // embedded log is the source of truth for this adapter. Pushing here too would\n // duplicate every entry (as a dedicated, append-only audit table would in\n // PostgresAdapter, this method exists to satisfy that separate-sink contract).\n }\n\n /** Test helper — total stored instances across all tenants. */\n get size(): number {\n return this.instances.size;\n }\n}\n\nfunction paginate<T>(items: T[], opts?: PaginationOpts): PaginatedResult<T> {\n const total = items.length;\n if (!opts) return { items, total };\n return { items: items.slice(opts.offset, opts.offset + opts.limit), total };\n}\n\nfunction encodeCursor(instance: ApprovalInstance): string {\n return Buffer.from(`${instance.updatedAt.getTime()}|${instance.id}`).toString('base64');\n}\n\nfunction decodeCursor(cursor: string): [number, string] {\n const decoded = Buffer.from(cursor, 'base64').toString('utf8');\n const pipeIdx = decoded.indexOf('|');\n return [Number(decoded.slice(0, pipeIdx)), decoded.slice(pipeIdx + 1)];\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-yX9ERfQE.cjs';
2
- import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-weh2w7Ji.cjs';
1
+ import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-C0qeZgs4.cjs';
2
+ import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-BvOyT00S.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-ChPT7ZDp.js';
2
- import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-weh2w7Ji.js';
1
+ import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-C2QxUwbX.js';
2
+ import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-BvOyT00S.js';
3
3
 
4
4
  declare class MemoryAdapter implements IStorageAdapter {
5
5
  private templates;
@@ -42,10 +42,14 @@ function reviveDates(instance) {
42
42
  slaDeadlineAt: instance.slaDeadlineAt ? new Date(instance.slaDeadlineAt) : void 0,
43
43
  slaBreachedAt: instance.slaBreachedAt ? new Date(instance.slaBreachedAt) : void 0,
44
44
  auditLog: instance.auditLog.map((e) => ({ ...e, timestamp: new Date(e.timestamp) })),
45
+ infoRequest: instance.infoRequest ? { ...instance.infoRequest, askedAt: new Date(instance.infoRequest.askedAt) } : void 0,
46
+ attachments: instance.attachments ? instance.attachments.map((a) => ({ ...a, addedAt: new Date(a.addedAt) })) : void 0,
47
+ comments: instance.comments ? instance.comments.map((c) => ({ ...c, createdAt: new Date(c.createdAt) })) : void 0,
45
48
  levels: instance.levels.map((l) => {
46
49
  const level = { ...l };
47
50
  if (l.escalationDueAt) level.escalationDueAt = new Date(l.escalationDueAt);
48
51
  if (l.delegatedUntil) level.delegatedUntil = new Date(l.delegatedUntil);
52
+ if (l.reminderDueAt) level.reminderDueAt = new Date(l.reminderDueAt);
49
53
  return level;
50
54
  })
51
55
  };