@feathers-baas/plugin-notifications 0.1.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.
@@ -0,0 +1,252 @@
1
+ import { Queue, Worker } from 'bullmq';
2
+ import { Redis } from 'ioredis';
3
+
4
+ /**
5
+ * A single notification to be delivered to one recipient.
6
+ * Templates are EJS strings — the worker renders them just before sending.
7
+ */
8
+ interface Notification {
9
+ /** Recipient address — email address, phone number, device token, etc. */
10
+ to: string;
11
+ /** Notification channel — determines which driver handles delivery. */
12
+ channel: 'email' | 'sms' | 'push' | 'webhook';
13
+ /** Email subject line (email channel only). */
14
+ subject?: string;
15
+ /** EJS template string for the message body. */
16
+ bodyTemplate: string;
17
+ /** Variables injected into the EJS template at render time. */
18
+ data: Record<string, unknown>;
19
+ /** Optional plain-text fallback for HTML email. */
20
+ textTemplate?: string;
21
+ }
22
+ /**
23
+ * A named notification driver — responsible for delivering one channel.
24
+ * Drivers are registered in the DriverRegistry and looked up by name at
25
+ * job processing time.
26
+ */
27
+ interface NotifierDriver {
28
+ /** Unique name used as the registry key (e.g. 'smtp', 'resend'). */
29
+ readonly name: string;
30
+ /** Deliver the notification to the recipient. */
31
+ send(notification: Notification): Promise<void>;
32
+ }
33
+ /**
34
+ * Payload stored inside each BullMQ job.
35
+ * Worker resolves driverName → NotifierDriver from the registry.
36
+ */
37
+ interface NotificationJob {
38
+ driverName: string;
39
+ notification: Notification;
40
+ }
41
+ /**
42
+ * Maps auth-management event types to a human-readable event key used in
43
+ * the notification event→driver registry.
44
+ */
45
+ type AuthNotificationEvent = 'auth.resend-verify-signup' | 'auth.verify-signup' | 'auth.verify-signup-set-password' | 'auth.send-reset-pwd' | 'auth.reset-pwd' | 'auth.password-change' | 'auth.identity-change';
46
+ /** Union of all supported notification event keys. */
47
+ type NotificationEvent = AuthNotificationEvent | string;
48
+
49
+ /**
50
+ * DriverRegistry maps event keys to the list of drivers that should handle
51
+ * that event.
52
+ *
53
+ * A single driver instance can handle multiple events (e.g. the SMTP driver
54
+ * handles all auth.* email events).
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * const registry = new DriverRegistry()
59
+ * registry.registerDriver(smtpDriver)
60
+ * registry.mapEvent('auth.verify-signup', 'smtp')
61
+ * registry.mapEvent('auth.send-reset-pwd', 'smtp')
62
+ * ```
63
+ */
64
+ declare class DriverRegistry {
65
+ private readonly drivers;
66
+ private readonly eventMap;
67
+ /** Register a driver under its name. Overwrites any existing driver with the same name. */
68
+ registerDriver(driver: NotifierDriver): void;
69
+ /** Map a notification event to a driver name. Multiple calls for the same event add drivers. */
70
+ mapEvent(event: NotificationEvent, driverName: string): void;
71
+ /** Look up a driver by name. Returns undefined if not registered. */
72
+ getDriver(name: string): NotifierDriver | undefined;
73
+ /** Get all driver names mapped to the given event. */
74
+ getDriversForEvent(event: NotificationEvent): string[];
75
+ /** All registered driver names. */
76
+ listDriverNames(): string[];
77
+ }
78
+
79
+ type NotifierFn = (type: string, user: Record<string, unknown>, notifierOptions: Record<string, unknown>) => Promise<void>;
80
+ interface BullMQNotifierOptions {
81
+ /** BullMQ Queue instance — from createNotificationQueue(). */
82
+ queue: Queue<NotificationJob>;
83
+ /** Driver registry — used to determine which drivers handle each event. */
84
+ registry: DriverRegistry;
85
+ /**
86
+ * Public base URL of the app, e.g. https://api.example.com
87
+ * Used to construct verification / reset URLs in email templates.
88
+ */
89
+ appUrl: string;
90
+ }
91
+ /**
92
+ * Create a notifier function compatible with feathers-authentication-management.
93
+ *
94
+ * When an auth event fires (verifySignup, sendResetPwd, etc.), this notifier:
95
+ * 1. Maps the event type to a feathers-baas notification event key
96
+ * 2. Looks up all registered drivers for that event
97
+ * 3. Enqueues one BullMQ job per driver
98
+ * 4. Returns immediately — delivery is asynchronous
99
+ *
100
+ * Wire into the app:
101
+ * ```ts
102
+ * app.set('notifier', createBullMQNotifier({ queue, registry, appUrl }))
103
+ * ```
104
+ */
105
+ interface DirectNotifierOptions {
106
+ /** Driver registry — used to determine which drivers handle each event. */
107
+ registry: DriverRegistry;
108
+ /** Public base URL of the app. */
109
+ appUrl: string;
110
+ }
111
+ /**
112
+ * Create a notifier that sends notifications directly via the driver (no queue).
113
+ * Use this when Redis/BullMQ is not available — suitable for API-based drivers
114
+ * (Brevo, Resend, SendGrid) in development or low-traffic environments.
115
+ */
116
+ declare function createDirectNotifier(opts: DirectNotifierOptions): NotifierFn;
117
+ declare function createBullMQNotifier(opts: BullMQNotifierOptions): NotifierFn;
118
+
119
+ declare const NOTIFICATION_QUEUE_NAME = "feathers-baas:notifications";
120
+ /**
121
+ * Create a BullMQ Queue instance for notification jobs.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const redis = new Redis(process.env.REDIS_URL)
126
+ * const queue = createNotificationQueue(redis)
127
+ * await enqueueNotification(queue, { driverName: 'smtp', notification: { ... } })
128
+ * ```
129
+ */
130
+ declare function createNotificationQueue(redis: Redis): Queue<NotificationJob>;
131
+ /**
132
+ * Enqueue a notification job.
133
+ *
134
+ * @param queue BullMQ Queue instance from createNotificationQueue()
135
+ * @param job Job payload { driverName, notification }
136
+ */
137
+ declare function enqueueNotification(queue: Queue<NotificationJob>, job: NotificationJob): Promise<void>;
138
+
139
+ interface WorkerOptions {
140
+ /** BullMQ concurrency — number of jobs processed in parallel. */
141
+ concurrency?: number;
142
+ }
143
+ /**
144
+ * Create and start the BullMQ worker that processes notification jobs.
145
+ *
146
+ * The worker looks up the driver from the registry by name, renders the
147
+ * EJS template, and calls driver.send(). BullMQ handles retries with
148
+ * exponential back-off automatically.
149
+ *
150
+ * Call worker.close() during graceful shutdown to drain in-flight jobs.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * const worker = createNotificationWorker(registry, redis, { concurrency: 5 })
155
+ *
156
+ * process.on('SIGTERM', async () => {
157
+ * await worker.close()
158
+ * })
159
+ * ```
160
+ */
161
+ declare function createNotificationWorker(registry: DriverRegistry, redis: Redis, opts?: WorkerOptions): Worker<NotificationJob>;
162
+
163
+ interface SMTPDriverOptions {
164
+ /** SMTP connection URL, e.g. smtp://user:pass@mail.example.com:587 */
165
+ url: string;
166
+ /** Display name shown in the From field. */
167
+ fromName: string;
168
+ /** Email address shown in the From field. */
169
+ fromAddress: string;
170
+ }
171
+ /**
172
+ * Email driver backed by nodemailer over SMTP.
173
+ * Templates are rendered with EJS at send time.
174
+ *
175
+ * Use this driver for self-hosted SMTP servers (Postfix, Mailtrap, etc.).
176
+ * For transactional email services, prefer ResendDriver.
177
+ */
178
+ declare class SMTPDriver implements NotifierDriver {
179
+ private readonly opts;
180
+ readonly name = "smtp";
181
+ private readonly transporter;
182
+ constructor(opts: SMTPDriverOptions);
183
+ send(notification: Notification): Promise<void>;
184
+ /** Verify SMTP connectivity. Throws if the server is unreachable. */
185
+ verify(): Promise<void>;
186
+ /** Close the underlying connection pool. */
187
+ close(): void;
188
+ }
189
+
190
+ interface ResendDriverOptions {
191
+ /** Resend API key (from https://resend.com/api-keys). */
192
+ apiKey: string;
193
+ /** Display name shown in the From field. */
194
+ fromName: string;
195
+ /** Verified sender address. Must be a verified domain in your Resend account. */
196
+ fromAddress: string;
197
+ }
198
+ /**
199
+ * Email driver backed by the Resend transactional email API.
200
+ * Templates are rendered with EJS at send time.
201
+ *
202
+ * Resend offers generous free tier and excellent deliverability,
203
+ * making it the recommended choice for new projects.
204
+ */
205
+ declare class ResendDriver implements NotifierDriver {
206
+ private readonly opts;
207
+ readonly name = "resend";
208
+ private readonly client;
209
+ constructor(opts: ResendDriverOptions);
210
+ send(notification: Notification): Promise<void>;
211
+ }
212
+
213
+ interface SendGridDriverOptions {
214
+ /** SendGrid API key (from https://app.sendgrid.com/settings/api_keys). */
215
+ apiKey: string;
216
+ /** Display name shown in the From field. */
217
+ fromName: string;
218
+ /** Verified sender address. Must be verified in your SendGrid account. */
219
+ fromAddress: string;
220
+ }
221
+ /**
222
+ * Email driver backed by the SendGrid API.
223
+ * Templates are rendered with EJS at send time.
224
+ */
225
+ declare class SendGridDriver implements NotifierDriver {
226
+ private readonly opts;
227
+ readonly name = "sendgrid";
228
+ constructor(opts: SendGridDriverOptions);
229
+ send(notification: Notification): Promise<void>;
230
+ }
231
+
232
+ interface BrevoDriverOptions {
233
+ /** Brevo API key (from https://app.brevo.com/settings/keys/api). */
234
+ apiKey: string;
235
+ /** Display name shown in the From field. */
236
+ fromName: string;
237
+ /** Verified sender address. Must be verified in your Brevo account. */
238
+ fromAddress: string;
239
+ }
240
+ /**
241
+ * Email driver backed by the Brevo (formerly Sendinblue) Transactional Email API.
242
+ * Templates are rendered with EJS at send time.
243
+ */
244
+ declare class BrevoDriver implements NotifierDriver {
245
+ private readonly opts;
246
+ readonly name = "brevo";
247
+ private readonly apiInstance;
248
+ constructor(opts: BrevoDriverOptions);
249
+ send(notification: Notification): Promise<void>;
250
+ }
251
+
252
+ export { type AuthNotificationEvent, BrevoDriver, type BrevoDriverOptions, type BullMQNotifierOptions, type DirectNotifierOptions, DriverRegistry, NOTIFICATION_QUEUE_NAME, type Notification, type NotificationEvent, type NotificationJob, type NotifierDriver, type NotifierFn, ResendDriver, type ResendDriverOptions, SMTPDriver, type SMTPDriverOptions, SendGridDriver, type SendGridDriverOptions, type WorkerOptions, createBullMQNotifier, createDirectNotifier, createNotificationQueue, createNotificationWorker, enqueueNotification };
@@ -0,0 +1,252 @@
1
+ import { Queue, Worker } from 'bullmq';
2
+ import { Redis } from 'ioredis';
3
+
4
+ /**
5
+ * A single notification to be delivered to one recipient.
6
+ * Templates are EJS strings — the worker renders them just before sending.
7
+ */
8
+ interface Notification {
9
+ /** Recipient address — email address, phone number, device token, etc. */
10
+ to: string;
11
+ /** Notification channel — determines which driver handles delivery. */
12
+ channel: 'email' | 'sms' | 'push' | 'webhook';
13
+ /** Email subject line (email channel only). */
14
+ subject?: string;
15
+ /** EJS template string for the message body. */
16
+ bodyTemplate: string;
17
+ /** Variables injected into the EJS template at render time. */
18
+ data: Record<string, unknown>;
19
+ /** Optional plain-text fallback for HTML email. */
20
+ textTemplate?: string;
21
+ }
22
+ /**
23
+ * A named notification driver — responsible for delivering one channel.
24
+ * Drivers are registered in the DriverRegistry and looked up by name at
25
+ * job processing time.
26
+ */
27
+ interface NotifierDriver {
28
+ /** Unique name used as the registry key (e.g. 'smtp', 'resend'). */
29
+ readonly name: string;
30
+ /** Deliver the notification to the recipient. */
31
+ send(notification: Notification): Promise<void>;
32
+ }
33
+ /**
34
+ * Payload stored inside each BullMQ job.
35
+ * Worker resolves driverName → NotifierDriver from the registry.
36
+ */
37
+ interface NotificationJob {
38
+ driverName: string;
39
+ notification: Notification;
40
+ }
41
+ /**
42
+ * Maps auth-management event types to a human-readable event key used in
43
+ * the notification event→driver registry.
44
+ */
45
+ type AuthNotificationEvent = 'auth.resend-verify-signup' | 'auth.verify-signup' | 'auth.verify-signup-set-password' | 'auth.send-reset-pwd' | 'auth.reset-pwd' | 'auth.password-change' | 'auth.identity-change';
46
+ /** Union of all supported notification event keys. */
47
+ type NotificationEvent = AuthNotificationEvent | string;
48
+
49
+ /**
50
+ * DriverRegistry maps event keys to the list of drivers that should handle
51
+ * that event.
52
+ *
53
+ * A single driver instance can handle multiple events (e.g. the SMTP driver
54
+ * handles all auth.* email events).
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * const registry = new DriverRegistry()
59
+ * registry.registerDriver(smtpDriver)
60
+ * registry.mapEvent('auth.verify-signup', 'smtp')
61
+ * registry.mapEvent('auth.send-reset-pwd', 'smtp')
62
+ * ```
63
+ */
64
+ declare class DriverRegistry {
65
+ private readonly drivers;
66
+ private readonly eventMap;
67
+ /** Register a driver under its name. Overwrites any existing driver with the same name. */
68
+ registerDriver(driver: NotifierDriver): void;
69
+ /** Map a notification event to a driver name. Multiple calls for the same event add drivers. */
70
+ mapEvent(event: NotificationEvent, driverName: string): void;
71
+ /** Look up a driver by name. Returns undefined if not registered. */
72
+ getDriver(name: string): NotifierDriver | undefined;
73
+ /** Get all driver names mapped to the given event. */
74
+ getDriversForEvent(event: NotificationEvent): string[];
75
+ /** All registered driver names. */
76
+ listDriverNames(): string[];
77
+ }
78
+
79
+ type NotifierFn = (type: string, user: Record<string, unknown>, notifierOptions: Record<string, unknown>) => Promise<void>;
80
+ interface BullMQNotifierOptions {
81
+ /** BullMQ Queue instance — from createNotificationQueue(). */
82
+ queue: Queue<NotificationJob>;
83
+ /** Driver registry — used to determine which drivers handle each event. */
84
+ registry: DriverRegistry;
85
+ /**
86
+ * Public base URL of the app, e.g. https://api.example.com
87
+ * Used to construct verification / reset URLs in email templates.
88
+ */
89
+ appUrl: string;
90
+ }
91
+ /**
92
+ * Create a notifier function compatible with feathers-authentication-management.
93
+ *
94
+ * When an auth event fires (verifySignup, sendResetPwd, etc.), this notifier:
95
+ * 1. Maps the event type to a feathers-baas notification event key
96
+ * 2. Looks up all registered drivers for that event
97
+ * 3. Enqueues one BullMQ job per driver
98
+ * 4. Returns immediately — delivery is asynchronous
99
+ *
100
+ * Wire into the app:
101
+ * ```ts
102
+ * app.set('notifier', createBullMQNotifier({ queue, registry, appUrl }))
103
+ * ```
104
+ */
105
+ interface DirectNotifierOptions {
106
+ /** Driver registry — used to determine which drivers handle each event. */
107
+ registry: DriverRegistry;
108
+ /** Public base URL of the app. */
109
+ appUrl: string;
110
+ }
111
+ /**
112
+ * Create a notifier that sends notifications directly via the driver (no queue).
113
+ * Use this when Redis/BullMQ is not available — suitable for API-based drivers
114
+ * (Brevo, Resend, SendGrid) in development or low-traffic environments.
115
+ */
116
+ declare function createDirectNotifier(opts: DirectNotifierOptions): NotifierFn;
117
+ declare function createBullMQNotifier(opts: BullMQNotifierOptions): NotifierFn;
118
+
119
+ declare const NOTIFICATION_QUEUE_NAME = "feathers-baas:notifications";
120
+ /**
121
+ * Create a BullMQ Queue instance for notification jobs.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const redis = new Redis(process.env.REDIS_URL)
126
+ * const queue = createNotificationQueue(redis)
127
+ * await enqueueNotification(queue, { driverName: 'smtp', notification: { ... } })
128
+ * ```
129
+ */
130
+ declare function createNotificationQueue(redis: Redis): Queue<NotificationJob>;
131
+ /**
132
+ * Enqueue a notification job.
133
+ *
134
+ * @param queue BullMQ Queue instance from createNotificationQueue()
135
+ * @param job Job payload { driverName, notification }
136
+ */
137
+ declare function enqueueNotification(queue: Queue<NotificationJob>, job: NotificationJob): Promise<void>;
138
+
139
+ interface WorkerOptions {
140
+ /** BullMQ concurrency — number of jobs processed in parallel. */
141
+ concurrency?: number;
142
+ }
143
+ /**
144
+ * Create and start the BullMQ worker that processes notification jobs.
145
+ *
146
+ * The worker looks up the driver from the registry by name, renders the
147
+ * EJS template, and calls driver.send(). BullMQ handles retries with
148
+ * exponential back-off automatically.
149
+ *
150
+ * Call worker.close() during graceful shutdown to drain in-flight jobs.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * const worker = createNotificationWorker(registry, redis, { concurrency: 5 })
155
+ *
156
+ * process.on('SIGTERM', async () => {
157
+ * await worker.close()
158
+ * })
159
+ * ```
160
+ */
161
+ declare function createNotificationWorker(registry: DriverRegistry, redis: Redis, opts?: WorkerOptions): Worker<NotificationJob>;
162
+
163
+ interface SMTPDriverOptions {
164
+ /** SMTP connection URL, e.g. smtp://user:pass@mail.example.com:587 */
165
+ url: string;
166
+ /** Display name shown in the From field. */
167
+ fromName: string;
168
+ /** Email address shown in the From field. */
169
+ fromAddress: string;
170
+ }
171
+ /**
172
+ * Email driver backed by nodemailer over SMTP.
173
+ * Templates are rendered with EJS at send time.
174
+ *
175
+ * Use this driver for self-hosted SMTP servers (Postfix, Mailtrap, etc.).
176
+ * For transactional email services, prefer ResendDriver.
177
+ */
178
+ declare class SMTPDriver implements NotifierDriver {
179
+ private readonly opts;
180
+ readonly name = "smtp";
181
+ private readonly transporter;
182
+ constructor(opts: SMTPDriverOptions);
183
+ send(notification: Notification): Promise<void>;
184
+ /** Verify SMTP connectivity. Throws if the server is unreachable. */
185
+ verify(): Promise<void>;
186
+ /** Close the underlying connection pool. */
187
+ close(): void;
188
+ }
189
+
190
+ interface ResendDriverOptions {
191
+ /** Resend API key (from https://resend.com/api-keys). */
192
+ apiKey: string;
193
+ /** Display name shown in the From field. */
194
+ fromName: string;
195
+ /** Verified sender address. Must be a verified domain in your Resend account. */
196
+ fromAddress: string;
197
+ }
198
+ /**
199
+ * Email driver backed by the Resend transactional email API.
200
+ * Templates are rendered with EJS at send time.
201
+ *
202
+ * Resend offers generous free tier and excellent deliverability,
203
+ * making it the recommended choice for new projects.
204
+ */
205
+ declare class ResendDriver implements NotifierDriver {
206
+ private readonly opts;
207
+ readonly name = "resend";
208
+ private readonly client;
209
+ constructor(opts: ResendDriverOptions);
210
+ send(notification: Notification): Promise<void>;
211
+ }
212
+
213
+ interface SendGridDriverOptions {
214
+ /** SendGrid API key (from https://app.sendgrid.com/settings/api_keys). */
215
+ apiKey: string;
216
+ /** Display name shown in the From field. */
217
+ fromName: string;
218
+ /** Verified sender address. Must be verified in your SendGrid account. */
219
+ fromAddress: string;
220
+ }
221
+ /**
222
+ * Email driver backed by the SendGrid API.
223
+ * Templates are rendered with EJS at send time.
224
+ */
225
+ declare class SendGridDriver implements NotifierDriver {
226
+ private readonly opts;
227
+ readonly name = "sendgrid";
228
+ constructor(opts: SendGridDriverOptions);
229
+ send(notification: Notification): Promise<void>;
230
+ }
231
+
232
+ interface BrevoDriverOptions {
233
+ /** Brevo API key (from https://app.brevo.com/settings/keys/api). */
234
+ apiKey: string;
235
+ /** Display name shown in the From field. */
236
+ fromName: string;
237
+ /** Verified sender address. Must be verified in your Brevo account. */
238
+ fromAddress: string;
239
+ }
240
+ /**
241
+ * Email driver backed by the Brevo (formerly Sendinblue) Transactional Email API.
242
+ * Templates are rendered with EJS at send time.
243
+ */
244
+ declare class BrevoDriver implements NotifierDriver {
245
+ private readonly opts;
246
+ readonly name = "brevo";
247
+ private readonly apiInstance;
248
+ constructor(opts: BrevoDriverOptions);
249
+ send(notification: Notification): Promise<void>;
250
+ }
251
+
252
+ export { type AuthNotificationEvent, BrevoDriver, type BrevoDriverOptions, type BullMQNotifierOptions, type DirectNotifierOptions, DriverRegistry, NOTIFICATION_QUEUE_NAME, type Notification, type NotificationEvent, type NotificationJob, type NotifierDriver, type NotifierFn, ResendDriver, type ResendDriverOptions, SMTPDriver, type SMTPDriverOptions, SendGridDriver, type SendGridDriverOptions, type WorkerOptions, createBullMQNotifier, createDirectNotifier, createNotificationQueue, createNotificationWorker, enqueueNotification };