vintasend 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ export type { LocalFileAttachmentManagerConfig } from './services/attachment-man
|
|
|
3
3
|
export { LocalFileAttachmentManager } from './services/attachment-manager/local-file-attachment-manager';
|
|
4
4
|
export { BaseNotificationAdapter, isOneOffNotification, } from './services/notification-adapters/base-notification-adapter';
|
|
5
5
|
export type { BaseNotificationBackend } from './services/notification-backends/base-notification-backend';
|
|
6
|
-
export { supportsAttachments } from './services/notification-backends/base-notification-backend';
|
|
6
|
+
export { supportsAttachments, isFieldFilter } from './services/notification-backends/base-notification-backend';
|
|
7
|
+
export type { NotificationFilter, NotificationFilterFields, DateRange, NotificationFilterCapabilities } from './services/notification-backends/base-notification-backend';
|
|
7
8
|
export type { BaseNotificationQueueService } from './services/notification-queue-service/base-notification-queue-service';
|
|
8
9
|
export type { VintaSend } from './services/notification-service';
|
|
9
10
|
export { VintaSendFactory } from './services/notification-service';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isAttachmentReference = exports.VintaSendFactory = exports.supportsAttachments = exports.isOneOffNotification = exports.BaseNotificationAdapter = exports.LocalFileAttachmentManager = exports.BaseAttachmentManager = void 0;
|
|
3
|
+
exports.isAttachmentReference = exports.VintaSendFactory = exports.isFieldFilter = exports.supportsAttachments = exports.isOneOffNotification = exports.BaseNotificationAdapter = exports.LocalFileAttachmentManager = exports.BaseAttachmentManager = void 0;
|
|
4
4
|
// Attachment Manager
|
|
5
5
|
var base_attachment_manager_1 = require("./services/attachment-manager/base-attachment-manager");
|
|
6
6
|
Object.defineProperty(exports, "BaseAttachmentManager", { enumerable: true, get: function () { return base_attachment_manager_1.BaseAttachmentManager; } });
|
|
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "BaseNotificationAdapter", { enumerable: true, ge
|
|
|
11
11
|
Object.defineProperty(exports, "isOneOffNotification", { enumerable: true, get: function () { return base_notification_adapter_1.isOneOffNotification; } });
|
|
12
12
|
var base_notification_backend_1 = require("./services/notification-backends/base-notification-backend");
|
|
13
13
|
Object.defineProperty(exports, "supportsAttachments", { enumerable: true, get: function () { return base_notification_backend_1.supportsAttachments; } });
|
|
14
|
+
Object.defineProperty(exports, "isFieldFilter", { enumerable: true, get: function () { return base_notification_backend_1.isFieldFilter; } });
|
|
14
15
|
var notification_service_1 = require("./services/notification-service");
|
|
15
16
|
Object.defineProperty(exports, "VintaSendFactory", { enumerable: true, get: function () { return notification_service_1.VintaSendFactory; } });
|
|
16
17
|
var attachment_1 = require("./types/attachment");
|
|
@@ -1,8 +1,62 @@
|
|
|
1
1
|
import type { AttachmentFileRecord, StoredAttachment } from '../../types/attachment';
|
|
2
2
|
import type { InputJsonValue } from '../../types/json-values';
|
|
3
|
+
import type { NotificationStatus } from '../../types/notification-status';
|
|
4
|
+
import type { NotificationType } from '../../types/notification-type';
|
|
3
5
|
import type { AnyDatabaseNotification, AnyNotification, DatabaseNotification, DatabaseOneOffNotification, Notification, OneOffNotificationInput } from '../../types/notification';
|
|
4
6
|
import type { BaseNotificationTypeConfig } from '../../types/notification-type-config';
|
|
5
7
|
import type { BaseLogger } from '../loggers/base-logger';
|
|
8
|
+
/**
|
|
9
|
+
* Date range filter with optional lower and upper bounds.
|
|
10
|
+
*/
|
|
11
|
+
export type DateRange = {
|
|
12
|
+
from?: Date;
|
|
13
|
+
to?: Date;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Flat dotted key capability map describing which filter features a backend supports.
|
|
17
|
+
* Use flat dotted keys for logical operators, fields, and negations:
|
|
18
|
+
* - `logical.*`: Operator support (and, or, not, notNested)
|
|
19
|
+
* - `fields.*`: Field filtering support
|
|
20
|
+
* - `negation.*`: Negation support for specific fields
|
|
21
|
+
*
|
|
22
|
+
* When a backend implements getFilterCapabilities(), missing keys default to true (supported),
|
|
23
|
+
* ensuring forward compatibility when new capabilities are added.
|
|
24
|
+
* Backends that don't implement getFilterCapabilities() are treated as supporting all features.
|
|
25
|
+
*/
|
|
26
|
+
export type NotificationFilterCapabilities = {
|
|
27
|
+
[key: string]: boolean;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Leaf-level filter conditions for notification fields.
|
|
31
|
+
* All specified fields are combined with implicit AND.
|
|
32
|
+
*/
|
|
33
|
+
export type NotificationFilterFields<Config extends BaseNotificationTypeConfig> = {
|
|
34
|
+
status?: NotificationStatus | NotificationStatus[];
|
|
35
|
+
notificationType?: NotificationType | NotificationType[];
|
|
36
|
+
adapterUsed?: string | string[];
|
|
37
|
+
userId?: Config['UserIdType'];
|
|
38
|
+
bodyTemplate?: string;
|
|
39
|
+
subjectTemplate?: string;
|
|
40
|
+
contextName?: string;
|
|
41
|
+
sendAfterRange?: DateRange;
|
|
42
|
+
createdAtRange?: DateRange;
|
|
43
|
+
sentAtRange?: DateRange;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Composable notification filter supporting logical operators.
|
|
47
|
+
*
|
|
48
|
+
* - A plain field filter applies all conditions with implicit AND.
|
|
49
|
+
* - `{ and: [...] }` requires all sub-filters to match.
|
|
50
|
+
* - `{ or: [...] }` requires at least one sub-filter to match.
|
|
51
|
+
* - `{ not: filter }` inverts the sub-filter.
|
|
52
|
+
*/
|
|
53
|
+
export type NotificationFilter<Config extends BaseNotificationTypeConfig> = NotificationFilterFields<Config> | {
|
|
54
|
+
and: NotificationFilter<Config>[];
|
|
55
|
+
} | {
|
|
56
|
+
or: NotificationFilter<Config>[];
|
|
57
|
+
} | {
|
|
58
|
+
not: NotificationFilter<Config>;
|
|
59
|
+
};
|
|
6
60
|
export interface BaseNotificationBackend<Config extends BaseNotificationTypeConfig> {
|
|
7
61
|
getAllPendingNotifications(): Promise<AnyDatabaseNotification<Config>[]>;
|
|
8
62
|
getPendingNotifications(page: number, pageSize: number): Promise<AnyDatabaseNotification<Config>[]>;
|
|
@@ -23,12 +77,40 @@ export interface BaseNotificationBackend<Config extends BaseNotificationTypeConf
|
|
|
23
77
|
filterAllInAppUnreadNotifications(userId: Config['UserIdType']): Promise<DatabaseNotification<Config>[]>;
|
|
24
78
|
filterInAppUnreadNotifications(userId: Config['UserIdType'], page: number, pageSize: number): Promise<DatabaseNotification<Config>[]>;
|
|
25
79
|
getUserEmailFromNotification(notificationId: Config['NotificationIdType']): Promise<string | undefined>;
|
|
26
|
-
|
|
80
|
+
storeAdapterAndContextUsed(notificationId: Config['NotificationIdType'], adapterKey: string, context: InputJsonValue): Promise<void>;
|
|
27
81
|
persistOneOffNotification(notification: Omit<OneOffNotificationInput<Config>, 'id'>): Promise<DatabaseOneOffNotification<Config>>;
|
|
28
82
|
persistOneOffNotificationUpdate(notificationId: Config['NotificationIdType'], notification: Partial<Omit<OneOffNotificationInput<Config>, 'id'>>): Promise<DatabaseOneOffNotification<Config>>;
|
|
29
83
|
getOneOffNotification(notificationId: Config['NotificationIdType'], forUpdate: boolean): Promise<DatabaseOneOffNotification<Config> | null>;
|
|
30
84
|
getAllOneOffNotifications(): Promise<DatabaseOneOffNotification<Config>[]>;
|
|
31
85
|
getOneOffNotifications(page: number, pageSize: number): Promise<DatabaseOneOffNotification<Config>[]>;
|
|
86
|
+
/**
|
|
87
|
+
* Filter notifications using composable query filters.
|
|
88
|
+
* Supports filtering by status, notification type, adapter, recipient,
|
|
89
|
+
* body/subject templates, context, and date ranges (sendAfter, created, sent).
|
|
90
|
+
* Filters can be combined with logical operators (and, or, not).
|
|
91
|
+
*
|
|
92
|
+
* @param filter - Composable filter expression
|
|
93
|
+
* @param page - Page number (1-indexed) for pagination
|
|
94
|
+
* @param pageSize - Number of results per page
|
|
95
|
+
* @returns Matching notifications
|
|
96
|
+
*/
|
|
97
|
+
filterNotifications(filter: NotificationFilter<Config>, page: number, pageSize: number): Promise<AnyDatabaseNotification<Config>[]>;
|
|
98
|
+
/**
|
|
99
|
+
* Get the filter capabilities supported by this backend.
|
|
100
|
+
* Returns an object with flat dotted keys indicating which filtering features are supported.
|
|
101
|
+
*
|
|
102
|
+
* Example capability names:
|
|
103
|
+
* - `logical.and`, `logical.or`, `logical.not`, `logical.notNested`
|
|
104
|
+
* - `fields.status`, `fields.notificationType`, `fields.adapterUsed`, `fields.userId`,
|
|
105
|
+
* `fields.bodyTemplate`, `fields.subjectTemplate`, `fields.contextName`,
|
|
106
|
+
* `fields.sendAfterRange`, `fields.createdAtRange`, `fields.sentAtRange`
|
|
107
|
+
* - `negation.sendAfterRange`, `negation.createdAtRange`, `negation.sentAtRange`
|
|
108
|
+
*
|
|
109
|
+
* If this method is not implemented, all features are assumed to be supported.
|
|
110
|
+
* If this method is implemented, missing keys default to true (supported) for forward compatibility.
|
|
111
|
+
* Only explicitly set keys to false to indicate unsupported features.
|
|
112
|
+
*/
|
|
113
|
+
getFilterCapabilities?(): NotificationFilterCapabilities;
|
|
32
114
|
/**
|
|
33
115
|
* Inject logger into backend for debugging and monitoring
|
|
34
116
|
*/
|
|
@@ -73,6 +155,10 @@ export interface BaseNotificationBackend<Config extends BaseNotificationTypeConf
|
|
|
73
155
|
*/
|
|
74
156
|
deleteNotificationAttachment?(notificationId: Config['NotificationIdType'], attachmentId: string): Promise<void>;
|
|
75
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Type guard to check if a filter is a field filter (leaf node).
|
|
160
|
+
*/
|
|
161
|
+
export declare function isFieldFilter<Config extends BaseNotificationTypeConfig>(filter: NotificationFilter<Config>): filter is NotificationFilterFields<Config>;
|
|
76
162
|
/**
|
|
77
163
|
* Type guard to check if backend supports attachment operations
|
|
78
164
|
*/
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isFieldFilter = isFieldFilter;
|
|
3
4
|
exports.supportsAttachments = supportsAttachments;
|
|
5
|
+
/**
|
|
6
|
+
* Type guard to check if a filter is a field filter (leaf node).
|
|
7
|
+
*/
|
|
8
|
+
function isFieldFilter(filter) {
|
|
9
|
+
return !('and' in filter) && !('or' in filter) && !('not' in filter);
|
|
10
|
+
}
|
|
4
11
|
/**
|
|
5
12
|
* Type guard to check if backend supports attachment operations
|
|
6
13
|
*/
|
|
@@ -77,6 +77,7 @@ class VintaSend {
|
|
|
77
77
|
this.queueService = queueService;
|
|
78
78
|
}
|
|
79
79
|
async send(notification) {
|
|
80
|
+
var _a;
|
|
80
81
|
const adaptersOfType = this.adapters.filter((adapter) => adapter.notificationType === notification.notificationType);
|
|
81
82
|
if (adaptersOfType.length === 0) {
|
|
82
83
|
this.logger.error(`No adapter found for notification type ${notification.notificationType}`);
|
|
@@ -144,10 +145,10 @@ class VintaSend {
|
|
|
144
145
|
this.logger.error(`Error marking notification ${notification.id} as sent: ${markSentError}`);
|
|
145
146
|
}
|
|
146
147
|
try {
|
|
147
|
-
await this.backend.
|
|
148
|
+
await this.backend.storeAdapterAndContextUsed(notification.id, (_a = adapter.key) !== null && _a !== void 0 ? _a : 'unknown', context !== null && context !== void 0 ? context : {});
|
|
148
149
|
}
|
|
149
150
|
catch (storeContextError) {
|
|
150
|
-
this.logger.error(`Error storing context for notification ${notification.id}: ${storeContextError}`);
|
|
151
|
+
this.logger.error(`Error storing adapter and context for notification ${notification.id}: ${storeContextError}`);
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
}
|
|
@@ -347,6 +348,7 @@ class VintaSend {
|
|
|
347
348
|
return createdNotification;
|
|
348
349
|
}
|
|
349
350
|
async delayedSend(notificationId) {
|
|
351
|
+
var _a;
|
|
350
352
|
const notification = await this.getNotification(notificationId, false);
|
|
351
353
|
if (!notification) {
|
|
352
354
|
this.logger.error(`Notification ${notificationId} not found`);
|
|
@@ -364,7 +366,9 @@ class VintaSend {
|
|
|
364
366
|
return;
|
|
365
367
|
}
|
|
366
368
|
const context = await this.getNotificationContext(notification.contextName, notification.contextParameters);
|
|
369
|
+
let lastAdapterKey = 'unknown';
|
|
367
370
|
for (const adapter of enqueueNotificationsAdapters) {
|
|
371
|
+
lastAdapterKey = (_a = adapter.key) !== null && _a !== void 0 ? _a : 'unknown';
|
|
368
372
|
try {
|
|
369
373
|
await adapter.send(notification, context);
|
|
370
374
|
}
|
|
@@ -385,10 +389,10 @@ class VintaSend {
|
|
|
385
389
|
}
|
|
386
390
|
}
|
|
387
391
|
try {
|
|
388
|
-
await this.backend.
|
|
392
|
+
await this.backend.storeAdapterAndContextUsed(notification.id, lastAdapterKey, context);
|
|
389
393
|
}
|
|
390
394
|
catch (storeContextError) {
|
|
391
|
-
this.logger.error(`Error storing context for notification ${notification.id}: ${storeContextError}`);
|
|
395
|
+
this.logger.error(`Error storing adapter and context for notification ${notification.id}: ${storeContextError}`);
|
|
392
396
|
}
|
|
393
397
|
}
|
|
394
398
|
async bulkPersistNotifications(notifications) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vintasend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"test": "jest",
|
|
17
17
|
"test:watch": "jest --watch",
|
|
18
18
|
"test:coverage": "jest --coverage",
|
|
19
|
+
"test:implementations:local": "node scripts/test-implementations-local.js",
|
|
19
20
|
"release:bump": "node scripts/release-bump.js",
|
|
20
21
|
"release:bump:patch": "node scripts/release-bump.js --bump=patch",
|
|
21
22
|
"release:bump:minor": "node scripts/release-bump.js --bump=minor",
|