hierarchical-approval 2.3.0 → 2.5.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 +74 -0
- package/README.md +27 -0
- package/dist/{ApprovalEngine-Ci9urTDI.d.ts → ApprovalEngine-BHWls8-z.d.ts} +59 -7
- package/dist/{ApprovalEngine-C1dUPnLM.d.cts → ApprovalEngine-CW7Uu1bV.d.cts} +59 -7
- package/dist/{IAuditAdapter-C4OASS6u.d.ts → IAuditAdapter-CMCirvY-.d.ts} +1 -1
- package/dist/{IAuditAdapter-BHVKMMv6.d.cts → IAuditAdapter-Dwx8ZP4J.d.cts} +1 -1
- package/dist/{IAuthorizationPolicy-CtLkwQ8I.d.cts → IAuthorizationPolicy-B3vaV69v.d.cts} +1 -1
- package/dist/{IAuthorizationPolicy-BA_m2Dg9.d.ts → IAuthorizationPolicy-BryQIKko.d.ts} +1 -1
- package/dist/{INotificationAdapter-JJAiMyMd.d.cts → INotificationAdapter-AwbhZEqz.d.cts} +1 -1
- package/dist/{INotificationAdapter-BdgfUrIn.d.ts → INotificationAdapter-BBq2czpO.d.ts} +1 -1
- package/dist/{IOperationMiddleware-D0ksiFM3.d.cts → IOperationMiddleware-3J-_NSGq.d.cts} +1 -1
- package/dist/{IOperationMiddleware-BGYrAqep.d.ts → IOperationMiddleware-CqzFklMn.d.ts} +1 -1
- package/dist/{IStorageAdapter-BU3sau5W.d.ts → IStorageAdapter-Dw2xXP9B.d.ts} +1 -1
- package/dist/{IStorageAdapter-DdHO4Rf1.d.cts → IStorageAdapter-Pq9J60xv.d.cts} +1 -1
- package/dist/adapters/MemoryAdapter.d.cts +2 -2
- package/dist/adapters/MemoryAdapter.d.ts +2 -2
- package/dist/adapters/PostgresAdapter.d.cts +2 -2
- package/dist/adapters/PostgresAdapter.d.ts +2 -2
- package/dist/index.cjs +117 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +117 -7
- package/dist/index.js.map +1 -1
- package/dist/{instance-DUJY_Axf.d.cts → instance-BO-i9-nq.d.cts} +13 -0
- package/dist/{instance-DUJY_Axf.d.ts → instance-BO-i9-nq.d.ts} +13 -0
- package/dist/nestjs.cjs +49 -6
- package/dist/nestjs.cjs.map +1 -1
- package/dist/nestjs.d.cts +7 -7
- package/dist/nestjs.d.ts +7 -7
- package/dist/nestjs.js +49 -6
- package/dist/nestjs.js.map +1 -1
- package/dist/plugins/audit.d.cts +2 -2
- package/dist/plugins/audit.d.ts +2 -2
- package/dist/plugins/notify.cjs +105 -9
- package/dist/plugins/notify.cjs.map +1 -1
- package/dist/plugins/notify.d.cts +91 -3
- package/dist/plugins/notify.d.ts +91 -3
- package/dist/plugins/notify.js +105 -10
- package/dist/plugins/notify.js.map +1 -1
- 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 +49 -6
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +7 -7
- package/dist/testing.d.ts +7 -7
- package/dist/testing.js +49 -6
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,80 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
_Nothing yet._
|
|
9
9
|
|
|
10
|
+
## [2.5.0] - 2026-09-04
|
|
11
|
+
|
|
12
|
+
### Added — deadlines in working hours
|
|
13
|
+
|
|
14
|
+
- **`escalationAfterHours` on a level and `slaDeadlineHours` on a template.**
|
|
15
|
+
Deadlines were whole days, but ERP SLAs are quoted in hours far more often —
|
|
16
|
+
"respond within four working hours" — and approximating that as a fraction of
|
|
17
|
+
a day counted evenings and weekends, so a request submitted at 16:00 on a
|
|
18
|
+
Friday was overdue before anybody could have looked at it.
|
|
19
|
+
|
|
20
|
+
- **`businessHoursCalendar({ workdayStartHour, workdayEndHour, weekendDays,
|
|
21
|
+
holidays })`** advances the clock only through the configured working window.
|
|
22
|
+
A `from` outside working hours is first moved to the next working moment, so a
|
|
23
|
+
deadline never starts counting from a time nobody was at work.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
const engine = new ApprovalEngine({
|
|
27
|
+
adapter,
|
|
28
|
+
calendar: businessHoursCalendar({ workdayStartHour: 9, workdayEndHour: 17 }),
|
|
29
|
+
});
|
|
30
|
+
// Friday 16:00 + 4 working hours -> Monday 12:00
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- **`BusinessCalendar.addBusinessHours` is optional.** `weekendCalendar` knows
|
|
34
|
+
whole days only; given an hour-based deadline the engine falls back to elapsed
|
|
35
|
+
clock time rather than quietly pretending the calendar was applied. A custom
|
|
36
|
+
calendar opts in by implementing the method.
|
|
37
|
+
|
|
38
|
+
- `validateTemplate()` rejects setting both units on the same level or template,
|
|
39
|
+
and non-positive hour values.
|
|
40
|
+
|
|
41
|
+
New exports: `businessHoursCalendar`, `BusinessHoursCalendarOptions`.
|
|
42
|
+
|
|
43
|
+
## [2.4.0] - 2026-09-04
|
|
44
|
+
|
|
45
|
+
### Added — `DigestNotificationAdapter`
|
|
46
|
+
|
|
47
|
+
- **Batches notifications per recipient instead of sending one per event.** An
|
|
48
|
+
approver on twenty documents received twenty separate messages a day, which is
|
|
49
|
+
how approval email ends up filtered into a folder nobody reads — the
|
|
50
|
+
notifications defeat themselves.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { DigestNotificationAdapter } from 'hierarchical-approval/plugins/notify';
|
|
54
|
+
|
|
55
|
+
const digest = new DigestNotificationAdapter({
|
|
56
|
+
intervalMs: 15 * 60_000,
|
|
57
|
+
send: async ({ recipient, events }) => mailer.send(recipient, summarise(events)),
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- **Urgent events still go straight through.** Batching a rejection or a
|
|
62
|
+
completed approval behind a digest window would make the library's own
|
|
63
|
+
notifications the reason a decision was late, so `approval:rejected`,
|
|
64
|
+
`approval:completed`, `approval:sla_breached` and `approval:expired` bypass
|
|
65
|
+
the buffer by default — configurable via `passthrough`.
|
|
66
|
+
|
|
67
|
+
- **`maxBatchSize`** (default 50) flushes a recipient early under a burst so the
|
|
68
|
+
buffer stays bounded, and flushes only *that* recipient: a burst aimed at one
|
|
69
|
+
person must not force everybody else's digest out early. Omit `intervalMs` to
|
|
70
|
+
disable the timer and drive `flush()` from a cron job or queue worker instead.
|
|
71
|
+
|
|
72
|
+
- A failed `send` is logged and swallowed, as the notification-adapter contract
|
|
73
|
+
requires, and the buffer is cleared before sending so a failure cannot replay
|
|
74
|
+
the same events into every subsequent digest.
|
|
75
|
+
|
|
76
|
+
- Buffers are in memory: a restart drops what has not been flushed. That is the
|
|
77
|
+
right trade for a convenience digest but not for delivery guarantees — put
|
|
78
|
+
`OutboxNotificationAdapter` underneath when an event must not be lost.
|
|
79
|
+
|
|
80
|
+
New exports from `hierarchical-approval/plugins/notify`:
|
|
81
|
+
`DigestNotificationAdapter`, `DigestNotificationAdapterOptions`, `Digest`,
|
|
82
|
+
`DigestSendFn`.
|
|
83
|
+
|
|
10
84
|
## [2.3.0] - 2026-09-04
|
|
11
85
|
|
|
12
86
|
### Added — retention
|
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
|
+
### SLAs in working hours
|
|
472
|
+
|
|
473
|
+
Deadlines were whole days, so "respond within four working hours" could only be
|
|
474
|
+
approximated. Levels take `escalationAfterHours` and templates take
|
|
475
|
+
`slaDeadlineHours`, counted through a working-hours calendar:
|
|
476
|
+
|
|
477
|
+
```ts
|
|
478
|
+
import { businessHoursCalendar } from 'hierarchical-approval';
|
|
479
|
+
|
|
480
|
+
const engine = new ApprovalEngine({
|
|
481
|
+
adapter,
|
|
482
|
+
calendar: businessHoursCalendar({ workdayStartHour: 9, workdayEndHour: 17 }),
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Submitted Friday 16:00, escalationAfterHours: 4 -> due Monday 12:00
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
Evenings, weekends and holidays are skipped, and a request arriving outside
|
|
489
|
+
working hours starts counting from the next working moment — so one submitted at
|
|
490
|
+
20:00 is not already overdue by morning.
|
|
491
|
+
|
|
492
|
+
`weekendCalendar` knows whole days only. Give it an hour-based deadline and the
|
|
493
|
+
engine falls back to **elapsed clock time**, rather than quietly pretending the
|
|
494
|
+
calendar applied; use `businessHoursCalendar` when hours should skip
|
|
495
|
+
non-working time. Days and hours are mutually exclusive on the same level or
|
|
496
|
+
template, and `validateTemplate()` rejects setting both.
|
|
497
|
+
|
|
471
498
|
### Retention
|
|
472
499
|
|
|
473
500
|
Approval tables only grow, and data-minimisation rules eventually require old
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-
|
|
2
|
-
import { l as ConditionExpression, p as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k 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-Dw2xXP9B.js';
|
|
2
|
+
import { l as ConditionExpression, p as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, e as ApprovalMode, b as AuditEntry } from './instance-BO-i9-nq.js';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-BBq2czpO.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { L as Logger } from './Logger-BplhlU7l.js';
|
|
6
6
|
import { C as Clock } from './Clock-3FnOczFJ.js';
|
|
7
|
-
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-
|
|
8
|
-
import { I as IAuditAdapter } from './IAuditAdapter-
|
|
7
|
+
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-CqzFklMn.js';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-CMCirvY-.js';
|
|
9
9
|
import { I as IMetricsAdapter } from './IMetricsAdapter-DWq8IFaf.js';
|
|
10
10
|
import { I as ISchedulerAdapter } from './ISchedulerAdapter-DKv_QjVN.js';
|
|
11
|
-
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-
|
|
11
|
+
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-BryQIKko.js';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -141,6 +141,16 @@ interface BusinessCalendar {
|
|
|
141
141
|
* elapsed clock time within the resulting business day.
|
|
142
142
|
*/
|
|
143
143
|
addBusinessDays(from: Date, days: number): Date;
|
|
144
|
+
/**
|
|
145
|
+
* Return the date that is `hours` **working** hours after `from`, skipping
|
|
146
|
+
* non-working hours, weekends and holidays.
|
|
147
|
+
*
|
|
148
|
+
* Optional. A calendar that only knows about whole days — such as
|
|
149
|
+
* {@link weekendCalendar} — omits it, and the engine falls back to elapsed
|
|
150
|
+
* clock time for any hour-based deadline rather than silently pretending the
|
|
151
|
+
* calendar was applied.
|
|
152
|
+
*/
|
|
153
|
+
addBusinessHours?(from: Date, hours: number): Date;
|
|
144
154
|
}
|
|
145
155
|
interface WeekendCalendarOptions {
|
|
146
156
|
/** Dates to treat as non-working days (compared by local calendar date). */
|
|
@@ -157,6 +167,37 @@ interface WeekendCalendarOptions {
|
|
|
157
167
|
* holidays. Day arithmetic is performed in the host's local timezone.
|
|
158
168
|
*/
|
|
159
169
|
declare function weekendCalendar(options?: WeekendCalendarOptions): BusinessCalendar;
|
|
170
|
+
interface BusinessHoursCalendarOptions extends WeekendCalendarOptions {
|
|
171
|
+
/** First working hour of the day, 0-23 in local time. Defaults to 9. */
|
|
172
|
+
workdayStartHour?: number;
|
|
173
|
+
/**
|
|
174
|
+
* Hour at which the working day ends, 0-24 in local time. Defaults to 17.
|
|
175
|
+
* Must be greater than {@link workdayStartHour}.
|
|
176
|
+
*/
|
|
177
|
+
workdayEndHour?: number;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* A calendar that counts only working hours within working days.
|
|
181
|
+
*
|
|
182
|
+
* SLAs in an ERP are quoted in hours far more often than days — "respond within
|
|
183
|
+
* four working hours" — and counting those as elapsed clock time makes a
|
|
184
|
+
* request submitted at 16:00 on a Friday overdue before anybody could have
|
|
185
|
+
* looked at it. This advances the clock only through the configured working
|
|
186
|
+
* window, skipping evenings, weekends and holidays.
|
|
187
|
+
*
|
|
188
|
+
* A `from` outside working hours is first moved forward to the next working
|
|
189
|
+
* moment, so a deadline never starts counting from a time nobody was at work.
|
|
190
|
+
*
|
|
191
|
+
* Arithmetic is performed in the host's local timezone, matching
|
|
192
|
+
* {@link weekendCalendar}.
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```ts
|
|
196
|
+
* const calendar = businessHoursCalendar({ workdayStartHour: 9, workdayEndHour: 17 });
|
|
197
|
+
* // Friday 16:00 + 4 working hours -> Monday 12:00
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
declare function businessHoursCalendar(options?: BusinessHoursCalendarOptions): Required<Pick<BusinessCalendar, 'addBusinessDays' | 'addBusinessHours'>>;
|
|
160
201
|
|
|
161
202
|
type IdGeneratorPrefix = 'inst' | 'tpl' | 'att';
|
|
162
203
|
type IdGeneratorFn = (prefix: IdGeneratorPrefix) => string;
|
|
@@ -800,6 +841,17 @@ declare class ApprovalEngine {
|
|
|
800
841
|
private withOptimisticRetry;
|
|
801
842
|
/** Compute a deadline `days` from `from`, honouring the business calendar if one is configured. */
|
|
802
843
|
private deadlineFrom;
|
|
844
|
+
/**
|
|
845
|
+
* Resolve an hour-based deadline.
|
|
846
|
+
*
|
|
847
|
+
* A calendar that only knows whole days — `weekendCalendar`, say — has no
|
|
848
|
+
* `addBusinessHours`, and this falls back to elapsed clock time rather than
|
|
849
|
+
* quietly pretending the calendar was applied. Configure
|
|
850
|
+
* `businessHoursCalendar` to have hours skip evenings and weekends.
|
|
851
|
+
*/
|
|
852
|
+
private deadlineFromHours;
|
|
853
|
+
/** Level deadline from whichever of days/hours the template configured. */
|
|
854
|
+
private levelEscalationDue;
|
|
803
855
|
private requireInstance;
|
|
804
856
|
/**
|
|
805
857
|
* Identity of the parallel branch group a level belongs to.
|
|
@@ -890,4 +942,4 @@ declare class ApprovalEngine {
|
|
|
890
942
|
private runExternalAudit;
|
|
891
943
|
}
|
|
892
944
|
|
|
893
|
-
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E,
|
|
945
|
+
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E, toComparableNumber as F, validateConditionExpression as G, type HealthResult as H, type IdGeneratorFn as I, weekendCalendar as J, type OrgProvider as O, type PreviewChainLevel as P, type ReassignOptions as R, type SubmitOptions as S, TEMPLATE_BUNDLE_VERSION as T, type UpdateDataOptions as U, type ValidationResult as V, type WeekendCalendarOptions as W, ApprovalEngine as a, type ApprovalEngineOptions as b, type ApprovalStatistics as c, type ApproveOptions as d, type ApproverResolverFn as e, type ApproverWorkload as f, type BusinessCalendar as g, type BusinessHoursCalendarOptions as h, type CancelOptions as i, type ConditionOperatorFn as j, type CycleTimeStats as k, type IdempotencyKeyFn as l, type ImportResult as m, type OutOfOfficeProvider as n, type OverrideOptions as o, type PreviewResult as p, type ProvideInfoOptions as q, type PurgeResult as r, type RejectOptions as s, type RequestInfoOptions as t, type ResubmitOptions as u, type RetryPolicy as v, type TemplateBundle as w, type TransferResult as x, businessHoursCalendar as y, defaultIdGenerator as z };
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from './IStorageAdapter-
|
|
2
|
-
import { l as ConditionExpression, p as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k 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-Pq9J60xv.cjs';
|
|
2
|
+
import { l as ConditionExpression, p as ResolverFn, g as ApprovalTemplateConfig, A as ApprovalTemplate, k as AuditContext, a as ApprovalInstance, e as ApprovalMode, b as AuditEntry } from './instance-BO-i9-nq.cjs';
|
|
3
|
+
import { I as INotificationAdapter, b as ApprovalEventName, a as ApprovalEventMap } from './INotificationAdapter-AwbhZEqz.cjs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { L as Logger } from './Logger-BplhlU7l.cjs';
|
|
6
6
|
import { C as Clock } from './Clock-3FnOczFJ.cjs';
|
|
7
|
-
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-
|
|
8
|
-
import { I as IAuditAdapter } from './IAuditAdapter-
|
|
7
|
+
import { I as IOperationMiddleware, a as ApprovalError } from './IOperationMiddleware-3J-_NSGq.cjs';
|
|
8
|
+
import { I as IAuditAdapter } from './IAuditAdapter-Dwx8ZP4J.cjs';
|
|
9
9
|
import { I as IMetricsAdapter } from './IMetricsAdapter-DWq8IFaf.cjs';
|
|
10
10
|
import { I as ISchedulerAdapter } from './ISchedulerAdapter-DKv_QjVN.cjs';
|
|
11
|
-
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-
|
|
11
|
+
import { I as IAuthorizationPolicy } from './IAuthorizationPolicy-B3vaV69v.cjs';
|
|
12
12
|
|
|
13
13
|
declare const SubmitOptionsSchema: z.ZodObject<{
|
|
14
14
|
templateName: z.ZodString;
|
|
@@ -141,6 +141,16 @@ interface BusinessCalendar {
|
|
|
141
141
|
* elapsed clock time within the resulting business day.
|
|
142
142
|
*/
|
|
143
143
|
addBusinessDays(from: Date, days: number): Date;
|
|
144
|
+
/**
|
|
145
|
+
* Return the date that is `hours` **working** hours after `from`, skipping
|
|
146
|
+
* non-working hours, weekends and holidays.
|
|
147
|
+
*
|
|
148
|
+
* Optional. A calendar that only knows about whole days — such as
|
|
149
|
+
* {@link weekendCalendar} — omits it, and the engine falls back to elapsed
|
|
150
|
+
* clock time for any hour-based deadline rather than silently pretending the
|
|
151
|
+
* calendar was applied.
|
|
152
|
+
*/
|
|
153
|
+
addBusinessHours?(from: Date, hours: number): Date;
|
|
144
154
|
}
|
|
145
155
|
interface WeekendCalendarOptions {
|
|
146
156
|
/** Dates to treat as non-working days (compared by local calendar date). */
|
|
@@ -157,6 +167,37 @@ interface WeekendCalendarOptions {
|
|
|
157
167
|
* holidays. Day arithmetic is performed in the host's local timezone.
|
|
158
168
|
*/
|
|
159
169
|
declare function weekendCalendar(options?: WeekendCalendarOptions): BusinessCalendar;
|
|
170
|
+
interface BusinessHoursCalendarOptions extends WeekendCalendarOptions {
|
|
171
|
+
/** First working hour of the day, 0-23 in local time. Defaults to 9. */
|
|
172
|
+
workdayStartHour?: number;
|
|
173
|
+
/**
|
|
174
|
+
* Hour at which the working day ends, 0-24 in local time. Defaults to 17.
|
|
175
|
+
* Must be greater than {@link workdayStartHour}.
|
|
176
|
+
*/
|
|
177
|
+
workdayEndHour?: number;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* A calendar that counts only working hours within working days.
|
|
181
|
+
*
|
|
182
|
+
* SLAs in an ERP are quoted in hours far more often than days — "respond within
|
|
183
|
+
* four working hours" — and counting those as elapsed clock time makes a
|
|
184
|
+
* request submitted at 16:00 on a Friday overdue before anybody could have
|
|
185
|
+
* looked at it. This advances the clock only through the configured working
|
|
186
|
+
* window, skipping evenings, weekends and holidays.
|
|
187
|
+
*
|
|
188
|
+
* A `from` outside working hours is first moved forward to the next working
|
|
189
|
+
* moment, so a deadline never starts counting from a time nobody was at work.
|
|
190
|
+
*
|
|
191
|
+
* Arithmetic is performed in the host's local timezone, matching
|
|
192
|
+
* {@link weekendCalendar}.
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```ts
|
|
196
|
+
* const calendar = businessHoursCalendar({ workdayStartHour: 9, workdayEndHour: 17 });
|
|
197
|
+
* // Friday 16:00 + 4 working hours -> Monday 12:00
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
declare function businessHoursCalendar(options?: BusinessHoursCalendarOptions): Required<Pick<BusinessCalendar, 'addBusinessDays' | 'addBusinessHours'>>;
|
|
160
201
|
|
|
161
202
|
type IdGeneratorPrefix = 'inst' | 'tpl' | 'att';
|
|
162
203
|
type IdGeneratorFn = (prefix: IdGeneratorPrefix) => string;
|
|
@@ -800,6 +841,17 @@ declare class ApprovalEngine {
|
|
|
800
841
|
private withOptimisticRetry;
|
|
801
842
|
/** Compute a deadline `days` from `from`, honouring the business calendar if one is configured. */
|
|
802
843
|
private deadlineFrom;
|
|
844
|
+
/**
|
|
845
|
+
* Resolve an hour-based deadline.
|
|
846
|
+
*
|
|
847
|
+
* A calendar that only knows whole days — `weekendCalendar`, say — has no
|
|
848
|
+
* `addBusinessHours`, and this falls back to elapsed clock time rather than
|
|
849
|
+
* quietly pretending the calendar was applied. Configure
|
|
850
|
+
* `businessHoursCalendar` to have hours skip evenings and weekends.
|
|
851
|
+
*/
|
|
852
|
+
private deadlineFromHours;
|
|
853
|
+
/** Level deadline from whichever of days/hours the template configured. */
|
|
854
|
+
private levelEscalationDue;
|
|
803
855
|
private requireInstance;
|
|
804
856
|
/**
|
|
805
857
|
* Identity of the parallel branch group a level belongs to.
|
|
@@ -890,4 +942,4 @@ declare class ApprovalEngine {
|
|
|
890
942
|
private runExternalAudit;
|
|
891
943
|
}
|
|
892
944
|
|
|
893
|
-
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E,
|
|
945
|
+
export { type AddCommentOptions as A, type BulkResult as B, type CanApproveResult as C, type DelegateOptions as D, type EscalateOptions as E, toComparableNumber as F, validateConditionExpression as G, type HealthResult as H, type IdGeneratorFn as I, weekendCalendar as J, type OrgProvider as O, type PreviewChainLevel as P, type ReassignOptions as R, type SubmitOptions as S, TEMPLATE_BUNDLE_VERSION as T, type UpdateDataOptions as U, type ValidationResult as V, type WeekendCalendarOptions as W, ApprovalEngine as a, type ApprovalEngineOptions as b, type ApprovalStatistics as c, type ApproveOptions as d, type ApproverResolverFn as e, type ApproverWorkload as f, type BusinessCalendar as g, type BusinessHoursCalendarOptions as h, type CancelOptions as i, type ConditionOperatorFn as j, type CycleTimeStats as k, type IdempotencyKeyFn as l, type ImportResult as m, type OutOfOfficeProvider as n, type OverrideOptions as o, type PreviewResult as p, type ProvideInfoOptions as q, type PurgeResult as r, type RejectOptions as s, type RequestInfoOptions as t, type ResubmitOptions as u, type RetryPolicy as v, type TemplateBundle as w, type TransferResult as x, businessHoursCalendar as y, defaultIdGenerator as z };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-
|
|
1
|
+
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-BO-i9-nq.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-
|
|
1
|
+
import { a as ApprovalInstance, d as ApprovalLevelInstance } from './instance-BO-i9-nq.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 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-BO-i9-nq.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-BO-i9-nq.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-Pq9J60xv.cjs';
|
|
2
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-BO-i9-nq.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-Dw2xXP9B.js';
|
|
2
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-BO-i9-nq.js';
|
|
3
3
|
|
|
4
4
|
declare class MemoryAdapter implements IStorageAdapter {
|
|
5
5
|
private templates;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as tls from 'tls';
|
|
2
2
|
import * as pg from 'pg';
|
|
3
|
-
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-
|
|
4
|
-
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-
|
|
3
|
+
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-Pq9J60xv.cjs';
|
|
4
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-BO-i9-nq.cjs';
|
|
5
5
|
|
|
6
6
|
interface PostgresAdapterOptions {
|
|
7
7
|
connectionString?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as tls from 'tls';
|
|
2
2
|
import * as pg from 'pg';
|
|
3
|
-
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-
|
|
4
|
-
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-
|
|
3
|
+
import { I as IStorageAdapter, P as PaginationOpts, a as PaginatedResult, b as InstanceFilter, C as CursorPaginationOpts, c as CursorPaginatedResult } from '../IStorageAdapter-Dw2xXP9B.js';
|
|
4
|
+
import { A as ApprovalTemplate, a as ApprovalInstance, b as AuditEntry } from '../instance-BO-i9-nq.js';
|
|
5
5
|
|
|
6
6
|
interface PostgresAdapterOptions {
|
|
7
7
|
connectionString?: string;
|
package/dist/index.cjs
CHANGED
|
@@ -1071,6 +1071,18 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1071
1071
|
message: `Level ${l.level} sets reminderEveryDays without reminderAfterDays, so no reminder would ever be sent.`
|
|
1072
1072
|
});
|
|
1073
1073
|
}
|
|
1074
|
+
if (l.escalationAfterHours !== void 0 && l.escalationAfterHours <= 0) {
|
|
1075
|
+
errors.push({
|
|
1076
|
+
field: `levels[${i}].escalationAfterHours`,
|
|
1077
|
+
message: `Level ${l.level} escalationAfterHours must be a positive number.`
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
if (l.escalationAfterDays !== void 0 && l.escalationAfterHours !== void 0) {
|
|
1081
|
+
errors.push({
|
|
1082
|
+
field: `levels[${i}].escalationAfterHours`,
|
|
1083
|
+
message: `Level ${l.level} sets both escalationAfterDays and escalationAfterHours; pick one so the deadline is unambiguous.`
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1074
1086
|
if (l.escalationAfterDays !== void 0 && l.escalationAfterDays <= 0) {
|
|
1075
1087
|
errors.push({
|
|
1076
1088
|
field: `levels[${i}].escalationAfterDays`,
|
|
@@ -1135,6 +1147,18 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1135
1147
|
}
|
|
1136
1148
|
});
|
|
1137
1149
|
}
|
|
1150
|
+
if (config.slaDeadlineDays !== void 0 && config.slaDeadlineHours !== void 0) {
|
|
1151
|
+
errors.push({
|
|
1152
|
+
field: "slaDeadlineHours",
|
|
1153
|
+
message: "Template sets both slaDeadlineDays and slaDeadlineHours; pick one so the SLA is unambiguous."
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
if (config.slaDeadlineHours !== void 0 && config.slaDeadlineHours <= 0) {
|
|
1157
|
+
errors.push({
|
|
1158
|
+
field: "slaDeadlineHours",
|
|
1159
|
+
message: "slaDeadlineHours must be a positive number."
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1138
1162
|
if (config.conditions) {
|
|
1139
1163
|
config.conditions.forEach((rule, ruleIdx) => {
|
|
1140
1164
|
errors.push(...validateConditionExpression(rule.when, `conditions[${ruleIdx}].when`));
|
|
@@ -1248,7 +1272,8 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1248
1272
|
threshold: cfg.threshold,
|
|
1249
1273
|
weights: cfg.weights,
|
|
1250
1274
|
escalationAfterDays: cfg.escalationAfterDays,
|
|
1251
|
-
|
|
1275
|
+
escalationAfterHours: cfg.escalationAfterHours,
|
|
1276
|
+
escalationDueAt: inFirstGroup ? this.levelEscalationDue(now, cfg) : void 0,
|
|
1252
1277
|
subWorkflowTemplate: cfg.subWorkflow?.templateName,
|
|
1253
1278
|
reminderAfterDays: cfg.reminderAfterDays,
|
|
1254
1279
|
reminderEveryDays: cfg.reminderEveryDays,
|
|
@@ -1278,7 +1303,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1278
1303
|
timestamp: now,
|
|
1279
1304
|
...auditCtx
|
|
1280
1305
|
};
|
|
1281
|
-
const slaDeadlineAt = template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
1306
|
+
const slaDeadlineAt = template.slaDeadlineHours ? this.deadlineFromHours(now, template.slaDeadlineHours) : template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
1282
1307
|
const instance = {
|
|
1283
1308
|
id: instanceId,
|
|
1284
1309
|
tenantId: this.tenantId,
|
|
@@ -1306,6 +1331,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
1306
1331
|
templateSnapshot: {
|
|
1307
1332
|
escalation: template.escalation,
|
|
1308
1333
|
slaDeadlineDays: template.slaDeadlineDays,
|
|
1334
|
+
slaDeadlineHours: template.slaDeadlineHours,
|
|
1309
1335
|
allowOverride: template.allowOverride
|
|
1310
1336
|
}
|
|
1311
1337
|
};
|
|
@@ -2577,7 +2603,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
2577
2603
|
reason: opts.reason,
|
|
2578
2604
|
...auditCtx
|
|
2579
2605
|
};
|
|
2580
|
-
const slaDeadlineAt = template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
2606
|
+
const slaDeadlineAt = template.slaDeadlineHours ? this.deadlineFromHours(now, template.slaDeadlineHours) : template.slaDeadlineDays ? this.deadlineFrom(now, template.slaDeadlineDays) : void 0;
|
|
2581
2607
|
const newInstance = {
|
|
2582
2608
|
id: newInstanceId,
|
|
2583
2609
|
tenantId: this.tenantId,
|
|
@@ -2600,6 +2626,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
2600
2626
|
templateSnapshot: {
|
|
2601
2627
|
escalation: template.escalation,
|
|
2602
2628
|
slaDeadlineDays: template.slaDeadlineDays,
|
|
2629
|
+
slaDeadlineHours: template.slaDeadlineHours,
|
|
2603
2630
|
allowOverride: template.allowOverride
|
|
2604
2631
|
}
|
|
2605
2632
|
};
|
|
@@ -3542,6 +3569,24 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
3542
3569
|
deadlineFrom(from, days) {
|
|
3543
3570
|
return this.calendar ? this.calendar.addBusinessDays(from, days) : new Date(from.getTime() + days * 864e5);
|
|
3544
3571
|
}
|
|
3572
|
+
/**
|
|
3573
|
+
* Resolve an hour-based deadline.
|
|
3574
|
+
*
|
|
3575
|
+
* A calendar that only knows whole days — `weekendCalendar`, say — has no
|
|
3576
|
+
* `addBusinessHours`, and this falls back to elapsed clock time rather than
|
|
3577
|
+
* quietly pretending the calendar was applied. Configure
|
|
3578
|
+
* `businessHoursCalendar` to have hours skip evenings and weekends.
|
|
3579
|
+
*/
|
|
3580
|
+
deadlineFromHours(from, hours) {
|
|
3581
|
+
const addHours = this.calendar?.addBusinessHours?.bind(this.calendar);
|
|
3582
|
+
return addHours ? addHours(from, hours) : new Date(from.getTime() + hours * 36e5);
|
|
3583
|
+
}
|
|
3584
|
+
/** Level deadline from whichever of days/hours the template configured. */
|
|
3585
|
+
levelEscalationDue(from, level) {
|
|
3586
|
+
if (level.escalationAfterHours) return this.deadlineFromHours(from, level.escalationAfterHours);
|
|
3587
|
+
if (level.escalationAfterDays) return this.deadlineFrom(from, level.escalationAfterDays);
|
|
3588
|
+
return void 0;
|
|
3589
|
+
}
|
|
3545
3590
|
async requireInstance(id) {
|
|
3546
3591
|
const instance = await this.opts.adapter.getInstance(this.tenantId, id);
|
|
3547
3592
|
if (!instance) throw new ApprovalNotFoundError("Instance", id);
|
|
@@ -3647,9 +3692,7 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
3647
3692
|
now
|
|
3648
3693
|
);
|
|
3649
3694
|
}
|
|
3650
|
-
|
|
3651
|
-
lvl.escalationDueAt = this.deadlineFrom(now, lvl.escalationAfterDays);
|
|
3652
|
-
}
|
|
3695
|
+
lvl.escalationDueAt = this.levelEscalationDue(now, lvl);
|
|
3653
3696
|
this.scheduleReminder(lvl, now);
|
|
3654
3697
|
lvl.status = "pending";
|
|
3655
3698
|
}
|
|
@@ -4290,6 +4333,73 @@ function weekendCalendar(options = {}) {
|
|
|
4290
4333
|
}
|
|
4291
4334
|
};
|
|
4292
4335
|
}
|
|
4336
|
+
var HOUR_MS = 36e5;
|
|
4337
|
+
function businessHoursCalendar(options = {}) {
|
|
4338
|
+
const startHour = options.workdayStartHour ?? 9;
|
|
4339
|
+
const endHour = options.workdayEndHour ?? 17;
|
|
4340
|
+
if (!(endHour > startHour)) {
|
|
4341
|
+
throw new Error(
|
|
4342
|
+
`businessHoursCalendar: workdayEndHour (${endHour}) must be greater than workdayStartHour (${startHour}).`
|
|
4343
|
+
);
|
|
4344
|
+
}
|
|
4345
|
+
const weekend = new Set(options.weekendDays ?? [0, 6]);
|
|
4346
|
+
const holidays = new Set((options.holidays ?? []).map(dayKey));
|
|
4347
|
+
const isBusinessDay = (d) => !weekend.has(d.getDay()) && !holidays.has(dayKey(d));
|
|
4348
|
+
const startOfWorkday = (d) => {
|
|
4349
|
+
const out = new Date(d.getTime());
|
|
4350
|
+
out.setHours(startHour, 0, 0, 0);
|
|
4351
|
+
return out;
|
|
4352
|
+
};
|
|
4353
|
+
const endOfWorkday = (d) => {
|
|
4354
|
+
const out = new Date(d.getTime());
|
|
4355
|
+
out.setHours(endHour, 0, 0, 0);
|
|
4356
|
+
return out;
|
|
4357
|
+
};
|
|
4358
|
+
const toWorkingMoment = (from) => {
|
|
4359
|
+
const cursor = new Date(from.getTime());
|
|
4360
|
+
for (let guard = 0; guard < 3660; guard++) {
|
|
4361
|
+
if (!isBusinessDay(cursor)) {
|
|
4362
|
+
cursor.setDate(cursor.getDate() + 1);
|
|
4363
|
+
cursor.setHours(startHour, 0, 0, 0);
|
|
4364
|
+
continue;
|
|
4365
|
+
}
|
|
4366
|
+
if (cursor.getTime() < startOfWorkday(cursor).getTime()) return startOfWorkday(cursor);
|
|
4367
|
+
if (cursor.getTime() >= endOfWorkday(cursor).getTime()) {
|
|
4368
|
+
cursor.setDate(cursor.getDate() + 1);
|
|
4369
|
+
cursor.setHours(startHour, 0, 0, 0);
|
|
4370
|
+
continue;
|
|
4371
|
+
}
|
|
4372
|
+
return cursor;
|
|
4373
|
+
}
|
|
4374
|
+
throw new Error(
|
|
4375
|
+
"businessHoursCalendar: no working day found within 10 years of the start date."
|
|
4376
|
+
);
|
|
4377
|
+
};
|
|
4378
|
+
const base = weekendCalendar(options);
|
|
4379
|
+
return {
|
|
4380
|
+
addBusinessDays: base.addBusinessDays,
|
|
4381
|
+
addBusinessHours(from, hours) {
|
|
4382
|
+
if (hours <= 0 || Number.isNaN(hours)) return new Date(from.getTime());
|
|
4383
|
+
let cursor = toWorkingMoment(from);
|
|
4384
|
+
let remainingMs = Math.round(hours * HOUR_MS);
|
|
4385
|
+
for (let guard = 0; remainingMs > 0 && guard < 3660; guard++) {
|
|
4386
|
+
const dayEnd = endOfWorkday(cursor);
|
|
4387
|
+
const availableMs = dayEnd.getTime() - cursor.getTime();
|
|
4388
|
+
if (remainingMs <= availableMs) {
|
|
4389
|
+
return new Date(cursor.getTime() + remainingMs);
|
|
4390
|
+
}
|
|
4391
|
+
remainingMs -= availableMs;
|
|
4392
|
+
const next = new Date(cursor.getTime());
|
|
4393
|
+
next.setDate(next.getDate() + 1);
|
|
4394
|
+
next.setHours(startHour, 0, 0, 0);
|
|
4395
|
+
cursor = toWorkingMoment(next);
|
|
4396
|
+
}
|
|
4397
|
+
throw new Error(
|
|
4398
|
+
`businessHoursCalendar: ${hours} working hours could not be scheduled within 10 years \u2014 check weekendDays and holidays.`
|
|
4399
|
+
);
|
|
4400
|
+
}
|
|
4401
|
+
};
|
|
4402
|
+
}
|
|
4293
4403
|
|
|
4294
4404
|
exports.ApprovalConflictError = ApprovalConflictError;
|
|
4295
4405
|
exports.ApprovalEngine = ApprovalEngine;
|
|
@@ -4301,6 +4411,7 @@ exports.ApprovalValidationError = ApprovalValidationError;
|
|
|
4301
4411
|
exports.EscalationScheduler = EscalationScheduler;
|
|
4302
4412
|
exports.MemoryAdapter = MemoryAdapter;
|
|
4303
4413
|
exports.TEMPLATE_BUNDLE_VERSION = TEMPLATE_BUNDLE_VERSION;
|
|
4414
|
+
exports.businessHoursCalendar = businessHoursCalendar;
|
|
4304
4415
|
exports.defaultIdGenerator = defaultIdGenerator;
|
|
4305
4416
|
exports.noopLogger = noopLogger;
|
|
4306
4417
|
exports.systemClock = systemClock;
|