vintasend 0.3.0 → 0.4.3
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 +142 -3
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/next.config.d.ts +1 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/next.config.js +9 -4
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/forgot-password/forgot-password-notification-context.d.ts +6 -6
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/forgot-password/forgot-password-notification-context.js +16 -17
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/forgot-password/route.d.ts +7 -5
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/forgot-password/route.js +105 -79
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/login/route.d.ts +4 -5
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/login/route.js +96 -66
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/reset-password/route.d.ts +7 -5
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/reset-password/route.js +95 -71
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/signup/email-verification-notification-context.d.ts +6 -6
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/signup/email-verification-notification-context.js +18 -18
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/signup/route.d.ts +4 -5
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/signup/route.js +124 -96
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/verify-email/route.d.ts +7 -5
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/verify-email/route.js +94 -70
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/login/page.d.ts +1 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/login/page.js +67 -55
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/reset-password/[token]/page.d.ts +1 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/reset-password/[token]/page.js +76 -63
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/signup/page.d.ts +1 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/signup/page.js +87 -63
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/verify-email/[token]/page.d.ts +1 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/verify-email/[token]/page.js +50 -35
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/verify-email-sent/page.d.ts +1 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/verify-email-sent/page.js +12 -12
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/layout.d.ts +7 -5
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/layout.js +15 -16
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/page.d.ts +1 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/page.js +65 -21
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/AuthLayout.d.ts +7 -4
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/AuthLayout.js +7 -8
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/accordion.d.ts +18 -6
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/accordion.js +86 -48
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/alert-dialog.d.ts +58 -14
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/alert-dialog.js +135 -53
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/alert.d.ts +21 -7
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/alert.js +85 -49
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/avatar.d.ts +14 -5
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/avatar.js +77 -40
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/button.d.ts +25 -9
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/button.js +80 -58
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/card.d.ts +19 -7
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/card.js +98 -48
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/checkbox.d.ts +6 -3
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/checkbox.js +66 -42
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/collapsible.d.ts +10 -4
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/collapsible.js +48 -35
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/dialog.d.ts +40 -13
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/dialog.js +116 -50
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/dropdown-menu.d.ts +83 -19
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/dropdown-menu.js +170 -68
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/form.d.ts +53 -21
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/form.js +137 -83
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/input.d.ts +8 -2
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/input.js +60 -37
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/label.d.ts +10 -4
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/label.js +61 -40
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/menubar.d.ts +77 -23
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/menubar.js +188 -64
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/select.d.ts +48 -12
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/select.js +148 -66
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/textarea.d.ts +8 -2
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/textarea.js +59 -37
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/api-clients/auth.d.ts +10 -10
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/api-clients/auth.js +54 -55
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/api-clients/core.d.ts +14 -11
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/api-clients/core.js +1 -2
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/email.js +0 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/logger.d.ts +3 -3
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/logger.js +9 -10
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/schemas/auth.d.ts +66 -32
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/schemas/auth.js +74 -54
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/auth.js +7 -8
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/notifications-with-queue.d.ts +14 -4
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/notifications-with-queue.js +14 -10
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/notifications.d.ts +11 -7
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/notifications.js +39 -26
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/temporal-queue-service.d.ts +11 -8
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/temporal-queue-service.js +14 -15
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/temporal.d.ts +1 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/temporal.js +9 -10
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/utils.d.ts +1 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/utils.js +4 -5
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/activities.d.ts +6 -5
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/activities.js +14 -14
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/config.d.ts +1 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/config.js +2 -3
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/constants.d.ts +1 -1
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/constants.js +1 -2
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/worker.js +20 -22
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/workflows.d.ts +3 -2
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/workflows.js +10 -9
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/tailwind.config.d.ts +74 -74
- package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/tailwind.config.js +80 -81
- package/dist/implementations/vintasend-nodemailer/src/index.js +6 -4
- package/dist/implementations/vintasend-nodemailer/src/nodemailer-notification-adapter.d.ts +36 -14
- package/dist/implementations/vintasend-nodemailer/src/nodemailer-notification-adapter.js +26 -28
- package/dist/implementations/vintasend-prisma/src/index.js +6 -4
- package/dist/implementations/vintasend-prisma/src/prisma-notification-backend.d.ts +232 -138
- package/dist/implementations/vintasend-prisma/src/prisma-notification-backend.js +275 -262
- package/dist/implementations/vintasend-pug/src/index.js +6 -4
- package/dist/implementations/vintasend-pug/src/pug-email-template-renderer.d.ts +18 -7
- package/dist/implementations/vintasend-pug/src/pug-email-template-renderer.js +19 -21
- package/dist/implementations/vintasend-winston/src/index.js +6 -4
- package/dist/implementations/vintasend-winston/src/winston-logger.d.ts +6 -6
- package/dist/implementations/vintasend-winston/src/winston-logger.js +65 -50
- package/dist/index.d.ts +13 -6
- package/dist/index.js +12 -3
- package/dist/services/attachment-manager/base-attachment-manager.d.ts +42 -0
- package/dist/services/attachment-manager/base-attachment-manager.js +114 -0
- package/dist/services/attachment-manager/local-file-attachment-manager.d.ts +58 -0
- package/dist/services/attachment-manager/local-file-attachment-manager.js +192 -0
- package/dist/services/notification-adapters/base-notification-adapter.d.ts +17 -4
- package/dist/services/notification-adapters/base-notification-adapter.js +23 -5
- package/dist/services/notification-backends/base-notification-backend.d.ts +39 -1
- package/dist/services/notification-backends/base-notification-backend.js +12 -0
- package/dist/services/notification-context-generators-map.d.ts +1 -1
- package/dist/services/notification-context-registry.d.ts +14 -9
- package/dist/services/notification-context-registry.js +30 -31
- package/dist/services/notification-queue-service/base-notification-queue-service.d.ts +1 -1
- package/dist/services/notification-service.d.ts +35 -7
- package/dist/services/notification-service.js +47 -3
- package/dist/services/notification-template-renderers/base-email-template-renderer.d.ts +7 -2
- package/dist/types/attachment.d.ts +42 -0
- package/dist/types/attachment.js +7 -0
- package/dist/types/notification-type-config.d.ts +2 -2
- package/dist/types/notification.d.ts +5 -1
- package/dist/types/one-off-notification.d.ts +4 -0
- package/package.json +13 -11
|
@@ -6,7 +6,7 @@ exports.isOneOffNotification = isOneOffNotification;
|
|
|
6
6
|
* Type guard to check if a notification is a one-off notification
|
|
7
7
|
*/
|
|
8
8
|
function isOneOffNotification(notification) {
|
|
9
|
-
return 'emailOrPhone' in notification && 'firstName' in notification && 'lastName' in notification;
|
|
9
|
+
return ('emailOrPhone' in notification && 'firstName' in notification && 'lastName' in notification);
|
|
10
10
|
}
|
|
11
11
|
class BaseNotificationAdapter {
|
|
12
12
|
constructor(templateRenderer, notificationType, enqueueNotifications) {
|
|
@@ -15,15 +15,31 @@ class BaseNotificationAdapter {
|
|
|
15
15
|
this.enqueueNotifications = enqueueNotifications;
|
|
16
16
|
this.key = null;
|
|
17
17
|
this.backend = null;
|
|
18
|
+
this.logger = null;
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
-
send(notification, context) {
|
|
20
|
+
send(_notification, _context) {
|
|
21
21
|
if (this.backend === null) {
|
|
22
22
|
return Promise.reject(new Error('Backend not injected'));
|
|
23
23
|
}
|
|
24
24
|
return Promise.resolve();
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Check if this adapter supports attachments
|
|
28
|
+
*/
|
|
29
|
+
get supportsAttachments() {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Prepare attachments for sending
|
|
34
|
+
* Override in adapters that support attachments
|
|
35
|
+
*/
|
|
36
|
+
async prepareAttachments(attachments) {
|
|
37
|
+
var _a, _b;
|
|
38
|
+
if (this.supportsAttachments && attachments.length > 0) {
|
|
39
|
+
(_b = (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn) === null || _b === void 0 ? void 0 : _b.call(_a, `Adapter ${this.key} claims to support attachments but prepareAttachments is not implemented`);
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
27
43
|
/**
|
|
28
44
|
* Get the recipient email address from a notification.
|
|
29
45
|
* For one-off notifications, returns the emailOrPhone field directly.
|
|
@@ -71,6 +87,8 @@ class BaseNotificationAdapter {
|
|
|
71
87
|
injectBackend(backend) {
|
|
72
88
|
this.backend = backend;
|
|
73
89
|
}
|
|
74
|
-
|
|
90
|
+
injectLogger(logger) {
|
|
91
|
+
this.logger = logger;
|
|
92
|
+
}
|
|
75
93
|
}
|
|
76
94
|
exports.BaseNotificationAdapter = BaseNotificationAdapter;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { AttachmentFileRecord, StoredAttachment } from '../../types/attachment';
|
|
1
2
|
import type { InputJsonValue } from '../../types/json-values';
|
|
2
|
-
import type {
|
|
3
|
+
import type { AnyDatabaseNotification, AnyNotification, DatabaseNotification, DatabaseOneOffNotification, Notification, OneOffNotificationInput } from '../../types/notification';
|
|
3
4
|
import type { BaseNotificationTypeConfig } from '../../types/notification-type-config';
|
|
4
5
|
export interface BaseNotificationBackend<Config extends BaseNotificationTypeConfig> {
|
|
5
6
|
getAllPendingNotifications(): Promise<AnyDatabaseNotification<Config>[]>;
|
|
@@ -27,4 +28,41 @@ export interface BaseNotificationBackend<Config extends BaseNotificationTypeConf
|
|
|
27
28
|
getOneOffNotification(notificationId: Config['NotificationIdType'], forUpdate: boolean): Promise<DatabaseOneOffNotification<Config> | null>;
|
|
28
29
|
getAllOneOffNotifications(): Promise<DatabaseOneOffNotification<Config>[]>;
|
|
29
30
|
getOneOffNotifications(page: number, pageSize: number): Promise<DatabaseOneOffNotification<Config>[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Get an attachment file record by ID
|
|
33
|
+
*/
|
|
34
|
+
getAttachmentFile?(fileId: string): Promise<AttachmentFileRecord | null>;
|
|
35
|
+
/**
|
|
36
|
+
* Find an attachment file by checksum for deduplication.
|
|
37
|
+
* Backend queries its database for files with matching checksums.
|
|
38
|
+
* Used during file upload to avoid storing duplicate files.
|
|
39
|
+
*/
|
|
40
|
+
findAttachmentFileByChecksum?(checksum: string): Promise<AttachmentFileRecord | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Delete an attachment file (only if not referenced by any notifications)
|
|
43
|
+
*/
|
|
44
|
+
deleteAttachmentFile?(fileId: string): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Get all attachment files not referenced by any notifications (for cleanup)
|
|
47
|
+
*/
|
|
48
|
+
getOrphanedAttachmentFiles?(): Promise<AttachmentFileRecord[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Get all attachments for a specific notification
|
|
51
|
+
*/
|
|
52
|
+
getAttachments?(notificationId: Config['NotificationIdType']): Promise<StoredAttachment[]>;
|
|
53
|
+
/**
|
|
54
|
+
* Delete a specific attachment from a notification
|
|
55
|
+
*/
|
|
56
|
+
deleteNotificationAttachment?(notificationId: Config['NotificationIdType'], attachmentId: string): Promise<void>;
|
|
30
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Type guard to check if backend supports attachment operations
|
|
60
|
+
*/
|
|
61
|
+
export declare function supportsAttachments<Config extends BaseNotificationTypeConfig>(backend: BaseNotificationBackend<Config>): backend is BaseNotificationBackend<Config> & {
|
|
62
|
+
getAttachmentFile(fileId: string): Promise<AttachmentFileRecord | null>;
|
|
63
|
+
findAttachmentFileByChecksum(checksum: string): Promise<AttachmentFileRecord | null>;
|
|
64
|
+
deleteAttachmentFile(fileId: string): Promise<void>;
|
|
65
|
+
getOrphanedAttachmentFiles(): Promise<AttachmentFileRecord[]>;
|
|
66
|
+
getAttachments(notificationId: Config['NotificationIdType']): Promise<StoredAttachment[]>;
|
|
67
|
+
deleteNotificationAttachment(notificationId: Config['NotificationIdType'], attachmentId: string): Promise<void>;
|
|
68
|
+
};
|
|
@@ -1,2 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.supportsAttachments = supportsAttachments;
|
|
4
|
+
/**
|
|
5
|
+
* Type guard to check if backend supports attachment operations
|
|
6
|
+
*/
|
|
7
|
+
function supportsAttachments(backend) {
|
|
8
|
+
return (typeof backend.getAttachmentFile === 'function' &&
|
|
9
|
+
typeof backend.findAttachmentFileByChecksum === 'function' &&
|
|
10
|
+
typeof backend.deleteAttachmentFile === 'function' &&
|
|
11
|
+
typeof backend.getOrphanedAttachmentFiles === 'function' &&
|
|
12
|
+
typeof backend.getAttachments === 'function' &&
|
|
13
|
+
typeof backend.deleteNotificationAttachment === 'function');
|
|
14
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ContextGenerator } from
|
|
1
|
+
import type { ContextGenerator } from '../types/notification-context-generators';
|
|
2
2
|
export declare class NotificationContextGeneratorsMap<ContextMapType extends {
|
|
3
3
|
[key: string]: ContextGenerator;
|
|
4
4
|
}> {
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import type { JsonObject, JsonPrimitive } from '../types/json-values';
|
|
2
2
|
export interface ContextGenerator {
|
|
3
|
-
|
|
3
|
+
generate(params: Record<string, JsonPrimitive>): JsonObject | Promise<JsonObject>;
|
|
4
4
|
}
|
|
5
5
|
export declare class NotificationContextRegistry<T extends Record<string, ContextGenerator>> {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
private static instance;
|
|
7
|
+
private readonly contexts;
|
|
8
|
+
private constructor();
|
|
9
|
+
static initialize<T extends Record<string, ContextGenerator>>(
|
|
10
|
+
contexts: T,
|
|
11
|
+
): NotificationContextRegistry<T>;
|
|
12
|
+
static getInstance<T extends Record<string, ContextGenerator>>(): NotificationContextRegistry<T>;
|
|
13
|
+
static resetInstance(): void;
|
|
14
|
+
getContext<K extends keyof T>(
|
|
15
|
+
key: K,
|
|
16
|
+
parameters: Parameters<T[K]['generate']>[0],
|
|
17
|
+
): Promise<JsonObject>;
|
|
18
|
+
getAvailableContexts(): (keyof T)[];
|
|
14
19
|
}
|
|
@@ -1,40 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
2
|
exports.NotificationContextRegistry = void 0;
|
|
4
3
|
class NotificationContextRegistry {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
constructor(contexts) {
|
|
5
|
+
this.contexts = contexts;
|
|
6
|
+
}
|
|
7
|
+
static initialize(contexts) {
|
|
8
|
+
if (NotificationContextRegistry.instance) {
|
|
9
|
+
throw new Error('NotificationContextRegistry already initialized');
|
|
7
10
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
NotificationContextRegistry.instance = new NotificationContextRegistry(contexts);
|
|
12
|
+
return NotificationContextRegistry.instance;
|
|
13
|
+
}
|
|
14
|
+
static getInstance() {
|
|
15
|
+
if (!NotificationContextRegistry.instance) {
|
|
16
|
+
throw new Error('NotificationContextRegistry not initialized');
|
|
14
17
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
return NotificationContextRegistry.instance;
|
|
19
|
+
}
|
|
20
|
+
static resetInstance() {
|
|
21
|
+
NotificationContextRegistry.instance = null;
|
|
22
|
+
}
|
|
23
|
+
async getContext(key, parameters) {
|
|
24
|
+
const contextGenerator = this.contexts[key];
|
|
25
|
+
if (!contextGenerator) {
|
|
26
|
+
throw new Error(`Context generator not found for context: ${key}`);
|
|
20
27
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
async getContext(key, parameters) {
|
|
25
|
-
const contextGenerator = this.contexts[key];
|
|
26
|
-
if (!contextGenerator) {
|
|
27
|
-
throw new Error(`Context generator not found for context: ${key}`);
|
|
28
|
-
}
|
|
29
|
-
const result = contextGenerator.generate(parameters);
|
|
30
|
-
if (result instanceof Promise) {
|
|
31
|
-
return await result;
|
|
32
|
-
}
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
getAvailableContexts() {
|
|
36
|
-
return Object.keys(this.contexts);
|
|
28
|
+
const result = contextGenerator.generate(parameters);
|
|
29
|
+
if (result instanceof Promise) {
|
|
30
|
+
return await result;
|
|
37
31
|
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
getAvailableContexts() {
|
|
35
|
+
return Object.keys(this.contexts);
|
|
36
|
+
}
|
|
38
37
|
}
|
|
39
38
|
exports.NotificationContextRegistry = NotificationContextRegistry;
|
|
40
39
|
// biome-ignore lint/suspicious/noExplicitAny: This won't be used since we'll always initialize the registry
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BaseNotificationTypeConfig } from
|
|
1
|
+
import type { BaseNotificationTypeConfig } from '../../types/notification-type-config';
|
|
2
2
|
export interface BaseNotificationQueueService<Config extends BaseNotificationTypeConfig> {
|
|
3
3
|
enqueueNotification(notificationId: Config['NotificationIdType']): Promise<void>;
|
|
4
4
|
}
|
|
@@ -1,26 +1,54 @@
|
|
|
1
|
-
import type { DatabaseNotification, Notification, AnyNotification, AnyDatabaseNotification, DatabaseOneOffNotification } from '../types/notification';
|
|
2
|
-
import type { OneOffNotificationInput } from '../types/one-off-notification';
|
|
3
1
|
import type { JsonObject } from '../types/json-values';
|
|
2
|
+
import type { AnyDatabaseNotification, AnyNotification, DatabaseNotification, DatabaseOneOffNotification, Notification } from '../types/notification';
|
|
4
3
|
import type { BaseNotificationTypeConfig } from '../types/notification-type-config';
|
|
4
|
+
import type { OneOffNotificationInput } from '../types/one-off-notification';
|
|
5
|
+
import type { BaseAttachmentManager } from './attachment-manager/base-attachment-manager';
|
|
6
|
+
import type { BaseLogger } from './loggers/base-logger';
|
|
5
7
|
import { type BaseNotificationAdapter } from './notification-adapters/base-notification-adapter';
|
|
6
|
-
import type { BaseNotificationTemplateRenderer } from './notification-template-renderers/base-notification-template-renderer';
|
|
7
8
|
import type { BaseNotificationBackend } from './notification-backends/base-notification-backend';
|
|
8
|
-
import type { BaseLogger } from './loggers/base-logger';
|
|
9
9
|
import type { BaseNotificationQueueService } from './notification-queue-service/base-notification-queue-service';
|
|
10
|
+
import type { BaseNotificationTemplateRenderer } from './notification-template-renderers/base-notification-template-renderer';
|
|
10
11
|
type VintaSendOptions = {
|
|
11
12
|
raiseErrorOnFailedSend: boolean;
|
|
12
13
|
};
|
|
13
14
|
export declare class VintaSendFactory<Config extends BaseNotificationTypeConfig> {
|
|
14
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new VintaSend notification service instance
|
|
17
|
+
*
|
|
18
|
+
* @param adapters - Array of notification adapters (email, SMS, push, etc.)
|
|
19
|
+
* @param backend - Notification storage backend
|
|
20
|
+
* @param logger - Logger instance
|
|
21
|
+
* @param contextGeneratorsMap - Map of context generators for notification rendering
|
|
22
|
+
* @param queueService - Optional queue service for background notification processing
|
|
23
|
+
* @param attachmentManager - Optional attachment manager for file handling
|
|
24
|
+
* @param options - Optional configuration options
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Without attachments or options
|
|
28
|
+
* factory.create(adapters, backend, logger, contextGeneratorsMap);
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // With queue service and options (note: pass undefined for attachmentManager)
|
|
32
|
+
* factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, undefined, { raiseErrorOnFailedSend: true });
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // With attachments and options
|
|
36
|
+
* factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, attachmentManager, { raiseErrorOnFailedSend: true });
|
|
37
|
+
*
|
|
38
|
+
* @since v0.4.0 - BREAKING CHANGE: attachmentManager parameter added before options
|
|
39
|
+
* @see https://github.com/vintasoftware/vintasend-ts/blob/main/README.md#migrating-to-v040-attachment-support
|
|
40
|
+
*/
|
|
41
|
+
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): VintaSend<Config, AdaptersList, Backend, Logger, QueueService, AttachmentMgr>;
|
|
15
42
|
}
|
|
16
|
-
export declare class VintaSend<Config extends BaseNotificationTypeConfig, AdaptersList extends BaseNotificationAdapter<BaseNotificationTemplateRenderer<Config>, Config>[], Backend extends BaseNotificationBackend<Config>, Logger extends BaseLogger, QueueService extends BaseNotificationQueueService<Config
|
|
43
|
+
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> {
|
|
17
44
|
private adapters;
|
|
18
45
|
private backend;
|
|
19
46
|
private logger;
|
|
20
47
|
private queueService?;
|
|
48
|
+
private attachmentManager?;
|
|
21
49
|
private options;
|
|
22
50
|
private contextGeneratorsMap;
|
|
23
|
-
constructor(adapters: AdaptersList, backend: Backend, logger: Logger, contextGeneratorsMap: Config['ContextMap'], queueService?: QueueService | undefined, options?: VintaSendOptions);
|
|
51
|
+
constructor(adapters: AdaptersList, backend: Backend, logger: Logger, contextGeneratorsMap: Config['ContextMap'], queueService?: QueueService | undefined, attachmentManager?: AttachmentMgr | undefined, options?: VintaSendOptions);
|
|
24
52
|
registerQueueService(queueService: QueueService): void;
|
|
25
53
|
send(notification: AnyDatabaseNotification<Config>): Promise<void>;
|
|
26
54
|
createNotification(notification: Omit<Notification<Config>, 'id'>): Promise<DatabaseNotification<Config>>;
|
|
@@ -4,25 +4,69 @@ exports.VintaSend = exports.VintaSendFactory = void 0;
|
|
|
4
4
|
const base_notification_adapter_1 = require("./notification-adapters/base-notification-adapter");
|
|
5
5
|
const notification_context_generators_map_1 = require("./notification-context-generators-map");
|
|
6
6
|
class VintaSendFactory {
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new VintaSend notification service instance
|
|
9
|
+
*
|
|
10
|
+
* @param adapters - Array of notification adapters (email, SMS, push, etc.)
|
|
11
|
+
* @param backend - Notification storage backend
|
|
12
|
+
* @param logger - Logger instance
|
|
13
|
+
* @param contextGeneratorsMap - Map of context generators for notification rendering
|
|
14
|
+
* @param queueService - Optional queue service for background notification processing
|
|
15
|
+
* @param attachmentManager - Optional attachment manager for file handling
|
|
16
|
+
* @param options - Optional configuration options
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Without attachments or options
|
|
20
|
+
* factory.create(adapters, backend, logger, contextGeneratorsMap);
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // With queue service and options (note: pass undefined for attachmentManager)
|
|
24
|
+
* factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, undefined, { raiseErrorOnFailedSend: true });
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // With attachments and options
|
|
28
|
+
* factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, attachmentManager, { raiseErrorOnFailedSend: true });
|
|
29
|
+
*
|
|
30
|
+
* @since v0.4.0 - BREAKING CHANGE: attachmentManager parameter added before options
|
|
31
|
+
* @see https://github.com/vintasoftware/vintasend-ts/blob/main/README.md#migrating-to-v040-attachment-support
|
|
32
|
+
*/
|
|
33
|
+
create(adapters, backend, logger, contextGeneratorsMap, queueService, attachmentManager, options = {
|
|
8
34
|
raiseErrorOnFailedSend: false,
|
|
9
35
|
}) {
|
|
10
|
-
return new VintaSend(adapters, backend, logger, contextGeneratorsMap, queueService, options);
|
|
36
|
+
return new VintaSend(adapters, backend, logger, contextGeneratorsMap, queueService, attachmentManager, options);
|
|
11
37
|
}
|
|
12
38
|
}
|
|
13
39
|
exports.VintaSendFactory = VintaSendFactory;
|
|
40
|
+
// Type guard to check if backend has attachment manager injection support
|
|
41
|
+
function hasAttachmentManagerInjection(backend) {
|
|
42
|
+
return ('injectAttachmentManager' in backend &&
|
|
43
|
+
// biome-ignore lint/suspicious/noExplicitAny:: this is a necessary check
|
|
44
|
+
typeof backend.injectAttachmentManager === 'function');
|
|
45
|
+
}
|
|
14
46
|
class VintaSend {
|
|
15
|
-
constructor(adapters, backend, logger, contextGeneratorsMap, queueService, options = {
|
|
47
|
+
constructor(adapters, backend, logger, contextGeneratorsMap, queueService, attachmentManager, options = {
|
|
16
48
|
raiseErrorOnFailedSend: false,
|
|
17
49
|
}) {
|
|
18
50
|
this.adapters = adapters;
|
|
19
51
|
this.backend = backend;
|
|
20
52
|
this.logger = logger;
|
|
21
53
|
this.queueService = queueService;
|
|
54
|
+
this.attachmentManager = attachmentManager;
|
|
22
55
|
this.options = options;
|
|
23
56
|
this.contextGeneratorsMap = new notification_context_generators_map_1.NotificationContextGeneratorsMap(contextGeneratorsMap);
|
|
24
57
|
for (const adapter of adapters) {
|
|
25
58
|
adapter.injectBackend(backend);
|
|
59
|
+
adapter.injectLogger(logger);
|
|
60
|
+
// Inject logger into template renderer if it supports it
|
|
61
|
+
// biome-ignore lint/suspicious/noExplicitAny: accessing protected templateRenderer property
|
|
62
|
+
const templateRenderer = adapter.templateRenderer;
|
|
63
|
+
if (templateRenderer && typeof templateRenderer.injectLogger === 'function') {
|
|
64
|
+
templateRenderer.injectLogger(logger);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Inject attachment manager into backend if both exist
|
|
68
|
+
if (this.attachmentManager && hasAttachmentManagerInjection(backend)) {
|
|
69
|
+
backend.injectAttachmentManager(this.attachmentManager);
|
|
26
70
|
}
|
|
27
71
|
}
|
|
28
72
|
registerQueueService(queueService) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { BaseNotificationTemplateRenderer } from './base-notification-template-renderer';
|
|
2
|
-
import type { AnyNotification } from '../../types/notification';
|
|
3
1
|
import type { Buffer } from 'node:buffer';
|
|
4
2
|
import type { JsonObject } from '../../types/json-values';
|
|
3
|
+
import type { AnyNotification } from '../../types/notification';
|
|
5
4
|
import type { BaseNotificationTypeConfig } from '../../types/notification-type-config';
|
|
5
|
+
import type { BaseLogger } from '../loggers/base-logger';
|
|
6
|
+
import type { BaseNotificationTemplateRenderer } from './base-notification-template-renderer';
|
|
6
7
|
export type Attachment = File | Buffer | string;
|
|
7
8
|
export type EmailTemplate = {
|
|
8
9
|
subject: string;
|
|
@@ -10,4 +11,8 @@ export type EmailTemplate = {
|
|
|
10
11
|
};
|
|
11
12
|
export interface BaseEmailTemplateRenderer<Config extends BaseNotificationTypeConfig> extends BaseNotificationTemplateRenderer<Config, EmailTemplate> {
|
|
12
13
|
render(notification: AnyNotification<Config>, context: JsonObject): Promise<EmailTemplate>;
|
|
14
|
+
/**
|
|
15
|
+
* Inject logger into the template renderer (optional - called by VintaSend when logger exists)
|
|
16
|
+
*/
|
|
17
|
+
injectLogger?(logger: BaseLogger): void;
|
|
13
18
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
2
|
+
export type FileAttachment = Buffer | ReadableStream | Readable | string;
|
|
3
|
+
export interface NotificationAttachmentUpload {
|
|
4
|
+
file: FileAttachment;
|
|
5
|
+
filename: string;
|
|
6
|
+
contentType?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface NotificationAttachmentReference {
|
|
10
|
+
fileId: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
}
|
|
13
|
+
export type NotificationAttachment = NotificationAttachmentUpload | NotificationAttachmentReference;
|
|
14
|
+
export declare function isAttachmentReference(attachment: NotificationAttachment): attachment is NotificationAttachmentReference;
|
|
15
|
+
export interface AttachmentFile {
|
|
16
|
+
read(): Promise<Buffer>;
|
|
17
|
+
stream(): Promise<ReadableStream>;
|
|
18
|
+
url(expiresIn?: number): Promise<string>;
|
|
19
|
+
delete(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export interface AttachmentFileRecord {
|
|
22
|
+
id: string;
|
|
23
|
+
filename: string;
|
|
24
|
+
contentType: string;
|
|
25
|
+
size: number;
|
|
26
|
+
checksum: string;
|
|
27
|
+
storageMetadata: Record<string, unknown>;
|
|
28
|
+
createdAt: Date;
|
|
29
|
+
updatedAt: Date;
|
|
30
|
+
}
|
|
31
|
+
export interface StoredAttachment {
|
|
32
|
+
id: string;
|
|
33
|
+
fileId: string;
|
|
34
|
+
filename: string;
|
|
35
|
+
contentType: string;
|
|
36
|
+
size: number;
|
|
37
|
+
checksum: string;
|
|
38
|
+
createdAt: Date;
|
|
39
|
+
file: AttachmentFile;
|
|
40
|
+
description?: string;
|
|
41
|
+
storageMetadata: Record<string, unknown>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isAttachmentReference = isAttachmentReference;
|
|
4
|
+
// Type guard to check if attachment is a reference
|
|
5
|
+
function isAttachmentReference(attachment) {
|
|
6
|
+
return 'fileId' in attachment;
|
|
7
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { Identifier } from './identifier';
|
|
2
|
+
import type { ContextGenerator } from './notification-context-generators';
|
|
3
3
|
export type BaseNotificationTypeConfig = {
|
|
4
4
|
ContextMap: Record<string, ContextGenerator>;
|
|
5
5
|
NotificationIdType: Identifier;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { NotificationAttachment, StoredAttachment } from './attachment';
|
|
1
2
|
import type { InputJsonValue, JsonValue } from './json-values';
|
|
2
3
|
import type { NotificationStatus } from './notification-status';
|
|
3
4
|
import type { NotificationType } from './notification-type';
|
|
4
5
|
import type { BaseNotificationTypeConfig } from './notification-type-config';
|
|
5
6
|
import type { DatabaseOneOffNotification, OneOffNotification, OneOffNotificationInput } from './one-off-notification';
|
|
6
|
-
export type {
|
|
7
|
+
export type { DatabaseOneOffNotification, OneOffNotification, OneOffNotificationInput, OneOffNotificationResendWithContextInput, } from './one-off-notification';
|
|
7
8
|
export type NotificationInput<Config extends BaseNotificationTypeConfig> = {
|
|
8
9
|
userId: Config['UserIdType'];
|
|
9
10
|
notificationType: NotificationType;
|
|
@@ -14,6 +15,7 @@ export type NotificationInput<Config extends BaseNotificationTypeConfig> = {
|
|
|
14
15
|
sendAfter: Date | null;
|
|
15
16
|
subjectTemplate: string | null;
|
|
16
17
|
extraParams: InputJsonValue | null;
|
|
18
|
+
attachments?: NotificationAttachment[];
|
|
17
19
|
};
|
|
18
20
|
export type NotificationResendWithContextInput<Config extends BaseNotificationTypeConfig> = {
|
|
19
21
|
userId: Config['UserIdType'];
|
|
@@ -26,6 +28,7 @@ export type NotificationResendWithContextInput<Config extends BaseNotificationTy
|
|
|
26
28
|
sendAfter: Date | null;
|
|
27
29
|
subjectTemplate: string | null;
|
|
28
30
|
extraParams: InputJsonValue | null;
|
|
31
|
+
attachments?: NotificationAttachment[];
|
|
29
32
|
};
|
|
30
33
|
export type DatabaseNotification<Config extends BaseNotificationTypeConfig> = {
|
|
31
34
|
id: Config['NotificationIdType'];
|
|
@@ -45,6 +48,7 @@ export type DatabaseNotification<Config extends BaseNotificationTypeConfig> = {
|
|
|
45
48
|
readAt: Date | null;
|
|
46
49
|
createdAt?: Date;
|
|
47
50
|
updatedAt?: Date;
|
|
51
|
+
attachments?: StoredAttachment[];
|
|
48
52
|
};
|
|
49
53
|
export type Notification<Config extends BaseNotificationTypeConfig> = NotificationInput<Config> | NotificationResendWithContextInput<Config> | DatabaseNotification<Config>;
|
|
50
54
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NotificationAttachment, StoredAttachment } from './attachment';
|
|
1
2
|
import type { InputJsonValue, JsonValue } from './json-values';
|
|
2
3
|
import type { NotificationStatus } from './notification-status';
|
|
3
4
|
import type { NotificationType } from './notification-type';
|
|
@@ -18,6 +19,7 @@ export type OneOffNotificationInput<Config extends BaseNotificationTypeConfig> =
|
|
|
18
19
|
sendAfter: Date | null;
|
|
19
20
|
subjectTemplate: string | null;
|
|
20
21
|
extraParams: InputJsonValue | null;
|
|
22
|
+
attachments?: NotificationAttachment[];
|
|
21
23
|
};
|
|
22
24
|
/**
|
|
23
25
|
* Input type for resending a one-off notification with stored context.
|
|
@@ -36,6 +38,7 @@ export type OneOffNotificationResendWithContextInput<Config extends BaseNotifica
|
|
|
36
38
|
sendAfter: Date | null;
|
|
37
39
|
subjectTemplate: string | null;
|
|
38
40
|
extraParams: InputJsonValue | null;
|
|
41
|
+
attachments?: NotificationAttachment[];
|
|
39
42
|
};
|
|
40
43
|
/**
|
|
41
44
|
* Database representation of a one-off notification.
|
|
@@ -61,6 +64,7 @@ export type DatabaseOneOffNotification<Config extends BaseNotificationTypeConfig
|
|
|
61
64
|
readAt: Date | null;
|
|
62
65
|
createdAt?: Date;
|
|
63
66
|
updatedAt?: Date;
|
|
67
|
+
attachments?: StoredAttachment[];
|
|
64
68
|
};
|
|
65
69
|
/**
|
|
66
70
|
* Union type representing any one-off notification (input, resend, or database).
|
package/package.json
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vintasend",
|
|
3
|
-
"version": "0.3
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
|
-
"files": [
|
|
6
|
-
"dist"
|
|
7
|
-
],
|
|
5
|
+
"files": ["dist"],
|
|
8
6
|
"author": "Hugo Bessa",
|
|
9
7
|
"license": "MIT",
|
|
10
8
|
"scripts": {
|
|
11
9
|
"lint": "biome check .",
|
|
12
10
|
"format": "biome format .",
|
|
13
|
-
"check": "biome check --
|
|
11
|
+
"check": "biome check --write .",
|
|
14
12
|
"build": "tsc",
|
|
15
13
|
"prepublishOnly": "npm run build",
|
|
16
14
|
"test": "jest",
|
|
@@ -18,11 +16,15 @@
|
|
|
18
16
|
"test:coverage": "jest --coverage"
|
|
19
17
|
},
|
|
20
18
|
"devDependencies": {
|
|
21
|
-
"@biomejs/biome": "
|
|
22
|
-
"@types/jest": "^
|
|
23
|
-
"@types/
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
19
|
+
"@biomejs/biome": "^2.3.11",
|
|
20
|
+
"@types/jest": "^30.0.0",
|
|
21
|
+
"@types/mime-types": "^3.0.1",
|
|
22
|
+
"@types/node": "^25.0.8",
|
|
23
|
+
"jest": "^30.2.0",
|
|
24
|
+
"ts-jest": "^29.4.6",
|
|
25
|
+
"typescript": "^5.9.3"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"mime-types": "^3.0.2"
|
|
27
29
|
}
|
|
28
30
|
}
|