@feathers-baas/plugin-notifications 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @feathers-baas/plugin-notifications
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add default FeathersBaasPlugin export so plugin discovery can validate and install plugins
8
+
9
+ ## 0.1.1
10
+
11
+ ### Patch Changes
12
+
13
+ - Expose ./package.json subpath in package exports to fix plugin discovery resolution
14
+
3
15
  ## 0.1.0
4
16
 
5
17
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -40,9 +40,11 @@ __export(index_exports, {
40
40
  createDirectNotifier: () => createDirectNotifier,
41
41
  createNotificationQueue: () => createNotificationQueue,
42
42
  createNotificationWorker: () => createNotificationWorker,
43
+ default: () => index_default,
43
44
  enqueueNotification: () => enqueueNotification
44
45
  });
45
46
  module.exports = __toCommonJS(index_exports);
47
+ var import_node_module = require("module");
46
48
 
47
49
  // src/registry.ts
48
50
  var DriverRegistry = class {
@@ -364,6 +366,17 @@ var BrevoDriver = class {
364
366
  await this.apiInstance.sendTransacEmail(email);
365
367
  }
366
368
  };
369
+
370
+ // src/index.ts
371
+ var import_meta = {};
372
+ var pkg = (0, import_node_module.createRequire)(import_meta.url)("../package.json");
373
+ var plugin = {
374
+ name: "@feathers-baas/plugin-notifications",
375
+ version: pkg.version,
376
+ async install(_app, _options) {
377
+ }
378
+ };
379
+ var index_default = plugin;
367
380
  // Annotate the CommonJS export names for ESM import in node:
