vintasend 0.6.2 → 0.7.1
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 +84 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/services/git-commit-sha/base-git-commit-sha-provider.d.ts +45 -0
- package/dist/services/git-commit-sha/base-git-commit-sha-provider.js +2 -0
- package/dist/services/git-commit-sha/index.d.ts +1 -0
- package/dist/services/git-commit-sha/index.js +2 -0
- package/dist/services/notification-adapters/base-notification-adapter.d.ts +3 -1
- package/dist/services/notification-adapters/base-notification-adapter.js +7 -0
- package/dist/services/notification-backends/base-notification-backend.d.ts +14 -3
- package/dist/services/notification-backends/base-notification-backend.js +8 -0
- package/dist/services/notification-service.d.ts +34 -2
- package/dist/services/notification-service.js +94 -62
- package/dist/services/notification-template-renderers/base-email-template-renderer.d.ts +5 -0
- package/dist/services/notification-template-renderers/base-notification-template-renderer.d.ts +1 -0
- package/dist/types/notification.d.ts +3 -0
- package/dist/types/one-off-notification.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -244,8 +244,92 @@ One-off notifications are stored in the same table as regular notifications usin
|
|
|
244
244
|
- **Regular notifications**: Have `userId` set, `emailOrPhone` is null
|
|
245
245
|
- **One-off notifications**: Have `emailOrPhone` set, `userId` is null
|
|
246
246
|
|
|
247
|
+
## Git Commit SHA Tracking
|
|
248
|
+
|
|
249
|
+
VintaSend can persist which source-code version executed each notification send/render flow.
|
|
250
|
+
|
|
251
|
+
### Field behavior
|
|
252
|
+
|
|
253
|
+
- Persisted notifications include `gitCommitSha: string | null`
|
|
254
|
+
- Notification input/resend payloads do not accept this field (`gitCommitSha` is system-managed)
|
|
255
|
+
- The SHA is resolved at execution time (send/render), not creation time
|
|
256
|
+
|
|
257
|
+
### Configuring a provider
|
|
258
|
+
|
|
259
|
+
Use `BaseGitCommitShaProvider` with `VintaSendFactory.create(...)`.
|
|
260
|
+
|
|
261
|
+
Preferred factory style (object parameter):
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import { VintaSendFactory, type BaseGitCommitShaProvider } from 'vintasend';
|
|
265
|
+
|
|
266
|
+
const gitCommitShaProvider: BaseGitCommitShaProvider = {
|
|
267
|
+
getCurrentGitCommitSha: () => process.env.GIT_COMMIT_SHA ?? null,
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const vintaSend = new VintaSendFactory<NotificationTypeConfig>().create({
|
|
271
|
+
adapters: [adapter],
|
|
272
|
+
backend,
|
|
273
|
+
logger,
|
|
274
|
+
contextGeneratorsMap,
|
|
275
|
+
gitCommitShaProvider,
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
> Positional `create(...)` parameters are still supported for backward compatibility, but object-style configuration is recommended.
|
|
280
|
+
|
|
281
|
+
### Provider examples
|
|
282
|
+
|
|
283
|
+
Environment variable (CI/CD injected):
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
const envProvider: BaseGitCommitShaProvider = {
|
|
287
|
+
getCurrentGitCommitSha: () => process.env.GIT_COMMIT_SHA ?? null,
|
|
288
|
+
};
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Shell command (async):
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import { execSync } from 'node:child_process';
|
|
295
|
+
|
|
296
|
+
const shellProvider: BaseGitCommitShaProvider = {
|
|
297
|
+
getCurrentGitCommitSha: async () => {
|
|
298
|
+
try {
|
|
299
|
+
return execSync('git rev-parse HEAD', { encoding: 'utf-8' }).trim() || null;
|
|
300
|
+
} catch {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Static provider (for deterministic environments):
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
const staticProvider: BaseGitCommitShaProvider = {
|
|
311
|
+
getCurrentGitCommitSha: () => '0123456789abcdef0123456789abcdef01234567',
|
|
312
|
+
};
|
|
313
|
+
```
|
|
314
|
+
|
|
247
315
|
### Migration Guides
|
|
248
316
|
|
|
317
|
+
#### Migrating to v0.7.0 (Git Commit SHA Tracking)
|
|
318
|
+
|
|
319
|
+
This migration is only needed if you're using the Prisma backend.
|
|
320
|
+
|
|
321
|
+
`gitCommitSha` is now persisted on notifications (regular and one-off) as a nullable, system-managed field.
|
|
322
|
+
|
|
323
|
+
1. Update your Prisma schema (`Notification` model):
|
|
324
|
+
- Add `gitCommitSha String?`
|
|
325
|
+
- Add `@@index([gitCommitSha])`
|
|
326
|
+
2. Run a migration in your app:
|
|
327
|
+
```bash
|
|
328
|
+
prisma migrate dev --name add-notification-git-commit-sha
|
|
329
|
+
```
|
|
330
|
+
3. Optionally configure a `BaseGitCommitShaProvider` in `VintaSendFactory`.
|
|
331
|
+
4. Do not add `gitCommitSha` to notification create/resend input payloads (it is provider-managed).
|
|
332
|
+
|
|
249
333
|
#### Migrating to v0.4.0 (Attachment Support)
|
|
250
334
|
|
|
251
335
|
Version 0.4.0 introduces file attachment support with a **breaking change** to the `VintaSendFactory.create()` method signature.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export { BaseAttachmentManager } from './services/attachment-manager/base-attachment-manager';
|
|
2
2
|
export type { LocalFileAttachmentManagerConfig } from './services/attachment-manager/local-file-attachment-manager';
|
|
3
3
|
export { LocalFileAttachmentManager } from './services/attachment-manager/local-file-attachment-manager';
|
|
4
|
+
export type { BaseGitCommitShaProvider } from './services/git-commit-sha/base-git-commit-sha-provider';
|
|
4
5
|
export { BaseNotificationAdapter, isOneOffNotification, } from './services/notification-adapters/base-notification-adapter';
|
|
5
6
|
export type { BaseNotificationBackend } from './services/notification-backends/base-notification-backend';
|
|
6
7
|
export { supportsAttachments, isFieldFilter } from './services/notification-backends/base-notification-backend';
|
|
7
|
-
export type { NotificationFilter, NotificationFilterFields, DateRange, NotificationFilterCapabilities } from './services/notification-backends/base-notification-backend';
|
|
8
|
+
export type { NotificationFilter, NotificationFilterFields, DateRange, NotificationFilterCapabilities, StringFilterLookup, StringFieldFilter, } from './services/notification-backends/base-notification-backend';
|
|
8
9
|
export type { BaseNotificationQueueService } from './services/notification-queue-service/base-notification-queue-service';
|
|
9
10
|
export type { VintaSend } from './services/notification-service';
|
|
10
11
|
export { VintaSendFactory } from './services/notification-service';
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ var base_attachment_manager_1 = require("./services/attachment-manager/base-atta
|
|
|
6
6
|
Object.defineProperty(exports, "BaseAttachmentManager", { enumerable: true, get: function () { return base_attachment_manager_1.BaseAttachmentManager; } });
|
|
7
7
|
var local_file_attachment_manager_1 = require("./services/attachment-manager/local-file-attachment-manager");
|
|
8
8
|
Object.defineProperty(exports, "LocalFileAttachmentManager", { enumerable: true, get: function () { return local_file_attachment_manager_1.LocalFileAttachmentManager; } });
|
|
9
|
+
// Notification Adapters and Backends
|
|
9
10
|
var base_notification_adapter_1 = require("./services/notification-adapters/base-notification-adapter");
|
|
10
11
|
Object.defineProperty(exports, "BaseNotificationAdapter", { enumerable: true, get: function () { return base_notification_adapter_1.BaseNotificationAdapter; } });
|
|
11
12
|
Object.defineProperty(exports, "isOneOffNotification", { enumerable: true, get: function () { return base_notification_adapter_1.isOneOffNotification; } });
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base interface for Git commit SHA providers.
|
|
3
|
+
*
|
|
4
|
+
* Implementations of this interface can resolve the current Git commit SHA
|
|
5
|
+
* at runtime, allowing VintaSend to automatically track which version of code
|
|
6
|
+
* produced each notification render.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* // Environment variable provider
|
|
11
|
+
* class EnvGitCommitShaProvider implements BaseGitCommitShaProvider {
|
|
12
|
+
* getCurrentGitCommitSha(): string | null {
|
|
13
|
+
* return process.env.GIT_COMMIT_SHA || null;
|
|
14
|
+
* }
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* // Shell command provider (for development)
|
|
18
|
+
* class ShellGitCommitShaProvider implements BaseGitCommitShaProvider {
|
|
19
|
+
* async getCurrentGitCommitSha(): Promise<string | null> {
|
|
20
|
+
* try {
|
|
21
|
+
* const { execSync } = require('child_process');
|
|
22
|
+
* const sha = execSync('git rev-parse HEAD', { encoding: 'utf-8' }).trim();
|
|
23
|
+
* return sha || null;
|
|
24
|
+
* } catch {
|
|
25
|
+
* return null;
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export interface BaseGitCommitShaProvider {
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the current Git commit SHA.
|
|
34
|
+
*
|
|
35
|
+
* Must return:
|
|
36
|
+
* - Full 40-character hex SHA (will be normalized to lowercase)
|
|
37
|
+
* - null if SHA cannot be determined
|
|
38
|
+
*
|
|
39
|
+
* May return synchronously or asynchronously.
|
|
40
|
+
* Invalid SHA formats will be rejected at notification creation boundary.
|
|
41
|
+
*
|
|
42
|
+
* @returns The current Git commit SHA, or null if unavailable
|
|
43
|
+
*/
|
|
44
|
+
getCurrentGitCommitSha(): string | Promise<string | null> | null;
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { BaseGitCommitShaProvider } from './base-git-commit-sha-provider';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { StoredAttachment } from '../../types/attachment';
|
|
2
|
-
import type { JsonValue } from '../../types/json-values';
|
|
2
|
+
import type { JsonObject, JsonValue } from '../../types/json-values';
|
|
3
3
|
import type { AnyDatabaseNotification, DatabaseOneOffNotification } from '../../types/notification';
|
|
4
4
|
import type { NotificationType } from '../../types/notification-type';
|
|
5
5
|
import type { BaseNotificationTypeConfig } from '../../types/notification-type-config';
|
|
6
|
+
import type { EmailTemplate, EmailTemplateContent } from '../notification-template-renderers/base-email-template-renderer';
|
|
6
7
|
import type { BaseLogger } from '../loggers/base-logger';
|
|
7
8
|
import type { BaseNotificationBackend } from '../notification-backends/base-notification-backend';
|
|
8
9
|
import type { BaseNotificationTemplateRenderer } from '../notification-template-renderers/base-notification-template-renderer';
|
|
@@ -45,4 +46,5 @@ export declare abstract class BaseNotificationAdapter<TemplateRenderer extends B
|
|
|
45
46
|
};
|
|
46
47
|
injectBackend(backend: BaseNotificationBackend<Config>): void;
|
|
47
48
|
injectLogger(logger: BaseLogger): void;
|
|
49
|
+
renderFromTemplateContent(notification: AnyDatabaseNotification<Config>, templateContent: EmailTemplateContent, context: JsonObject): Promise<EmailTemplate>;
|
|
48
50
|
}
|
|
@@ -90,5 +90,12 @@ class BaseNotificationAdapter {
|
|
|
90
90
|
injectLogger(logger) {
|
|
91
91
|
this.logger = logger;
|
|
92
92
|
}
|
|
93
|
+
async renderFromTemplateContent(notification, templateContent, context) {
|
|
94
|
+
if (typeof this.templateRenderer.renderFromTemplateContent !== 'function') {
|
|
95
|
+
throw new Error('Template renderer does not support renderFromTemplateContent.');
|
|
96
|
+
}
|
|
97
|
+
const rendered = await this.templateRenderer.renderFromTemplateContent(notification, templateContent, context);
|
|
98
|
+
return rendered;
|
|
99
|
+
}
|
|
93
100
|
}
|
|
94
101
|
exports.BaseNotificationAdapter = BaseNotificationAdapter;
|
|
@@ -12,6 +12,12 @@ export type DateRange = {
|
|
|
12
12
|
from?: Date;
|
|
13
13
|
to?: Date;
|
|
14
14
|
};
|
|
15
|
+
export type StringFilterLookup = {
|
|
16
|
+
lookup: 'exact' | 'startsWith' | 'endsWith' | 'includes';
|
|
17
|
+
value: string;
|
|
18
|
+
caseSensitive?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type StringFieldFilter = string | StringFilterLookup;
|
|
15
21
|
/**
|
|
16
22
|
* Flat dotted key capability map describing which filter features a backend supports.
|
|
17
23
|
* Use flat dotted keys for logical operators, fields, and negations:
|
|
@@ -35,9 +41,9 @@ export type NotificationFilterFields<Config extends BaseNotificationTypeConfig>
|
|
|
35
41
|
notificationType?: NotificationType | NotificationType[];
|
|
36
42
|
adapterUsed?: string | string[];
|
|
37
43
|
userId?: Config['UserIdType'];
|
|
38
|
-
bodyTemplate?:
|
|
39
|
-
subjectTemplate?:
|
|
40
|
-
contextName?:
|
|
44
|
+
bodyTemplate?: StringFieldFilter;
|
|
45
|
+
subjectTemplate?: StringFieldFilter;
|
|
46
|
+
contextName?: StringFieldFilter;
|
|
41
47
|
sendAfterRange?: DateRange;
|
|
42
48
|
createdAtRange?: DateRange;
|
|
43
49
|
sentAtRange?: DateRange;
|
|
@@ -75,6 +81,10 @@ export declare const DEFAULT_BACKEND_FILTER_CAPABILITIES: {
|
|
|
75
81
|
'negation.sendAfterRange': boolean;
|
|
76
82
|
'negation.createdAtRange': boolean;
|
|
77
83
|
'negation.sentAtRange': boolean;
|
|
84
|
+
'stringLookups.startsWith': boolean;
|
|
85
|
+
'stringLookups.endsWith': boolean;
|
|
86
|
+
'stringLookups.includes': boolean;
|
|
87
|
+
'stringLookups.caseInsensitive': boolean;
|
|
78
88
|
};
|
|
79
89
|
export interface BaseNotificationBackend<Config extends BaseNotificationTypeConfig> {
|
|
80
90
|
getAllPendingNotifications(): Promise<AnyDatabaseNotification<Config>[]>;
|
|
@@ -178,6 +188,7 @@ export interface BaseNotificationBackend<Config extends BaseNotificationTypeConf
|
|
|
178
188
|
* Type guard to check if a filter is a field filter (leaf node).
|
|
179
189
|
*/
|
|
180
190
|
export declare function isFieldFilter<Config extends BaseNotificationTypeConfig>(filter: NotificationFilter<Config>): filter is NotificationFilterFields<Config>;
|
|
191
|
+
export declare function isStringFilterLookup(value: StringFieldFilter): value is StringFilterLookup;
|
|
181
192
|
/**
|
|
182
193
|
* Type guard to check if backend supports attachment operations
|
|
183
194
|
*/
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DEFAULT_BACKEND_FILTER_CAPABILITIES = void 0;
|
|
4
4
|
exports.isFieldFilter = isFieldFilter;
|
|
5
|
+
exports.isStringFilterLookup = isStringFilterLookup;
|
|
5
6
|
exports.supportsAttachments = supportsAttachments;
|
|
6
7
|
exports.DEFAULT_BACKEND_FILTER_CAPABILITIES = {
|
|
7
8
|
'logical.and': true,
|
|
@@ -21,6 +22,10 @@ exports.DEFAULT_BACKEND_FILTER_CAPABILITIES = {
|
|
|
21
22
|
'negation.sendAfterRange': true,
|
|
22
23
|
'negation.createdAtRange': true,
|
|
23
24
|
'negation.sentAtRange': true,
|
|
25
|
+
'stringLookups.startsWith': true,
|
|
26
|
+
'stringLookups.endsWith': true,
|
|
27
|
+
'stringLookups.includes': true,
|
|
28
|
+
'stringLookups.caseInsensitive': true,
|
|
24
29
|
};
|
|
25
30
|
/**
|
|
26
31
|
* Type guard to check if a filter is a field filter (leaf node).
|
|
@@ -28,6 +33,9 @@ exports.DEFAULT_BACKEND_FILTER_CAPABILITIES = {
|
|
|
28
33
|
function isFieldFilter(filter) {
|
|
29
34
|
return !('and' in filter) && !('or' in filter) && !('not' in filter);
|
|
30
35
|
}
|
|
36
|
+
function isStringFilterLookup(value) {
|
|
37
|
+
return typeof value === 'object' && value !== null && 'lookup' in value && 'value' in value;
|
|
38
|
+
}
|
|
31
39
|
/**
|
|
32
40
|
* Type guard to check if backend supports attachment operations
|
|
33
41
|
*/
|
|
@@ -3,14 +3,32 @@ import type { AnyDatabaseNotification, AnyNotification, DatabaseNotification, Da
|
|
|
3
3
|
import type { BaseNotificationTypeConfig } from '../types/notification-type-config';
|
|
4
4
|
import type { OneOffNotificationInput } from '../types/one-off-notification';
|
|
5
5
|
import type { BaseAttachmentManager } from './attachment-manager/base-attachment-manager';
|
|
6
|
+
import type { BaseGitCommitShaProvider } from './git-commit-sha/base-git-commit-sha-provider';
|
|
6
7
|
import type { BaseLogger } from './loggers/base-logger';
|
|
7
8
|
import { type BaseNotificationAdapter } from './notification-adapters/base-notification-adapter';
|
|
8
9
|
import { type BaseNotificationBackend, type NotificationFilterFields } from './notification-backends/base-notification-backend';
|
|
9
10
|
import type { BaseNotificationQueueService } from './notification-queue-service/base-notification-queue-service';
|
|
11
|
+
import type { EmailTemplate, EmailTemplateContent } from './notification-template-renderers/base-email-template-renderer';
|
|
10
12
|
import type { BaseNotificationTemplateRenderer } from './notification-template-renderers/base-notification-template-renderer';
|
|
11
13
|
type VintaSendOptions = {
|
|
12
14
|
raiseErrorOnFailedSend: boolean;
|
|
13
15
|
};
|
|
16
|
+
type RenderEmailTemplateContextInput<Config extends BaseNotificationTypeConfig> = {
|
|
17
|
+
context: JsonObject;
|
|
18
|
+
} | {
|
|
19
|
+
contextName: string & keyof Config['ContextMap'];
|
|
20
|
+
contextParameters: JsonObject;
|
|
21
|
+
};
|
|
22
|
+
type VintaSendFactoryCreateParams<Config extends BaseNotificationTypeConfig, AdaptersList extends BaseNotificationAdapter<BaseNotificationTemplateRenderer<Config>, Config>[], Backend extends BaseNotificationBackend<Config>, Logger extends BaseLogger, QueueService extends BaseNotificationQueueService<Config>, AttachmentMgr extends BaseAttachmentManager> = {
|
|
23
|
+
adapters: AdaptersList;
|
|
24
|
+
backend: Backend;
|
|
25
|
+
logger: Logger;
|
|
26
|
+
contextGeneratorsMap: BaseNotificationTypeConfig['ContextMap'];
|
|
27
|
+
queueService?: QueueService;
|
|
28
|
+
attachmentManager?: AttachmentMgr;
|
|
29
|
+
options?: VintaSendOptions;
|
|
30
|
+
gitCommitShaProvider?: BaseGitCommitShaProvider;
|
|
31
|
+
};
|
|
14
32
|
export declare class VintaSendFactory<Config extends BaseNotificationTypeConfig> {
|
|
15
33
|
/**
|
|
16
34
|
* Creates a new VintaSend notification service instance
|
|
@@ -38,7 +56,11 @@ export declare class VintaSendFactory<Config extends BaseNotificationTypeConfig>
|
|
|
38
56
|
* @since v0.4.0 - BREAKING CHANGE: attachmentManager parameter added before options
|
|
39
57
|
* @see https://github.com/vintasoftware/vintasend-ts/blob/main/README.md#migrating-to-v040-attachment-support
|
|
40
58
|
*/
|
|
41
|
-
create<AdaptersList extends BaseNotificationAdapter<BaseNotificationTemplateRenderer<Config>, Config>[], Backend extends BaseNotificationBackend<Config>, Logger extends BaseLogger, QueueService extends BaseNotificationQueueService<Config>, AttachmentMgr extends BaseAttachmentManager>(
|
|
59
|
+
create<AdaptersList extends BaseNotificationAdapter<BaseNotificationTemplateRenderer<Config>, Config>[], Backend extends BaseNotificationBackend<Config>, Logger extends BaseLogger, QueueService extends BaseNotificationQueueService<Config>, AttachmentMgr extends BaseAttachmentManager>(params: VintaSendFactoryCreateParams<Config, AdaptersList, Backend, Logger, QueueService, AttachmentMgr>): VintaSend<Config, AdaptersList, Backend, Logger, QueueService, AttachmentMgr>;
|
|
60
|
+
/**
|
|
61
|
+
* @deprecated Use the object parameter overload instead.
|
|
62
|
+
*/
|
|
63
|
+
create<AdaptersList extends BaseNotificationAdapter<BaseNotificationTemplateRenderer<Config>, Config>[], Backend extends BaseNotificationBackend<Config>, Logger extends BaseLogger, QueueService extends BaseNotificationQueueService<Config>, AttachmentMgr extends BaseAttachmentManager>(adapters: AdaptersList, backend: Backend, logger: Logger, contextGeneratorsMap: BaseNotificationTypeConfig['ContextMap'], queueService?: QueueService, attachmentManager?: AttachmentMgr, options?: VintaSendOptions, gitCommitShaProvider?: BaseGitCommitShaProvider): VintaSend<Config, AdaptersList, Backend, Logger, QueueService, AttachmentMgr>;
|
|
42
64
|
}
|
|
43
65
|
export declare class VintaSend<Config extends BaseNotificationTypeConfig, AdaptersList extends BaseNotificationAdapter<BaseNotificationTemplateRenderer<Config>, Config>[], Backend extends BaseNotificationBackend<Config>, Logger extends BaseLogger, QueueService extends BaseNotificationQueueService<Config>, AttachmentMgr extends BaseAttachmentManager> {
|
|
44
66
|
private adapters;
|
|
@@ -47,9 +69,14 @@ export declare class VintaSend<Config extends BaseNotificationTypeConfig, Adapte
|
|
|
47
69
|
private queueService?;
|
|
48
70
|
private attachmentManager?;
|
|
49
71
|
private options;
|
|
72
|
+
private gitCommitShaProvider?;
|
|
50
73
|
private contextGeneratorsMap;
|
|
51
|
-
constructor(adapters: AdaptersList, backend: Backend, logger: Logger, contextGeneratorsMap: Config['ContextMap'], queueService?: QueueService | undefined, attachmentManager?: AttachmentMgr | undefined, options?: VintaSendOptions);
|
|
74
|
+
constructor(adapters: AdaptersList, backend: Backend, logger: Logger, contextGeneratorsMap: Config['ContextMap'], queueService?: QueueService | undefined, attachmentManager?: AttachmentMgr | undefined, options?: VintaSendOptions, gitCommitShaProvider?: BaseGitCommitShaProvider | undefined);
|
|
52
75
|
registerQueueService(queueService: QueueService): void;
|
|
76
|
+
private normalizeGitCommitSha;
|
|
77
|
+
private resolveGitCommitShaForExecution;
|
|
78
|
+
private persistGitCommitShaForExecution;
|
|
79
|
+
private resolveAndPersistGitCommitShaForExecution;
|
|
53
80
|
send(notification: AnyDatabaseNotification<Config>): Promise<void>;
|
|
54
81
|
createNotification(notification: Omit<Notification<Config>, 'id'>): Promise<DatabaseNotification<Config>>;
|
|
55
82
|
updateNotification(notificationId: Config['NotificationIdType'], notification: Partial<Omit<Notification<Config>, 'id'>>): Promise<DatabaseNotification<Config>>;
|
|
@@ -81,6 +108,7 @@ export declare class VintaSend<Config extends BaseNotificationTypeConfig, Adapte
|
|
|
81
108
|
getFutureNotificationsFromUser(userId: Config['NotificationIdType'], page: number, pageSize: number): Promise<DatabaseNotification<Config>[]>;
|
|
82
109
|
getFutureNotifications(page: number, pageSize: number): Promise<AnyDatabaseNotification<Config>[]>;
|
|
83
110
|
getNotificationContext<ContextName extends string & keyof Config['ContextMap']>(contextName: ContextName, parameters: Parameters<ReturnType<typeof this.contextGeneratorsMap.getContextGenerator<ContextName>>['generate']>[0]): Promise<JsonObject>;
|
|
111
|
+
renderEmailTemplateFromContent(notification: AnyDatabaseNotification<Config>, templateContent: EmailTemplateContent, contextInput: RenderEmailTemplateContextInput<Config>): Promise<EmailTemplate>;
|
|
84
112
|
sendPendingNotifications(): Promise<void>;
|
|
85
113
|
getPendingNotifications(page: number, pageSize: number): Promise<AnyDatabaseNotification<Config>[]>;
|
|
86
114
|
getNotifications(page: number, pageSize: number): Promise<AnyDatabaseNotification<Config>[]>;
|
|
@@ -105,6 +133,10 @@ export declare class VintaSend<Config extends BaseNotificationTypeConfig, Adapte
|
|
|
105
133
|
'negation.sendAfterRange': boolean;
|
|
106
134
|
'negation.createdAtRange': boolean;
|
|
107
135
|
'negation.sentAtRange': boolean;
|
|
136
|
+
'stringLookups.startsWith': boolean;
|
|
137
|
+
'stringLookups.endsWith': boolean;
|
|
138
|
+
'stringLookups.includes': boolean;
|
|
139
|
+
'stringLookups.caseInsensitive': boolean;
|
|
108
140
|
}>;
|
|
109
141
|
/**
|
|
110
142
|
* Gets a one-off notification by ID.
|
|
@@ -5,36 +5,16 @@ const base_notification_adapter_1 = require("./notification-adapters/base-notifi
|
|
|
5
5
|
const base_notification_backend_1 = require("./notification-backends/base-notification-backend");
|
|
6
6
|
const notification_context_generators_map_1 = require("./notification-context-generators-map");
|
|
7
7
|
class VintaSendFactory {
|
|
8
|
-
|
|
9
|
-
* Creates a new VintaSend notification service instance
|
|
10
|
-
*
|
|
11
|
-
* @param adapters - Array of notification adapters (email, SMS, push, etc.)
|
|
12
|
-
* @param backend - Notification storage backend
|
|
13
|
-
* @param logger - Logger instance
|
|
14
|
-
* @param contextGeneratorsMap - Map of context generators for notification rendering
|
|
15
|
-
* @param queueService - Optional queue service for background notification processing
|
|
16
|
-
* @param attachmentManager - Optional attachment manager for file handling
|
|
17
|
-
* @param options - Optional configuration options
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* // Without attachments or options
|
|
21
|
-
* factory.create(adapters, backend, logger, contextGeneratorsMap);
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* // With queue service and options (note: pass undefined for attachmentManager)
|
|
25
|
-
* factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, undefined, { raiseErrorOnFailedSend: true });
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* // With attachments and options
|
|
29
|
-
* factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, attachmentManager, { raiseErrorOnFailedSend: true });
|
|
30
|
-
*
|
|
31
|
-
* @since v0.4.0 - BREAKING CHANGE: attachmentManager parameter added before options
|
|
32
|
-
* @see https://github.com/vintasoftware/vintasend-ts/blob/main/README.md#migrating-to-v040-attachment-support
|
|
33
|
-
*/
|
|
34
|
-
create(adapters, backend, logger, contextGeneratorsMap, queueService, attachmentManager, options = {
|
|
8
|
+
create(adaptersOrParams, backend, logger, contextGeneratorsMap, queueService, attachmentManager, options = {
|
|
35
9
|
raiseErrorOnFailedSend: false,
|
|
36
|
-
}) {
|
|
37
|
-
|
|
10
|
+
}, gitCommitShaProvider) {
|
|
11
|
+
var _a;
|
|
12
|
+
if (!Array.isArray(adaptersOrParams)) {
|
|
13
|
+
return new VintaSend(adaptersOrParams.adapters, adaptersOrParams.backend, adaptersOrParams.logger, adaptersOrParams.contextGeneratorsMap, adaptersOrParams.queueService, adaptersOrParams.attachmentManager, (_a = adaptersOrParams.options) !== null && _a !== void 0 ? _a : {
|
|
14
|
+
raiseErrorOnFailedSend: false,
|
|
15
|
+
}, adaptersOrParams.gitCommitShaProvider);
|
|
16
|
+
}
|
|
17
|
+
return new VintaSend(adaptersOrParams, backend, logger, contextGeneratorsMap, queueService, attachmentManager, options, gitCommitShaProvider);
|
|
38
18
|
}
|
|
39
19
|
}
|
|
40
20
|
exports.VintaSendFactory = VintaSendFactory;
|
|
@@ -47,13 +27,14 @@ function hasAttachmentManagerInjection(backend) {
|
|
|
47
27
|
class VintaSend {
|
|
48
28
|
constructor(adapters, backend, logger, contextGeneratorsMap, queueService, attachmentManager, options = {
|
|
49
29
|
raiseErrorOnFailedSend: false,
|
|
50
|
-
}) {
|
|
30
|
+
}, gitCommitShaProvider) {
|
|
51
31
|
this.adapters = adapters;
|
|
52
32
|
this.backend = backend;
|
|
53
33
|
this.logger = logger;
|
|
54
34
|
this.queueService = queueService;
|
|
55
35
|
this.attachmentManager = attachmentManager;
|
|
56
36
|
this.options = options;
|
|
37
|
+
this.gitCommitShaProvider = gitCommitShaProvider;
|
|
57
38
|
this.contextGeneratorsMap = new notification_context_generators_map_1.NotificationContextGeneratorsMap(contextGeneratorsMap);
|
|
58
39
|
for (const adapter of adapters) {
|
|
59
40
|
adapter.injectBackend(backend);
|
|
@@ -77,17 +58,56 @@ class VintaSend {
|
|
|
77
58
|
registerQueueService(queueService) {
|
|
78
59
|
this.queueService = queueService;
|
|
79
60
|
}
|
|
61
|
+
normalizeGitCommitSha(gitCommitSha) {
|
|
62
|
+
const normalizedSha = gitCommitSha.trim().toLowerCase();
|
|
63
|
+
if (!/^[a-f0-9]{40}$/.test(normalizedSha)) {
|
|
64
|
+
throw new Error('Invalid gitCommitSha resolved by provider. Expected a 40-character hexadecimal SHA.');
|
|
65
|
+
}
|
|
66
|
+
return normalizedSha;
|
|
67
|
+
}
|
|
68
|
+
async resolveGitCommitShaForExecution() {
|
|
69
|
+
if (!this.gitCommitShaProvider) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const resolvedGitCommitSha = await this.gitCommitShaProvider.getCurrentGitCommitSha();
|
|
73
|
+
if (resolvedGitCommitSha === null) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return this.normalizeGitCommitSha(resolvedGitCommitSha);
|
|
77
|
+
}
|
|
78
|
+
async persistGitCommitShaForExecution(notification, gitCommitSha) {
|
|
79
|
+
var _a;
|
|
80
|
+
const currentGitCommitSha = (_a = notification.gitCommitSha) !== null && _a !== void 0 ? _a : null;
|
|
81
|
+
if (currentGitCommitSha === gitCommitSha) {
|
|
82
|
+
return notification;
|
|
83
|
+
}
|
|
84
|
+
if ((0, base_notification_adapter_1.isOneOffNotification)(notification)) {
|
|
85
|
+
const oneOffNotificationUpdate = {
|
|
86
|
+
gitCommitSha,
|
|
87
|
+
};
|
|
88
|
+
return this.backend.persistOneOffNotificationUpdate(notification.id, oneOffNotificationUpdate);
|
|
89
|
+
}
|
|
90
|
+
const notificationUpdate = {
|
|
91
|
+
gitCommitSha,
|
|
92
|
+
};
|
|
93
|
+
return this.backend.persistNotificationUpdate(notification.id, notificationUpdate);
|
|
94
|
+
}
|
|
95
|
+
async resolveAndPersistGitCommitShaForExecution(notification) {
|
|
96
|
+
const gitCommitSha = await this.resolveGitCommitShaForExecution();
|
|
97
|
+
return this.persistGitCommitShaForExecution(notification, gitCommitSha);
|
|
98
|
+
}
|
|
80
99
|
async send(notification) {
|
|
81
100
|
var _a;
|
|
82
|
-
const
|
|
101
|
+
const notificationWithExecutionGitCommitSha = await this.resolveAndPersistGitCommitShaForExecution(notification);
|
|
102
|
+
const adaptersOfType = this.adapters.filter((adapter) => adapter.notificationType === notificationWithExecutionGitCommitSha.notificationType);
|
|
83
103
|
if (adaptersOfType.length === 0) {
|
|
84
|
-
this.logger.error(`No adapter found for notification type ${
|
|
104
|
+
this.logger.error(`No adapter found for notification type ${notificationWithExecutionGitCommitSha.notificationType}`);
|
|
85
105
|
if (this.options.raiseErrorOnFailedSend) {
|
|
86
|
-
throw new Error(`No adapter found for notification type ${
|
|
106
|
+
throw new Error(`No adapter found for notification type ${notificationWithExecutionGitCommitSha.notificationType}`);
|
|
87
107
|
}
|
|
88
108
|
return;
|
|
89
109
|
}
|
|
90
|
-
if (!
|
|
110
|
+
if (!notificationWithExecutionGitCommitSha.id) {
|
|
91
111
|
throw new Error("Notification wasn't created in the database. Please create it first");
|
|
92
112
|
}
|
|
93
113
|
for (const adapter of adaptersOfType) {
|
|
@@ -97,27 +117,27 @@ class VintaSend {
|
|
|
97
117
|
continue;
|
|
98
118
|
}
|
|
99
119
|
try {
|
|
100
|
-
this.logger.info(`Enqueuing notification ${
|
|
101
|
-
await this.queueService.enqueueNotification(
|
|
102
|
-
this.logger.info(`Enqueued notification ${
|
|
120
|
+
this.logger.info(`Enqueuing notification ${notificationWithExecutionGitCommitSha.id} with adapter ${adapter.key}`);
|
|
121
|
+
await this.queueService.enqueueNotification(notificationWithExecutionGitCommitSha.id);
|
|
122
|
+
this.logger.info(`Enqueued notification ${notificationWithExecutionGitCommitSha.id} with adapter ${adapter.key} successfully`);
|
|
103
123
|
continue;
|
|
104
124
|
}
|
|
105
125
|
catch (enqueueError) {
|
|
106
|
-
this.logger.error(`Error enqueuing notification ${
|
|
126
|
+
this.logger.error(`Error enqueuing notification ${notificationWithExecutionGitCommitSha.id}: ${enqueueError} with adapter ${adapter.key}`);
|
|
107
127
|
continue;
|
|
108
128
|
}
|
|
109
129
|
}
|
|
110
130
|
let context = null;
|
|
111
|
-
if (
|
|
112
|
-
context =
|
|
131
|
+
if (notificationWithExecutionGitCommitSha.contextUsed) {
|
|
132
|
+
context = notificationWithExecutionGitCommitSha.contextUsed;
|
|
113
133
|
}
|
|
114
134
|
else {
|
|
115
135
|
try {
|
|
116
|
-
context = await this.getNotificationContext(
|
|
117
|
-
this.logger.info(`Generated context for notification ${
|
|
136
|
+
context = await this.getNotificationContext(notificationWithExecutionGitCommitSha.contextName, notificationWithExecutionGitCommitSha.contextParameters);
|
|
137
|
+
this.logger.info(`Generated context for notification ${notificationWithExecutionGitCommitSha.id}`);
|
|
118
138
|
}
|
|
119
139
|
catch (contextError) {
|
|
120
|
-
this.logger.error(`Error getting context for notification ${
|
|
140
|
+
this.logger.error(`Error getting context for notification ${notificationWithExecutionGitCommitSha.id}: ${contextError}`);
|
|
121
141
|
if (this.options.raiseErrorOnFailedSend) {
|
|
122
142
|
throw contextError;
|
|
123
143
|
}
|
|
@@ -125,31 +145,31 @@ class VintaSend {
|
|
|
125
145
|
}
|
|
126
146
|
}
|
|
127
147
|
try {
|
|
128
|
-
this.logger.info(`Sending notification ${
|
|
129
|
-
await adapter.send(
|
|
130
|
-
this.logger.info(`Sent notification ${
|
|
148
|
+
this.logger.info(`Sending notification ${notificationWithExecutionGitCommitSha.id} with adapter ${adapter.key}`);
|
|
149
|
+
await adapter.send(notificationWithExecutionGitCommitSha, context);
|
|
150
|
+
this.logger.info(`Sent notification ${notificationWithExecutionGitCommitSha.id} with adapter ${adapter.key} successfully`);
|
|
131
151
|
}
|
|
132
152
|
catch (sendError) {
|
|
133
|
-
this.logger.error(`Error sending notification ${
|
|
153
|
+
this.logger.error(`Error sending notification ${notificationWithExecutionGitCommitSha.id} with adapter ${adapter.key}: ${sendError}`);
|
|
134
154
|
try {
|
|
135
|
-
await this.backend.markAsFailed(
|
|
155
|
+
await this.backend.markAsFailed(notificationWithExecutionGitCommitSha.id, true);
|
|
136
156
|
}
|
|
137
157
|
catch (markFailedError) {
|
|
138
|
-
this.logger.error(`Error marking notification ${
|
|
158
|
+
this.logger.error(`Error marking notification ${notificationWithExecutionGitCommitSha.id} as failed: ${markFailedError}`);
|
|
139
159
|
}
|
|
140
160
|
continue;
|
|
141
161
|
}
|
|
142
162
|
try {
|
|
143
|
-
await this.backend.markAsSent(
|
|
163
|
+
await this.backend.markAsSent(notificationWithExecutionGitCommitSha.id, true);
|
|
144
164
|
}
|
|
145
165
|
catch (markSentError) {
|
|
146
|
-
this.logger.error(`Error marking notification ${
|
|
166
|
+
this.logger.error(`Error marking notification ${notificationWithExecutionGitCommitSha.id} as sent: ${markSentError}`);
|
|
147
167
|
}
|
|
148
168
|
try {
|
|
149
|
-
await this.backend.storeAdapterAndContextUsed(
|
|
169
|
+
await this.backend.storeAdapterAndContextUsed(notificationWithExecutionGitCommitSha.id, (_a = adapter.key) !== null && _a !== void 0 ? _a : 'unknown', context !== null && context !== void 0 ? context : {});
|
|
150
170
|
}
|
|
151
171
|
catch (storeContextError) {
|
|
152
|
-
this.logger.error(`Error storing adapter and context for notification ${
|
|
172
|
+
this.logger.error(`Error storing adapter and context for notification ${notificationWithExecutionGitCommitSha.id}: ${storeContextError}`);
|
|
153
173
|
}
|
|
154
174
|
}
|
|
155
175
|
}
|
|
@@ -249,6 +269,17 @@ class VintaSend {
|
|
|
249
269
|
}
|
|
250
270
|
return Promise.resolve(context);
|
|
251
271
|
}
|
|
272
|
+
async renderEmailTemplateFromContent(notification, templateContent, contextInput) {
|
|
273
|
+
const adaptersOfType = this.adapters.filter((adapter) => adapter.notificationType === notification.notificationType);
|
|
274
|
+
if (adaptersOfType.length === 0) {
|
|
275
|
+
throw new Error(`No adapter found for notification type ${notification.notificationType}`);
|
|
276
|
+
}
|
|
277
|
+
const adapter = adaptersOfType[0];
|
|
278
|
+
const context = 'context' in contextInput
|
|
279
|
+
? contextInput.context
|
|
280
|
+
: await this.getNotificationContext(contextInput.contextName, contextInput.contextParameters);
|
|
281
|
+
return adapter.renderFromTemplateContent(notification, templateContent, context);
|
|
282
|
+
}
|
|
252
283
|
async sendPendingNotifications() {
|
|
253
284
|
const pendingNotifications = await this.backend.getAllPendingNotifications();
|
|
254
285
|
await Promise.all(pendingNotifications.map((notification) => this.send(notification)));
|
|
@@ -373,34 +404,35 @@ class VintaSend {
|
|
|
373
404
|
}
|
|
374
405
|
return;
|
|
375
406
|
}
|
|
376
|
-
const
|
|
407
|
+
const notificationWithExecutionGitCommitSha = await this.resolveAndPersistGitCommitShaForExecution(notification);
|
|
408
|
+
const context = await this.getNotificationContext(notificationWithExecutionGitCommitSha.contextName, notificationWithExecutionGitCommitSha.contextParameters);
|
|
377
409
|
let lastAdapterKey = 'unknown';
|
|
378
410
|
for (const adapter of enqueueNotificationsAdapters) {
|
|
379
411
|
lastAdapterKey = (_a = adapter.key) !== null && _a !== void 0 ? _a : 'unknown';
|
|
380
412
|
try {
|
|
381
|
-
await adapter.send(
|
|
413
|
+
await adapter.send(notificationWithExecutionGitCommitSha, context);
|
|
382
414
|
}
|
|
383
415
|
catch (sendError) {
|
|
384
|
-
this.logger.error(`Error sending notification ${
|
|
416
|
+
this.logger.error(`Error sending notification ${notificationWithExecutionGitCommitSha.id} with adapter ${adapter.key}: ${sendError}`);
|
|
385
417
|
try {
|
|
386
|
-
await this.backend.markAsFailed(
|
|
418
|
+
await this.backend.markAsFailed(notificationWithExecutionGitCommitSha.id, true);
|
|
387
419
|
}
|
|
388
420
|
catch (markFailedError) {
|
|
389
|
-
this.logger.error(`Error marking notification ${
|
|
421
|
+
this.logger.error(`Error marking notification ${notificationWithExecutionGitCommitSha.id} as failed: ${markFailedError}`);
|
|
390
422
|
}
|
|
391
423
|
}
|
|
392
424
|
try {
|
|
393
|
-
await this.backend.markAsSent(
|
|
425
|
+
await this.backend.markAsSent(notificationWithExecutionGitCommitSha.id, true);
|
|
394
426
|
}
|
|
395
427
|
catch (markSentError) {
|
|
396
|
-
this.logger.error(`Error marking notification ${
|
|
428
|
+
this.logger.error(`Error marking notification ${notificationWithExecutionGitCommitSha.id} as sent: ${markSentError}`);
|
|
397
429
|
}
|
|
398
430
|
}
|
|
399
431
|
try {
|
|
400
|
-
await this.backend.storeAdapterAndContextUsed(
|
|
432
|
+
await this.backend.storeAdapterAndContextUsed(notificationWithExecutionGitCommitSha.id, lastAdapterKey, context);
|
|
401
433
|
}
|
|
402
434
|
catch (storeContextError) {
|
|
403
|
-
this.logger.error(`Error storing adapter and context for notification ${
|
|
435
|
+
this.logger.error(`Error storing adapter and context for notification ${notificationWithExecutionGitCommitSha.id}: ${storeContextError}`);
|
|
404
436
|
}
|
|
405
437
|
}
|
|
406
438
|
async bulkPersistNotifications(notifications) {
|
|
@@ -9,8 +9,13 @@ export type EmailTemplate = {
|
|
|
9
9
|
subject: string;
|
|
10
10
|
body: string;
|
|
11
11
|
};
|
|
12
|
+
export type EmailTemplateContent = {
|
|
13
|
+
subject: string | null;
|
|
14
|
+
body: string;
|
|
15
|
+
};
|
|
12
16
|
export interface BaseEmailTemplateRenderer<Config extends BaseNotificationTypeConfig> extends BaseNotificationTemplateRenderer<Config, EmailTemplate> {
|
|
13
17
|
render(notification: AnyNotification<Config>, context: JsonObject): Promise<EmailTemplate>;
|
|
18
|
+
renderFromTemplateContent(notification: AnyNotification<Config>, templateContent: EmailTemplateContent, context: JsonObject): Promise<EmailTemplate>;
|
|
14
19
|
/**
|
|
15
20
|
* Inject logger into the template renderer (optional - called by VintaSend when logger exists)
|
|
16
21
|
*/
|
package/dist/services/notification-template-renderers/base-notification-template-renderer.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ import type { AnyNotification } from '../../types/notification';
|
|
|
3
3
|
import type { BaseNotificationTypeConfig } from '../../types/notification-type-config';
|
|
4
4
|
export interface BaseNotificationTemplateRenderer<Config extends BaseNotificationTypeConfig, T = unknown> {
|
|
5
5
|
render(notification: AnyNotification<Config>, context: JsonObject): Promise<T>;
|
|
6
|
+
renderFromTemplateContent(notification: AnyNotification<Config>, templateContent: unknown, context: JsonObject): Promise<T>;
|
|
6
7
|
}
|
|
@@ -15,6 +15,7 @@ export type NotificationInput<Config extends BaseNotificationTypeConfig> = {
|
|
|
15
15
|
sendAfter: Date | null;
|
|
16
16
|
subjectTemplate: string | null;
|
|
17
17
|
extraParams: InputJsonValue | null;
|
|
18
|
+
gitCommitSha?: never;
|
|
18
19
|
attachments?: NotificationAttachment[];
|
|
19
20
|
};
|
|
20
21
|
export type NotificationResendWithContextInput<Config extends BaseNotificationTypeConfig> = {
|
|
@@ -28,6 +29,7 @@ export type NotificationResendWithContextInput<Config extends BaseNotificationTy
|
|
|
28
29
|
sendAfter: Date | null;
|
|
29
30
|
subjectTemplate: string | null;
|
|
30
31
|
extraParams: InputJsonValue | null;
|
|
32
|
+
gitCommitSha?: never;
|
|
31
33
|
attachments?: NotificationAttachment[];
|
|
32
34
|
};
|
|
33
35
|
export type DatabaseNotification<Config extends BaseNotificationTypeConfig> = {
|
|
@@ -48,6 +50,7 @@ export type DatabaseNotification<Config extends BaseNotificationTypeConfig> = {
|
|
|
48
50
|
readAt: Date | null;
|
|
49
51
|
createdAt?: Date;
|
|
50
52
|
updatedAt?: Date;
|
|
53
|
+
gitCommitSha: string | null;
|
|
51
54
|
attachments?: StoredAttachment[];
|
|
52
55
|
};
|
|
53
56
|
export type Notification<Config extends BaseNotificationTypeConfig> = NotificationInput<Config> | NotificationResendWithContextInput<Config> | DatabaseNotification<Config>;
|
|
@@ -19,6 +19,7 @@ export type OneOffNotificationInput<Config extends BaseNotificationTypeConfig> =
|
|
|
19
19
|
sendAfter: Date | null;
|
|
20
20
|
subjectTemplate: string | null;
|
|
21
21
|
extraParams: InputJsonValue | null;
|
|
22
|
+
gitCommitSha?: never;
|
|
22
23
|
attachments?: NotificationAttachment[];
|
|
23
24
|
};
|
|
24
25
|
/**
|
|
@@ -38,6 +39,7 @@ export type OneOffNotificationResendWithContextInput<Config extends BaseNotifica
|
|
|
38
39
|
sendAfter: Date | null;
|
|
39
40
|
subjectTemplate: string | null;
|
|
40
41
|
extraParams: InputJsonValue | null;
|
|
42
|
+
gitCommitSha?: never;
|
|
41
43
|
attachments?: NotificationAttachment[];
|
|
42
44
|
};
|
|
43
45
|
/**
|
|
@@ -64,6 +66,7 @@ export type DatabaseOneOffNotification<Config extends BaseNotificationTypeConfig
|
|
|
64
66
|
readAt: Date | null;
|
|
65
67
|
createdAt?: Date;
|
|
66
68
|
updatedAt?: Date;
|
|
69
|
+
gitCommitSha: string | null;
|
|
67
70
|
attachments?: StoredAttachment[];
|
|
68
71
|
};
|
|
69
72
|
/**
|