@classytic/arc-notifications 0.8.0 → 0.10.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/README.md +15 -0
- package/dist/index.d.mts +384 -61
- package/dist/index.mjs +643 -229
- package/llms.txt +29 -0
- package/package.json +8 -6
- package/skills/arc-notifications/SKILL.md +129 -0
- package/skills/arc-notifications/references/date-rules.md +55 -0
- package/skills/arc-notifications/references/delivery-log.md +33 -0
- package/skills/arc-notifications/references/feed.md +55 -0
- package/skills/arc-notifications/references/triggers-templates.md +36 -0
package/dist/index.d.mts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ArcModule, EventHandlerDefinition } from "@classytic/arc/factory";
|
|
2
|
+
import { BaseChannel, ChannelConfig, DeliveryLog, DeliveryLogEntry, DispatchResult, NotificationPayload, NotificationService, NotificationServiceConfig, QueueAdapter, Recipient, SendResult, TemplateResolver } from "@classytic/notifications";
|
|
2
3
|
import { JobDefinition, JobDispatcher } from "@classytic/arc/integrations/jobs";
|
|
3
|
-
import { ArcModule } from "@classytic/arc/factory";
|
|
4
4
|
import { PermissionCheck } from "@classytic/arc/permissions";
|
|
5
|
+
import { OffsetPaginationResult } from "@classytic/repo-core/pagination";
|
|
5
6
|
import { FastifyInstance, FastifyRequest } from "fastify";
|
|
6
7
|
import { DomainEvent } from "@classytic/primitives/events";
|
|
7
8
|
import { RepositoryLike } from "@classytic/repo-core/adapter";
|
|
8
|
-
//#region src/inApp.d.ts
|
|
9
9
|
/**
|
|
10
10
|
* In-app notification content — the shared seam between a `notifyOn` trigger's
|
|
11
11
|
* declarative `inApp` template and the channels that deliver it (the DB feed
|
|
@@ -23,7 +23,7 @@ import { RepositoryLike } from "@classytic/repo-core/adapter";
|
|
|
23
23
|
* in-app convention; the email template layer keeps its own `${var}` engine.
|
|
24
24
|
*/
|
|
25
25
|
/** Reserved `payload.data` key carrying the rendered in-app content. */
|
|
26
|
-
declare const INAPP_DATA_KEY = "__inApp";
|
|
26
|
+
export declare const INAPP_DATA_KEY = "__inApp";
|
|
27
27
|
/**
|
|
28
28
|
* Host realtime transport — push an event to one user within a tenant.
|
|
29
29
|
* The transport itself (connection registry, heartbeats, the `/stream`
|
|
@@ -59,11 +59,21 @@ interface InAppContent {
|
|
|
59
59
|
readonly priority: InAppPriority;
|
|
60
60
|
}
|
|
61
61
|
/** Render a trigger's in-app template against event data + priority. */
|
|
62
|
-
declare function renderInApp(template: InAppTemplate, vars: Record<string, unknown>, priority?: InAppPriority): InAppContent;
|
|
62
|
+
export declare function renderInApp(template: InAppTemplate, vars: Record<string, unknown>, priority?: InAppPriority): InAppContent;
|
|
63
63
|
/** Read the rendered in-app content a trigger parked on the send payload's data. */
|
|
64
|
-
declare function readInAppContent(data: Record<string, unknown> | undefined): InAppContent | null;
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
export declare function readInAppContent(data: Record<string, unknown> | undefined): InAppContent | null;
|
|
65
|
+
/**
|
|
66
|
+
* The notification clock — one seam so every "now" the in-app feed, the reconnect backfill
|
|
67
|
+
* and the delivery log stamp goes through a single injectable source.
|
|
68
|
+
*
|
|
69
|
+
* Structural on purpose: a host's own `Clock` (`{ now(): Date }`) satisfies it with no
|
|
70
|
+
* adapter, and a test passes a frozen or stepped one to make unread-cache expiry, ordering
|
|
71
|
+
* and `timestamp` stamps deterministic. The default is real time.
|
|
72
|
+
*/
|
|
73
|
+
interface NotificationClock {
|
|
74
|
+
now(): Date;
|
|
75
|
+
}
|
|
76
|
+
export declare const systemNotificationClock: NotificationClock;
|
|
67
77
|
/** A feed row the {@link InAppChannel} writes and this resource reads. */
|
|
68
78
|
interface InAppFeedRow {
|
|
69
79
|
readonly organizationId: string;
|
|
@@ -124,6 +134,18 @@ interface InAppFeedRepository<TRow = unknown> {
|
|
|
124
134
|
* the debounce window) from double-toasting the same rows.
|
|
125
135
|
*/
|
|
126
136
|
claimUndelivered?(organizationId: string, userId: string, limit: number): Promise<TRow[]>;
|
|
137
|
+
/**
|
|
138
|
+
* OPTIONAL — rows created AFTER `since`, oldest first, WITHOUT claiming them. Powers the
|
|
139
|
+
* cursor flavour of reconnect backfill (`onConnect(user, org, { since })`): a client that
|
|
140
|
+
* remembers the time of its last event (SSE `Last-Event-ID`) replays exactly the gap,
|
|
141
|
+
* including rows an earlier connection already received but dropped before rendering.
|
|
142
|
+
*/
|
|
143
|
+
listSince?(organizationId: string, userId: string, since: Date, limit: number): Promise<TRow[]>;
|
|
144
|
+
/**
|
|
145
|
+
* OPTIONAL — stamp one row delivered (a live push reached a client), so the next
|
|
146
|
+
* reconnect does not re-send it. `false` when not found / not owned.
|
|
147
|
+
*/
|
|
148
|
+
markDelivered?(organizationId: string, userId: string, id: string): Promise<boolean>;
|
|
127
149
|
/**
|
|
128
150
|
* OPTIONAL — delete one row (user dismissed it for good); `false` when not
|
|
129
151
|
* found / not owned. When implemented, the feed resource registers
|
|
@@ -168,13 +190,15 @@ interface CreateUnreadCountCacheOptions {
|
|
|
168
190
|
readonly ttlMs?: number;
|
|
169
191
|
/** Entry cap — oldest entries evicted past it (bounds memory). Default 10_000. */
|
|
170
192
|
readonly maxEntries?: number;
|
|
193
|
+
/** Source of "now" for entry freshness. Default real time. */
|
|
194
|
+
readonly clock?: NotificationClock;
|
|
171
195
|
}
|
|
172
196
|
/**
|
|
173
197
|
* In-process TTL cache for unread counts. Per-process: invalidations don't
|
|
174
198
|
* cross workers, so a multi-process host either accepts up-to-TTL staleness on
|
|
175
199
|
* the other workers or brings a Redis-backed {@link UnreadCountCache}.
|
|
176
200
|
*/
|
|
177
|
-
declare function createUnreadCountCache(options?: CreateUnreadCountCacheOptions): UnreadCountCache;
|
|
201
|
+
export declare function createUnreadCountCache(options?: CreateUnreadCountCacheOptions): UnreadCountCache;
|
|
178
202
|
/** The current user + tenant, resolved off a request by the host. */
|
|
179
203
|
interface InAppFeedScope {
|
|
180
204
|
readonly userId: string;
|
|
@@ -206,10 +230,31 @@ interface InAppFeedResourceOptions {
|
|
|
206
230
|
* instance with `InAppChannel` so new-row writes invalidate too.
|
|
207
231
|
*/
|
|
208
232
|
readonly unreadCache?: UnreadCountCache;
|
|
233
|
+
/**
|
|
234
|
+
* Shape of `GET <prefix>/`. `"native"` (default) answers the port's `{ data, pagination }`
|
|
235
|
+
* page untouched; `"offset"` answers repo-core's canonical `OffsetPaginationResult` (see
|
|
236
|
+
* {@link feedListResult}) for a host whose every other list is that shape.
|
|
237
|
+
*/
|
|
238
|
+
readonly listEnvelope?: "native" | "offset";
|
|
239
|
+
/**
|
|
240
|
+
* Also mount `markRead` (`POST /:id/action`) and `markAllRead` (`POST /action`) as arc
|
|
241
|
+
* action-router verbs ({@link FEED_ACTION_VERBS}, camelCase), beside the `PATCH` routes.
|
|
242
|
+
* Same handlers, same cache invalidation and cross-tab read-sync. Default false.
|
|
243
|
+
*/
|
|
244
|
+
readonly actions?: boolean;
|
|
209
245
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
246
|
+
/** The action-router verbs {@link InAppFeedResourceOptions.actions} mounts. */
|
|
247
|
+
export declare const FEED_ACTION_VERBS: {
|
|
248
|
+
readonly markRead: "markRead";
|
|
249
|
+
readonly markAllRead: "markAllRead";
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* A feed page in repo-core's canonical offset envelope. Pure and exported so a host that
|
|
253
|
+
* builds its own list route (or asserts the envelope in a test) converts the same way the
|
|
254
|
+
* resource does. `hasNext`/`hasPrev` follow from the page arithmetic the port already did.
|
|
255
|
+
*/
|
|
256
|
+
export declare function feedListResult<TRow>(page: InAppFeedPage<TRow>): OffsetPaginationResult<TRow>;
|
|
257
|
+
export declare function inAppFeedResource(options: InAppFeedResourceOptions): import("@classytic/arc").ResourceDefinition<import("@classytic/arc").AnyRecord>;
|
|
213
258
|
interface InAppBackfillOptions {
|
|
214
259
|
/** Feed repository — MUST implement the optional `claimUndelivered`. */
|
|
215
260
|
readonly repository: InAppFeedRepository;
|
|
@@ -224,15 +269,18 @@ interface InAppBackfillOptions {
|
|
|
224
269
|
/** Claim/push failure sink (backfill is best-effort). Default: swallow. */
|
|
225
270
|
readonly onError?: (error: unknown) => void;
|
|
226
271
|
}
|
|
272
|
+
/** Per-connection hints for {@link InAppBackfill.onConnect}. */
|
|
273
|
+
interface InAppBackfillConnect {
|
|
274
|
+
/** Replay rows created after this instant (a `Last-Event-ID` / `since` cursor). */
|
|
275
|
+
readonly since?: Date | string | number | undefined;
|
|
276
|
+
}
|
|
227
277
|
interface InAppBackfill {
|
|
228
278
|
/** Call from the host's SSE/WS connection handler. Sync, never throws. */
|
|
229
|
-
onConnect(userId: string, organizationId: string): void;
|
|
279
|
+
onConnect(userId: string, organizationId: string, connect?: InAppBackfillConnect): void;
|
|
230
280
|
/** Cancel pending debounces (graceful shutdown / tests). */
|
|
231
281
|
close(): void;
|
|
232
282
|
}
|
|
233
|
-
declare function createInAppBackfill(options: InAppBackfillOptions): InAppBackfill;
|
|
234
|
-
//#endregion
|
|
235
|
-
//#region src/triggerOverride.d.ts
|
|
283
|
+
export declare function createInAppBackfill(options: InAppBackfillOptions): InAppBackfill;
|
|
236
284
|
/**
|
|
237
285
|
* Notification trigger OVERRIDES — the admin-editable half of the registry.
|
|
238
286
|
*
|
|
@@ -299,7 +347,7 @@ declare function createInAppBackfill(options: InAppBackfillOptions): InAppBackfi
|
|
|
299
347
|
* which a host fills from its integrations registry. A channel nobody registered
|
|
300
348
|
* is simply not offered, instead of being unrepresentable.
|
|
301
349
|
*/
|
|
302
|
-
declare const IN_APP_CHANNEL = "in-app";
|
|
350
|
+
export declare const IN_APP_CHANNEL = "in-app";
|
|
303
351
|
/**
|
|
304
352
|
* Open by design — see above. Kept as a named alias rather than inlining
|
|
305
353
|
* `string` so the intent reads at every call site and a future narrowing has one
|
|
@@ -339,6 +387,18 @@ interface NotificationTriggerOverrideDoc {
|
|
|
339
387
|
* answer is a host-supplied predicate, never an expression DSL.
|
|
340
388
|
*/
|
|
341
389
|
sourceChannels?: string[] | null;
|
|
390
|
+
/**
|
|
391
|
+
* Literal addresses to notify IN ADDITION to the role recipients.
|
|
392
|
+
*
|
|
393
|
+
* `recipients` holds ROLES, which only reaches people who hold an account.
|
|
394
|
+
* The person who wants order mail — an owner, an accountant, a shared
|
|
395
|
+
* ops@ inbox — often has no dashboard login at all, so a role list cannot
|
|
396
|
+
* express them.
|
|
397
|
+
*
|
|
398
|
+
* ADDITIVE, never a replacement: adding an address must not silence the staff
|
|
399
|
+
* feed, which is what substituting for `recipients` would do.
|
|
400
|
+
*/
|
|
401
|
+
recipientEmails?: string[] | null;
|
|
342
402
|
/** Copy overrides. Unset fields fall back to the code template. */
|
|
343
403
|
title?: string | null;
|
|
344
404
|
message?: string | null;
|
|
@@ -378,7 +438,7 @@ interface CreateCachedTriggerOverrideStoreOptions {
|
|
|
378
438
|
* any mutation so an operator's change applies within one save rather than one
|
|
379
439
|
* TTL window.
|
|
380
440
|
*/
|
|
381
|
-
declare function createCachedTriggerOverrideStore(load: TriggerOverrideLoader, options?: CreateCachedTriggerOverrideStoreOptions): TriggerOverrideStore & {
|
|
441
|
+
export declare function createCachedTriggerOverrideStore(load: TriggerOverrideLoader, options?: CreateCachedTriggerOverrideStoreOptions): TriggerOverrideStore & {
|
|
382
442
|
invalidate: () => void;
|
|
383
443
|
};
|
|
384
444
|
/**
|
|
@@ -395,6 +455,8 @@ interface EffectiveTriggerSettings {
|
|
|
395
455
|
channels: NotificationChannel[];
|
|
396
456
|
/** `null` = unrestricted. See {@link NotificationTriggerOverrideDoc.sourceChannels}. */
|
|
397
457
|
sourceChannels: string[] | null;
|
|
458
|
+
/** Extra literal addresses, ADDED to the role recipients. `[]` when none. */
|
|
459
|
+
recipientEmails: string[];
|
|
398
460
|
title: string;
|
|
399
461
|
message: string;
|
|
400
462
|
priority?: "low" | "normal" | "high" | undefined;
|
|
@@ -405,8 +467,23 @@ interface CodeTriggerDefaults {
|
|
|
405
467
|
title: string;
|
|
406
468
|
message: string;
|
|
407
469
|
priority?: "low" | "normal" | "high" | undefined;
|
|
470
|
+
/**
|
|
471
|
+
* The trigger's OWN origin restriction — `null`/absent means unrestricted.
|
|
472
|
+
*
|
|
473
|
+
* Every other field here is a code default an operator's row REPLACES. This
|
|
474
|
+
* one was the exception: the merge below hardcoded `?? null`, so a deployment
|
|
475
|
+
* could not state "a new-order alert is for web orders, not for the till" and
|
|
476
|
+
* every deployment shipped unscoped until someone hand-configured the UI.
|
|
477
|
+
*
|
|
478
|
+
* That matters most where it is least visible. A branch runs several
|
|
479
|
+
* registers, and unscoped means every counter sale fans out to every staff
|
|
480
|
+
* role on every one of them — resolved, written to the feed, and attempted on
|
|
481
|
+
* each connector, all before the response. The cashier who rang the sale is
|
|
482
|
+
* already looking at it.
|
|
483
|
+
*/
|
|
484
|
+
sourceChannels?: readonly string[] | undefined;
|
|
408
485
|
}
|
|
409
|
-
declare function resolveEffectiveSettings(triggerType: string, organizationId: string | null, rows: readonly NotificationTriggerOverrideDoc[], defaults: CodeTriggerDefaults): EffectiveTriggerSettings;
|
|
486
|
+
export declare function resolveEffectiveSettings(triggerType: string, organizationId: string | null, rows: readonly NotificationTriggerOverrideDoc[], defaults: CodeTriggerDefaults): EffectiveTriggerSettings;
|
|
410
487
|
/**
|
|
411
488
|
* Why a notification was not delivered.
|
|
412
489
|
*
|
|
@@ -447,9 +524,7 @@ type NotificationDecision = {
|
|
|
447
524
|
* is ever consulted, so an event with no `sourceChannel` under no restriction
|
|
448
525
|
* still delivers.
|
|
449
526
|
*/
|
|
450
|
-
declare function evaluateSourceChannel(sourceChannels: string[] | null | undefined, sourceChannel: string | undefined): NotificationDecision;
|
|
451
|
-
//#endregion
|
|
452
|
-
//#region src/channelCatalog.d.ts
|
|
527
|
+
export declare function evaluateSourceChannel(sourceChannels: string[] | null | undefined, sourceChannel: string | undefined): NotificationDecision;
|
|
453
528
|
/**
|
|
454
529
|
* Which channels a deployment can deliver on.
|
|
455
530
|
*
|
|
@@ -480,9 +555,206 @@ interface NotificationChannelCatalog {
|
|
|
480
555
|
* than stored, so a misconfigured provider cannot introduce an unusable ""
|
|
481
556
|
* channel that validation would then accept.
|
|
482
557
|
*/
|
|
483
|
-
declare function createNotificationChannelCatalog(connectorChannels?: readonly string[]): NotificationChannelCatalog;
|
|
484
|
-
|
|
485
|
-
|
|
558
|
+
export declare function createNotificationChannelCatalog(connectorChannels?: readonly string[]): NotificationChannelCatalog;
|
|
559
|
+
/** `YYYY-MM-DD`. */
|
|
560
|
+
type CivilDateString = string;
|
|
561
|
+
/** True for a real calendar date in `YYYY-MM-DD` form. */
|
|
562
|
+
export declare function isCivilDateString(value: unknown): value is CivilDateString;
|
|
563
|
+
/** `date` moved by `days` (negative = earlier). */
|
|
564
|
+
export declare function addCivilDays(date: CivilDateString, days: number): CivilDateString;
|
|
565
|
+
/** Whole days from `a` to `b` (`b - a`). */
|
|
566
|
+
export declare function civilDaysBetween(a: CivilDateString, b: CivilDateString): number;
|
|
567
|
+
/** When a rule fires, relative to the item's date. */
|
|
568
|
+
interface DateRuleSchedule {
|
|
569
|
+
/**
|
|
570
|
+
* Days from the date to the FIRST occurrence: negative = before (`-30` = 30 days
|
|
571
|
+
* before), `0` = on the day, positive = after (`1` = the day after: "overdue").
|
|
572
|
+
*/
|
|
573
|
+
readonly offsetDays: number;
|
|
574
|
+
/** Fire again every N days after the first occurrence while the item stays open. Omit = once. */
|
|
575
|
+
readonly repeatEveryDays?: number;
|
|
576
|
+
/** Bounds on repetition. Without it a repeating rule runs until the item is no longer open. */
|
|
577
|
+
readonly until?: {
|
|
578
|
+
/** Total occurrences, the first included. */
|
|
579
|
+
readonly occurrences?: number;
|
|
580
|
+
/** No occurrence after `date + offsetDays` (relative to the item's date, like `offsetDays`). */
|
|
581
|
+
readonly offsetDays?: number;
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* One admin-defined date rule. `TAudience` is the host's audience selector — this
|
|
586
|
+
* package never interprets it; it is handed to the host's `resolveAudience`.
|
|
587
|
+
*/
|
|
588
|
+
interface DateRule<TAudience = unknown> {
|
|
589
|
+
/** Stable id: part of every ledger key, so it must not change for the life of the rule. */
|
|
590
|
+
readonly id: string;
|
|
591
|
+
/** Default true. */
|
|
592
|
+
readonly enabled?: boolean;
|
|
593
|
+
/** A registered {@link DateRuleSource} key. */
|
|
594
|
+
readonly source: string;
|
|
595
|
+
/** One of that source's `dateFields`. */
|
|
596
|
+
readonly dateField: string;
|
|
597
|
+
readonly schedule: DateRuleSchedule;
|
|
598
|
+
readonly audience: TAudience;
|
|
599
|
+
/** Restrict to these channel names. Omit = every channel that applies. */
|
|
600
|
+
readonly channels?: readonly string[];
|
|
601
|
+
/**
|
|
602
|
+
* Template id / notification kind (the feed row's type). Default
|
|
603
|
+
* `<source>.<dateField>`. With a template resolver configured on the service it
|
|
604
|
+
* also names the email template.
|
|
605
|
+
*/
|
|
606
|
+
readonly template?: string;
|
|
607
|
+
/** In-app copy with `{var}` placeholders resolved against the item's `data`. */
|
|
608
|
+
readonly inApp?: InAppTemplate;
|
|
609
|
+
readonly priority?: InAppPriority;
|
|
610
|
+
/** Scope passed to the source; `null`/absent = every organization the source serves. */
|
|
611
|
+
readonly organizationId?: string | null;
|
|
612
|
+
}
|
|
613
|
+
/** One record a source reports, with the date the rule's `dateField` names. */
|
|
614
|
+
interface DateRuleItem {
|
|
615
|
+
/** Stable record id (part of the ledger key). */
|
|
616
|
+
readonly id: string;
|
|
617
|
+
/** The `dateField` value, `YYYY-MM-DD`. */
|
|
618
|
+
readonly date: CivilDateString;
|
|
619
|
+
/** Template variables (and anything the host's audience resolver needs). */
|
|
620
|
+
readonly data: Record<string, unknown>;
|
|
621
|
+
/** The business document, for the delivery log's per-document history. */
|
|
622
|
+
readonly subject?: {
|
|
623
|
+
readonly sourceModel: string;
|
|
624
|
+
readonly sourceId: string;
|
|
625
|
+
};
|
|
626
|
+
readonly organizationId?: string;
|
|
627
|
+
}
|
|
628
|
+
interface DateRuleSourceQuery {
|
|
629
|
+
readonly dateField: string;
|
|
630
|
+
/** Inclusive lower bound; absent = unbounded (an open-ended repeating rule). */
|
|
631
|
+
readonly from?: CivilDateString;
|
|
632
|
+
/** Inclusive upper bound. */
|
|
633
|
+
readonly to: CivilDateString;
|
|
634
|
+
readonly organizationId?: string | null;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* A host record type that carries dates. Registered by the host (one per record
|
|
638
|
+
* type); the admin screen lists them through {@link DateRuleEngine.catalog}.
|
|
639
|
+
*/
|
|
640
|
+
interface DateRuleSource {
|
|
641
|
+
readonly key: string;
|
|
642
|
+
readonly label?: string;
|
|
643
|
+
readonly dateFields: ReadonlyArray<{
|
|
644
|
+
readonly key: string;
|
|
645
|
+
readonly label?: string;
|
|
646
|
+
}>;
|
|
647
|
+
/** Template variables items carry, as a hint for rule authors (`{employeeName}`). */
|
|
648
|
+
readonly variables?: readonly string[];
|
|
649
|
+
/**
|
|
650
|
+
* OPEN items whose `dateField` lies in `[from, to]`. "Open" is the source's call:
|
|
651
|
+
* an item that no longer needs reminding must not be returned.
|
|
652
|
+
*/
|
|
653
|
+
find(query: DateRuleSourceQuery): Promise<readonly DateRuleItem[]> | AsyncIterable<DateRuleItem>;
|
|
654
|
+
}
|
|
655
|
+
type DateRuleAudienceResolver<TAudience = unknown> = (audience: TAudience, context: {
|
|
656
|
+
readonly rule: DateRule<TAudience>;
|
|
657
|
+
readonly item: DateRuleItem;
|
|
658
|
+
readonly occurrence: DateRuleOccurrence;
|
|
659
|
+
}) => readonly Recipient[] | null | undefined | Promise<readonly Recipient[] | null | undefined>;
|
|
660
|
+
/**
|
|
661
|
+
* "Has this occurrence fired?" — claimed BEFORE dispatch, released on failure so the
|
|
662
|
+
* next sweep retries. Back it with a unique index in production; the memory ledger
|
|
663
|
+
* is for tests and single-process dev.
|
|
664
|
+
*/
|
|
665
|
+
interface DateRuleLedger {
|
|
666
|
+
/** `true` when this call claimed the key (first time), `false` when already claimed. */
|
|
667
|
+
claim(key: string, info: DateRuleLedgerEntry): Promise<boolean>;
|
|
668
|
+
/** Undo a claim whose dispatch failed. */
|
|
669
|
+
release(key: string): Promise<void>;
|
|
670
|
+
}
|
|
671
|
+
interface DateRuleLedgerEntry {
|
|
672
|
+
readonly ruleId: string;
|
|
673
|
+
readonly itemId: string;
|
|
674
|
+
readonly occurrenceOn: CivilDateString;
|
|
675
|
+
readonly index: number;
|
|
676
|
+
readonly sweptOn: CivilDateString;
|
|
677
|
+
}
|
|
678
|
+
export declare function createMemoryDateRuleLedger(): DateRuleLedger & {
|
|
679
|
+
keys(): string[];
|
|
680
|
+
};
|
|
681
|
+
interface DateRuleOccurrence {
|
|
682
|
+
/** 0-based occurrence index. */
|
|
683
|
+
readonly index: number;
|
|
684
|
+
/** The civil date this occurrence is scheduled for. */
|
|
685
|
+
readonly on: CivilDateString;
|
|
686
|
+
}
|
|
687
|
+
/** Problems with a schedule, as messages; empty = valid. */
|
|
688
|
+
export declare function validateDateRuleSchedule(schedule: DateRuleSchedule): string[];
|
|
689
|
+
/**
|
|
690
|
+
* The occurrence of `schedule` for an item dated `date` that a sweep on `today`
|
|
691
|
+
* should send, or `null`. The latest occurrence on or before `today` is due when it
|
|
692
|
+
* is at most `graceDays` old — so a missed sweep catches up, but a rule created
|
|
693
|
+
* today does not replay years of history.
|
|
694
|
+
*/
|
|
695
|
+
export declare function dueOccurrence(schedule: DateRuleSchedule, date: CivilDateString, today: CivilDateString, graceDays?: number): DateRuleOccurrence | null;
|
|
696
|
+
/** The `[from, to]` range of item dates a sweep on `today` must ask a source for. */
|
|
697
|
+
export declare function dateRuleWindow(schedule: DateRuleSchedule, today: CivilDateString, graceDays?: number): {
|
|
698
|
+
from?: CivilDateString;
|
|
699
|
+
to: CivilDateString;
|
|
700
|
+
};
|
|
701
|
+
interface DateRuleEngineOptions<TAudience = unknown> {
|
|
702
|
+
readonly sources: Iterable<DateRuleSource>;
|
|
703
|
+
/** The rules to evaluate (admin-created rows); disabled rows are skipped. */
|
|
704
|
+
readonly rules: () => readonly DateRule<TAudience>[] | Promise<readonly DateRule<TAudience>[]>;
|
|
705
|
+
readonly resolveAudience: DateRuleAudienceResolver<TAudience>;
|
|
706
|
+
/** The notification service (read per sweep, so a module export can back it). */
|
|
707
|
+
readonly service: () => NotificationService;
|
|
708
|
+
/** Default: an in-memory ledger (single process, lost on restart). */
|
|
709
|
+
readonly ledger?: DateRuleLedger;
|
|
710
|
+
/** How many days late an occurrence may still be sent (missed sweeps). Default 0. */
|
|
711
|
+
readonly graceDays?: number;
|
|
712
|
+
/** Extra send data per item (e.g. email `subject`/`text` rendered from the rule's copy). */
|
|
713
|
+
readonly data?: (rule: DateRule<TAudience>, item: DateRuleItem) => Record<string, unknown>;
|
|
714
|
+
/** A rule or item failed; the sweep continues. */
|
|
715
|
+
readonly onError?: (err: unknown, context: {
|
|
716
|
+
readonly ruleId: string;
|
|
717
|
+
readonly itemId?: string;
|
|
718
|
+
}) => void;
|
|
719
|
+
}
|
|
720
|
+
interface DateRuleSweepResult {
|
|
721
|
+
readonly date: CivilDateString;
|
|
722
|
+
/** Enabled rules evaluated. */
|
|
723
|
+
readonly rules: number;
|
|
724
|
+
/** Items whose occurrence was due. */
|
|
725
|
+
readonly due: number;
|
|
726
|
+
/** Occurrences dispatched by this sweep. */
|
|
727
|
+
readonly fired: number;
|
|
728
|
+
/** Occurrences already claimed (an earlier sweep, another replica). */
|
|
729
|
+
readonly duplicates: number;
|
|
730
|
+
/** Occurrences with nobody to tell. */
|
|
731
|
+
readonly noRecipients: number;
|
|
732
|
+
readonly failed: number;
|
|
733
|
+
}
|
|
734
|
+
interface DateRuleCatalogEntry {
|
|
735
|
+
readonly key: string;
|
|
736
|
+
readonly label: string;
|
|
737
|
+
readonly dateFields: ReadonlyArray<{
|
|
738
|
+
readonly key: string;
|
|
739
|
+
readonly label: string;
|
|
740
|
+
}>;
|
|
741
|
+
readonly variables: readonly string[];
|
|
742
|
+
}
|
|
743
|
+
interface DateRuleEngine<TAudience = unknown> {
|
|
744
|
+
/** Evaluate every enabled rule for the civil date `today`. */
|
|
745
|
+
sweep(today: CivilDateString): Promise<DateRuleSweepResult>;
|
|
746
|
+
/** Items a rule would notify on `today` (no claims, no sends): the admin "preview". */
|
|
747
|
+
preview(rule: DateRule<TAudience>, today: CivilDateString): Promise<Array<DateRuleItem & {
|
|
748
|
+
occurrence: DateRuleOccurrence;
|
|
749
|
+
}>>;
|
|
750
|
+
/** Registered sources for the rule editor. */
|
|
751
|
+
catalog(): DateRuleCatalogEntry[];
|
|
752
|
+
/** Problems with a rule against the registered sources, as messages; empty = valid. */
|
|
753
|
+
validate(rule: Pick<DateRule<TAudience>, "source" | "dateField" | "schedule">): string[];
|
|
754
|
+
}
|
|
755
|
+
/** Ledger key of one occurrence. */
|
|
756
|
+
export declare const dateRuleOccurrenceKey: (ruleId: string, itemId: string, on: CivilDateString) => string;
|
|
757
|
+
export declare function createDateRuleEngine<TAudience = unknown>(options: DateRuleEngineOptions<TAudience>): DateRuleEngine<TAudience>;
|
|
486
758
|
/** One draftable template row — the shape be-prod's EmailTemplate model proved. */
|
|
487
759
|
interface StoredTemplate {
|
|
488
760
|
readonly key: string;
|
|
@@ -518,9 +790,7 @@ interface DbFirstTemplateResolver {
|
|
|
518
790
|
/** Drop one key (after a draft is saved) or the whole cache. */
|
|
519
791
|
invalidate(key?: string): void;
|
|
520
792
|
}
|
|
521
|
-
declare function createDbFirstResolver(options: DbFirstResolverOptions): DbFirstTemplateResolver;
|
|
522
|
-
//#endregion
|
|
523
|
-
//#region src/inAppChannel.d.ts
|
|
793
|
+
export declare function createDbFirstResolver(options: DbFirstResolverOptions): DbFirstTemplateResolver;
|
|
524
794
|
interface InAppChannelConfig extends ChannelConfig {
|
|
525
795
|
/** Feed store the channel writes rows to (host-owned persistence). */
|
|
526
796
|
readonly repository: InAppFeedRepository;
|
|
@@ -542,7 +812,7 @@ interface InAppChannelConfig extends ChannelConfig {
|
|
|
542
812
|
*/
|
|
543
813
|
readonly unreadCache?: UnreadCountCache;
|
|
544
814
|
}
|
|
545
|
-
declare class InAppChannel extends BaseChannel<InAppChannelConfig> {
|
|
815
|
+
export declare class InAppChannel extends BaseChannel<InAppChannelConfig> {
|
|
546
816
|
private readonly repository;
|
|
547
817
|
private readonly port;
|
|
548
818
|
private readonly eventName;
|
|
@@ -553,8 +823,6 @@ declare class InAppChannel extends BaseChannel<InAppChannelConfig> {
|
|
|
553
823
|
send(payload: NotificationPayload): Promise<SendResult>;
|
|
554
824
|
private orgOf;
|
|
555
825
|
}
|
|
556
|
-
//#endregion
|
|
557
|
-
//#region src/jobsQueue.d.ts
|
|
558
826
|
interface NotificationsJobOptions {
|
|
559
827
|
/** Job/queue name registered with `jobsPlugin`. Default "notifications". */
|
|
560
828
|
readonly name?: string;
|
|
@@ -590,9 +858,7 @@ interface NotificationsJob {
|
|
|
590
858
|
* `enqueue`/`getJob`/`size` through `fastify.jobs`, keying `enqueue` on
|
|
591
859
|
* `idempotencyKey` so crash-recovery / at-least-once redelivery dedupes.
|
|
592
860
|
*/
|
|
593
|
-
declare function notificationsJob(options?: NotificationsJobOptions): NotificationsJob;
|
|
594
|
-
//#endregion
|
|
595
|
-
//#region src/module.d.ts
|
|
861
|
+
export declare function notificationsJob(options?: NotificationsJobOptions): NotificationsJob;
|
|
596
862
|
/**
|
|
597
863
|
* Resolve a trigger's host-typed `audience` selector to concrete recipients —
|
|
598
864
|
* the shared seam so branch-role → members (or team → users, tenant → admins)
|
|
@@ -699,13 +965,23 @@ declare module "fastify" {
|
|
|
699
965
|
notifications?: NotificationService;
|
|
700
966
|
}
|
|
701
967
|
}
|
|
702
|
-
declare function notificationsModule(config: NotificationsModuleConfig): ArcModule<NotificationService>;
|
|
968
|
+
export declare function notificationsModule(config: NotificationsModuleConfig): ArcModule<NotificationService>;
|
|
969
|
+
/**
|
|
970
|
+
* Triggers as arc `eventHandlers` — what `notifyOn` subscribes, exported so a BRIDGE module can own
|
|
971
|
+
* the triggers for one producer (`dependsOn: [producer, "notifications"]`) and vanish with it.
|
|
972
|
+
* `service` is read per dispatch, so a bridge can resolve it from the notifications module's export.
|
|
973
|
+
*/
|
|
974
|
+
export declare function notifyTriggerHandlers(options: {
|
|
975
|
+
/** Handler-name prefix; arc rejects duplicate handler names across the module graph. */
|
|
976
|
+
readonly name: string;
|
|
977
|
+
readonly service: () => NotificationService;
|
|
978
|
+
readonly triggers: readonly NotifyTrigger[];
|
|
979
|
+
readonly recipientResolver?: RecipientResolver;
|
|
980
|
+
}): EventHandlerDefinition[];
|
|
703
981
|
/** One event → N sends. Exported for direct unit testing. */
|
|
704
|
-
declare function dispatchTrigger(service: NotificationService, trigger: NotifyTrigger, event: DomainEvent, opts?: {
|
|
982
|
+
export declare function dispatchTrigger(service: NotificationService, trigger: NotifyTrigger, event: DomainEvent, opts?: {
|
|
705
983
|
recipientResolver?: RecipientResolver;
|
|
706
984
|
}): Promise<void>;
|
|
707
|
-
//#endregion
|
|
708
|
-
//#region src/repositoryDeliveryLog.d.ts
|
|
709
985
|
type EntryStatus = DeliveryLogEntry["status"];
|
|
710
986
|
/** The persisted document shape (also what the resource/UI reads). */
|
|
711
987
|
interface DeliveryLogDoc {
|
|
@@ -719,6 +995,11 @@ interface DeliveryLogDoc {
|
|
|
719
995
|
readonly status: EntryStatus;
|
|
720
996
|
readonly duration: number;
|
|
721
997
|
readonly metadata?: Record<string, unknown>;
|
|
998
|
+
/**
|
|
999
|
+
* The organization the delivery belongs to — top-level so a store can index it and a
|
|
1000
|
+
* reader can scope on it. `null` = company-wide (no organization named).
|
|
1001
|
+
*/
|
|
1002
|
+
readonly organizationId?: string | null;
|
|
722
1003
|
/**
|
|
723
1004
|
* The business document this delivery concerned. A STORE MUST INDEX IT —
|
|
724
1005
|
* this field exists precisely so "every message about invoice X" is a keyed
|
|
@@ -728,21 +1009,54 @@ interface DeliveryLogDoc {
|
|
|
728
1009
|
readonly sourceModel: string;
|
|
729
1010
|
readonly sourceId: string;
|
|
730
1011
|
};
|
|
1012
|
+
/**
|
|
1013
|
+
* The authored rule (an admin-defined trigger) that produced this delivery, when there
|
|
1014
|
+
* is one. A top-level column — not a `metadata` key — so a store can index it and
|
|
1015
|
+
* {@link RepositoryDeliveryLog.listByRule} is a keyed read.
|
|
1016
|
+
*/
|
|
1017
|
+
readonly ruleId?: string;
|
|
1018
|
+
/** Host-typed columns stored verbatim beside the package's own. Never read by the package. */
|
|
1019
|
+
readonly extraFields?: Record<string, unknown>;
|
|
731
1020
|
}
|
|
732
|
-
interface RepositoryDeliveryLogOptions {
|
|
1021
|
+
interface RepositoryDeliveryLogOptions<TExtra extends Record<string, unknown> = Record<string, unknown>> {
|
|
733
1022
|
/** Cap for `query()` when the caller gives no limit. Default 100. */
|
|
734
1023
|
readonly defaultLimit?: number;
|
|
1024
|
+
/** Source of the entry `timestamp`. Default real time. */
|
|
1025
|
+
readonly clock?: NotificationClock;
|
|
1026
|
+
/**
|
|
1027
|
+
* Which authored rule a send belongs to. Default: `payload.metadata.ruleId` when it is a
|
|
1028
|
+
* non-empty string. A host whose rule id rides elsewhere (an event name such as
|
|
1029
|
+
* `notify.rule.<id>`) supplies its own resolver.
|
|
1030
|
+
*/
|
|
1031
|
+
readonly ruleId?: (payload: NotificationPayload) => string | undefined;
|
|
1032
|
+
/** Host-typed columns to persist on the entry (`DeliveryLogDoc.extraFields`). */
|
|
1033
|
+
readonly extraFields?: (payload: NotificationPayload, dispatch: DispatchResult) => TExtra | undefined;
|
|
1034
|
+
}
|
|
1035
|
+
/** A delivery entry with the rule and host columns the plain `DeliveryLogEntry` has no slot for. */
|
|
1036
|
+
type RuleDeliveryLogEntry<TExtra extends Record<string, unknown> = Record<string, unknown>> = DeliveryLogEntry & {
|
|
1037
|
+
readonly ruleId?: string;
|
|
1038
|
+
readonly extraFields?: TExtra;
|
|
1039
|
+
};
|
|
1040
|
+
interface DeliveryLogByRuleQuery {
|
|
1041
|
+
/** Cap on rows returned. Falls back to the log's `defaultLimit`. */
|
|
1042
|
+
readonly limit?: number;
|
|
1043
|
+
readonly status?: EntryStatus;
|
|
1044
|
+
readonly after?: Date;
|
|
1045
|
+
readonly before?: Date;
|
|
1046
|
+
}
|
|
1047
|
+
/** The kernel `DeliveryLog` plus the per-rule read. Assignable wherever a `DeliveryLog` is. */
|
|
1048
|
+
interface RepositoryDeliveryLog<TExtra extends Record<string, unknown> = Record<string, unknown>> extends DeliveryLog {
|
|
1049
|
+
/** Newest-first deliveries of ONE authored rule. */
|
|
1050
|
+
listByRule(ruleId: string, query?: DeliveryLogByRuleQuery): Promise<RuleDeliveryLogEntry<TExtra>[]>;
|
|
735
1051
|
}
|
|
736
|
-
declare function createRepositoryDeliveryLog(repository: RepositoryLike, options?: RepositoryDeliveryLogOptions):
|
|
737
|
-
//#endregion
|
|
738
|
-
//#region src/sseChannel.d.ts
|
|
1052
|
+
export declare function createRepositoryDeliveryLog<TExtra extends Record<string, unknown> = Record<string, unknown>>(repository: RepositoryLike, options?: RepositoryDeliveryLogOptions<TExtra>): RepositoryDeliveryLog<TExtra>;
|
|
739
1053
|
interface SseChannelConfig extends ChannelConfig {
|
|
740
1054
|
/** Host realtime transport (be-prod's SSE manager, a WS hub, ...). */
|
|
741
1055
|
readonly port: RealtimePushPort;
|
|
742
1056
|
/** SSE event name pushed to the client. Default "notification". */
|
|
743
1057
|
readonly eventName?: string;
|
|
744
1058
|
}
|
|
745
|
-
declare class SseChannel extends BaseChannel<SseChannelConfig> {
|
|
1059
|
+
export declare class SseChannel extends BaseChannel<SseChannelConfig> {
|
|
746
1060
|
private readonly port;
|
|
747
1061
|
private readonly eventName;
|
|
748
1062
|
constructor(config: SseChannelConfig);
|
|
@@ -750,8 +1064,6 @@ declare class SseChannel extends BaseChannel<SseChannelConfig> {
|
|
|
750
1064
|
send(payload: NotificationPayload): Promise<SendResult>;
|
|
751
1065
|
private orgOf;
|
|
752
1066
|
}
|
|
753
|
-
//#endregion
|
|
754
|
-
//#region src/templatesResource.d.ts
|
|
755
1067
|
/** Code-defined template map (same shape `createSimpleResolver` accepts). */
|
|
756
1068
|
type CodeTemplates = Record<string, {
|
|
757
1069
|
readonly subject?: string;
|
|
@@ -778,9 +1090,7 @@ interface TemplatesResourceOptions {
|
|
|
778
1090
|
/** Tenant field. Default `false` — templates are company-wide (be-prod convention). */
|
|
779
1091
|
readonly tenantField?: string | false;
|
|
780
1092
|
}
|
|
781
|
-
declare function templatesResource(options: TemplatesResourceOptions): import("@classytic/arc").ResourceDefinition<unknown>;
|
|
782
|
-
//#endregion
|
|
783
|
-
//#region src/triggerAuthoring.d.ts
|
|
1093
|
+
export declare function templatesResource(options: TemplatesResourceOptions): import("@classytic/arc").ResourceDefinition<unknown>;
|
|
784
1094
|
/** Who a trigger targets. */
|
|
785
1095
|
type NotificationAudienceType = "staff" | "customer";
|
|
786
1096
|
/** Direct contact for a customer-facing notification. */
|
|
@@ -872,6 +1182,24 @@ interface NotificationTrigger {
|
|
|
872
1182
|
* boot.
|
|
873
1183
|
*/
|
|
874
1184
|
channels?: readonly NotificationChannel[];
|
|
1185
|
+
/**
|
|
1186
|
+
* Restrict this trigger to events ORIGINATING from these sources — the
|
|
1187
|
+
* deployment's own sales channels (`web` / `pos` / `marketplace`), NOT the
|
|
1188
|
+
* delivery channels above. Two adjacent fields, two vocabularies.
|
|
1189
|
+
*
|
|
1190
|
+
* Absent = unrestricted, which is the historical behaviour and stays the
|
|
1191
|
+
* default. An operator's stored setting REPLACES this, so declaring one
|
|
1192
|
+
* narrows the out-of-the-box noise without taking the choice away.
|
|
1193
|
+
*
|
|
1194
|
+
* State it for any staff alert whose event can originate at a till: a branch
|
|
1195
|
+
* runs several registers, and unscoped means every counter sale is resolved,
|
|
1196
|
+
* written to the feed and attempted on every connector — before the response —
|
|
1197
|
+
* to tell staff about a sale the cashier is already looking at.
|
|
1198
|
+
*
|
|
1199
|
+
* Never `[]`. Empty means "match nothing", which silences the trigger while
|
|
1200
|
+
* looking like a filter; `enabled: false` is how you say that.
|
|
1201
|
+
*/
|
|
1202
|
+
sourceChannels?: readonly string[];
|
|
875
1203
|
/**
|
|
876
1204
|
* Pull dispatch variables out of the event payload.
|
|
877
1205
|
*
|
|
@@ -882,8 +1210,6 @@ interface NotificationTrigger {
|
|
|
882
1210
|
*/
|
|
883
1211
|
extract: (payload: Record<string, any>) => NotificationExtractResult | null;
|
|
884
1212
|
}
|
|
885
|
-
//#endregion
|
|
886
|
-
//#region src/triggerAdapter.d.ts
|
|
887
1213
|
/**
|
|
888
1214
|
* `entityType` → the `extract` variable holding that entity's id.
|
|
889
1215
|
*
|
|
@@ -891,7 +1217,7 @@ interface NotificationTrigger {
|
|
|
891
1217
|
* the variable it lives under is named per entity. This is the default
|
|
892
1218
|
* commerce mapping; a host with its own entity kinds passes `entityIdVars`.
|
|
893
1219
|
*/
|
|
894
|
-
declare const DEFAULT_ENTITY_ID_VARS: Record<string, string>;
|
|
1220
|
+
export declare const DEFAULT_ENTITY_ID_VARS: Record<string, string>;
|
|
895
1221
|
interface TriggerAdapterOptions {
|
|
896
1222
|
/**
|
|
897
1223
|
* Extra `entityType` → id-variable mappings, merged over the defaults.
|
|
@@ -987,14 +1313,14 @@ interface TriggerOverrideResolution {
|
|
|
987
1313
|
* calls both per dispatch, so an `extract` with side effects would run them
|
|
988
1314
|
* twice — keep extractors pure (every one in practice is a payload reshape).
|
|
989
1315
|
*/
|
|
990
|
-
declare function toNotifyTrigger(t: NotificationTrigger, options?: TriggerAdapterOptions): NotifyTrigger;
|
|
1316
|
+
export declare function toNotifyTrigger(t: NotificationTrigger, options?: TriggerAdapterOptions): NotifyTrigger;
|
|
991
1317
|
/**
|
|
992
1318
|
* Adapt a whole registry — what a host assigns to the module's `notifyOn`.
|
|
993
1319
|
*
|
|
994
1320
|
* With `options.overrides`, each trigger becomes one entry per channel, gated on
|
|
995
1321
|
* the admin's stored settings. Without it, the mapping is 1:1 and unchanged.
|
|
996
1322
|
*/
|
|
997
|
-
declare function toNotifyTriggers(triggers: readonly NotificationTrigger[], options?: TriggerAdapterOptions): NotifyTrigger[];
|
|
1323
|
+
export declare function toNotifyTriggers(triggers: readonly NotificationTrigger[], options?: TriggerAdapterOptions): NotifyTrigger[];
|
|
998
1324
|
/**
|
|
999
1325
|
* First trigger registered for an event.
|
|
1000
1326
|
*
|
|
@@ -1003,20 +1329,18 @@ declare function toNotifyTriggers(triggers: readonly NotificationTrigger[], opti
|
|
|
1003
1329
|
* returns only the first. For anything that must see all of them — a docs page,
|
|
1004
1330
|
* an admin matrix, a dispatch count — use `getTriggersByEvent`.
|
|
1005
1331
|
*/
|
|
1006
|
-
declare function getTriggerByEvent(triggers: readonly NotificationTrigger[], event: string): NotificationTrigger | undefined;
|
|
1332
|
+
export declare function getTriggerByEvent(triggers: readonly NotificationTrigger[], event: string): NotificationTrigger | undefined;
|
|
1007
1333
|
/** Every trigger registered for an event, in registration order. */
|
|
1008
|
-
declare function getTriggersByEvent(triggers: readonly NotificationTrigger[], event: string): NotificationTrigger[];
|
|
1334
|
+
export declare function getTriggersByEvent(triggers: readonly NotificationTrigger[], event: string): NotificationTrigger[];
|
|
1009
1335
|
/** Distinct event names the registry subscribes to (for docs / admin UI). */
|
|
1010
|
-
declare function getRegisteredEvents(triggers: readonly NotificationTrigger[]): string[];
|
|
1336
|
+
export declare function getRegisteredEvents(triggers: readonly NotificationTrigger[]): string[];
|
|
1011
1337
|
/**
|
|
1012
1338
|
* Notification kind → recipient roles (for the admin UI).
|
|
1013
1339
|
*
|
|
1014
1340
|
* Keyed by `type ?? event`, so two triggers sharing an event stay distinct
|
|
1015
1341
|
* rows — which is exactly why they carry different `type`s.
|
|
1016
1342
|
*/
|
|
1017
|
-
declare function getRecipientMatrix(triggers: readonly NotificationTrigger[]): Record<string, string[]>;
|
|
1018
|
-
//#endregion
|
|
1019
|
-
//#region src/triggerCatalog.d.ts
|
|
1343
|
+
export declare function getRecipientMatrix(triggers: readonly NotificationTrigger[]): Record<string, string[]>;
|
|
1020
1344
|
/**
|
|
1021
1345
|
* One trigger as an operator sees it, with the CODE defaults an override replaces.
|
|
1022
1346
|
*
|
|
@@ -1057,5 +1381,4 @@ interface TriggerCatalogEntry {
|
|
|
1057
1381
|
* exists", which is the silent-empty failure this file exists to avoid.
|
|
1058
1382
|
*/
|
|
1059
1383
|
type TriggerCatalogPort = () => ReadonlyArray<TriggerCatalogEntry> | Promise<ReadonlyArray<TriggerCatalogEntry>>;
|
|
1060
|
-
|
|
1061
|
-
export { type CodeTemplates, type CodeTriggerDefaults, type CreateCachedTriggerOverrideStoreOptions, type CreateUnreadCountCacheOptions, DEFAULT_ENTITY_ID_VARS, type DbFirstResolverOptions, type DbFirstTemplateResolver, type DeliveryLogDoc, type EffectiveTriggerSettings, INAPP_DATA_KEY, IN_APP_CHANNEL, type InAppBackfill, type InAppBackfillOptions, InAppChannel, type InAppChannelConfig, type InAppContent, type InAppFeedListOptions, type InAppFeedPage, type InAppFeedRepository, type InAppFeedResourceOptions, type InAppFeedRow, type InAppFeedScope, type InAppPriority, type InAppTemplate, type NotificationAudienceType, type NotificationChannel, type NotificationChannelCatalog, type NotificationDecision, type NotificationExtractResult, type NotificationRecipientContact, type NotificationSkipReason, type NotificationTrigger, type NotificationTriggerOverrideDoc, type NotificationsJob, type NotificationsJobOptions, type NotificationsModuleConfig, type NotifyTrigger, type RealtimePushPort, type RecipientResolver, type RepositoryDeliveryLogOptions, SseChannel, type SseChannelConfig, type StoredTemplate, type TemplateStore, type TemplatesResourceOptions, type TriggerAdapterOptions, type TriggerCatalogEntry, type TriggerCatalogPort, type TriggerOverrideLoader, type TriggerOverrideResolution, type TriggerOverrideStore, type UnreadCountCache, createCachedTriggerOverrideStore, createDbFirstResolver, createInAppBackfill, createNotificationChannelCatalog, createRepositoryDeliveryLog, createUnreadCountCache, dispatchTrigger, evaluateSourceChannel, getRecipientMatrix, getRegisteredEvents, getTriggerByEvent, getTriggersByEvent, inAppFeedResource, notificationsJob, notificationsModule, readInAppContent, renderInApp, resolveEffectiveSettings, templatesResource, toNotifyTrigger, toNotifyTriggers };
|
|
1384
|
+
export type { CivilDateString, CodeTemplates, CodeTriggerDefaults, CreateCachedTriggerOverrideStoreOptions, CreateUnreadCountCacheOptions, DateRule, DateRuleAudienceResolver, DateRuleCatalogEntry, DateRuleEngine, DateRuleEngineOptions, DateRuleItem, DateRuleLedger, DateRuleLedgerEntry, DateRuleOccurrence, DateRuleSchedule, DateRuleSource, DateRuleSourceQuery, DateRuleSweepResult, DbFirstResolverOptions, DbFirstTemplateResolver, DeliveryLogByRuleQuery, DeliveryLogDoc, EffectiveTriggerSettings, InAppBackfill, InAppBackfillConnect, InAppBackfillOptions, InAppChannelConfig, InAppContent, InAppFeedListOptions, InAppFeedPage, InAppFeedRepository, InAppFeedResourceOptions, InAppFeedRow, InAppFeedScope, InAppPriority, InAppTemplate, NotificationAudienceType, NotificationChannel, NotificationChannelCatalog, NotificationClock, NotificationDecision, NotificationExtractResult, NotificationRecipientContact, NotificationSkipReason, NotificationTrigger, NotificationTriggerOverrideDoc, NotificationsJob, NotificationsJobOptions, NotificationsModuleConfig, NotifyTrigger, RealtimePushPort, RecipientResolver, RepositoryDeliveryLog, RepositoryDeliveryLogOptions, RuleDeliveryLogEntry, SseChannelConfig, StoredTemplate, TemplateStore, TemplatesResourceOptions, TriggerAdapterOptions, TriggerCatalogEntry, TriggerCatalogPort, TriggerOverrideLoader, TriggerOverrideResolution, TriggerOverrideStore, UnreadCountCache };
|