368
381
  0 && (module.exports = {
369
382
  BrevoDriver,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/registry.ts","../src/queue/producer.ts","../src/notifier.ts","../src/queue/worker.ts","../src/drivers/smtp.driver.ts","../src/drivers/resend.driver.ts","../src/drivers/sendgrid.driver.ts","../src/drivers/brevo.driver.ts"],"sourcesContent":["// Generated by feathers-baas. Safe to edit.\nexport { DriverRegistry } from './registry.js'\nexport { createBullMQNotifier, createDirectNotifier } from './notifier.js'\nexport type { NotifierFn, BullMQNotifierOptions, DirectNotifierOptions } from './notifier.js'\n\n// Queue\nexport { createNotificationQueue, enqueueNotification, NOTIFICATION_QUEUE_NAME } from './queue/producer.js'\nexport { createNotificationWorker } from './queue/worker.js'\nexport type { WorkerOptions } from './queue/worker.js'\n\n// Drivers\nexport { SMTPDriver } from './drivers/smtp.driver.js'\nexport type { SMTPDriverOptions } from './drivers/smtp.driver.js'\nexport { ResendDriver } from './drivers/resend.driver.js'\nexport type { ResendDriverOptions } from './drivers/resend.driver.js'\nexport { SendGridDriver } from './drivers/sendgrid.driver.js'\nexport type { SendGridDriverOptions } from './drivers/sendgrid.driver.js'\nexport { BrevoDriver } from './drivers/brevo.driver.js'\nexport type { BrevoDriverOptions } from './drivers/brevo.driver.js'\n\n// Types\nexport type {\n Notification,\n NotifierDriver,\n NotificationJob,\n NotificationEvent,\n AuthNotificationEvent,\n} from './types.js'\n","// Generated by feathers-baas. Safe to edit.\nimport type { NotifierDriver, NotificationEvent } from './types.js'\n\n/**\n * DriverRegistry maps event keys to the list of drivers that should handle\n * that event.\n *\n * A single driver instance can handle multiple events (e.g. the SMTP driver\n * handles all auth.* email events).\n *\n * @example\n * ```ts\n * const registry = new DriverRegistry()\n * registry.registerDriver(smtpDriver)\n * registry.mapEvent('auth.verify-signup', 'smtp')\n * registry.mapEvent('auth.send-reset-pwd', 'smtp')\n * ```\n */\nexport class DriverRegistry {\n private readonly drivers = new Map<string, NotifierDriver>()\n private readonly eventMap = new Map<NotificationEvent, string[]>()\n\n /** Register a driver under its name. Overwrites any existing driver with the same name. */\n registerDriver(driver: NotifierDriver): void {\n this.drivers.set(driver.name, driver)\n }\n\n /** Map a notification event to a driver name. Multiple calls for the same event add drivers. */\n mapEvent(event: NotificationEvent, driverName: string): void {\n const existing = this.eventMap.get(event) ?? []\n if (!existing.includes(driverName)) {\n existing.push(driverName)\n }\n this.eventMap.set(event, existing)\n }\n\n /** Look up a driver by name. Returns undefined if not registered. */\n getDriver(name: string): NotifierDriver | undefined {\n return this.drivers.get(name)\n }\n\n /** Get all driver names mapped to the given event. */\n getDriversForEvent(event: NotificationEvent): string[] {\n return this.eventMap.get(event) ?? []\n }\n\n /** All registered driver names. */\n listDriverNames(): string[] {\n return [...this.drivers.keys()]\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Queue } from 'bullmq'\nimport type { Redis } from 'ioredis'\nimport type { NotificationJob } from '../types.js'\n\nexport const NOTIFICATION_QUEUE_NAME = 'feathers-baas:notifications'\n\n/**\n * Create a BullMQ Queue instance for notification jobs.\n *\n * @example\n * ```ts\n * const redis = new Redis(process.env.REDIS_URL)\n * const queue = createNotificationQueue(redis)\n * await enqueueNotification(queue, { driverName: 'smtp', notification: { ... } })\n * ```\n */\nexport function createNotificationQueue(redis: Redis): Queue<NotificationJob> {\n return new Queue<NotificationJob>(NOTIFICATION_QUEUE_NAME, {\n connection: redis,\n defaultJobOptions: {\n attempts: 5,\n backoff: {\n type: 'exponential',\n delay: 1_000, // initial delay: 1s → 2s → 4s → 8s → 16s\n },\n removeOnComplete: { count: 100 },\n removeOnFail: { count: 500 },\n },\n })\n}\n\n/**\n * Enqueue a notification job.\n *\n * @param queue BullMQ Queue instance from createNotificationQueue()\n * @param job Job payload { driverName, notification }\n */\nexport async function enqueueNotification(\n queue: Queue<NotificationJob>,\n job: NotificationJob,\n): Promise<void> {\n await queue.add('send', job)\n}\n","// Generated by feathers-baas. Safe to edit.\nimport type { Queue } from 'bullmq'\nimport type { NotificationJob, Notification, AuthNotificationEvent } from './types.js'\nimport { enqueueNotification } from './queue/producer.js'\nimport type { DriverRegistry } from './registry.js'\n\n/**\n * Auth-management notification types mapped to feathers-baas event keys.\n * feathers-authentication-management passes these type strings to the notifier.\n */\nconst AUTH_MANAGEMENT_EVENT_MAP: Record<string, AuthNotificationEvent> = {\n resendVerifySignup: 'auth.resend-verify-signup',\n verifySignup: 'auth.verify-signup',\n verifySignupSetPassword: 'auth.verify-signup-set-password',\n sendResetPwd: 'auth.send-reset-pwd',\n resetPwd: 'auth.reset-pwd',\n passwordChange: 'auth.password-change',\n identityChange: 'auth.identity-change',\n}\n\n/**\n * Build a default Notification payload for each auth event type.\n * Templates use EJS — `<%= variable %>` for output, `<% code %>` for logic.\n */\nfunction buildAuthNotification(\n type: AuthNotificationEvent,\n user: Record<string, unknown>,\n appUrl: string,\n): Notification | null {\n const email = user['email'] as string\n if (!email) return null\n\n switch (type) {\n case 'auth.verify-signup':\n case 'auth.resend-verify-signup': {\n const token = user['verifyToken'] as string | undefined\n const verifyUrl = `${appUrl}/verify?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Verify your email address',\n bodyTemplate: `\n <h2>Welcome!</h2>\n <p>Click the link below to verify your email address:</p>\n <p><a href=\"<%= verifyUrl %>\">Verify email</a></p>\n <p>This link expires in 24 hours.</p>\n `,\n textTemplate: `Verify your email: <%= verifyUrl %>`,\n data: { verifyUrl, user },\n }\n }\n\n case 'auth.send-reset-pwd': {\n const token = user['resetToken'] as string | undefined\n const resetUrl = `${appUrl}/reset-password?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Reset your password',\n bodyTemplate: `\n <h2>Password reset requested</h2>\n <p>Click the link below to reset your password:</p>\n <p><a href=\"<%= resetUrl %>\">Reset password</a></p>\n <p>This link expires in 2 hours. If you did not request this, ignore this email.</p>\n `,\n textTemplate: `Reset your password: <%= resetUrl %>`,\n data: { resetUrl, user },\n }\n }\n\n case 'auth.reset-pwd': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your password has been reset',\n bodyTemplate: `<p>Your password has been successfully reset. If you did not do this, please contact support immediately.</p>`,\n textTemplate: `Your password has been reset. If you did not do this, please contact support.`,\n data: { user },\n }\n }\n\n case 'auth.password-change': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your password has been changed',\n bodyTemplate: `<p>Your account password was just changed. If this was not you, please contact support immediately.</p>`,\n textTemplate: `Your password was changed. Contact support if this wasn't you.`,\n data: { user },\n }\n }\n\n case 'auth.identity-change': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your email address is being changed',\n bodyTemplate: `<p>A request was made to change your email address. Click the link to confirm.</p>`,\n textTemplate: `Confirm your email change by clicking the link in the HTML email.`,\n data: { user },\n }\n }\n\n case 'auth.verify-signup-set-password': {\n const token = user['verifyToken'] as string | undefined\n const verifyUrl = `${appUrl}/verify-set-password?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Set your password',\n bodyTemplate: `\n <h2>Set your password</h2>\n <p><a href=\"<%= verifyUrl %>\">Set password</a></p>\n `,\n textTemplate: `Set your password: <%= verifyUrl %>`,\n data: { verifyUrl, user },\n }\n }\n }\n}\n\nexport type NotifierFn = (\n type: string,\n user: Record<string, unknown>,\n notifierOptions: Record<string, unknown>,\n) => Promise<void>\n\nexport interface BullMQNotifierOptions {\n /** BullMQ Queue instance — from createNotificationQueue(). */\n queue: Queue<NotificationJob>\n /** Driver registry — used to determine which drivers handle each event. */\n registry: DriverRegistry\n /**\n * Public base URL of the app, e.g. https://api.example.com\n * Used to construct verification / reset URLs in email templates.\n */\n appUrl: string\n}\n\n/**\n * Create a notifier function compatible with feathers-authentication-management.\n *\n * When an auth event fires (verifySignup, sendResetPwd, etc.), this notifier:\n * 1. Maps the event type to a feathers-baas notification event key\n * 2. Looks up all registered drivers for that event\n * 3. Enqueues one BullMQ job per driver\n * 4. Returns immediately — delivery is asynchronous\n *\n * Wire into the app:\n * ```ts\n * app.set('notifier', createBullMQNotifier({ queue, registry, appUrl }))\n * ```\n */\nexport interface DirectNotifierOptions {\n /** Driver registry — used to determine which drivers handle each event. */\n registry: DriverRegistry\n /** Public base URL of the app. */\n appUrl: string\n}\n\n/**\n * Create a notifier that sends notifications directly via the driver (no queue).\n * Use this when Redis/BullMQ is not available — suitable for API-based drivers\n * (Brevo, Resend, SendGrid) in development or low-traffic environments.\n */\nexport function createDirectNotifier(opts: DirectNotifierOptions): NotifierFn {\n return async (type, user, _notifierOptions) => {\n const eventKey = AUTH_MANAGEMENT_EVENT_MAP[type]\n if (!eventKey) return\n\n const notification = buildAuthNotification(eventKey, user, opts.appUrl)\n if (!notification) return\n\n const driverNames = opts.registry.getDriversForEvent(eventKey)\n if (driverNames.length === 0) return\n\n await Promise.all(\n driverNames.map(async (driverName) => {\n const driver = opts.registry.getDriver(driverName)\n if (!driver) return\n await driver.send(notification)\n }),\n )\n }\n}\n\nexport function createBullMQNotifier(opts: BullMQNotifierOptions): NotifierFn {\n return async (type, user, _notifierOptions) => {\n const eventKey = AUTH_MANAGEMENT_EVENT_MAP[type]\n if (!eventKey) return // Unknown event type — no-op\n\n const notification = buildAuthNotification(eventKey, user, opts.appUrl)\n if (!notification) return // Missing required user fields — no-op\n\n const driverNames = opts.registry.getDriversForEvent(eventKey)\n if (driverNames.length === 0) return // No drivers configured for this event\n\n await Promise.all(\n driverNames.map((driverName) =>\n enqueueNotification(opts.queue, { driverName, notification }),\n ),\n )\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Worker } from 'bullmq'\nimport type { Redis } from 'ioredis'\nimport type { DriverRegistry } from '../registry.js'\nimport type { NotificationJob } from '../types.js'\nimport { NOTIFICATION_QUEUE_NAME } from './producer.js'\n\nexport interface WorkerOptions {\n /** BullMQ concurrency — number of jobs processed in parallel. */\n concurrency?: number\n}\n\n/**\n * Create and start the BullMQ worker that processes notification jobs.\n *\n * The worker looks up the driver from the registry by name, renders the\n * EJS template, and calls driver.send(). BullMQ handles retries with\n * exponential back-off automatically.\n *\n * Call worker.close() during graceful shutdown to drain in-flight jobs.\n *\n * @example\n * ```ts\n * const worker = createNotificationWorker(registry, redis, { concurrency: 5 })\n *\n * process.on('SIGTERM', async () => {\n * await worker.close()\n * })\n * ```\n */\nexport function createNotificationWorker(\n registry: DriverRegistry,\n redis: Redis,\n opts: WorkerOptions = {},\n): Worker<NotificationJob> {\n const concurrency = opts.concurrency ?? 5\n\n const worker = new Worker<NotificationJob>(\n NOTIFICATION_QUEUE_NAME,\n async (job) => {\n const { driverName, notification } = job.data\n\n const driver = registry.getDriver(driverName)\n if (!driver) {\n throw new Error(`Notification driver \"${driverName}\" is not registered`)\n }\n\n await driver.send(notification)\n },\n {\n connection: redis,\n concurrency,\n // Don't create a new redis connection per job — reuse the pool\n autorun: true,\n },\n )\n\n worker.on('failed', (job, err) => {\n // BullMQ handles retries; this is just for observability\n const jobId = job?.id ?? 'unknown'\n const message = err instanceof Error ? err.message : String(err)\n console.error(`[notifications] Job ${jobId} failed: ${message}`)\n })\n\n return worker\n}\n","// Generated by feathers-baas. Safe to edit.\nimport nodemailer from 'nodemailer'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface SMTPDriverOptions {\n /** SMTP connection URL, e.g. smtp://user:pass@mail.example.com:587 */\n url: string\n /** Display name shown in the From field. */\n fromName: string\n /** Email address shown in the From field. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by nodemailer over SMTP.\n * Templates are rendered with EJS at send time.\n *\n * Use this driver for self-hosted SMTP servers (Postfix, Mailtrap, etc.).\n * For transactional email services, prefer ResendDriver.\n */\nexport class SMTPDriver implements NotifierDriver {\n readonly name = 'smtp'\n\n private readonly transporter: nodemailer.Transporter\n\n constructor(private readonly opts: SMTPDriverOptions) {\n this.transporter = nodemailer.createTransport(opts.url)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n await this.transporter.sendMail({\n from: `\"${this.opts.fromName}\" <${this.opts.fromAddress}>`,\n to: notification.to,\n subject: notification.subject ?? '(no subject)',\n html,\n text,\n })\n }\n\n /** Verify SMTP connectivity. Throws if the server is unreachable. */\n async verify(): Promise<void> {\n await this.transporter.verify()\n }\n\n /** Close the underlying connection pool. */\n close(): void {\n this.transporter.close()\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Resend } from 'resend'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface ResendDriverOptions {\n /** Resend API key (from https://resend.com/api-keys). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be a verified domain in your Resend account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the Resend transactional email API.\n * Templates are rendered with EJS at send time.\n *\n * Resend offers generous free tier and excellent deliverability,\n * making it the recommended choice for new projects.\n */\nexport class ResendDriver implements NotifierDriver {\n readonly name = 'resend'\n\n private readonly client: Resend\n\n constructor(private readonly opts: ResendDriverOptions) {\n this.client = new Resend(opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n const result = await this.client.emails.send({\n from: `${this.opts.fromName} <${this.opts.fromAddress}>`,\n to: [notification.to],\n subject: notification.subject ?? '(no subject)',\n html,\n text,\n })\n\n if ('error' in result && result.error) {\n throw new Error(`Resend error: ${result.error.message}`)\n }\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport sgMail from '@sendgrid/mail'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface SendGridDriverOptions {\n /** SendGrid API key (from https://app.sendgrid.com/settings/api_keys). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be verified in your SendGrid account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the SendGrid API.\n * Templates are rendered with EJS at send time.\n */\nexport class SendGridDriver implements NotifierDriver {\n readonly name = 'sendgrid'\n\n constructor(private readonly opts: SendGridDriverOptions) {\n sgMail.setApiKey(opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n await sgMail.send({\n from: { name: this.opts.fromName, email: this.opts.fromAddress },\n to: notification.to,\n subject: notification.subject ?? '(no subject)',\n html,\n ...(text ? { text } : {}),\n })\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { TransactionalEmailsApi, TransactionalEmailsApiApiKeys, SendSmtpEmail } from '@getbrevo/brevo'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface BrevoDriverOptions {\n /** Brevo API key (from https://app.brevo.com/settings/keys/api). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be verified in your Brevo account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the Brevo (formerly Sendinblue) Transactional Email API.\n * Templates are rendered with EJS at send time.\n */\nexport class BrevoDriver implements NotifierDriver {\n readonly name = 'brevo'\n\n private readonly apiInstance: TransactionalEmailsApi\n\n constructor(private readonly opts: BrevoDriverOptions) {\n this.apiInstance = new TransactionalEmailsApi()\n this.apiInstance.setApiKey(TransactionalEmailsApiApiKeys.apiKey, opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n const email = new SendSmtpEmail()\n email.sender = { name: this.opts.fromName, email: this.opts.fromAddress }\n email.to = [{ email: notification.to }]\n email.subject = notification.subject ?? '(no subject)'\n email.htmlContent = html\n if (text) email.textContent = text\n\n await this.apiInstance.sendTransacEmail(email)\n }\n}\n"],"mappings":"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","names":["import_bullmq","nodemailer","ejs","ejsRender","ejs","ejsRender","sgMail","ejs","ejsRender"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/registry.ts","../src/queue/producer.ts","../src/notifier.ts","../src/queue/worker.ts","../src/drivers/smtp.driver.ts","../src/drivers/resend.driver.ts","../src/drivers/sendgrid.driver.ts","../src/drivers/brevo.driver.ts"],"sourcesContent":["// Generated by feathers-baas. Safe to edit.\nimport { createRequire } from 'node:module'\nimport type { FeathersBaasPlugin } from './plugin.js'\n\nconst pkg = createRequire(import.meta.url)('../package.json') as { version: string }\n\nconst plugin: FeathersBaasPlugin = {\n name: '@feathers-baas/plugin-notifications',\n version: pkg.version,\n async install(_app, _options) {\n // Notification drivers are configured via core's configureNotifications()\n // based on env vars. Auto-install is a no-op; discovery confirms the plugin is present.\n },\n}\n\nexport default plugin\n\nexport { DriverRegistry } from './registry.js'\nexport { createBullMQNotifier, createDirectNotifier } from './notifier.js'\nexport type { NotifierFn, BullMQNotifierOptions, DirectNotifierOptions } from './notifier.js'\n\n// Queue\nexport { createNotificationQueue, enqueueNotification, NOTIFICATION_QUEUE_NAME } from './queue/producer.js'\nexport { createNotificationWorker } from './queue/worker.js'\nexport type { WorkerOptions } from './queue/worker.js'\n\n// Drivers\nexport { SMTPDriver } from './drivers/smtp.driver.js'\nexport type { SMTPDriverOptions } from './drivers/smtp.driver.js'\nexport { ResendDriver } from './drivers/resend.driver.js'\nexport type { ResendDriverOptions } from './drivers/resend.driver.js'\nexport { SendGridDriver } from './drivers/sendgrid.driver.js'\nexport type { SendGridDriverOptions } from './drivers/sendgrid.driver.js'\nexport { BrevoDriver } from './drivers/brevo.driver.js'\nexport type { BrevoDriverOptions } from './drivers/brevo.driver.js'\n\n// Types\nexport type {\n Notification,\n NotifierDriver,\n NotificationJob,\n NotificationEvent,\n AuthNotificationEvent,\n} from './types.js'\n","// Generated by feathers-baas. Safe to edit.\nimport type { NotifierDriver, NotificationEvent } from './types.js'\n\n/**\n * DriverRegistry maps event keys to the list of drivers that should handle\n * that event.\n *\n * A single driver instance can handle multiple events (e.g. the SMTP driver\n * handles all auth.* email events).\n *\n * @example\n * ```ts\n * const registry = new DriverRegistry()\n * registry.registerDriver(smtpDriver)\n * registry.mapEvent('auth.verify-signup', 'smtp')\n * registry.mapEvent('auth.send-reset-pwd', 'smtp')\n * ```\n */\nexport class DriverRegistry {\n private readonly drivers = new Map<string, NotifierDriver>()\n private readonly eventMap = new Map<NotificationEvent, string[]>()\n\n /** Register a driver under its name. Overwrites any existing driver with the same name. */\n registerDriver(driver: NotifierDriver): void {\n this.drivers.set(driver.name, driver)\n }\n\n /** Map a notification event to a driver name. Multiple calls for the same event add drivers. */\n mapEvent(event: NotificationEvent, driverName: string): void {\n const existing = this.eventMap.get(event) ?? []\n if (!existing.includes(driverName)) {\n existing.push(driverName)\n }\n this.eventMap.set(event, existing)\n }\n\n /** Look up a driver by name. Returns undefined if not registered. */\n getDriver(name: string): NotifierDriver | undefined {\n return this.drivers.get(name)\n }\n\n /** Get all driver names mapped to the given event. */\n getDriversForEvent(event: NotificationEvent): string[] {\n return this.eventMap.get(event) ?? []\n }\n\n /** All registered driver names. */\n listDriverNames(): string[] {\n return [...this.drivers.keys()]\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Queue } from 'bullmq'\nimport type { Redis } from 'ioredis'\nimport type { NotificationJob } from '../types.js'\n\nexport const NOTIFICATION_QUEUE_NAME = 'feathers-baas:notifications'\n\n/**\n * Create a BullMQ Queue instance for notification jobs.\n *\n * @example\n * ```ts\n * const redis = new Redis(process.env.REDIS_URL)\n * const queue = createNotificationQueue(redis)\n * await enqueueNotification(queue, { driverName: 'smtp', notification: { ... } })\n * ```\n */\nexport function createNotificationQueue(redis: Redis): Queue<NotificationJob> {\n return new Queue<NotificationJob>(NOTIFICATION_QUEUE_NAME, {\n connection: redis,\n defaultJobOptions: {\n attempts: 5,\n backoff: {\n type: 'exponential',\n delay: 1_000, // initial delay: 1s → 2s → 4s → 8s → 16s\n },\n removeOnComplete: { count: 100 },\n removeOnFail: { count: 500 },\n },\n })\n}\n\n/**\n * Enqueue a notification job.\n *\n * @param queue BullMQ Queue instance from createNotificationQueue()\n * @param job Job payload { driverName, notification }\n */\nexport async function enqueueNotification(\n queue: Queue<NotificationJob>,\n job: NotificationJob,\n): Promise<void> {\n await queue.add('send', job)\n}\n","// Generated by feathers-baas. Safe to edit.\nimport type { Queue } from 'bullmq'\nimport type { NotificationJob, Notification, AuthNotificationEvent } from './types.js'\nimport { enqueueNotification } from './queue/producer.js'\nimport type { DriverRegistry } from './registry.js'\n\n/**\n * Auth-management notification types mapped to feathers-baas event keys.\n * feathers-authentication-management passes these type strings to the notifier.\n */\nconst AUTH_MANAGEMENT_EVENT_MAP: Record<string, AuthNotificationEvent> = {\n resendVerifySignup: 'auth.resend-verify-signup',\n verifySignup: 'auth.verify-signup',\n verifySignupSetPassword: 'auth.verify-signup-set-password',\n sendResetPwd: 'auth.send-reset-pwd',\n resetPwd: 'auth.reset-pwd',\n passwordChange: 'auth.password-change',\n identityChange: 'auth.identity-change',\n}\n\n/**\n * Build a default Notification payload for each auth event type.\n * Templates use EJS — `<%= variable %>` for output, `<% code %>` for logic.\n */\nfunction buildAuthNotification(\n type: AuthNotificationEvent,\n user: Record<string, unknown>,\n appUrl: string,\n): Notification | null {\n const email = user['email'] as string\n if (!email) return null\n\n switch (type) {\n case 'auth.verify-signup':\n case 'auth.resend-verify-signup': {\n const token = user['verifyToken'] as string | undefined\n const verifyUrl = `${appUrl}/verify?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Verify your email address',\n bodyTemplate: `\n <h2>Welcome!</h2>\n <p>Click the link below to verify your email address:</p>\n <p><a href=\"<%= verifyUrl %>\">Verify email</a></p>\n <p>This link expires in 24 hours.</p>\n `,\n textTemplate: `Verify your email: <%= verifyUrl %>`,\n data: { verifyUrl, user },\n }\n }\n\n case 'auth.send-reset-pwd': {\n const token = user['resetToken'] as string | undefined\n const resetUrl = `${appUrl}/reset-password?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Reset your password',\n bodyTemplate: `\n <h2>Password reset requested</h2>\n <p>Click the link below to reset your password:</p>\n <p><a href=\"<%= resetUrl %>\">Reset password</a></p>\n <p>This link expires in 2 hours. If you did not request this, ignore this email.</p>\n `,\n textTemplate: `Reset your password: <%= resetUrl %>`,\n data: { resetUrl, user },\n }\n }\n\n case 'auth.reset-pwd': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your password has been reset',\n bodyTemplate: `<p>Your password has been successfully reset. If you did not do this, please contact support immediately.</p>`,\n textTemplate: `Your password has been reset. If you did not do this, please contact support.`,\n data: { user },\n }\n }\n\n case 'auth.password-change': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your password has been changed',\n bodyTemplate: `<p>Your account password was just changed. If this was not you, please contact support immediately.</p>`,\n textTemplate: `Your password was changed. Contact support if this wasn't you.`,\n data: { user },\n }\n }\n\n case 'auth.identity-change': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your email address is being changed',\n bodyTemplate: `<p>A request was made to change your email address. Click the link to confirm.</p>`,\n textTemplate: `Confirm your email change by clicking the link in the HTML email.`,\n data: { user },\n }\n }\n\n case 'auth.verify-signup-set-password': {\n const token = user['verifyToken'] as string | undefined\n const verifyUrl = `${appUrl}/verify-set-password?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Set your password',\n bodyTemplate: `\n <h2>Set your password</h2>\n <p><a href=\"<%= verifyUrl %>\">Set password</a></p>\n `,\n textTemplate: `Set your password: <%= verifyUrl %>`,\n data: { verifyUrl, user },\n }\n }\n }\n}\n\nexport type NotifierFn = (\n type: string,\n user: Record<string, unknown>,\n notifierOptions: Record<string, unknown>,\n) => Promise<void>\n\nexport interface BullMQNotifierOptions {\n /** BullMQ Queue instance — from createNotificationQueue(). */\n queue: Queue<NotificationJob>\n /** Driver registry — used to determine which drivers handle each event. */\n registry: DriverRegistry\n /**\n * Public base URL of the app, e.g. https://api.example.com\n * Used to construct verification / reset URLs in email templates.\n */\n appUrl: string\n}\n\n/**\n * Create a notifier function compatible with feathers-authentication-management.\n *\n * When an auth event fires (verifySignup, sendResetPwd, etc.), this notifier:\n * 1. Maps the event type to a feathers-baas notification event key\n * 2. Looks up all registered drivers for that event\n * 3. Enqueues one BullMQ job per driver\n * 4. Returns immediately — delivery is asynchronous\n *\n * Wire into the app:\n * ```ts\n * app.set('notifier', createBullMQNotifier({ queue, registry, appUrl }))\n * ```\n */\nexport interface DirectNotifierOptions {\n /** Driver registry — used to determine which drivers handle each event. */\n registry: DriverRegistry\n /** Public base URL of the app. */\n appUrl: string\n}\n\n/**\n * Create a notifier that sends notifications directly via the driver (no queue).\n * Use this when Redis/BullMQ is not available — suitable for API-based drivers\n * (Brevo, Resend, SendGrid) in development or low-traffic environments.\n */\nexport function createDirectNotifier(opts: DirectNotifierOptions): NotifierFn {\n return async (type, user, _notifierOptions) => {\n const eventKey = AUTH_MANAGEMENT_EVENT_MAP[type]\n if (!eventKey) return\n\n const notification = buildAuthNotification(eventKey, user, opts.appUrl)\n if (!notification) return\n\n const driverNames = opts.registry.getDriversForEvent(eventKey)\n if (driverNames.length === 0) return\n\n await Promise.all(\n driverNames.map(async (driverName) => {\n const driver = opts.registry.getDriver(driverName)\n if (!driver) return\n await driver.send(notification)\n }),\n )\n }\n}\n\nexport function createBullMQNotifier(opts: BullMQNotifierOptions): NotifierFn {\n return async (type, user, _notifierOptions) => {\n const eventKey = AUTH_MANAGEMENT_EVENT_MAP[type]\n if (!eventKey) return // Unknown event type — no-op\n\n const notification = buildAuthNotification(eventKey, user, opts.appUrl)\n if (!notification) return // Missing required user fields — no-op\n\n const driverNames = opts.registry.getDriversForEvent(eventKey)\n if (driverNames.length === 0) return // No drivers configured for this event\n\n await Promise.all(\n driverNames.map((driverName) =>\n enqueueNotification(opts.queue, { driverName, notification }),\n ),\n )\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Worker } from 'bullmq'\nimport type { Redis } from 'ioredis'\nimport type { DriverRegistry } from '../registry.js'\nimport type { NotificationJob } from '../types.js'\nimport { NOTIFICATION_QUEUE_NAME } from './producer.js'\n\nexport interface WorkerOptions {\n /** BullMQ concurrency — number of jobs processed in parallel. */\n concurrency?: number\n}\n\n/**\n * Create and start the BullMQ worker that processes notification jobs.\n *\n * The worker looks up the driver from the registry by name, renders the\n * EJS template, and calls driver.send(). BullMQ handles retries with\n * exponential back-off automatically.\n *\n * Call worker.close() during graceful shutdown to drain in-flight jobs.\n *\n * @example\n * ```ts\n * const worker = createNotificationWorker(registry, redis, { concurrency: 5 })\n *\n * process.on('SIGTERM', async () => {\n * await worker.close()\n * })\n * ```\n */\nexport function createNotificationWorker(\n registry: DriverRegistry,\n redis: Redis,\n opts: WorkerOptions = {},\n): Worker<NotificationJob> {\n const concurrency = opts.concurrency ?? 5\n\n const worker = new Worker<NotificationJob>(\n NOTIFICATION_QUEUE_NAME,\n async (job) => {\n const { driverName, notification } = job.data\n\n const driver = registry.getDriver(driverName)\n if (!driver) {\n throw new Error(`Notification driver \"${driverName}\" is not registered`)\n }\n\n await driver.send(notification)\n },\n {\n connection: redis,\n concurrency,\n // Don't create a new redis connection per job — reuse the pool\n autorun: true,\n },\n )\n\n worker.on('failed', (job, err) => {\n // BullMQ handles retries; this is just for observability\n const jobId = job?.id ?? 'unknown'\n const message = err instanceof Error ? err.message : String(err)\n console.error(`[notifications] Job ${jobId} failed: ${message}`)\n })\n\n return worker\n}\n","// Generated by feathers-baas. Safe to edit.\nimport nodemailer from 'nodemailer'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface SMTPDriverOptions {\n /** SMTP connection URL, e.g. smtp://user:pass@mail.example.com:587 */\n url: string\n /** Display name shown in the From field. */\n fromName: string\n /** Email address shown in the From field. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by nodemailer over SMTP.\n * Templates are rendered with EJS at send time.\n *\n * Use this driver for self-hosted SMTP servers (Postfix, Mailtrap, etc.).\n * For transactional email services, prefer ResendDriver.\n */\nexport class SMTPDriver implements NotifierDriver {\n readonly name = 'smtp'\n\n private readonly transporter: nodemailer.Transporter\n\n constructor(private readonly opts: SMTPDriverOptions) {\n this.transporter = nodemailer.createTransport(opts.url)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n await this.transporter.sendMail({\n from: `\"${this.opts.fromName}\" <${this.opts.fromAddress}>`,\n to: notification.to,\n subject: notification.subject ?? '(no subject)',\n html,\n text,\n })\n }\n\n /** Verify SMTP connectivity. Throws if the server is unreachable. */\n async verify(): Promise<void> {\n await this.transporter.verify()\n }\n\n /** Close the underlying connection pool. */\n close(): void {\n this.transporter.close()\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Resend } from 'resend'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface ResendDriverOptions {\n /** Resend API key (from https://resend.com/api-keys). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be a verified domain in your Resend account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the Resend transactional email API.\n * Templates are rendered with EJS at send time.\n *\n * Resend offers generous free tier and excellent deliverability,\n * making it the recommended choice for new projects.\n */\nexport class ResendDriver implements NotifierDriver {\n readonly name = 'resend'\n\n private readonly client: Resend\n\n constructor(private readonly opts: ResendDriverOptions) {\n this.client = new Resend(opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n const result = await this.client.emails.send({\n from: `${this.opts.fromName} <${this.opts.fromAddress}>`,\n to: [notification.to],\n subject: notification.subject ?? '(no subject)',\n html,\n text,\n })\n\n if ('error' in result && result.error) {\n throw new Error(`Resend error: ${result.error.message}`)\n }\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport sgMail from '@sendgrid/mail'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface SendGridDriverOptions {\n /** SendGrid API key (from https://app.sendgrid.com/settings/api_keys). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be verified in your SendGrid account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the SendGrid API.\n * Templates are rendered with EJS at send time.\n */\nexport class SendGridDriver implements NotifierDriver {\n readonly name = 'sendgrid'\n\n constructor(private readonly opts: SendGridDriverOptions) {\n sgMail.setApiKey(opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n await sgMail.send({\n from: { name: this.opts.fromName, email: this.opts.fromAddress },\n to: notification.to,\n subject: notification.subject ?? '(no subject)',\n html,\n ...(text ? { text } : {}),\n })\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { TransactionalEmailsApi, TransactionalEmailsApiApiKeys, SendSmtpEmail } from '@getbrevo/brevo'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface BrevoDriverOptions {\n /** Brevo API key (from https://app.brevo.com/settings/keys/api). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be verified in your Brevo account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the Brevo (formerly Sendinblue) Transactional Email API.\n * Templates are rendered with EJS at send time.\n */\nexport class BrevoDriver implements NotifierDriver {\n readonly name = 'brevo'\n\n private readonly apiInstance: TransactionalEmailsApi\n\n constructor(private readonly opts: BrevoDriverOptions) {\n this.apiInstance = new TransactionalEmailsApi()\n this.apiInstance.setApiKey(TransactionalEmailsApiApiKeys.apiKey, opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n const email = new SendSmtpEmail()\n email.sender = { name: this.opts.fromName, email: this.opts.fromAddress }\n email.to = [{ email: notification.to }]\n email.subject = notification.subject ?? '(no subject)'\n email.htmlContent = html\n if (text) email.textContent = text\n\n await this.apiInstance.sendTransacEmail(email)\n }\n}\n"],"mappings":"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","names":["import_bullmq","nodemailer","ejs","ejsRender","ejs","ejsRender","sgMail","ejs","ejsRender"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,12 @@
1
1
  import { Queue, Worker } from 'bullmq';
2
2
  import { Redis } from 'ioredis';
3
3
 
4
+ interface FeathersBaasPlugin {
5
+ name: string;
6
+ version: string;
7
+ install(app: unknown, options: Record<string, unknown>): Promise<void>;
8
+ }
9
+
4
10
  /**
5
11
  * A single notification to be delivered to one recipient.
6
12
  * Templates are EJS strings — the worker renders them just before sending.
@@ -249,4 +255,6 @@ declare class BrevoDriver implements NotifierDriver {
249
255
  send(notification: Notification): Promise<void>;
250
256
  }
251
257
 
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 };
258
+ declare const plugin: FeathersBaasPlugin;
259
+
260
+ 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, plugin as default, enqueueNotification };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  import { Queue, Worker } from 'bullmq';
2
2
  import { Redis } from 'ioredis';
3
3
 
4
+ interface FeathersBaasPlugin {
5
+ name: string;
6
+ version: string;
7
+ install(app: unknown, options: Record<string, unknown>): Promise<void>;
8
+ }
9
+
4
10
  /**
5
11
  * A single notification to be delivered to one recipient.
6
12
  * Templates are EJS strings — the worker renders them just before sending.
@@ -249,4 +255,6 @@ declare class BrevoDriver implements NotifierDriver {
249
255
  send(notification: Notification): Promise<void>;
250
256
  }
251
257
 
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 };
258
+ declare const plugin: FeathersBaasPlugin;
259
+
260
+ 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, plugin as default, enqueueNotification };
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/index.ts
2
+ import { createRequire } from "module";
3
+
1
4
  // src/registry.ts
2
5
  var DriverRegistry = class {
3
6
  drivers = /* @__PURE__ */ new Map();
@@ -318,6 +321,16 @@ var BrevoDriver = class {
318
321
  await this.apiInstance.sendTransacEmail(email);
319
322
  }
320
323
  };
324
+
325
+ // src/index.ts
326
+ var pkg = createRequire(import.meta.url)("../package.json");
327
+ var plugin = {
328
+ name: "@feathers-baas/plugin-notifications",
329
+ version: pkg.version,
330
+ async install(_app, _options) {
331
+ }
332
+ };
333
+ var index_default = plugin;
321
334
  export {
322
335
  BrevoDriver,
323
336
  DriverRegistry,
@@ -329,6 +342,7 @@ export {
329
342
  createDirectNotifier,
330
343
  createNotificationQueue,
331
344
  createNotificationWorker,
345
+ index_default as default,
332
346
  enqueueNotification
333
347
  };
334
348
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/registry.ts","../src/queue/producer.ts","../src/notifier.ts","../src/queue/worker.ts","../src/drivers/smtp.driver.ts","../src/drivers/resend.driver.ts","../src/drivers/sendgrid.driver.ts","../src/drivers/brevo.driver.ts"],"sourcesContent":["// Generated by feathers-baas. Safe to edit.\nimport type { NotifierDriver, NotificationEvent } from './types.js'\n\n/**\n * DriverRegistry maps event keys to the list of drivers that should handle\n * that event.\n *\n * A single driver instance can handle multiple events (e.g. the SMTP driver\n * handles all auth.* email events).\n *\n * @example\n * ```ts\n * const registry = new DriverRegistry()\n * registry.registerDriver(smtpDriver)\n * registry.mapEvent('auth.verify-signup', 'smtp')\n * registry.mapEvent('auth.send-reset-pwd', 'smtp')\n * ```\n */\nexport class DriverRegistry {\n private readonly drivers = new Map<string, NotifierDriver>()\n private readonly eventMap = new Map<NotificationEvent, string[]>()\n\n /** Register a driver under its name. Overwrites any existing driver with the same name. */\n registerDriver(driver: NotifierDriver): void {\n this.drivers.set(driver.name, driver)\n }\n\n /** Map a notification event to a driver name. Multiple calls for the same event add drivers. */\n mapEvent(event: NotificationEvent, driverName: string): void {\n const existing = this.eventMap.get(event) ?? []\n if (!existing.includes(driverName)) {\n existing.push(driverName)\n }\n this.eventMap.set(event, existing)\n }\n\n /** Look up a driver by name. Returns undefined if not registered. */\n getDriver(name: string): NotifierDriver | undefined {\n return this.drivers.get(name)\n }\n\n /** Get all driver names mapped to the given event. */\n getDriversForEvent(event: NotificationEvent): string[] {\n return this.eventMap.get(event) ?? []\n }\n\n /** All registered driver names. */\n listDriverNames(): string[] {\n return [...this.drivers.keys()]\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Queue } from 'bullmq'\nimport type { Redis } from 'ioredis'\nimport type { NotificationJob } from '../types.js'\n\nexport const NOTIFICATION_QUEUE_NAME = 'feathers-baas:notifications'\n\n/**\n * Create a BullMQ Queue instance for notification jobs.\n *\n * @example\n * ```ts\n * const redis = new Redis(process.env.REDIS_URL)\n * const queue = createNotificationQueue(redis)\n * await enqueueNotification(queue, { driverName: 'smtp', notification: { ... } })\n * ```\n */\nexport function createNotificationQueue(redis: Redis): Queue<NotificationJob> {\n return new Queue<NotificationJob>(NOTIFICATION_QUEUE_NAME, {\n connection: redis,\n defaultJobOptions: {\n attempts: 5,\n backoff: {\n type: 'exponential',\n delay: 1_000, // initial delay: 1s → 2s → 4s → 8s → 16s\n },\n removeOnComplete: { count: 100 },\n removeOnFail: { count: 500 },\n },\n })\n}\n\n/**\n * Enqueue a notification job.\n *\n * @param queue BullMQ Queue instance from createNotificationQueue()\n * @param job Job payload { driverName, notification }\n */\nexport async function enqueueNotification(\n queue: Queue<NotificationJob>,\n job: NotificationJob,\n): Promise<void> {\n await queue.add('send', job)\n}\n","// Generated by feathers-baas. Safe to edit.\nimport type { Queue } from 'bullmq'\nimport type { NotificationJob, Notification, AuthNotificationEvent } from './types.js'\nimport { enqueueNotification } from './queue/producer.js'\nimport type { DriverRegistry } from './registry.js'\n\n/**\n * Auth-management notification types mapped to feathers-baas event keys.\n * feathers-authentication-management passes these type strings to the notifier.\n */\nconst AUTH_MANAGEMENT_EVENT_MAP: Record<string, AuthNotificationEvent> = {\n resendVerifySignup: 'auth.resend-verify-signup',\n verifySignup: 'auth.verify-signup',\n verifySignupSetPassword: 'auth.verify-signup-set-password',\n sendResetPwd: 'auth.send-reset-pwd',\n resetPwd: 'auth.reset-pwd',\n passwordChange: 'auth.password-change',\n identityChange: 'auth.identity-change',\n}\n\n/**\n * Build a default Notification payload for each auth event type.\n * Templates use EJS — `<%= variable %>` for output, `<% code %>` for logic.\n */\nfunction buildAuthNotification(\n type: AuthNotificationEvent,\n user: Record<string, unknown>,\n appUrl: string,\n): Notification | null {\n const email = user['email'] as string\n if (!email) return null\n\n switch (type) {\n case 'auth.verify-signup':\n case 'auth.resend-verify-signup': {\n const token = user['verifyToken'] as string | undefined\n const verifyUrl = `${appUrl}/verify?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Verify your email address',\n bodyTemplate: `\n <h2>Welcome!</h2>\n <p>Click the link below to verify your email address:</p>\n <p><a href=\"<%= verifyUrl %>\">Verify email</a></p>\n <p>This link expires in 24 hours.</p>\n `,\n textTemplate: `Verify your email: <%= verifyUrl %>`,\n data: { verifyUrl, user },\n }\n }\n\n case 'auth.send-reset-pwd': {\n const token = user['resetToken'] as string | undefined\n const resetUrl = `${appUrl}/reset-password?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Reset your password',\n bodyTemplate: `\n <h2>Password reset requested</h2>\n <p>Click the link below to reset your password:</p>\n <p><a href=\"<%= resetUrl %>\">Reset password</a></p>\n <p>This link expires in 2 hours. If you did not request this, ignore this email.</p>\n `,\n textTemplate: `Reset your password: <%= resetUrl %>`,\n data: { resetUrl, user },\n }\n }\n\n case 'auth.reset-pwd': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your password has been reset',\n bodyTemplate: `<p>Your password has been successfully reset. If you did not do this, please contact support immediately.</p>`,\n textTemplate: `Your password has been reset. If you did not do this, please contact support.`,\n data: { user },\n }\n }\n\n case 'auth.password-change': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your password has been changed',\n bodyTemplate: `<p>Your account password was just changed. If this was not you, please contact support immediately.</p>`,\n textTemplate: `Your password was changed. Contact support if this wasn't you.`,\n data: { user },\n }\n }\n\n case 'auth.identity-change': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your email address is being changed',\n bodyTemplate: `<p>A request was made to change your email address. Click the link to confirm.</p>`,\n textTemplate: `Confirm your email change by clicking the link in the HTML email.`,\n data: { user },\n }\n }\n\n case 'auth.verify-signup-set-password': {\n const token = user['verifyToken'] as string | undefined\n const verifyUrl = `${appUrl}/verify-set-password?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Set your password',\n bodyTemplate: `\n <h2>Set your password</h2>\n <p><a href=\"<%= verifyUrl %>\">Set password</a></p>\n `,\n textTemplate: `Set your password: <%= verifyUrl %>`,\n data: { verifyUrl, user },\n }\n }\n }\n}\n\nexport type NotifierFn = (\n type: string,\n user: Record<string, unknown>,\n notifierOptions: Record<string, unknown>,\n) => Promise<void>\n\nexport interface BullMQNotifierOptions {\n /** BullMQ Queue instance — from createNotificationQueue(). */\n queue: Queue<NotificationJob>\n /** Driver registry — used to determine which drivers handle each event. */\n registry: DriverRegistry\n /**\n * Public base URL of the app, e.g. https://api.example.com\n * Used to construct verification / reset URLs in email templates.\n */\n appUrl: string\n}\n\n/**\n * Create a notifier function compatible with feathers-authentication-management.\n *\n * When an auth event fires (verifySignup, sendResetPwd, etc.), this notifier:\n * 1. Maps the event type to a feathers-baas notification event key\n * 2. Looks up all registered drivers for that event\n * 3. Enqueues one BullMQ job per driver\n * 4. Returns immediately — delivery is asynchronous\n *\n * Wire into the app:\n * ```ts\n * app.set('notifier', createBullMQNotifier({ queue, registry, appUrl }))\n * ```\n */\nexport interface DirectNotifierOptions {\n /** Driver registry — used to determine which drivers handle each event. */\n registry: DriverRegistry\n /** Public base URL of the app. */\n appUrl: string\n}\n\n/**\n * Create a notifier that sends notifications directly via the driver (no queue).\n * Use this when Redis/BullMQ is not available — suitable for API-based drivers\n * (Brevo, Resend, SendGrid) in development or low-traffic environments.\n */\nexport function createDirectNotifier(opts: DirectNotifierOptions): NotifierFn {\n return async (type, user, _notifierOptions) => {\n const eventKey = AUTH_MANAGEMENT_EVENT_MAP[type]\n if (!eventKey) return\n\n const notification = buildAuthNotification(eventKey, user, opts.appUrl)\n if (!notification) return\n\n const driverNames = opts.registry.getDriversForEvent(eventKey)\n if (driverNames.length === 0) return\n\n await Promise.all(\n driverNames.map(async (driverName) => {\n const driver = opts.registry.getDriver(driverName)\n if (!driver) return\n await driver.send(notification)\n }),\n )\n }\n}\n\nexport function createBullMQNotifier(opts: BullMQNotifierOptions): NotifierFn {\n return async (type, user, _notifierOptions) => {\n const eventKey = AUTH_MANAGEMENT_EVENT_MAP[type]\n if (!eventKey) return // Unknown event type — no-op\n\n const notification = buildAuthNotification(eventKey, user, opts.appUrl)\n if (!notification) return // Missing required user fields — no-op\n\n const driverNames = opts.registry.getDriversForEvent(eventKey)\n if (driverNames.length === 0) return // No drivers configured for this event\n\n await Promise.all(\n driverNames.map((driverName) =>\n enqueueNotification(opts.queue, { driverName, notification }),\n ),\n )\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Worker } from 'bullmq'\nimport type { Redis } from 'ioredis'\nimport type { DriverRegistry } from '../registry.js'\nimport type { NotificationJob } from '../types.js'\nimport { NOTIFICATION_QUEUE_NAME } from './producer.js'\n\nexport interface WorkerOptions {\n /** BullMQ concurrency — number of jobs processed in parallel. */\n concurrency?: number\n}\n\n/**\n * Create and start the BullMQ worker that processes notification jobs.\n *\n * The worker looks up the driver from the registry by name, renders the\n * EJS template, and calls driver.send(). BullMQ handles retries with\n * exponential back-off automatically.\n *\n * Call worker.close() during graceful shutdown to drain in-flight jobs.\n *\n * @example\n * ```ts\n * const worker = createNotificationWorker(registry, redis, { concurrency: 5 })\n *\n * process.on('SIGTERM', async () => {\n * await worker.close()\n * })\n * ```\n */\nexport function createNotificationWorker(\n registry: DriverRegistry,\n redis: Redis,\n opts: WorkerOptions = {},\n): Worker<NotificationJob> {\n const concurrency = opts.concurrency ?? 5\n\n const worker = new Worker<NotificationJob>(\n NOTIFICATION_QUEUE_NAME,\n async (job) => {\n const { driverName, notification } = job.data\n\n const driver = registry.getDriver(driverName)\n if (!driver) {\n throw new Error(`Notification driver \"${driverName}\" is not registered`)\n }\n\n await driver.send(notification)\n },\n {\n connection: redis,\n concurrency,\n // Don't create a new redis connection per job — reuse the pool\n autorun: true,\n },\n )\n\n worker.on('failed', (job, err) => {\n // BullMQ handles retries; this is just for observability\n const jobId = job?.id ?? 'unknown'\n const message = err instanceof Error ? err.message : String(err)\n console.error(`[notifications] Job ${jobId} failed: ${message}`)\n })\n\n return worker\n}\n","// Generated by feathers-baas. Safe to edit.\nimport nodemailer from 'nodemailer'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface SMTPDriverOptions {\n /** SMTP connection URL, e.g. smtp://user:pass@mail.example.com:587 */\n url: string\n /** Display name shown in the From field. */\n fromName: string\n /** Email address shown in the From field. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by nodemailer over SMTP.\n * Templates are rendered with EJS at send time.\n *\n * Use this driver for self-hosted SMTP servers (Postfix, Mailtrap, etc.).\n * For transactional email services, prefer ResendDriver.\n */\nexport class SMTPDriver implements NotifierDriver {\n readonly name = 'smtp'\n\n private readonly transporter: nodemailer.Transporter\n\n constructor(private readonly opts: SMTPDriverOptions) {\n this.transporter = nodemailer.createTransport(opts.url)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n await this.transporter.sendMail({\n from: `\"${this.opts.fromName}\" <${this.opts.fromAddress}>`,\n to: notification.to,\n subject: notification.subject ?? '(no subject)',\n html,\n text,\n })\n }\n\n /** Verify SMTP connectivity. Throws if the server is unreachable. */\n async verify(): Promise<void> {\n await this.transporter.verify()\n }\n\n /** Close the underlying connection pool. */\n close(): void {\n this.transporter.close()\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Resend } from 'resend'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface ResendDriverOptions {\n /** Resend API key (from https://resend.com/api-keys). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be a verified domain in your Resend account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the Resend transactional email API.\n * Templates are rendered with EJS at send time.\n *\n * Resend offers generous free tier and excellent deliverability,\n * making it the recommended choice for new projects.\n */\nexport class ResendDriver implements NotifierDriver {\n readonly name = 'resend'\n\n private readonly client: Resend\n\n constructor(private readonly opts: ResendDriverOptions) {\n this.client = new Resend(opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n const result = await this.client.emails.send({\n from: `${this.opts.fromName} <${this.opts.fromAddress}>`,\n to: [notification.to],\n subject: notification.subject ?? '(no subject)',\n html,\n text,\n })\n\n if ('error' in result && result.error) {\n throw new Error(`Resend error: ${result.error.message}`)\n }\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport sgMail from '@sendgrid/mail'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface SendGridDriverOptions {\n /** SendGrid API key (from https://app.sendgrid.com/settings/api_keys). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be verified in your SendGrid account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the SendGrid API.\n * Templates are rendered with EJS at send time.\n */\nexport class SendGridDriver implements NotifierDriver {\n readonly name = 'sendgrid'\n\n constructor(private readonly opts: SendGridDriverOptions) {\n sgMail.setApiKey(opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n await sgMail.send({\n from: { name: this.opts.fromName, email: this.opts.fromAddress },\n to: notification.to,\n subject: notification.subject ?? '(no subject)',\n html,\n ...(text ? { text } : {}),\n })\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { TransactionalEmailsApi, TransactionalEmailsApiApiKeys, SendSmtpEmail } from '@getbrevo/brevo'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface BrevoDriverOptions {\n /** Brevo API key (from https://app.brevo.com/settings/keys/api). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be verified in your Brevo account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the Brevo (formerly Sendinblue) Transactional Email API.\n * Templates are rendered with EJS at send time.\n */\nexport class BrevoDriver implements NotifierDriver {\n readonly name = 'brevo'\n\n private readonly apiInstance: TransactionalEmailsApi\n\n constructor(private readonly opts: BrevoDriverOptions) {\n this.apiInstance = new TransactionalEmailsApi()\n this.apiInstance.setApiKey(TransactionalEmailsApiApiKeys.apiKey, opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n const email = new SendSmtpEmail()\n email.sender = { name: this.opts.fromName, email: this.opts.fromAddress }\n email.to = [{ email: notification.to }]\n email.subject = notification.subject ?? '(no subject)'\n email.htmlContent = html\n if (text) email.textContent = text\n\n await this.apiInstance.sendTransacEmail(email)\n }\n}\n"],"mappings":"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","names":["ejs","ejsRender","ejs","ejsRender","ejs","ejsRender"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/registry.ts","../src/queue/producer.ts","../src/notifier.ts","../src/queue/worker.ts","../src/drivers/smtp.driver.ts","../src/drivers/resend.driver.ts","../src/drivers/sendgrid.driver.ts","../src/drivers/brevo.driver.ts"],"sourcesContent":["// Generated by feathers-baas. Safe to edit.\nimport { createRequire } from 'node:module'\nimport type { FeathersBaasPlugin } from './plugin.js'\n\nconst pkg = createRequire(import.meta.url)('../package.json') as { version: string }\n\nconst plugin: FeathersBaasPlugin = {\n name: '@feathers-baas/plugin-notifications',\n version: pkg.version,\n async install(_app, _options) {\n // Notification drivers are configured via core's configureNotifications()\n // based on env vars. Auto-install is a no-op; discovery confirms the plugin is present.\n },\n}\n\nexport default plugin\n\nexport { DriverRegistry } from './registry.js'\nexport { createBullMQNotifier, createDirectNotifier } from './notifier.js'\nexport type { NotifierFn, BullMQNotifierOptions, DirectNotifierOptions } from './notifier.js'\n\n// Queue\nexport { createNotificationQueue, enqueueNotification, NOTIFICATION_QUEUE_NAME } from './queue/producer.js'\nexport { createNotificationWorker } from './queue/worker.js'\nexport type { WorkerOptions } from './queue/worker.js'\n\n// Drivers\nexport { SMTPDriver } from './drivers/smtp.driver.js'\nexport type { SMTPDriverOptions } from './drivers/smtp.driver.js'\nexport { ResendDriver } from './drivers/resend.driver.js'\nexport type { ResendDriverOptions } from './drivers/resend.driver.js'\nexport { SendGridDriver } from './drivers/sendgrid.driver.js'\nexport type { SendGridDriverOptions } from './drivers/sendgrid.driver.js'\nexport { BrevoDriver } from './drivers/brevo.driver.js'\nexport type { BrevoDriverOptions } from './drivers/brevo.driver.js'\n\n// Types\nexport type {\n Notification,\n NotifierDriver,\n NotificationJob,\n NotificationEvent,\n AuthNotificationEvent,\n} from './types.js'\n","// Generated by feathers-baas. Safe to edit.\nimport type { NotifierDriver, NotificationEvent } from './types.js'\n\n/**\n * DriverRegistry maps event keys to the list of drivers that should handle\n * that event.\n *\n * A single driver instance can handle multiple events (e.g. the SMTP driver\n * handles all auth.* email events).\n *\n * @example\n * ```ts\n * const registry = new DriverRegistry()\n * registry.registerDriver(smtpDriver)\n * registry.mapEvent('auth.verify-signup', 'smtp')\n * registry.mapEvent('auth.send-reset-pwd', 'smtp')\n * ```\n */\nexport class DriverRegistry {\n private readonly drivers = new Map<string, NotifierDriver>()\n private readonly eventMap = new Map<NotificationEvent, string[]>()\n\n /** Register a driver under its name. Overwrites any existing driver with the same name. */\n registerDriver(driver: NotifierDriver): void {\n this.drivers.set(driver.name, driver)\n }\n\n /** Map a notification event to a driver name. Multiple calls for the same event add drivers. */\n mapEvent(event: NotificationEvent, driverName: string): void {\n const existing = this.eventMap.get(event) ?? []\n if (!existing.includes(driverName)) {\n existing.push(driverName)\n }\n this.eventMap.set(event, existing)\n }\n\n /** Look up a driver by name. Returns undefined if not registered. */\n getDriver(name: string): NotifierDriver | undefined {\n return this.drivers.get(name)\n }\n\n /** Get all driver names mapped to the given event. */\n getDriversForEvent(event: NotificationEvent): string[] {\n return this.eventMap.get(event) ?? []\n }\n\n /** All registered driver names. */\n listDriverNames(): string[] {\n return [...this.drivers.keys()]\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Queue } from 'bullmq'\nimport type { Redis } from 'ioredis'\nimport type { NotificationJob } from '../types.js'\n\nexport const NOTIFICATION_QUEUE_NAME = 'feathers-baas:notifications'\n\n/**\n * Create a BullMQ Queue instance for notification jobs.\n *\n * @example\n * ```ts\n * const redis = new Redis(process.env.REDIS_URL)\n * const queue = createNotificationQueue(redis)\n * await enqueueNotification(queue, { driverName: 'smtp', notification: { ... } })\n * ```\n */\nexport function createNotificationQueue(redis: Redis): Queue<NotificationJob> {\n return new Queue<NotificationJob>(NOTIFICATION_QUEUE_NAME, {\n connection: redis,\n defaultJobOptions: {\n attempts: 5,\n backoff: {\n type: 'exponential',\n delay: 1_000, // initial delay: 1s → 2s → 4s → 8s → 16s\n },\n removeOnComplete: { count: 100 },\n removeOnFail: { count: 500 },\n },\n })\n}\n\n/**\n * Enqueue a notification job.\n *\n * @param queue BullMQ Queue instance from createNotificationQueue()\n * @param job Job payload { driverName, notification }\n */\nexport async function enqueueNotification(\n queue: Queue<NotificationJob>,\n job: NotificationJob,\n): Promise<void> {\n await queue.add('send', job)\n}\n","// Generated by feathers-baas. Safe to edit.\nimport type { Queue } from 'bullmq'\nimport type { NotificationJob, Notification, AuthNotificationEvent } from './types.js'\nimport { enqueueNotification } from './queue/producer.js'\nimport type { DriverRegistry } from './registry.js'\n\n/**\n * Auth-management notification types mapped to feathers-baas event keys.\n * feathers-authentication-management passes these type strings to the notifier.\n */\nconst AUTH_MANAGEMENT_EVENT_MAP: Record<string, AuthNotificationEvent> = {\n resendVerifySignup: 'auth.resend-verify-signup',\n verifySignup: 'auth.verify-signup',\n verifySignupSetPassword: 'auth.verify-signup-set-password',\n sendResetPwd: 'auth.send-reset-pwd',\n resetPwd: 'auth.reset-pwd',\n passwordChange: 'auth.password-change',\n identityChange: 'auth.identity-change',\n}\n\n/**\n * Build a default Notification payload for each auth event type.\n * Templates use EJS — `<%= variable %>` for output, `<% code %>` for logic.\n */\nfunction buildAuthNotification(\n type: AuthNotificationEvent,\n user: Record<string, unknown>,\n appUrl: string,\n): Notification | null {\n const email = user['email'] as string\n if (!email) return null\n\n switch (type) {\n case 'auth.verify-signup':\n case 'auth.resend-verify-signup': {\n const token = user['verifyToken'] as string | undefined\n const verifyUrl = `${appUrl}/verify?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Verify your email address',\n bodyTemplate: `\n <h2>Welcome!</h2>\n <p>Click the link below to verify your email address:</p>\n <p><a href=\"<%= verifyUrl %>\">Verify email</a></p>\n <p>This link expires in 24 hours.</p>\n `,\n textTemplate: `Verify your email: <%= verifyUrl %>`,\n data: { verifyUrl, user },\n }\n }\n\n case 'auth.send-reset-pwd': {\n const token = user['resetToken'] as string | undefined\n const resetUrl = `${appUrl}/reset-password?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Reset your password',\n bodyTemplate: `\n <h2>Password reset requested</h2>\n <p>Click the link below to reset your password:</p>\n <p><a href=\"<%= resetUrl %>\">Reset password</a></p>\n <p>This link expires in 2 hours. If you did not request this, ignore this email.</p>\n `,\n textTemplate: `Reset your password: <%= resetUrl %>`,\n data: { resetUrl, user },\n }\n }\n\n case 'auth.reset-pwd': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your password has been reset',\n bodyTemplate: `<p>Your password has been successfully reset. If you did not do this, please contact support immediately.</p>`,\n textTemplate: `Your password has been reset. If you did not do this, please contact support.`,\n data: { user },\n }\n }\n\n case 'auth.password-change': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your password has been changed',\n bodyTemplate: `<p>Your account password was just changed. If this was not you, please contact support immediately.</p>`,\n textTemplate: `Your password was changed. Contact support if this wasn't you.`,\n data: { user },\n }\n }\n\n case 'auth.identity-change': {\n return {\n to: email,\n channel: 'email',\n subject: 'Your email address is being changed',\n bodyTemplate: `<p>A request was made to change your email address. Click the link to confirm.</p>`,\n textTemplate: `Confirm your email change by clicking the link in the HTML email.`,\n data: { user },\n }\n }\n\n case 'auth.verify-signup-set-password': {\n const token = user['verifyToken'] as string | undefined\n const verifyUrl = `${appUrl}/verify-set-password?token=${token ?? ''}`\n return {\n to: email,\n channel: 'email',\n subject: 'Set your password',\n bodyTemplate: `\n <h2>Set your password</h2>\n <p><a href=\"<%= verifyUrl %>\">Set password</a></p>\n `,\n textTemplate: `Set your password: <%= verifyUrl %>`,\n data: { verifyUrl, user },\n }\n }\n }\n}\n\nexport type NotifierFn = (\n type: string,\n user: Record<string, unknown>,\n notifierOptions: Record<string, unknown>,\n) => Promise<void>\n\nexport interface BullMQNotifierOptions {\n /** BullMQ Queue instance — from createNotificationQueue(). */\n queue: Queue<NotificationJob>\n /** Driver registry — used to determine which drivers handle each event. */\n registry: DriverRegistry\n /**\n * Public base URL of the app, e.g. https://api.example.com\n * Used to construct verification / reset URLs in email templates.\n */\n appUrl: string\n}\n\n/**\n * Create a notifier function compatible with feathers-authentication-management.\n *\n * When an auth event fires (verifySignup, sendResetPwd, etc.), this notifier:\n * 1. Maps the event type to a feathers-baas notification event key\n * 2. Looks up all registered drivers for that event\n * 3. Enqueues one BullMQ job per driver\n * 4. Returns immediately — delivery is asynchronous\n *\n * Wire into the app:\n * ```ts\n * app.set('notifier', createBullMQNotifier({ queue, registry, appUrl }))\n * ```\n */\nexport interface DirectNotifierOptions {\n /** Driver registry — used to determine which drivers handle each event. */\n registry: DriverRegistry\n /** Public base URL of the app. */\n appUrl: string\n}\n\n/**\n * Create a notifier that sends notifications directly via the driver (no queue).\n * Use this when Redis/BullMQ is not available — suitable for API-based drivers\n * (Brevo, Resend, SendGrid) in development or low-traffic environments.\n */\nexport function createDirectNotifier(opts: DirectNotifierOptions): NotifierFn {\n return async (type, user, _notifierOptions) => {\n const eventKey = AUTH_MANAGEMENT_EVENT_MAP[type]\n if (!eventKey) return\n\n const notification = buildAuthNotification(eventKey, user, opts.appUrl)\n if (!notification) return\n\n const driverNames = opts.registry.getDriversForEvent(eventKey)\n if (driverNames.length === 0) return\n\n await Promise.all(\n driverNames.map(async (driverName) => {\n const driver = opts.registry.getDriver(driverName)\n if (!driver) return\n await driver.send(notification)\n }),\n )\n }\n}\n\nexport function createBullMQNotifier(opts: BullMQNotifierOptions): NotifierFn {\n return async (type, user, _notifierOptions) => {\n const eventKey = AUTH_MANAGEMENT_EVENT_MAP[type]\n if (!eventKey) return // Unknown event type — no-op\n\n const notification = buildAuthNotification(eventKey, user, opts.appUrl)\n if (!notification) return // Missing required user fields — no-op\n\n const driverNames = opts.registry.getDriversForEvent(eventKey)\n if (driverNames.length === 0) return // No drivers configured for this event\n\n await Promise.all(\n driverNames.map((driverName) =>\n enqueueNotification(opts.queue, { driverName, notification }),\n ),\n )\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Worker } from 'bullmq'\nimport type { Redis } from 'ioredis'\nimport type { DriverRegistry } from '../registry.js'\nimport type { NotificationJob } from '../types.js'\nimport { NOTIFICATION_QUEUE_NAME } from './producer.js'\n\nexport interface WorkerOptions {\n /** BullMQ concurrency — number of jobs processed in parallel. */\n concurrency?: number\n}\n\n/**\n * Create and start the BullMQ worker that processes notification jobs.\n *\n * The worker looks up the driver from the registry by name, renders the\n * EJS template, and calls driver.send(). BullMQ handles retries with\n * exponential back-off automatically.\n *\n * Call worker.close() during graceful shutdown to drain in-flight jobs.\n *\n * @example\n * ```ts\n * const worker = createNotificationWorker(registry, redis, { concurrency: 5 })\n *\n * process.on('SIGTERM', async () => {\n * await worker.close()\n * })\n * ```\n */\nexport function createNotificationWorker(\n registry: DriverRegistry,\n redis: Redis,\n opts: WorkerOptions = {},\n): Worker<NotificationJob> {\n const concurrency = opts.concurrency ?? 5\n\n const worker = new Worker<NotificationJob>(\n NOTIFICATION_QUEUE_NAME,\n async (job) => {\n const { driverName, notification } = job.data\n\n const driver = registry.getDriver(driverName)\n if (!driver) {\n throw new Error(`Notification driver \"${driverName}\" is not registered`)\n }\n\n await driver.send(notification)\n },\n {\n connection: redis,\n concurrency,\n // Don't create a new redis connection per job — reuse the pool\n autorun: true,\n },\n )\n\n worker.on('failed', (job, err) => {\n // BullMQ handles retries; this is just for observability\n const jobId = job?.id ?? 'unknown'\n const message = err instanceof Error ? err.message : String(err)\n console.error(`[notifications] Job ${jobId} failed: ${message}`)\n })\n\n return worker\n}\n","// Generated by feathers-baas. Safe to edit.\nimport nodemailer from 'nodemailer'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface SMTPDriverOptions {\n /** SMTP connection URL, e.g. smtp://user:pass@mail.example.com:587 */\n url: string\n /** Display name shown in the From field. */\n fromName: string\n /** Email address shown in the From field. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by nodemailer over SMTP.\n * Templates are rendered with EJS at send time.\n *\n * Use this driver for self-hosted SMTP servers (Postfix, Mailtrap, etc.).\n * For transactional email services, prefer ResendDriver.\n */\nexport class SMTPDriver implements NotifierDriver {\n readonly name = 'smtp'\n\n private readonly transporter: nodemailer.Transporter\n\n constructor(private readonly opts: SMTPDriverOptions) {\n this.transporter = nodemailer.createTransport(opts.url)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n await this.transporter.sendMail({\n from: `\"${this.opts.fromName}\" <${this.opts.fromAddress}>`,\n to: notification.to,\n subject: notification.subject ?? '(no subject)',\n html,\n text,\n })\n }\n\n /** Verify SMTP connectivity. Throws if the server is unreachable. */\n async verify(): Promise<void> {\n await this.transporter.verify()\n }\n\n /** Close the underlying connection pool. */\n close(): void {\n this.transporter.close()\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { Resend } from 'resend'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface ResendDriverOptions {\n /** Resend API key (from https://resend.com/api-keys). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be a verified domain in your Resend account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the Resend transactional email API.\n * Templates are rendered with EJS at send time.\n *\n * Resend offers generous free tier and excellent deliverability,\n * making it the recommended choice for new projects.\n */\nexport class ResendDriver implements NotifierDriver {\n readonly name = 'resend'\n\n private readonly client: Resend\n\n constructor(private readonly opts: ResendDriverOptions) {\n this.client = new Resend(opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n const result = await this.client.emails.send({\n from: `${this.opts.fromName} <${this.opts.fromAddress}>`,\n to: [notification.to],\n subject: notification.subject ?? '(no subject)',\n html,\n text,\n })\n\n if ('error' in result && result.error) {\n throw new Error(`Resend error: ${result.error.message}`)\n }\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport sgMail from '@sendgrid/mail'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface SendGridDriverOptions {\n /** SendGrid API key (from https://app.sendgrid.com/settings/api_keys). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be verified in your SendGrid account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the SendGrid API.\n * Templates are rendered with EJS at send time.\n */\nexport class SendGridDriver implements NotifierDriver {\n readonly name = 'sendgrid'\n\n constructor(private readonly opts: SendGridDriverOptions) {\n sgMail.setApiKey(opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n await sgMail.send({\n from: { name: this.opts.fromName, email: this.opts.fromAddress },\n to: notification.to,\n subject: notification.subject ?? '(no subject)',\n html,\n ...(text ? { text } : {}),\n })\n }\n}\n","// Generated by feathers-baas. Safe to edit.\nimport { TransactionalEmailsApi, TransactionalEmailsApiApiKeys, SendSmtpEmail } from '@getbrevo/brevo'\nimport * as ejs from 'ejs'\nimport type { NotifierDriver, Notification } from '../types.js'\n\n// ejs is a CommonJS module — alias the render function defensively\nconst ejsRender = (ejs as unknown as { render?: typeof ejs.render }).render ?? ejs.render\n\nexport interface BrevoDriverOptions {\n /** Brevo API key (from https://app.brevo.com/settings/keys/api). */\n apiKey: string\n /** Display name shown in the From field. */\n fromName: string\n /** Verified sender address. Must be verified in your Brevo account. */\n fromAddress: string\n}\n\n/**\n * Email driver backed by the Brevo (formerly Sendinblue) Transactional Email API.\n * Templates are rendered with EJS at send time.\n */\nexport class BrevoDriver implements NotifierDriver {\n readonly name = 'brevo'\n\n private readonly apiInstance: TransactionalEmailsApi\n\n constructor(private readonly opts: BrevoDriverOptions) {\n this.apiInstance = new TransactionalEmailsApi()\n this.apiInstance.setApiKey(TransactionalEmailsApiApiKeys.apiKey, opts.apiKey)\n }\n\n async send(notification: Notification): Promise<void> {\n const html = ejsRender(notification.bodyTemplate, notification.data, { async: false })\n const text = notification.textTemplate\n ? ejsRender(notification.textTemplate, notification.data, { async: false })\n : undefined\n\n const email = new SendSmtpEmail()\n email.sender = { name: this.opts.fromName, email: this.opts.fromAddress }\n email.to = [{ email: notification.to }]\n email.subject = notification.subject ?? '(no subject)'\n email.htmlContent = html\n if (text) email.textContent = text\n\n await this.apiInstance.sendTransacEmail(email)\n }\n}\n"],"mappings":"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","names":["ejs","ejsRender","ejs","ejsRender","ejs","ejsRender"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feathers-baas/plugin-notifications",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Notifications plugin for feathers-baas — BullMQ-backed email drivers (SMTP, Resend, SendGrid, Brevo)",
5
5
  "keywords": [
6
6
  "feathers",
@@ -28,7 +28,8 @@
28
28
  "types": "./dist/index.d.cts",
29
29
  "default": "./dist/index.cjs"
30
30
  }
31
- }
31
+ },
32
+ "./package.json": "./package.json"
32
33
  },
