@futdevpro/nts-dynamo 1.15.359 → 1.15.363
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/build/_modules/git-sync/_models/git-sync.interface.d.ts +94 -0
- package/build/_modules/git-sync/_models/git-sync.interface.d.ts.map +1 -0
- package/build/_modules/git-sync/_models/git-sync.interface.js +3 -0
- package/build/_modules/git-sync/_models/git-sync.interface.js.map +1 -0
- package/build/_modules/git-sync/git-sync-input.util.d.ts +28 -0
- package/build/_modules/git-sync/git-sync-input.util.d.ts.map +1 -0
- package/build/_modules/git-sync/git-sync-input.util.js +146 -0
- package/build/_modules/git-sync/git-sync-input.util.js.map +1 -0
- package/build/_modules/git-sync/git-sync.service.d.ts +54 -0
- package/build/_modules/git-sync/git-sync.service.d.ts.map +1 -0
- package/build/_modules/git-sync/git-sync.service.js +274 -0
- package/build/_modules/git-sync/git-sync.service.js.map +1 -0
- package/build/_modules/git-sync/index.d.ts +8 -0
- package/build/_modules/git-sync/index.d.ts.map +1 -0
- package/build/_modules/git-sync/index.js +11 -0
- package/build/_modules/git-sync/index.js.map +1 -0
- package/build/_modules/imap/_models/imap-mailbox-status.interface.d.ts +10 -0
- package/build/_modules/imap/_models/imap-mailbox-status.interface.d.ts.map +1 -0
- package/build/_modules/imap/_models/imap-mailbox-status.interface.js +3 -0
- package/build/_modules/imap/_models/imap-mailbox-status.interface.js.map +1 -0
- package/build/_modules/imap/_models/imap-message-meta.interface.d.ts +18 -0
- package/build/_modules/imap/_models/imap-message-meta.interface.d.ts.map +1 -0
- package/build/_modules/imap/_models/imap-message-meta.interface.js +3 -0
- package/build/_modules/imap/_models/imap-message-meta.interface.js.map +1 -0
- package/build/_modules/imap/_models/imap-sync.interface.d.ts +22 -0
- package/build/_modules/imap/_models/imap-sync.interface.d.ts.map +1 -0
- package/build/_modules/imap/_models/imap-sync.interface.js +3 -0
- package/build/_modules/imap/_models/imap-sync.interface.js.map +1 -0
- package/build/_modules/imap/_models/imap-transport.interface.d.ts +16 -0
- package/build/_modules/imap/_models/imap-transport.interface.d.ts.map +1 -0
- package/build/_modules/imap/_models/imap-transport.interface.js +3 -0
- package/build/_modules/imap/_models/imap-transport.interface.js.map +1 -0
- package/build/_modules/imap/imap-flow.transport.d.ts +58 -0
- package/build/_modules/imap/imap-flow.transport.d.ts.map +1 -0
- package/build/_modules/imap/imap-flow.transport.js +156 -0
- package/build/_modules/imap/imap-flow.transport.js.map +1 -0
- package/build/_modules/imap/imap-inbox.service.d.ts +36 -0
- package/build/_modules/imap/imap-inbox.service.d.ts.map +1 -0
- package/build/_modules/imap/imap-inbox.service.js +81 -0
- package/build/_modules/imap/imap-inbox.service.js.map +1 -0
- package/build/_modules/imap/index.d.ts +11 -0
- package/build/_modules/imap/index.d.ts.map +1 -0
- package/build/_modules/imap/index.js +14 -0
- package/build/_modules/imap/index.js.map +1 -0
- package/build/_modules/notifications/_models/notification-mongo-collection.interface.d.ts +21 -0
- package/build/_modules/notifications/_models/notification-mongo-collection.interface.d.ts.map +1 -0
- package/build/_modules/notifications/_models/notification-mongo-collection.interface.js +3 -0
- package/build/_modules/notifications/_models/notification-mongo-collection.interface.js.map +1 -0
- package/build/_modules/notifications/_models/notification-publisher.interface.d.ts +32 -0
- package/build/_modules/notifications/_models/notification-publisher.interface.d.ts.map +1 -0
- package/build/_modules/notifications/_models/notification-publisher.interface.js +3 -0
- package/build/_modules/notifications/_models/notification-publisher.interface.js.map +1 -0
- package/build/_modules/notifications/_models/notification-store.interface.d.ts +35 -0
- package/build/_modules/notifications/_models/notification-store.interface.d.ts.map +1 -0
- package/build/_modules/notifications/_models/notification-store.interface.js +3 -0
- package/build/_modules/notifications/_models/notification-store.interface.js.map +1 -0
- package/build/_modules/notifications/_models/notification.interface.d.ts +59 -0
- package/build/_modules/notifications/_models/notification.interface.d.ts.map +1 -0
- package/build/_modules/notifications/_models/notification.interface.js +3 -0
- package/build/_modules/notifications/_models/notification.interface.js.map +1 -0
- package/build/_modules/notifications/get-notifications-routing-module.util.d.ts +18 -0
- package/build/_modules/notifications/get-notifications-routing-module.util.d.ts.map +1 -0
- package/build/_modules/notifications/get-notifications-routing-module.util.js +26 -0
- package/build/_modules/notifications/get-notifications-routing-module.util.js.map +1 -0
- package/build/_modules/notifications/index.d.ts +15 -0
- package/build/_modules/notifications/index.d.ts.map +1 -0
- package/build/_modules/notifications/index.js +18 -0
- package/build/_modules/notifications/index.js.map +1 -0
- package/build/_modules/notifications/notification-in-memory.store.d.ts +30 -0
- package/build/_modules/notifications/notification-in-memory.store.d.ts.map +1 -0
- package/build/_modules/notifications/notification-in-memory.store.js +68 -0
- package/build/_modules/notifications/notification-in-memory.store.js.map +1 -0
- package/build/_modules/notifications/notification-mongo.store.d.ts +51 -0
- package/build/_modules/notifications/notification-mongo.store.d.ts.map +1 -0
- package/build/_modules/notifications/notification-mongo.store.js +136 -0
- package/build/_modules/notifications/notification-mongo.store.js.map +1 -0
- package/build/_modules/notifications/notification.service.d.ts +67 -0
- package/build/_modules/notifications/notification.service.d.ts.map +1 -0
- package/build/_modules/notifications/notification.service.js +271 -0
- package/build/_modules/notifications/notification.service.js.map +1 -0
- package/build/_modules/notifications/notifications.controller.d.ts +31 -0
- package/build/_modules/notifications/notifications.controller.d.ts.map +1 -0
- package/build/_modules/notifications/notifications.controller.js +118 -0
- package/build/_modules/notifications/notifications.controller.js.map +1 -0
- package/package.json +33 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { DyNTS_Notification_Interface } from './notification.interface';
|
|
2
|
+
/**
|
|
3
|
+
* BFR-OLDLIGHTSITE-001 — a change pushed to the account's other devices / tabs.
|
|
4
|
+
*
|
|
5
|
+
* @description Every event carries the account's `unreadCount` after the change, so a client can set its badge
|
|
6
|
+
* without a round-trip. A read event carries the ids and the `seen` time; a client applies it MONOTONICALLY (it
|
|
7
|
+
* only ever turns unread → read), so an event arriving late or out of order can never make a read item unread.
|
|
8
|
+
*/
|
|
9
|
+
export type DyNTS_NotificationEvent_Type = {
|
|
10
|
+
kind: 'created';
|
|
11
|
+
accountId: string;
|
|
12
|
+
notification: DyNTS_Notification_Interface;
|
|
13
|
+
unreadCount: number;
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'read';
|
|
16
|
+
accountId: string;
|
|
17
|
+
ids: string[];
|
|
18
|
+
seen: Date;
|
|
19
|
+
unreadCount: number;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'read-all';
|
|
22
|
+
accountId: string;
|
|
23
|
+
upTo: Date;
|
|
24
|
+
seen: Date;
|
|
25
|
+
unreadCount: number;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* BFR-OLDLIGHTSITE-001 — delivers the events (e.g. a socket emit to the account's room). Best effort: the stored
|
|
29
|
+
* list is the truth, so a failing publisher is logged and never fails the operation that raised the event.
|
|
30
|
+
*/
|
|
31
|
+
export type DyNTS_NotificationPublisher_Type = (event: DyNTS_NotificationEvent_Type) => void | Promise<void>;
|
|
32
|
+
//# sourceMappingURL=notification-publisher.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-publisher.interface.d.ts","sourceRoot":"","sources":["../../../../src/_modules/notifications/_models/notification-publisher.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAExE;;;;;;GAMG;AACH,MAAM,MAAM,4BAA4B,GACpC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,4BAA4B,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACvG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACnF;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzF;;;GAGG;AACH,MAAM,MAAM,gCAAgC,GAAG,CAAC,KAAK,EAAE,4BAA4B,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-publisher.interface.js","sourceRoot":"","sources":["../../../../src/_modules/notifications/_models/notification-publisher.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DyNTS_Notification_Interface } from './notification.interface';
|
|
2
|
+
/** A position in the newest-first order: the page continues with the notifications strictly OLDER than it. */
|
|
3
|
+
export interface DyNTS_NotificationCursor_Interface {
|
|
4
|
+
created: Date;
|
|
5
|
+
id: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* BFR-OLDLIGHTSITE-001 — the persistence contract behind `DyNTS_Notification_Service` (in-memory and MongoDB
|
|
9
|
+
* implementations). The store does NO validation — the service does it before every call.
|
|
10
|
+
*/
|
|
11
|
+
export interface DyNTS_NotificationStore_Interface {
|
|
12
|
+
/** Inserts, or — same `accountId` + `dedupeKey` — returns the EXISTING one unchanged. */
|
|
13
|
+
insert(notification: DyNTS_Notification_Interface): Promise<DyNTS_Notification_Interface>;
|
|
14
|
+
/** Newest first (`__created` desc, then `_id` desc), strictly after `before`. */
|
|
15
|
+
list(query: {
|
|
16
|
+
accountId: string;
|
|
17
|
+
limit: number;
|
|
18
|
+
before?: DyNTS_NotificationCursor_Interface;
|
|
19
|
+
unreadOnly?: boolean;
|
|
20
|
+
}): Promise<DyNTS_Notification_Interface[]>;
|
|
21
|
+
countUnread(accountId: string): Promise<number>;
|
|
22
|
+
/** The ids (of the given ones) that exist for the account. */
|
|
23
|
+
existingIds(accountId: string, ids: string[]): Promise<string[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Sets `seen = at` on the account's UNREAD notifications among `ids` (an already read one is left untouched), or —
|
|
26
|
+
* `ids` omitted — on every unread one created at or before `upTo`. Returns how many changed.
|
|
27
|
+
*/
|
|
28
|
+
markSeen(query: {
|
|
29
|
+
accountId: string;
|
|
30
|
+
at: Date;
|
|
31
|
+
ids?: string[];
|
|
32
|
+
upTo?: Date;
|
|
33
|
+
}): Promise<number>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=notification-store.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-store.interface.d.ts","sourceRoot":"","sources":["../../../../src/_modules/notifications/_models/notification-store.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAExE,8GAA8G;AAC9G,MAAM,WAAW,kCAAkC;IACjD,OAAO,EAAE,IAAI,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;GAGG;AACH,MAAM,WAAW,iCAAiC;IAChD,yFAAyF;IACzF,MAAM,CAAC,YAAY,EAAE,4BAA4B,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;IAC1F,iFAAiF;IACjF,IAAI,CAAC,KAAK,EAAE;QACV,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,kCAAkC,CAAC;QAC5C,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,GAAG,OAAO,CAAC,4BAA4B,EAAE,CAAC,CAAC;IAC5C,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,8DAA8D;IAC9D,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjE;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,IAAI,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAChG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-store.interface.js","sourceRoot":"","sources":["../../../../src/_modules/notifications/_models/notification-store.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** BFR-OLDLIGHTSITE-001 — the severity of a notification (the values of the client's `DyNX_Notification_Type`). */
|
|
2
|
+
export type DyNTS_NotificationKind_Type = 'info' | 'success' | 'warning' | 'error';
|
|
3
|
+
/**
|
|
4
|
+
* BFR-OLDLIGHTSITE-001 — one stored notification of one account.
|
|
5
|
+
*
|
|
6
|
+
* @description The display fields (`title`, `message`, `type`, `routerLink`, `queryParams`, `seen`, `_id`,
|
|
7
|
+
* `__created`) carry the SAME names as the client's `DyNX_Notification`, so a stored record can be handed to the
|
|
8
|
+
* `ngx-dynamo` notification components unchanged.
|
|
9
|
+
*/
|
|
10
|
+
export interface DyNTS_Notification_Interface {
|
|
11
|
+
_id: string;
|
|
12
|
+
/** The recipient (`fdp-issuer-is-account-id`: always the accountId, never the userId). */
|
|
13
|
+
accountId: string;
|
|
14
|
+
/** The domain event that raised it, e.g. `sp-review.reminder` — for filtering and client routing. */
|
|
15
|
+
eventType: string;
|
|
16
|
+
title: string;
|
|
17
|
+
message?: string;
|
|
18
|
+
type: DyNTS_NotificationKind_Type;
|
|
19
|
+
/** An app-internal route (always starts with `/` — an external or `javascript:` link is rejected). */
|
|
20
|
+
routerLink?: string;
|
|
21
|
+
queryParams?: Record<string, string | number>;
|
|
22
|
+
/** Event-specific data (JSON, at most 16 KB). */
|
|
23
|
+
payload?: Record<string, unknown>;
|
|
24
|
+
/** Same account + same key → the SAME notification (a retried producer never duplicates it). */
|
|
25
|
+
dedupeKey?: string;
|
|
26
|
+
/** `null` = unread. Set ONCE, by the first read; never cleared, never moved (the read state cannot jump back). */
|
|
27
|
+
seen: Date | null;
|
|
28
|
+
__created: Date;
|
|
29
|
+
}
|
|
30
|
+
/** BFR-OLDLIGHTSITE-001 — what a producer passes to `create`. */
|
|
31
|
+
export interface DyNTS_NotificationCreate_Interface {
|
|
32
|
+
accountId: string;
|
|
33
|
+
eventType: string;
|
|
34
|
+
title: string;
|
|
35
|
+
message?: string;
|
|
36
|
+
/** Default `info`. */
|
|
37
|
+
type?: DyNTS_NotificationKind_Type;
|
|
38
|
+
routerLink?: string;
|
|
39
|
+
queryParams?: Record<string, string | number>;
|
|
40
|
+
payload?: Record<string, unknown>;
|
|
41
|
+
dedupeKey?: string;
|
|
42
|
+
}
|
|
43
|
+
/** BFR-OLDLIGHTSITE-001 — one page of an account's notifications, newest first. */
|
|
44
|
+
export interface DyNTS_NotificationPage_Interface {
|
|
45
|
+
items: DyNTS_Notification_Interface[];
|
|
46
|
+
/** Pass it as `before` for the next (older) page; `null` = no more. */
|
|
47
|
+
nextCursor: string | null;
|
|
48
|
+
unreadCount: number;
|
|
49
|
+
}
|
|
50
|
+
/** BFR-OLDLIGHTSITE-001 — the outcome of `markRead` / `markAllRead`. */
|
|
51
|
+
export interface DyNTS_NotificationReadResult_Interface {
|
|
52
|
+
/** How many notifications THIS call turned from unread to read (a repeated call → 0: idempotent). */
|
|
53
|
+
updated: number;
|
|
54
|
+
/** Requested ids that do not exist for this account (`markRead` only). */
|
|
55
|
+
notFound: string[];
|
|
56
|
+
/** The account's unread count after the call — the client sets its badge from it. */
|
|
57
|
+
unreadCount: number;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=notification.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification.interface.d.ts","sourceRoot":"","sources":["../../../../src/_modules/notifications/_models/notification.interface.ts"],"names":[],"mappings":"AAAA,mHAAmH;AACnH,MAAM,MAAM,2BAA2B,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnF;;;;;;GAMG;AACH,MAAM,WAAW,4BAA4B;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,0FAA0F;IAC1F,SAAS,EAAE,MAAM,CAAC;IAClB,qGAAqG;IACrG,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,2BAA2B,CAAC;IAClC,sGAAsG;IACtG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IAC9C,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kHAAkH;IAClH,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,iEAAiE;AACjE,MAAM,WAAW,kCAAkC;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sBAAsB;IACtB,IAAI,CAAC,EAAE,2BAA2B,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mFAAmF;AACnF,MAAM,WAAW,gCAAgC;IAC/C,KAAK,EAAE,4BAA4B,EAAE,CAAC;IACtC,uEAAuE;IACvE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wEAAwE;AACxE,MAAM,WAAW,sCAAsC;IACrD,qGAAqG;IACrG,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,qFAAqF;IACrF,WAAW,EAAE,MAAM,CAAC;CACrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification.interface.js","sourceRoot":"","sources":["../../../../src/_modules/notifications/_models/notification.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { DyNTS_RoutingModule } from '../../_services/route/routing-module.service';
|
|
2
|
+
import { DyNTS_NotificationsController_Config } from './notifications.controller';
|
|
3
|
+
/**
|
|
4
|
+
* BFR-OLDLIGHTSITE-001 — the routing module of the account notification routes (default mount: `/notifications`).
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const notifications = new DyNTS_Notification_Service({
|
|
9
|
+
* store: new DyNTS_Notification_MongoStore({ collection: mongoose.connection.collection('dynts_notifications') }),
|
|
10
|
+
* publisher: (event) => socketServer.to(`account:${event.accountId}`).emit('notification', event),
|
|
11
|
+
* });
|
|
12
|
+
* routingModules.push(DyNTS_getNotificationsRoutingModule({ service: notifications, authPreProcess: auth }));
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function DyNTS_getNotificationsRoutingModule(config: DyNTS_NotificationsController_Config & {
|
|
16
|
+
route?: string;
|
|
17
|
+
}): DyNTS_RoutingModule;
|
|
18
|
+
//# sourceMappingURL=get-notifications-routing-module.util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-notifications-routing-module.util.d.ts","sourceRoot":"","sources":["../../../src/_modules/notifications/get-notifications-routing-module.util.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AACnF,OAAO,EAAkC,oCAAoC,EAAE,MAAM,4BAA4B,CAAC;AAElH;;;;;;;;;;;GAWG;AACH,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,oCAAoC,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,mBAAmB,CASrB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DyNTS_getNotificationsRoutingModule = DyNTS_getNotificationsRoutingModule;
|
|
4
|
+
const routing_module_service_1 = require("../../_services/route/routing-module.service");
|
|
5
|
+
const notifications_controller_1 = require("./notifications.controller");
|
|
6
|
+
/**
|
|
7
|
+
* BFR-OLDLIGHTSITE-001 — the routing module of the account notification routes (default mount: `/notifications`).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const notifications = new DyNTS_Notification_Service({
|
|
12
|
+
* store: new DyNTS_Notification_MongoStore({ collection: mongoose.connection.collection('dynts_notifications') }),
|
|
13
|
+
* publisher: (event) => socketServer.to(`account:${event.accountId}`).emit('notification', event),
|
|
14
|
+
* });
|
|
15
|
+
* routingModules.push(DyNTS_getNotificationsRoutingModule({ service: notifications, authPreProcess: auth }));
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
function DyNTS_getNotificationsRoutingModule(config) {
|
|
19
|
+
notifications_controller_1.DyNTS_Notifications_Controller.configure(config);
|
|
20
|
+
const set = {
|
|
21
|
+
route: config.route ?? '/notifications',
|
|
22
|
+
controllers: [notifications_controller_1.DyNTS_Notifications_Controller.getInstance()],
|
|
23
|
+
};
|
|
24
|
+
return new routing_module_service_1.DyNTS_RoutingModule(set);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=get-notifications-routing-module.util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-notifications-routing-module.util.js","sourceRoot":"","sources":["../../../src/_modules/notifications/get-notifications-routing-module.util.ts"],"names":[],"mappings":";;AAgBA,kFAWC;AA1BD,yFAAmF;AACnF,yEAAkH;AAElH;;;;;;;;;;;GAWG;AACH,SAAgB,mCAAmC,CACjD,MAAiE;IAEjE,yDAA8B,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAEjD,MAAM,GAAG,GAAiC;QACxC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,gBAAgB;QACvC,WAAW,EAAE,CAAE,yDAA8B,CAAC,WAAW,EAAE,CAAE;KAC9D,CAAC;IAEF,OAAO,IAAI,4CAAmB,CAAC,GAAG,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BFR-OLDLIGHTSITE-001 — `@futdevpro/nts-dynamo/notifications`: persisted per-account notifications
|
|
3
|
+
* (create · list · unread count · mark read · mark all read) with a monotonic read state, an optional push
|
|
4
|
+
* publisher, and the account routes. In-memory and MongoDB stores.
|
|
5
|
+
*/
|
|
6
|
+
export * from './_models/notification.interface';
|
|
7
|
+
export * from './_models/notification-store.interface';
|
|
8
|
+
export * from './_models/notification-publisher.interface';
|
|
9
|
+
export * from './_models/notification-mongo-collection.interface';
|
|
10
|
+
export * from './notification-in-memory.store';
|
|
11
|
+
export * from './notification-mongo.store';
|
|
12
|
+
export * from './notification.service';
|
|
13
|
+
export * from './notifications.controller';
|
|
14
|
+
export * from './get-notifications-routing-module.util';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/_modules/notifications/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,kCAAkC,CAAC;AACjD,cAAc,wCAAwC,CAAC;AACvD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,mDAAmD,CAAC;AAClE,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yCAAyC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
/**
|
|
5
|
+
* BFR-OLDLIGHTSITE-001 — `@futdevpro/nts-dynamo/notifications`: persisted per-account notifications
|
|
6
|
+
* (create · list · unread count · mark read · mark all read) with a monotonic read state, an optional push
|
|
7
|
+
* publisher, and the account routes. In-memory and MongoDB stores.
|
|
8
|
+
*/
|
|
9
|
+
tslib_1.__exportStar(require("./_models/notification.interface"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./_models/notification-store.interface"), exports);
|
|
11
|
+
tslib_1.__exportStar(require("./_models/notification-publisher.interface"), exports);
|
|
12
|
+
tslib_1.__exportStar(require("./_models/notification-mongo-collection.interface"), exports);
|
|
13
|
+
tslib_1.__exportStar(require("./notification-in-memory.store"), exports);
|
|
14
|
+
tslib_1.__exportStar(require("./notification-mongo.store"), exports);
|
|
15
|
+
tslib_1.__exportStar(require("./notification.service"), exports);
|
|
16
|
+
tslib_1.__exportStar(require("./notifications.controller"), exports);
|
|
17
|
+
tslib_1.__exportStar(require("./get-notifications-routing-module.util"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/_modules/notifications/index.ts"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,2EAAiD;AACjD,iFAAuD;AACvD,qFAA2D;AAC3D,4FAAkE;AAClE,yEAA+C;AAC/C,qEAA2C;AAC3C,iEAAuC;AACvC,qEAA2C;AAC3C,kFAAwD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { DyNTS_Notification_Interface } from './_models/notification.interface';
|
|
2
|
+
import { DyNTS_NotificationCursor_Interface, DyNTS_NotificationStore_Interface } from './_models/notification-store.interface';
|
|
3
|
+
/**
|
|
4
|
+
* BFR-OLDLIGHTSITE-001 — a single-process notification store (tests, local development, one-replica tools).
|
|
5
|
+
* Same semantics as `DyNTS_Notification_MongoStore`; every returned record is a copy.
|
|
6
|
+
*/
|
|
7
|
+
export declare class DyNTS_Notification_InMemoryStore implements DyNTS_NotificationStore_Interface {
|
|
8
|
+
private readonly records;
|
|
9
|
+
insert(notification: DyNTS_Notification_Interface): Promise<DyNTS_Notification_Interface>;
|
|
10
|
+
list(query: {
|
|
11
|
+
accountId: string;
|
|
12
|
+
limit: number;
|
|
13
|
+
before?: DyNTS_NotificationCursor_Interface;
|
|
14
|
+
unreadOnly?: boolean;
|
|
15
|
+
}): Promise<DyNTS_Notification_Interface[]>;
|
|
16
|
+
countUnread(accountId: string): Promise<number>;
|
|
17
|
+
existingIds(accountId: string, ids: string[]): Promise<string[]>;
|
|
18
|
+
markSeen(query: {
|
|
19
|
+
accountId: string;
|
|
20
|
+
at: Date;
|
|
21
|
+
ids?: string[];
|
|
22
|
+
upTo?: Date;
|
|
23
|
+
}): Promise<number>;
|
|
24
|
+
/** Newest first: `__created` desc, then `_id` desc (a stable total order for paging). */
|
|
25
|
+
static newestFirst(a: DyNTS_Notification_Interface, b: DyNTS_Notification_Interface): number;
|
|
26
|
+
/** Strictly after the cursor in the newest-first order. */
|
|
27
|
+
private static isOlder;
|
|
28
|
+
private static copy;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=notification-in-memory.store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-in-memory.store.d.ts","sourceRoot":"","sources":["../../../src/_modules/notifications/notification-in-memory.store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EACL,kCAAkC,EAAE,iCAAiC,EACtE,MAAM,wCAAwC,CAAC;AAEhD;;;GAGG;AACH,qBAAa,gCAAiC,YAAW,iCAAiC;IACxF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAExD,MAAM,CAAC,YAAY,EAAE,4BAA4B,GAAG,OAAO,CAAC,4BAA4B,CAAC;IAgBzF,IAAI,CAAC,KAAK,EAAE;QAChB,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,kCAAkC,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAC;KACrG,GAAG,OAAO,CAAC,4BAA4B,EAAE,CAAC;IAUrC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM/C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAMhE,QAAQ,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,IAAI,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBpG,yFAAyF;IACzF,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,4BAA4B,EAAE,CAAC,EAAE,4BAA4B,GAAG,MAAM;IAI5F,2DAA2D;IAC3D,OAAO,CAAC,MAAM,CAAC,OAAO;IAMtB,OAAO,CAAC,MAAM,CAAC,IAAI;CASpB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DyNTS_Notification_InMemoryStore = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* BFR-OLDLIGHTSITE-001 — a single-process notification store (tests, local development, one-replica tools).
|
|
6
|
+
* Same semantics as `DyNTS_Notification_MongoStore`; every returned record is a copy.
|
|
7
|
+
*/
|
|
8
|
+
class DyNTS_Notification_InMemoryStore {
|
|
9
|
+
records = [];
|
|
10
|
+
async insert(notification) {
|
|
11
|
+
const existing = notification.dedupeKey
|
|
12
|
+
? this.records.find((record) => (record.accountId === notification.accountId && record.dedupeKey === notification.dedupeKey))
|
|
13
|
+
: undefined;
|
|
14
|
+
if (existing) {
|
|
15
|
+
return DyNTS_Notification_InMemoryStore.copy(existing);
|
|
16
|
+
}
|
|
17
|
+
this.records.push(DyNTS_Notification_InMemoryStore.copy(notification));
|
|
18
|
+
return DyNTS_Notification_InMemoryStore.copy(notification);
|
|
19
|
+
}
|
|
20
|
+
async list(query) {
|
|
21
|
+
return this.records
|
|
22
|
+
.filter((record) => record.accountId === query.accountId
|
|
23
|
+
&& (!query.unreadOnly || !record.seen)
|
|
24
|
+
&& (!query.before || DyNTS_Notification_InMemoryStore.isOlder(record, query.before)))
|
|
25
|
+
.sort(DyNTS_Notification_InMemoryStore.newestFirst)
|
|
26
|
+
.slice(0, query.limit)
|
|
27
|
+
.map(DyNTS_Notification_InMemoryStore.copy);
|
|
28
|
+
}
|
|
29
|
+
async countUnread(accountId) {
|
|
30
|
+
return this.records.filter((record) => (record.accountId === accountId && !record.seen)).length;
|
|
31
|
+
}
|
|
32
|
+
async existingIds(accountId, ids) {
|
|
33
|
+
return ids.filter((id) => this.records.some((record) => (record.accountId === accountId && record._id === id)));
|
|
34
|
+
}
|
|
35
|
+
async markSeen(query) {
|
|
36
|
+
let updated = 0;
|
|
37
|
+
for (const record of this.records) {
|
|
38
|
+
const selected = query.ids
|
|
39
|
+
? query.ids.includes(record._id)
|
|
40
|
+
: !query.upTo || record.__created.getTime() <= query.upTo.getTime();
|
|
41
|
+
if (record.accountId === query.accountId && !record.seen && selected) {
|
|
42
|
+
record.seen = new Date(query.at);
|
|
43
|
+
updated++;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return updated;
|
|
47
|
+
}
|
|
48
|
+
/** Newest first: `__created` desc, then `_id` desc (a stable total order for paging). */
|
|
49
|
+
static newestFirst(a, b) {
|
|
50
|
+
return b.__created.getTime() - a.__created.getTime() || (a._id < b._id ? 1 : a._id > b._id ? -1 : 0);
|
|
51
|
+
}
|
|
52
|
+
/** Strictly after the cursor in the newest-first order. */
|
|
53
|
+
static isOlder(record, before) {
|
|
54
|
+
const created = record.__created.getTime();
|
|
55
|
+
return created < before.created.getTime() || (created === before.created.getTime() && record._id < before.id);
|
|
56
|
+
}
|
|
57
|
+
static copy(record) {
|
|
58
|
+
return {
|
|
59
|
+
...record,
|
|
60
|
+
__created: new Date(record.__created),
|
|
61
|
+
seen: record.seen ? new Date(record.seen) : null,
|
|
62
|
+
queryParams: record.queryParams ? { ...record.queryParams } : undefined,
|
|
63
|
+
payload: record.payload ? JSON.parse(JSON.stringify(record.payload)) : undefined,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.DyNTS_Notification_InMemoryStore = DyNTS_Notification_InMemoryStore;
|
|
68
|
+
//# sourceMappingURL=notification-in-memory.store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-in-memory.store.js","sourceRoot":"","sources":["../../../src/_modules/notifications/notification-in-memory.store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { DyNTS_Notification_Interface } from './_models/notification.interface';
|
|
2
|
+
import { DyNTS_NotificationMongoCollection_Interface } from './_models/notification-mongo-collection.interface';
|
|
3
|
+
import { DyNTS_NotificationCursor_Interface, DyNTS_NotificationStore_Interface } from './_models/notification-store.interface';
|
|
4
|
+
/**
|
|
5
|
+
* BFR-OLDLIGHTSITE-001 — the shared (multi-process, multi-replica) notification store, on MongoDB.
|
|
6
|
+
*
|
|
7
|
+
* @description
|
|
8
|
+
* - **Read state never jumps back:** `markSeen` updates with the filter `seen: null`, so an already read
|
|
9
|
+
* notification is never touched again — two devices, the API and the socket may race, the first read wins and
|
|
10
|
+
* the state only ever goes unread → read. There is no operation that clears `seen`.
|
|
11
|
+
* - **Idempotent create:** a unique partial index on `(accountId, dedupeKey)` makes a retried producer's second
|
|
12
|
+
* insert fail with a duplicate key; the store then returns the EXISTING record.
|
|
13
|
+
* - Paging is a keyset on `(__created desc, _id desc)` — stable under concurrent inserts, no skip/offset.
|
|
14
|
+
*
|
|
15
|
+
* Usage: `new DyNTS_Notification_MongoStore({ collection: mongoose.connection.collection('dynts_notifications') })`,
|
|
16
|
+
* then `ensureIndexes()` once at boot.
|
|
17
|
+
*/
|
|
18
|
+
export declare class DyNTS_Notification_MongoStore implements DyNTS_NotificationStore_Interface {
|
|
19
|
+
private readonly collection;
|
|
20
|
+
/** Every replica must point at the SAME collection. */
|
|
21
|
+
constructor(set: {
|
|
22
|
+
collection: DyNTS_NotificationMongoCollection_Interface;
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* The list, unread-count and dedupe indexes; with `retentionDays`, a TTL index that deletes notifications older than
|
|
26
|
+
* that. Idempotent — call once at boot.
|
|
27
|
+
*/
|
|
28
|
+
ensureIndexes(options?: {
|
|
29
|
+
retentionDays?: number;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
insert(notification: DyNTS_Notification_Interface): Promise<DyNTS_Notification_Interface>;
|
|
32
|
+
list(query: {
|
|
33
|
+
accountId: string;
|
|
34
|
+
limit: number;
|
|
35
|
+
before?: DyNTS_NotificationCursor_Interface;
|
|
36
|
+
unreadOnly?: boolean;
|
|
37
|
+
}): Promise<DyNTS_Notification_Interface[]>;
|
|
38
|
+
countUnread(accountId: string): Promise<number>;
|
|
39
|
+
existingIds(accountId: string, ids: string[]): Promise<string[]>;
|
|
40
|
+
markSeen(query: {
|
|
41
|
+
accountId: string;
|
|
42
|
+
at: Date;
|
|
43
|
+
ids?: string[];
|
|
44
|
+
upTo?: Date;
|
|
45
|
+
}): Promise<number>;
|
|
46
|
+
/** A stored document → a clean record (dates as `Date`, no driver-specific fields). */
|
|
47
|
+
static fromDocument(document: unknown): DyNTS_Notification_Interface;
|
|
48
|
+
private static toDate;
|
|
49
|
+
private static kindOf;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=notification-mongo.store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-mongo.store.d.ts","sourceRoot":"","sources":["../../../src/_modules/notifications/notification-mongo.store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAE,2CAA2C,EAAE,MAAM,mDAAmD,CAAC;AAChH,OAAO,EACL,kCAAkC,EAAE,iCAAiC,EACtE,MAAM,wCAAwC,CAAC;AAKhD;;;;;;;;;;;;;GAaG;AACH,qBAAa,6BAA8B,YAAW,iCAAiC;IACrF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8C;IAEzE,uDAAuD;gBAC3C,GAAG,EAAE;QAAE,UAAU,EAAE,2CAA2C,CAAA;KAAE;IAO5E;;;OAGG;IACG,aAAa,CAAC,OAAO,GAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBtE,MAAM,CAAC,YAAY,EAAE,4BAA4B,GAAG,OAAO,CAAC,4BAA4B,CAAC;IA+BzF,IAAI,CAAC,KAAK,EAAE;QAChB,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,kCAAkC,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAC;KACrG,GAAG,OAAO,CAAC,4BAA4B,EAAE,CAAC;IAoBrC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI/C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAWhE,QAAQ,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,IAAI,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAepG,uFAAuF;IACvF,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,GAAG,4BAA4B;IAsBpE,OAAO,CAAC,MAAM,CAAC,MAAM;IAIrB,OAAO,CAAC,MAAM,CAAC,MAAM;CAGtB"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DyNTS_Notification_MongoStore = void 0;
|
|
4
|
+
/** MongoDB's duplicate-key error code. */
|
|
5
|
+
const DUPLICATE_KEY = 11000;
|
|
6
|
+
/**
|
|
7
|
+
* BFR-OLDLIGHTSITE-001 — the shared (multi-process, multi-replica) notification store, on MongoDB.
|
|
8
|
+
*
|
|
9
|
+
* @description
|
|
10
|
+
* - **Read state never jumps back:** `markSeen` updates with the filter `seen: null`, so an already read
|
|
11
|
+
* notification is never touched again — two devices, the API and the socket may race, the first read wins and
|
|
12
|
+
* the state only ever goes unread → read. There is no operation that clears `seen`.
|
|
13
|
+
* - **Idempotent create:** a unique partial index on `(accountId, dedupeKey)` makes a retried producer's second
|
|
14
|
+
* insert fail with a duplicate key; the store then returns the EXISTING record.
|
|
15
|
+
* - Paging is a keyset on `(__created desc, _id desc)` — stable under concurrent inserts, no skip/offset.
|
|
16
|
+
*
|
|
17
|
+
* Usage: `new DyNTS_Notification_MongoStore({ collection: mongoose.connection.collection('dynts_notifications') })`,
|
|
18
|
+
* then `ensureIndexes()` once at boot.
|
|
19
|
+
*/
|
|
20
|
+
class DyNTS_Notification_MongoStore {
|
|
21
|
+
collection;
|
|
22
|
+
/** Every replica must point at the SAME collection. */
|
|
23
|
+
constructor(set) {
|
|
24
|
+
if (!set?.collection) {
|
|
25
|
+
throw new Error('DYNTS_NOTIFICATION_MONGO_STORE_COLLECTION_MISSING');
|
|
26
|
+
}
|
|
27
|
+
this.collection = set.collection;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The list, unread-count and dedupe indexes; with `retentionDays`, a TTL index that deletes notifications older than
|
|
31
|
+
* that. Idempotent — call once at boot.
|
|
32
|
+
*/
|
|
33
|
+
async ensureIndexes(options = {}) {
|
|
34
|
+
await this.collection.createIndex({ accountId: 1, __created: -1, _id: -1 }, { name: 'dynts_notification_list' });
|
|
35
|
+
await this.collection.createIndex({ accountId: 1, seen: 1 }, { name: 'dynts_notification_unread' });
|
|
36
|
+
await this.collection.createIndex({ accountId: 1, dedupeKey: 1 }, {
|
|
37
|
+
name: 'dynts_notification_dedupe', unique: true, partialFilterExpression: { dedupeKey: { $type: 'string' } },
|
|
38
|
+
});
|
|
39
|
+
if (options.retentionDays) {
|
|
40
|
+
await this.collection.createIndex({ __created: 1 }, {
|
|
41
|
+
name: 'dynts_notification_retention', expireAfterSeconds: Math.round(options.retentionDays * 86400),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async insert(notification) {
|
|
46
|
+
const document = { ...notification };
|
|
47
|
+
// an absent optional field is not stored (a `null` dedupeKey would collide in the unique index)
|
|
48
|
+
Object.keys(document).forEach((key) => {
|
|
49
|
+
if (document[key] === undefined) {
|
|
50
|
+
delete document[key];
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
try {
|
|
54
|
+
await this.collection.insertOne(document);
|
|
55
|
+
return notification;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (Reflect.get(Object(error), 'code') !== DUPLICATE_KEY || !notification.dedupeKey) {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
const existing = await this.collection.findOne({
|
|
62
|
+
accountId: notification.accountId, dedupeKey: notification.dedupeKey,
|
|
63
|
+
});
|
|
64
|
+
if (!existing) {
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
return DyNTS_Notification_MongoStore.fromDocument(existing);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async list(query) {
|
|
71
|
+
const filter = { accountId: query.accountId };
|
|
72
|
+
if (query.unreadOnly) {
|
|
73
|
+
filter.seen = null;
|
|
74
|
+
}
|
|
75
|
+
if (query.before) {
|
|
76
|
+
filter.$or = [
|
|
77
|
+
{ __created: { $lt: query.before.created } },
|
|
78
|
+
{ __created: query.before.created, _id: { $lt: query.before.id } },
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
const documents = await this.collection
|
|
82
|
+
.find(filter, { sort: { __created: -1, _id: -1 }, limit: query.limit })
|
|
83
|
+
.toArray();
|
|
84
|
+
return documents.map(DyNTS_Notification_MongoStore.fromDocument);
|
|
85
|
+
}
|
|
86
|
+
async countUnread(accountId) {
|
|
87
|
+
return this.collection.countDocuments({ accountId: accountId, seen: null });
|
|
88
|
+
}
|
|
89
|
+
async existingIds(accountId, ids) {
|
|
90
|
+
const documents = await this.collection
|
|
91
|
+
.find({ accountId: accountId, _id: { $in: ids } }, { projection: { _id: 1 } })
|
|
92
|
+
.toArray();
|
|
93
|
+
const found = new Set(documents.map((document) => (String(Reflect.get(Object(document), '_id')))));
|
|
94
|
+
return ids.filter((id) => found.has(id));
|
|
95
|
+
}
|
|
96
|
+
async markSeen(query) {
|
|
97
|
+
// `seen: null` in the filter is the whole guarantee: a read notification is never matched again
|
|
98
|
+
const filter = { accountId: query.accountId, seen: null };
|
|
99
|
+
if (query.ids) {
|
|
100
|
+
filter._id = { $in: query.ids };
|
|
101
|
+
}
|
|
102
|
+
else if (query.upTo) {
|
|
103
|
+
filter.__created = { $lte: query.upTo };
|
|
104
|
+
}
|
|
105
|
+
const result = await this.collection.updateMany(filter, { $set: { seen: query.at } });
|
|
106
|
+
return Number(result?.modifiedCount ?? 0);
|
|
107
|
+
}
|
|
108
|
+
/** A stored document → a clean record (dates as `Date`, no driver-specific fields). */
|
|
109
|
+
static fromDocument(document) {
|
|
110
|
+
const read = (key) => Reflect.get(Object(document), key);
|
|
111
|
+
const seen = read('seen');
|
|
112
|
+
const record = {
|
|
113
|
+
_id: String(read('_id')),
|
|
114
|
+
accountId: String(read('accountId')),
|
|
115
|
+
eventType: String(read('eventType')),
|
|
116
|
+
title: String(read('title')),
|
|
117
|
+
type: DyNTS_Notification_MongoStore.kindOf(read('type')),
|
|
118
|
+
seen: seen ? DyNTS_Notification_MongoStore.toDate(seen) : null,
|
|
119
|
+
__created: DyNTS_Notification_MongoStore.toDate(read('__created')),
|
|
120
|
+
};
|
|
121
|
+
for (const key of ['message', 'routerLink', 'queryParams', 'payload', 'dedupeKey']) {
|
|
122
|
+
if (read(key) !== undefined && read(key) !== null) {
|
|
123
|
+
Reflect.set(record, key, read(key));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return record;
|
|
127
|
+
}
|
|
128
|
+
static toDate(value) {
|
|
129
|
+
return value instanceof Date ? new Date(value.getTime()) : new Date(String(value));
|
|
130
|
+
}
|
|
131
|
+
static kindOf(value) {
|
|
132
|
+
return value === 'success' || value === 'warning' || value === 'error' ? value : 'info';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.DyNTS_Notification_MongoStore = DyNTS_Notification_MongoStore;
|
|
136
|
+
//# sourceMappingURL=notification-mongo.store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-mongo.store.js","sourceRoot":"","sources":["../../../src/_modules/notifications/notification-mongo.store.ts"],"names":[],"mappings":"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"}
|