33
34
  "dependencies": {
34
35
  "@feathersjs/feathers": "^5.0.30",
package/src/index.ts CHANGED
@@ -1,4 +1,20 @@
1
1
  // Generated by feathers-baas. Safe to edit.
2
+ import { createRequire } from 'node:module'
3
+ import type { FeathersBaasPlugin } from './plugin.js'
4
+
5
+ const pkg = createRequire(import.meta.url)('../package.json') as { version: string }
6
+
7
+ const plugin: FeathersBaasPlugin = {
8
+ name: '@feathers-baas/plugin-notifications',
9
+ version: pkg.version,
10
+ async install(_app, _options) {
11
+ // Notification drivers are configured via core's configureNotifications()
12
+ // based on env vars. Auto-install is a no-op; discovery confirms the plugin is present.
13
+ },
14
+ }
15
+
16
+ export default plugin
17
+
2
18
  export { DriverRegistry } from './registry.js'
3
19
  export { createBullMQNotifier, createDirectNotifier } from './notifier.js'
4
20
  export type { NotifierFn, BullMQNotifierOptions, DirectNotifierOptions } from './notifier.js'
package/src/plugin.ts ADDED
@@ -0,0 +1,5 @@
1
+ export interface FeathersBaasPlugin {
2
+ name: string
3
+ version: string
4
+ install(app: unknown, options: Record<string, unknown>): Promise<void>
5
+ }