@arkstack/notifications 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,7 +30,7 @@ export default () => ({
30
30
  drivers: {
31
31
  mail: {
32
32
  transport: 'smtp',
33
- from: env('SMTP_FROM_ADDRESS', 'no-reply@example.com'),
33
+ from: env('MAIL_FROM_ADDRESS', 'no-reply@example.com'),
34
34
  },
35
35
  sms: {
36
36
  transport: 'africastalking',
@@ -42,8 +42,8 @@ export default () => ({
42
42
  },
43
43
  transports: {
44
44
  smtp: {
45
- host: env('SMTP_HOST', 'localhost'),
46
- port: env('SMTP_PORT', 1025),
45
+ host: env('MAIL_HOST', 'localhost'),
46
+ port: env('MAIL_PORT', 1025),
47
47
  },
48
48
  africastalking: {
49
49
  username: env('AFRICASTALKING_USERNAME', 'sandbox'),
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { DotPath, DotPathValue, MergedConfig } from "@arkstack/common";
1
2
  import { Model } from "@arkstack/database";
2
3
  import { Transporter } from "nodemailer";
3
4
  import * as _$arkormx from "arkormx";
@@ -35,7 +36,7 @@ type NotificationUser = {
35
36
  phone?: string | null;
36
37
  };
37
38
  type MailDriverOptions = {
38
- transport?: string;
39
+ transport?: 'africastalking' | 'twilio' | 'file' | 'smtp';
39
40
  host?: string;
40
41
  port?: number;
41
42
  secure?: boolean;
@@ -43,6 +44,7 @@ type MailDriverOptions = {
43
44
  pass?: string;
44
45
  from?: string;
45
46
  testAddress?: string;
47
+ directory?: string;
46
48
  };
47
49
  type SmsDriverOptions = {
48
50
  transport?: SmsDriverName;
@@ -73,6 +75,64 @@ type NotificationDriverMap = {
73
75
  sms: DriverResult;
74
76
  db: UserNotification;
75
77
  };
78
+ interface NotificationConfig {
79
+ default_driver: 'mail' | 'sms' | 'db';
80
+ drivers: {
81
+ mail: {
82
+ transport: 'smtp' | 'file';
83
+ from: string;
84
+ test_address: string;
85
+ };
86
+ sms: {
87
+ transport: 'africastalking';
88
+ from: string;
89
+ };
90
+ db: {
91
+ table: 'user_notifications';
92
+ };
93
+ };
94
+ transports: {
95
+ smtp: {
96
+ host: string;
97
+ port: number;
98
+ secure: boolean;
99
+ auth: {
100
+ user: string;
101
+ pass: string;
102
+ };
103
+ user?: string;
104
+ pass?: string;
105
+ test_address?: string;
106
+ } | {
107
+ host: string;
108
+ port: number;
109
+ secure: boolean;
110
+ auth?: {
111
+ user: string;
112
+ pass: string;
113
+ };
114
+ user: string;
115
+ pass: string;
116
+ test_address?: string;
117
+ };
118
+ file: {
119
+ directory: string;
120
+ from?: string;
121
+ test_address?: string;
122
+ };
123
+ africastalking: {
124
+ username: string;
125
+ apiKey: string;
126
+ senderId: string;
127
+ };
128
+ twilio: {
129
+ accountSid: string;
130
+ authToken: string;
131
+ from: string;
132
+ };
133
+ };
134
+ }
135
+ type MergedTransportConfig = MergedConfig<NotificationConfig['transports'][NonNullable<MailDriverOptions['transport']>]>;
76
136
  //#endregion
77
137
  //#region src/Contracts/NotificationContract.d.ts
78
138
  declare abstract class NotificationContract<TResult = DriverResult> {
@@ -110,6 +170,7 @@ declare class MailNotification extends NotificationContract {
110
170
  private ViewName;
111
171
  private htmlTemplate?;
112
172
  private textTemplate?;
173
+ private fileDirectory?;
113
174
  constructor(options?: MailDriverOptions);
114
175
  from(from: string): this;
115
176
  subject(subject: string): this;
@@ -119,6 +180,7 @@ declare class MailNotification extends NotificationContract {
119
180
  text(content: string): this;
120
181
  prepare(user?: null | string | string[] | User, data?: Record<string, any>): this;
121
182
  send(message: string, subject?: string, recipient?: MailRecipient, data?: NotificationData): Promise<any>;
183
+ private storeFileMail;
122
184
  private resolveRecipients;
123
185
  private normalizeRecipient;
124
186
  }
@@ -168,7 +230,7 @@ declare class Notification<D extends keyof DriverMap = keyof DriverMap> {
168
230
  static email(options?: MailDriverOptions): MailNotification;
169
231
  static sms(options?: SmsDriverOptions): SmsNotification;
170
232
  static db(): DbNotification;
171
- static channel(channel?: NotificationChannel | 'email', options?: MailDriverOptions | SmsDriverOptions): MailNotification | SmsNotification | DbNotification;
233
+ static channel(channel?: NotificationChannel | 'email', options?: MailDriverOptions | SmsDriverOptions): DbNotification | MailNotification | SmsNotification;
172
234
  prepare(recipient?: null | MailRecipient | NotificationRecipient | NotificationUser, data?: NotificationData): DriverMap[D];
173
235
  private static createDriver;
174
236
  }
@@ -185,10 +247,10 @@ declare class UserNotificationCenter {
185
247
  }
186
248
  //#endregion
187
249
  //#region src/config.d.ts
188
- declare const notificationConfig: <T = unknown>(key: string, defaultValue: T) => T;
250
+ declare const configure: <T extends DotPath<NotificationConfig>>(key: T, defaultValue: unknown) => DotPathValue<NotificationConfig, T>;
189
251
  //#endregion
190
252
  //#region src/utils/template.d.ts
191
253
  declare const interpolate: (value: string, data?: NotificationData) => string;
192
254
  //#endregion
193
- export { AfricasTalkingSmsDriver, DbNotification, DbNotificationPayload, DbNotificationType, DriverResult, MailDriverOptions, MailNotification, MailRecipient, MailRecipientAddress, Notification, NotificationChannel, NotificationContract, NotificationData, NotificationDriverMap, NotificationRecipient, NotificationUser, SmsDriverName, SmsDriverOptions, SmsNotification, TwilioSmsDriver, UserNotification, UserNotificationCenter, interpolate, notificationConfig };
255
+ export { AfricasTalkingSmsDriver, DbNotification, DbNotificationPayload, DbNotificationType, DriverResult, MailDriverOptions, MailNotification, MailRecipient, MailRecipientAddress, MergedTransportConfig, Notification, NotificationChannel, NotificationConfig, NotificationContract, NotificationData, NotificationDriverMap, NotificationRecipient, NotificationUser, SmsDriverName, SmsDriverOptions, SmsNotification, TwilioSmsDriver, UserNotification, UserNotificationCenter, configure, interpolate };
194
256
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { config, env, getModel } from "@arkstack/common";
2
2
  import { Model } from "@arkstack/database";
3
+ import { mkdir, writeFile } from "node:fs/promises";
4
+ import { join } from "node:path";
3
5
  import nodemailer from "nodemailer";
4
6
  import africastalking from "africastalking";
5
7
  import twilio from "twilio";
@@ -128,7 +130,7 @@ var DbNotification = class extends NotificationContract {
128
130
  };
129
131
  //#endregion
130
132
  //#region src/config.ts
131
- const notificationConfig = (key, defaultValue) => {
133
+ const configure = (key, defaultValue) => {
132
134
  try {
133
135
  return config(`notifications.${key}`, defaultValue);
134
136
  } catch {
@@ -145,22 +147,27 @@ var MailNotification = class extends NotificationContract {
145
147
  ViewName = "~arkstack/notifications.mail";
146
148
  htmlTemplate;
147
149
  textTemplate;
150
+ fileDirectory;
148
151
  constructor(options = {}) {
149
152
  super();
150
- const driverConfig = notificationConfig("drivers.mail", {});
151
- const transportConfig = notificationConfig(`transports.${options.transport ?? driverConfig.transport ?? "smtp"}`, {});
152
- const legacyMailConfig = notificationConfig("mail", {});
153
- const smtpConfig = {
154
- ...notificationConfig("smtp", {}),
155
- ...legacyMailConfig.smtp ?? {},
156
- ...transportConfig
157
- };
158
- const host = options.host ?? smtpConfig.host ?? env("SMTP_HOST", "localhost");
159
- const port = options.port ?? smtpConfig.port ?? env("SMTP_PORT", 1025);
160
- const secure = options.secure ?? smtpConfig.secure ?? env("SMTP_SECURE", false);
161
- const user = options.user ?? smtpConfig.auth?.user ?? smtpConfig.user ?? env("SMTP_USERNAME", "");
162
- const pass = options.pass ?? smtpConfig.auth?.pass ?? smtpConfig.pass ?? env("SMTP_PASSWORD", "");
163
- this.fromAddress = options.from ?? driverConfig.from ?? legacyMailConfig.from ?? smtpConfig.from ?? env("SMTP_FROM_ADDRESS", "no-reply@example.com");
153
+ const driver = configure("drivers.mail", {});
154
+ const trpt = driver.transport ?? "smtp";
155
+ const transport = configure(`transports.${trpt}`, {});
156
+ if (trpt === "file") {
157
+ this.fileDirectory = options.directory ?? transport.directory ?? env("MAIL_NOTIFICATION_FILE_PATH", "storage/framework/mails");
158
+ this.fromAddress = options.from ?? driver.from ?? transport.from ?? env("MAIL_FROM_ADDRESS", "no-reply@example.com");
159
+ this.driver = nodemailer.createTransport({
160
+ streamTransport: true,
161
+ buffer: true
162
+ });
163
+ return;
164
+ }
165
+ const host = options.host ?? transport.host ?? env("MAIL_HOST", "localhost");
166
+ const port = options.port ?? transport.port ?? env("MAIL_PORT", 1025);
167
+ const secure = options.secure ?? transport.secure ?? env("MAIL_SECURE", false);
168
+ const user = options.user ?? transport.auth?.user ?? transport.user ?? env("MAIL_USERNAME", "");
169
+ const pass = options.pass ?? transport.auth?.pass ?? transport.pass ?? env("MAIL_PASSWORD", "");
170
+ this.fromAddress = options.from ?? driver.from ?? transport.from ?? env("MAIL_FROM_ADDRESS", "no-reply@example.com");
164
171
  this.driver = nodemailer.createTransport({
165
172
  host,
166
173
  port: Number(port),
@@ -177,9 +184,9 @@ var MailNotification = class extends NotificationContract {
177
184
  }
178
185
  /**
179
186
  * The email subject
180
- *
181
- * @param subject
182
- * @returns
187
+ *
188
+ * @param subject
189
+ * @returns
183
190
  */
184
191
  subject(subject) {
185
192
  this.subjectLine = subject;
@@ -187,9 +194,9 @@ var MailNotification = class extends NotificationContract {
187
194
  }
188
195
  /**
189
196
  * Set email the notification recipeint
190
- *
197
+ *
191
198
  * @param recipient string or array of email addresses
192
- * @returns
199
+ * @returns
193
200
  */
194
201
  recipient(recipient) {
195
202
  this.recipients = recipient;
@@ -197,9 +204,9 @@ var MailNotification = class extends NotificationContract {
197
204
  }
198
205
  /**
199
206
  * Set email the notification view name
200
- *
207
+ *
201
208
  * @param view view name
202
- * @returns
209
+ * @returns
203
210
  */
204
211
  view(view) {
205
212
  this.ViewName = view;
@@ -207,9 +214,9 @@ var MailNotification = class extends NotificationContract {
207
214
  }
208
215
  /**
209
216
  * Set email the notification html template
210
- *
217
+ *
211
218
  * @param content view name
212
- * @returns
219
+ * @returns
213
220
  */
214
221
  html(content) {
215
222
  this.htmlTemplate = content;
@@ -217,9 +224,9 @@ var MailNotification = class extends NotificationContract {
217
224
  }
218
225
  /**
219
226
  * Set email the notification text template
220
- *
227
+ *
221
228
  * @param content view name
222
- * @returns
229
+ * @returns
223
230
  */
224
231
  text(content) {
225
232
  this.textTemplate = content;
@@ -227,7 +234,7 @@ var MailNotification = class extends NotificationContract {
227
234
  }
228
235
  /**
229
236
  * Prepare a notification to be sent.
230
- *
237
+ *
231
238
  * @param user The recipient user(s) for the notification.
232
239
  */
233
240
  prepare(user, data = {}) {
@@ -238,12 +245,12 @@ var MailNotification = class extends NotificationContract {
238
245
  }
239
246
  /**
240
247
  * Send a notification to the specified recipient(s) with the given message.
241
- *
248
+ *
242
249
  * @param message The message content to be sent to the recipient(s).
243
250
  * @param subject The message subject to be sent to the recipient(s).
244
251
  * @param recipient An array of recipient identifiers
245
252
  * @param data Additioal context data
246
- * @returns
253
+ * @returns
247
254
  */
248
255
  async send(message, subject, recipient, data) {
249
256
  const mergedData = {
@@ -260,7 +267,7 @@ var MailNotification = class extends NotificationContract {
260
267
  subject: resolvedSubject
261
268
  };
262
269
  const textMessage = resolvedMessage.replace(/<\/?[^>]+(>|$)/g, "");
263
- return await this.driver.sendMail({
270
+ const payload = {
264
271
  to,
265
272
  subject: resolvedSubject,
266
273
  from: this.fromAddress,
@@ -269,14 +276,44 @@ var MailNotification = class extends NotificationContract {
269
276
  message: textMessage
270
277
  }),
271
278
  html: this.htmlTemplate ? interpolate(this.htmlTemplate, templateData) : await globalThis.view(this.ViewName, templateData)
272
- });
279
+ };
280
+ const result = await this.driver.sendMail(payload);
281
+ if (this.fileDirectory) await this.storeFileMail(payload, result);
282
+ return result;
283
+ }
284
+ async storeFileMail(payload, result) {
285
+ const now = /* @__PURE__ */ new Date();
286
+ const date = now.toISOString().slice(0, 10);
287
+ const directory = join(this.fileDirectory, date);
288
+ const id = String(result.messageId || now.getTime()).replace(/[^a-zA-Z0-9_.-]/g, "-");
289
+ const path = join(directory, now.getTime() + "-" + id + ".json");
290
+ const message = result.message;
291
+ await mkdir(directory, { recursive: true });
292
+ await writeFile(path, JSON.stringify({
293
+ id: result.messageId,
294
+ date: now.toISOString(),
295
+ envelope: result.envelope,
296
+ accepted: result.accepted,
297
+ rejected: result.rejected,
298
+ pending: result.pending,
299
+ response: result.response,
300
+ message: {
301
+ from: payload.from,
302
+ to: payload.to,
303
+ subject: payload.subject,
304
+ text: payload.text,
305
+ html: payload.html,
306
+ raw: Buffer.isBuffer(message) ? message.toString("utf8") : String(message ?? "")
307
+ }
308
+ }, null, 2), "utf8");
309
+ return path;
273
310
  }
274
311
  resolveRecipients(recipient) {
275
312
  const recipients = recipient ?? this.recipients;
276
313
  const resolved = (Array.isArray(recipients) ? [...recipients] : recipients ? [recipients] : []).flatMap((recipient) => this.normalizeRecipient(recipient));
277
- const driverConfig = notificationConfig("drivers.mail", {});
278
- const transportConfig = notificationConfig(`transports.${driverConfig.transport ?? "smtp"}`, {});
279
- const testAddress = driverConfig.testAddress ?? driverConfig.test_address ?? transportConfig.testAddress ?? transportConfig.test_address ?? env("SMTP_TEST_ADDRESS");
314
+ const driver = configure("drivers.mail", {});
315
+ const transport = configure(`transports.${driver.transport ?? "smtp"}`, {});
316
+ const testAddress = driver.test_address ?? driver.test_address ?? transport.test_address ?? transport.test_address ?? env("MAIL_TEST_ADDRESS");
280
317
  if (env("NODE_ENV") !== "production" && testAddress) resolved.push(testAddress);
281
318
  return resolved;
282
319
  }
@@ -346,10 +383,10 @@ var SmsNotification = class extends NotificationContract {
346
383
  fromValue;
347
384
  constructor(options = {}) {
348
385
  super();
349
- const driverConfig = notificationConfig("drivers.sms", {});
386
+ const driverConfig = configure("drivers.sms", {});
350
387
  const transport = options.transport ?? driverConfig.transport ?? "twilio";
351
- const transportConfig = notificationConfig(`transports.${transport}`, {});
352
- const legacySmsConfig = notificationConfig("sms", {});
388
+ const transportConfig = configure(`transports.${transport}`, {});
389
+ const legacySmsConfig = configure("sms", {});
353
390
  const from = options.from ?? driverConfig.from ?? legacySmsConfig.from;
354
391
  this.fromValue = from;
355
392
  this.driver = transport === "twilio" ? new TwilioSmsDriver({
@@ -405,7 +442,7 @@ var Notification = class Notification {
405
442
  return new DbNotification();
406
443
  }
407
444
  static channel(channel, options) {
408
- return Notification.createDriver(channel ?? notificationConfig("default_driver", "mail"), options);
445
+ return Notification.createDriver(channel ?? configure("default_driver", "mail"), options);
409
446
  }
410
447
  prepare(recipient, data = {}) {
411
448
  this.driver.data(data);
@@ -431,6 +468,6 @@ var Notification = class Notification {
431
468
  }
432
469
  };
433
470
  //#endregion
434
- export { AfricasTalkingSmsDriver, DbNotification, MailNotification, Notification, NotificationContract, SmsNotification, TwilioSmsDriver, UserNotification, UserNotificationCenter, interpolate, notificationConfig };
471
+ export { AfricasTalkingSmsDriver, DbNotification, MailNotification, Notification, NotificationContract, SmsNotification, TwilioSmsDriver, UserNotification, UserNotificationCenter, configure, interpolate };
435
472
 
436
473
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/Contracts/NotificationContract.ts","../src/Contracts/UserNotification.ts","../src/UserNotificationCenter.ts","../src/utils/template.ts","../src/drivers/DbNotification.ts","../src/config.ts","../src/drivers/MailNotification.ts","../src/drivers/sms/AfricasTalkingSmsDriver.ts","../src/drivers/sms/TwilioSmsDriver.ts","../src/drivers/SmsNotification.ts","../src/Notification.ts"],"sourcesContent":["import type { DriverResult, NotificationData, NotificationRecipient } from '../types'\n\nexport abstract class NotificationContract<TResult = DriverResult> {\n protected contextData: NotificationData = {}\n\n abstract send (\n message: string,\n subject?: string,\n recipient?: NotificationRecipient,\n data?: NotificationData\n ): Promise<TResult>\n\n abstract from (from: string): this\n abstract subject (subject: string): this\n abstract recipient (recipient: NotificationRecipient): this\n\n data (data: NotificationData): this {\n this.contextData = data\n\n return this\n }\n\n protected mergeData (data?: NotificationData) {\n return {\n ...this.contextData,\n ...data,\n year: new Date().getFullYear(),\n }\n }\n}\n","import type { DbNotificationType, NotificationData } from '../types'\n\nimport { Model } from '@arkstack/database'\n\nexport abstract class UserNotification extends Model {\n [key: string]: any\n declare type: DbNotificationType | null\n declare title: string\n declare description: string\n declare actionText: string | null\n declare actionLink: string | null\n declare meta: NotificationData | null\n declare readAt: Date | null\n declare createdAt: Date\n declare updatedAt: Date\n\n protected static table?: string | undefined = 'user_notifications'\n\n protected casts = {\n meta: 'json',\n } as const\n}\n","import { getModel } from '@arkstack/common'\n\nimport { UserNotification } from './Contracts/UserNotification'\nimport type { DbNotificationPayload, NotificationUser } from './types'\n\nexport class UserNotificationCenter {\n private static async getModel () {\n return await getModel<typeof UserNotification>('UserNotification')\n }\n\n static async create (user: NotificationUser, payload: DbNotificationPayload) {\n const Model = await this.getModel()\n\n return await Model.query().create({\n userId: user.id,\n type: payload.type ?? null,\n title: payload.title,\n description: payload.description,\n actionText: payload.actionText ?? null,\n actionLink: payload.actionLink ?? null,\n meta: payload.meta ?? null,\n })\n }\n\n static async forUser (user: NotificationUser) {\n const Model = await this.getModel()\n\n return await Model.query().where({ userId: user.id }).get()\n }\n\n static async unreadForUser (user: NotificationUser) {\n const Model = await this.getModel()\n\n return await Model.query().where({ userId: user.id, readAt: null }).get()\n }\n\n static async markRead (notification: UserNotification | UserNotification['id']) {\n const Model = await this.getModel()\n const id = notification instanceof UserNotification ? notification.id : notification\n const readAt = new Date()\n\n await Model.query().where({ id }).update({ readAt })\n\n if (notification instanceof UserNotification) {\n notification.readAt = readAt\n }\n }\n\n static async markAllRead (user: NotificationUser) {\n const Model = await this.getModel()\n\n await Model.query().where({ userId: user.id, readAt: null }).update({ readAt: new Date() })\n }\n\n static async delete (notification: UserNotification | UserNotification['id']) {\n const Model = await this.getModel()\n const id = notification instanceof UserNotification ? notification.id : notification\n\n await Model.query().where({ id }).delete()\n }\n}\n","import type { NotificationData } from '../types'\n\nexport const interpolate = (value: string, data: NotificationData = {}) => {\n return value.replace(/\\{([^{}]+)\\}/g, (match, key) => {\n const replacement = data[key.trim()]\n\n return replacement === undefined || replacement === null ? match : String(replacement)\n })\n}\n","import type { DbNotificationPayload, NotificationData, NotificationRecipient, NotificationUser } from '../types'\n\nimport { NotificationContract } from '../Contracts/NotificationContract'\nimport { UserNotification } from '../Contracts/UserNotification'\nimport { UserNotificationCenter } from '../UserNotificationCenter'\nimport { getModel } from '@arkstack/common'\nimport { interpolate } from '../utils/template'\n\nexport class DbNotification extends NotificationContract<UserNotification> {\n private user?: NotificationUser\n private payload: Partial<DbNotificationPayload> = {}\n\n from (_from: string): this {\n return this\n }\n\n subject (subject: string): this {\n this.payload.title = subject\n\n return this\n }\n\n recipient (recipient: NotificationRecipient | NotificationUser): this {\n if (typeof recipient === 'object' && !Array.isArray(recipient) && 'id' in recipient) {\n this.user = recipient\n\n return this\n }\n\n throw new Error('Database notifications require a user recipient')\n }\n\n type (type: DbNotificationPayload['type']): this {\n this.payload.type = type\n\n return this\n }\n\n action (text?: string | null, link?: string | null): this {\n this.payload.actionText = text\n this.payload.actionLink = link\n\n return this\n }\n\n meta (meta?: NotificationData | null): this {\n this.payload.meta = meta\n\n return this\n }\n\n async create (user: NotificationUser, payload: DbNotificationPayload) {\n await getModel<typeof UserNotification>('UserNotification')\n\n return await UserNotificationCenter.create(user, payload)\n }\n\n async send (\n message: string,\n subject?: string,\n _recipient?: NotificationRecipient,\n data?: NotificationData\n ) {\n if (!this.user) {\n throw new Error('No user recipient provided for database notification')\n }\n\n const mergedData = this.mergeData(data)\n\n return await this.create(this.user, {\n type: this.payload.type ?? null,\n title: interpolate(subject ?? this.payload.title ?? '', mergedData),\n description: interpolate(message, mergedData),\n actionText: this.payload.actionText ?? null,\n actionLink: this.payload.actionLink ?? null,\n meta: this.payload.meta ?? null,\n })\n }\n}\n","import { config } from '@arkstack/common'\n\nexport const notificationConfig = <T = unknown> (key: string, defaultValue: T): T => {\n try {\n return config(`notifications.${key}`, defaultValue) as T\n } catch {\n return defaultValue\n }\n}\n","import { config, env } from '@arkstack/common'\nimport nodemailer, { type Transporter } from 'nodemailer'\n\nimport { NotificationContract } from '../Contracts/NotificationContract'\nimport { interpolate } from '../utils/template'\nimport { notificationConfig } from '../config'\nimport type { MailDriverOptions, MailRecipient, MailRecipientAddress, NotificationData } from '../types'\nimport type { User } from '@arkstack/auth'\n\nexport class MailNotification extends NotificationContract {\n driver: Transporter\n private fromAddress?: string\n private subjectLine?: string\n private recipients?: MailRecipient\n private ViewName: string = '~arkstack/notifications.mail'\n private htmlTemplate?: string\n private textTemplate?: string\n\n constructor(options: MailDriverOptions = {}) {\n super()\n\n const driverConfig = notificationConfig<Record<string, any>>('drivers.mail', {})\n const transport = options.transport ?? driverConfig.transport ?? 'smtp'\n const transportConfig = notificationConfig<Record<string, any>>(`transports.${transport}`, {})\n const legacyMailConfig = notificationConfig<Record<string, any>>('mail', {})\n const legacySmtpConfig = notificationConfig<Record<string, any>>('smtp', {})\n const smtpConfig = {\n ...legacySmtpConfig,\n ...(legacyMailConfig.smtp ?? {}),\n ...transportConfig,\n }\n const host = options.host ?? smtpConfig.host ?? env('SMTP_HOST', 'localhost')\n const port = options.port ?? smtpConfig.port ?? env('SMTP_PORT', 1025)\n const secure = options.secure ?? smtpConfig.secure ?? env('SMTP_SECURE', false)\n const user = options.user ?? smtpConfig.auth?.user ?? smtpConfig.user ?? env('SMTP_USERNAME', '')\n const pass = options.pass ?? smtpConfig.auth?.pass ?? smtpConfig.pass ?? env('SMTP_PASSWORD', '')\n\n this.fromAddress = options.from ?? driverConfig.from ?? legacyMailConfig.from ?? smtpConfig.from ?? env('SMTP_FROM_ADDRESS', 'no-reply@example.com')\n this.driver = nodemailer.createTransport({\n host,\n port: Number(port),\n secure: Boolean(secure),\n auth: user || pass ? { user, pass } : undefined,\n })\n }\n\n from (from: string): this {\n this.fromAddress = from\n\n return this\n }\n\n /**\n * The email subject\n * \n * @param subject \n * @returns \n */\n subject (subject: string): this {\n this.subjectLine = subject\n\n return this\n }\n\n /**\n * Set email the notification recipeint\n * \n * @param recipient string or array of email addresses\n * @returns \n */\n recipient (recipient: MailRecipient): this {\n this.recipients = recipient\n\n return this\n }\n\n /**\n * Set email the notification view name\n * \n * @param view view name\n * @returns \n */\n view (view: string): this {\n this.ViewName = view\n\n return this\n }\n\n /**\n * Set email the notification html template\n * \n * @param content view name\n * @returns \n */\n html (content: string): this {\n this.htmlTemplate = content\n\n return this\n }\n\n /**\n * Set email the notification text template\n * \n * @param content view name\n * @returns \n */\n text (content: string): this {\n this.textTemplate = content\n\n return this\n }\n\n /**\n * Prepare a notification to be sent.\n * \n * @param user The recipient user(s) for the notification.\n */\n prepare (user?: null | string | string[] | User, data: Record<string, any> = {}) {\n this.data(data)\n\n if (user && typeof user === 'object' && !Array.isArray(user)) {\n user = user.email\n }\n\n if (user) {\n this.recipient(user)\n }\n\n return this\n }\n\n /**\n * Send a notification to the specified recipient(s) with the given message.\n * \n * @param message The message content to be sent to the recipient(s).\n * @param subject The message subject to be sent to the recipient(s).\n * @param recipient An array of recipient identifiers\n * @param data Additioal context data\n * @returns \n */\n async send (\n message: string,\n subject?: string,\n recipient?: MailRecipient,\n data?: NotificationData\n ) {\n const mergedData = {\n app_name: config('app.name', 'Arkstack'),\n ...this.mergeData(data),\n }\n const resolvedSubject = interpolate(subject ?? this.subjectLine ?? '', mergedData)\n const resolvedMessage = interpolate(message, mergedData)\n const to = this.resolveRecipients(recipient)\n\n if (to.length < 1) {\n throw new Error('No recipient provided for mail notification')\n }\n\n const templateData = {\n ...mergedData,\n message: resolvedMessage,\n subject: resolvedSubject,\n }\n\n const textMessage = resolvedMessage.replace(/<\\/?[^>]+(>|$)/g, '')\n\n return await this.driver.sendMail({\n to: to as never,\n subject: resolvedSubject,\n from: this.fromAddress,\n text: interpolate(\n this.textTemplate ?? textMessage,\n { ...templateData, message: textMessage }\n ),\n html: this.htmlTemplate\n ? interpolate(this.htmlTemplate, templateData)\n : await globalThis.view(this.ViewName, templateData),\n })\n }\n\n private resolveRecipients (recipient?: MailRecipient) {\n const recipients = recipient ?? this.recipients\n const resolved = (Array.isArray(recipients)\n ? [...recipients]\n : recipients\n ? [recipients]\n : []\n ).flatMap(recipient => this.normalizeRecipient(recipient) as unknown)\n\n const driverConfig = notificationConfig<Record<string, any>>('drivers.mail', {})\n const transport = driverConfig.transport ?? 'smtp'\n const transportConfig = notificationConfig<Record<string, any>>(`transports.${transport}`, {})\n const testAddress = driverConfig.testAddress\n ?? driverConfig.test_address\n ?? transportConfig.testAddress\n ?? transportConfig.test_address\n ?? env<string | undefined>('SMTP_TEST_ADDRESS')\n\n if (env('NODE_ENV') !== 'production' && testAddress) {\n resolved.push(testAddress)\n }\n\n return resolved\n }\n\n private normalizeRecipient (recipient: string | MailRecipientAddress) {\n if (typeof recipient === 'string') {\n return [recipient]\n }\n\n return Object.entries(recipient).map(([address, name]) => ({ address, name }))\n }\n}\n","import { env } from '@arkstack/common'\nimport africastalking from 'africastalking'\n\nimport { interpolate } from '../../utils/template'\nimport type { NotificationData, NotificationRecipient, SmsDriverOptions } from '../../types'\n\nexport class AfricasTalkingSmsDriver {\n private driver: {\n send: (payload: { to: string; from?: string; message: string }) => Promise<unknown>\n sendBulk: (payload: { to: string[]; from?: string; message: string }) => Promise<unknown>\n }\n private senderId?: string\n\n constructor(options: SmsDriverOptions['africastalking'] = {}) {\n const username = options.username ?? env('AFRICASTALKING_USERNAME', 'sandbox')\n const apiKey = options.apiKey ?? env('AFRICASTALKING_API_KEY', 'sandbox')\n\n this.senderId = options.senderId ?? env('AFRICASTALKING_SENDER_ID', env('SMS_FROM', 'Arkstack'))\n this.driver = (africastalking as (config: { username: string; apiKey: string }) => {\n SMS: AfricasTalkingSmsDriver['driver']\n })({ username, apiKey }).SMS\n }\n\n async send (message: string, recipient: NotificationRecipient, data: NotificationData = {}) {\n const recipients = Array.isArray(recipient) ? recipient : [recipient]\n const resolvedMessage = interpolate(message, data)\n\n if (recipients.length > 1) {\n return await this.driver.sendBulk({\n to: recipients,\n from: this.senderId,\n message: resolvedMessage,\n })\n }\n\n return await this.driver.send({\n to: recipients[0],\n from: this.senderId,\n message: resolvedMessage,\n })\n }\n}\n","import type { NotificationData, NotificationRecipient, SmsDriverOptions } from '../../types'\n\nimport { env } from '@arkstack/common'\nimport { interpolate } from '../../utils/template'\nimport twilio from 'twilio'\n\nexport class TwilioSmsDriver {\n private client: ReturnType<typeof twilio>\n private fromNumber: string\n\n constructor(options: SmsDriverOptions['twilio'] = {}) {\n const accountSid = options.accountSid ?? env('TWILIO_ACCOUNT_SID', '')\n const authToken = options.authToken ?? env('TWILIO_AUTH_TOKEN', '')\n\n this.fromNumber = options.from ?? env('TWILIO_FROM', env('SMS_FROM', ''))\n this.client = twilio(accountSid, authToken)\n }\n\n async send (message: string, recipient: NotificationRecipient, data: NotificationData = {}) {\n const recipients = Array.isArray(recipient) ? recipient : [recipient]\n const resolvedMessage = interpolate(message, data)\n\n return await Promise.all(recipients.map(async to => await this.client.messages.create({\n body: resolvedMessage,\n from: this.fromNumber,\n to,\n })))\n }\n}\n","import type { NotificationData, NotificationRecipient, SmsDriverOptions } from '../types'\n\nimport { AfricasTalkingSmsDriver } from './sms/AfricasTalkingSmsDriver'\nimport { NotificationContract } from '../Contracts/NotificationContract'\nimport { TwilioSmsDriver } from './sms/TwilioSmsDriver'\nimport { env } from '@arkstack/common'\nimport { notificationConfig } from '../config'\n\ntype SmsProvider = AfricasTalkingSmsDriver | TwilioSmsDriver\n\nexport class SmsNotification extends NotificationContract {\n driver: SmsProvider\n private recipients?: NotificationRecipient\n private fromValue?: string\n\n constructor(options: SmsDriverOptions = {}) {\n super()\n\n const driverConfig = notificationConfig<SmsDriverOptions>('drivers.sms', {})\n const transport = options.transport ?? driverConfig.transport ?? 'twilio'\n const transportConfig = notificationConfig<Record<string, any>>(`transports.${transport}`, {})\n const legacySmsConfig = notificationConfig<SmsDriverOptions>('sms', {})\n const from = options.from ?? driverConfig.from ?? legacySmsConfig.from\n\n this.fromValue = from\n this.driver = transport === 'twilio'\n ? new TwilioSmsDriver({\n ...legacySmsConfig.twilio,\n ...transportConfig,\n ...options.twilio,\n from: options.twilio?.from ?? transportConfig.from ?? legacySmsConfig.twilio?.from ?? from,\n })\n : new AfricasTalkingSmsDriver({\n ...legacySmsConfig.africastalking,\n ...transportConfig,\n ...options.africastalking,\n senderId: options.africastalking?.senderId ?? transportConfig.senderId ?? legacySmsConfig.africastalking?.senderId ?? from,\n })\n }\n\n from (from: string): this {\n this.fromValue = from\n\n return this\n }\n\n subject (_subject: string): this {\n return this\n }\n\n recipient (recipient: NotificationRecipient): this {\n this.recipients = recipient\n\n return this\n }\n\n async send (\n message: string,\n _subject?: string,\n recipient?: NotificationRecipient,\n data?: NotificationData\n ) {\n const resolvedRecipient = recipient ?? this.recipients\n\n if (!resolvedRecipient) {\n throw new Error('No recipient provided for SMS notification')\n }\n\n return await this.driver.send(message, resolvedRecipient, {\n app_name: env('APP_NAME', 'Arkstack'),\n from: this.fromValue,\n ...this.mergeData(data),\n })\n }\n}\n","import type { MailDriverOptions, MailRecipient, NotificationChannel, NotificationData, NotificationRecipient, NotificationUser, SmsDriverOptions } from './types'\n\nimport { DbNotification } from './drivers/DbNotification'\nimport { DriverMap } from './Contracts/Maps'\nimport { MailNotification } from './drivers/MailNotification'\nimport { SmsNotification } from './drivers/SmsNotification'\nimport { notificationConfig } from './config'\n\nexport class Notification<D extends keyof DriverMap = keyof DriverMap> {\n private driver: DriverMap[D]\n\n constructor(driver: D, options: MailDriverOptions | SmsDriverOptions = {}) {\n this.driver = Notification.createDriver(driver, options) as DriverMap[D]\n }\n\n static mail (options?: MailDriverOptions) {\n return new MailNotification(options)\n }\n\n static email (options?: MailDriverOptions) {\n return this.mail(options)\n }\n\n static sms (options?: SmsDriverOptions) {\n return new SmsNotification(options)\n }\n\n static db () {\n return new DbNotification()\n }\n\n static channel (\n channel?: NotificationChannel | 'email',\n options?: MailDriverOptions | SmsDriverOptions\n ) {\n return Notification.createDriver(channel ?? notificationConfig<NotificationChannel>('default_driver', 'mail'), options)\n }\n\n prepare (\n recipient?: null | MailRecipient | NotificationRecipient | NotificationUser,\n data: NotificationData = {}\n ) {\n this.driver.data(data)\n\n if (recipient && typeof recipient === 'object' && !Array.isArray(recipient) && 'id' in recipient) {\n if (this.driver instanceof MailNotification) {\n if (recipient.email) {\n this.driver.recipient(recipient.email)\n }\n } else if (this.driver instanceof SmsNotification) {\n if (recipient.phone) {\n this.driver.recipient(recipient.phone)\n }\n } else {\n this.driver.recipient(recipient as never)\n }\n\n return this.driver\n }\n\n if (recipient) {\n this.driver.recipient(recipient as NotificationRecipient)\n }\n\n return this.driver\n }\n\n private static createDriver (\n driver: NotificationChannel | 'email',\n options: MailDriverOptions | SmsDriverOptions = {}\n ) {\n switch (driver) {\n case 'mail':\n case 'email':\n return new MailNotification(options as MailDriverOptions)\n case 'sms':\n return new SmsNotification(options as SmsDriverOptions)\n case 'db':\n return new DbNotification()\n default:\n throw new Error(`Unsupported notification driver: ${driver}`)\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/Contracts/NotificationContract.ts","../src/Contracts/UserNotification.ts","../src/UserNotificationCenter.ts","../src/utils/template.ts","../src/drivers/DbNotification.ts","../src/config.ts","../src/drivers/MailNotification.ts","../src/drivers/sms/AfricasTalkingSmsDriver.ts","../src/drivers/sms/TwilioSmsDriver.ts","../src/drivers/SmsNotification.ts","../src/Notification.ts"],"sourcesContent":["import type { DriverResult, NotificationData, NotificationRecipient } from '../types'\n\nexport abstract class NotificationContract<TResult = DriverResult> {\n protected contextData: NotificationData = {}\n\n abstract send (\n message: string,\n subject?: string,\n recipient?: NotificationRecipient,\n data?: NotificationData\n ): Promise<TResult>\n\n abstract from (from: string): this\n abstract subject (subject: string): this\n abstract recipient (recipient: NotificationRecipient): this\n\n data (data: NotificationData): this {\n this.contextData = data\n\n return this\n }\n\n protected mergeData (data?: NotificationData) {\n return {\n ...this.contextData,\n ...data,\n year: new Date().getFullYear(),\n }\n }\n}\n","import type { DbNotificationType, NotificationData } from '../types'\n\nimport { Model } from '@arkstack/database'\n\nexport abstract class UserNotification extends Model {\n [key: string]: any\n declare type: DbNotificationType | null\n declare title: string\n declare description: string\n declare actionText: string | null\n declare actionLink: string | null\n declare meta: NotificationData | null\n declare readAt: Date | null\n declare createdAt: Date\n declare updatedAt: Date\n\n protected static table?: string | undefined = 'user_notifications'\n\n protected casts = {\n meta: 'json',\n } as const\n}\n","import { getModel } from '@arkstack/common'\n\nimport { UserNotification } from './Contracts/UserNotification'\nimport type { DbNotificationPayload, NotificationUser } from './types'\n\nexport class UserNotificationCenter {\n private static async getModel () {\n return await getModel<typeof UserNotification>('UserNotification')\n }\n\n static async create (user: NotificationUser, payload: DbNotificationPayload) {\n const Model = await this.getModel()\n\n return await Model.query().create({\n userId: user.id,\n type: payload.type ?? null,\n title: payload.title,\n description: payload.description,\n actionText: payload.actionText ?? null,\n actionLink: payload.actionLink ?? null,\n meta: payload.meta ?? null,\n })\n }\n\n static async forUser (user: NotificationUser) {\n const Model = await this.getModel()\n\n return await Model.query().where({ userId: user.id }).get()\n }\n\n static async unreadForUser (user: NotificationUser) {\n const Model = await this.getModel()\n\n return await Model.query().where({ userId: user.id, readAt: null }).get()\n }\n\n static async markRead (notification: UserNotification | UserNotification['id']) {\n const Model = await this.getModel()\n const id = notification instanceof UserNotification ? notification.id : notification\n const readAt = new Date()\n\n await Model.query().where({ id }).update({ readAt })\n\n if (notification instanceof UserNotification) {\n notification.readAt = readAt\n }\n }\n\n static async markAllRead (user: NotificationUser) {\n const Model = await this.getModel()\n\n await Model.query().where({ userId: user.id, readAt: null }).update({ readAt: new Date() })\n }\n\n static async delete (notification: UserNotification | UserNotification['id']) {\n const Model = await this.getModel()\n const id = notification instanceof UserNotification ? notification.id : notification\n\n await Model.query().where({ id }).delete()\n }\n}\n","import type { NotificationData } from '../types'\n\nexport const interpolate = (value: string, data: NotificationData = {}) => {\n return value.replace(/\\{([^{}]+)\\}/g, (match, key) => {\n const replacement = data[key.trim()]\n\n return replacement === undefined || replacement === null ? match : String(replacement)\n })\n}\n","import type { DbNotificationPayload, NotificationData, NotificationRecipient, NotificationUser } from '../types'\n\nimport { NotificationContract } from '../Contracts/NotificationContract'\nimport { UserNotification } from '../Contracts/UserNotification'\nimport { UserNotificationCenter } from '../UserNotificationCenter'\nimport { getModel } from '@arkstack/common'\nimport { interpolate } from '../utils/template'\n\nexport class DbNotification extends NotificationContract<UserNotification> {\n private user?: NotificationUser\n private payload: Partial<DbNotificationPayload> = {}\n\n from (_from: string): this {\n return this\n }\n\n subject (subject: string): this {\n this.payload.title = subject\n\n return this\n }\n\n recipient (recipient: NotificationRecipient | NotificationUser): this {\n if (typeof recipient === 'object' && !Array.isArray(recipient) && 'id' in recipient) {\n this.user = recipient\n\n return this\n }\n\n throw new Error('Database notifications require a user recipient')\n }\n\n type (type: DbNotificationPayload['type']): this {\n this.payload.type = type\n\n return this\n }\n\n action (text?: string | null, link?: string | null): this {\n this.payload.actionText = text\n this.payload.actionLink = link\n\n return this\n }\n\n meta (meta?: NotificationData | null): this {\n this.payload.meta = meta\n\n return this\n }\n\n async create (user: NotificationUser, payload: DbNotificationPayload) {\n await getModel<typeof UserNotification>('UserNotification')\n\n return await UserNotificationCenter.create(user, payload)\n }\n\n async send (\n message: string,\n subject?: string,\n _recipient?: NotificationRecipient,\n data?: NotificationData\n ) {\n if (!this.user) {\n throw new Error('No user recipient provided for database notification')\n }\n\n const mergedData = this.mergeData(data)\n\n return await this.create(this.user, {\n type: this.payload.type ?? null,\n title: interpolate(subject ?? this.payload.title ?? '', mergedData),\n description: interpolate(message, mergedData),\n actionText: this.payload.actionText ?? null,\n actionLink: this.payload.actionLink ?? null,\n meta: this.payload.meta ?? null,\n })\n }\n}\n","import { DotPath, DotPathValue, config } from '@arkstack/common'\n\nimport { NotificationConfig } from './types'\n\nexport const configure = <T extends DotPath<NotificationConfig>> (\n key: T,\n defaultValue: unknown,\n): DotPathValue<NotificationConfig, T> => {\n try {\n return config(`notifications.${key}`, defaultValue) as never\n } catch {\n return defaultValue as never\n }\n}\n","import { config, env } from '@arkstack/common'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport nodemailer, { type Transporter } from 'nodemailer'\n\nimport { NotificationContract } from '../Contracts/NotificationContract'\nimport { interpolate } from '../utils/template'\nimport { configure } from '../config'\nimport type {\n MailDriverOptions,\n MailRecipient,\n MailRecipientAddress,\n MergedTransportConfig,\n NotificationData,\n} from '../types'\nimport type { User } from '@arkstack/auth'\n\nexport class MailNotification extends NotificationContract {\n driver: Transporter\n private fromAddress?: string\n private subjectLine?: string\n private recipients?: MailRecipient\n private ViewName: string = '~arkstack/notifications.mail'\n private htmlTemplate?: string\n private textTemplate?: string\n private fileDirectory?: string\n\n constructor(options: MailDriverOptions = {}) {\n super()\n\n const driver = configure('drivers.mail', {})\n const trpt = driver.transport ?? 'smtp'\n const transport = configure(\n `transports.${trpt}`,\n {},\n ) as MergedTransportConfig\n\n if (trpt === 'file') {\n this.fileDirectory =\n options.directory ??\n transport.directory ??\n env('MAIL_NOTIFICATION_FILE_PATH', 'storage/framework/mails')\n\n this.fromAddress =\n options.from ??\n driver.from ??\n transport.from ??\n env('MAIL_FROM_ADDRESS', 'no-reply@example.com')\n\n this.driver = nodemailer.createTransport({\n streamTransport: true,\n buffer: true,\n })\n\n return\n }\n\n const host =\n options.host ?? transport.host ?? env('MAIL_HOST', 'localhost')\n const port = options.port ?? transport.port ?? env('MAIL_PORT', 1025)\n const secure =\n options.secure ?? transport.secure ?? env('MAIL_SECURE', false)\n\n const user =\n options.user ??\n transport.auth?.user ??\n transport.user ??\n env('MAIL_USERNAME', '')\n\n const pass =\n options.pass ??\n transport.auth?.pass ??\n transport.pass ??\n env('MAIL_PASSWORD', '')\n\n this.fromAddress =\n options.from ??\n driver.from ??\n transport.from ??\n env('MAIL_FROM_ADDRESS', 'no-reply@example.com')\n\n this.driver = nodemailer.createTransport({\n host,\n port: Number(port),\n secure: Boolean(secure),\n auth: user || pass ? { user, pass } : undefined,\n })\n }\n\n from (from: string): this {\n this.fromAddress = from\n\n return this\n }\n\n /**\n * The email subject\n *\n * @param subject\n * @returns\n */\n subject (subject: string): this {\n this.subjectLine = subject\n\n return this\n }\n\n /**\n * Set email the notification recipeint\n *\n * @param recipient string or array of email addresses\n * @returns\n */\n recipient (recipient: MailRecipient): this {\n this.recipients = recipient\n\n return this\n }\n\n /**\n * Set email the notification view name\n *\n * @param view view name\n * @returns\n */\n view (view: string): this {\n this.ViewName = view\n\n return this\n }\n\n /**\n * Set email the notification html template\n *\n * @param content view name\n * @returns\n */\n html (content: string): this {\n this.htmlTemplate = content\n\n return this\n }\n\n /**\n * Set email the notification text template\n *\n * @param content view name\n * @returns\n */\n text (content: string): this {\n this.textTemplate = content\n\n return this\n }\n\n /**\n * Prepare a notification to be sent.\n *\n * @param user The recipient user(s) for the notification.\n */\n prepare (\n user?: null | string | string[] | User,\n data: Record<string, any> = {},\n ) {\n this.data(data)\n\n if (user && typeof user === 'object' && !Array.isArray(user)) {\n user = user.email\n }\n\n if (user) {\n this.recipient(user)\n }\n\n return this\n }\n\n /**\n * Send a notification to the specified recipient(s) with the given message.\n *\n * @param message The message content to be sent to the recipient(s).\n * @param subject The message subject to be sent to the recipient(s).\n * @param recipient An array of recipient identifiers\n * @param data Additioal context data\n * @returns\n */\n async send (\n message: string,\n subject?: string,\n recipient?: MailRecipient,\n data?: NotificationData,\n ) {\n const mergedData = {\n app_name: config('app.name', 'Arkstack'),\n ...this.mergeData(data),\n }\n const resolvedSubject = interpolate(\n subject ?? this.subjectLine ?? '',\n mergedData,\n )\n const resolvedMessage = interpolate(message, mergedData)\n const to = this.resolveRecipients(recipient)\n\n if (to.length < 1) {\n throw new Error('No recipient provided for mail notification')\n }\n\n const templateData = {\n ...mergedData,\n message: resolvedMessage,\n subject: resolvedSubject,\n }\n\n const textMessage = resolvedMessage.replace(/<\\/?[^>]+(>|$)/g, '')\n\n const payload = {\n to: to as never,\n subject: resolvedSubject,\n from: this.fromAddress,\n text: interpolate(this.textTemplate ?? textMessage, {\n ...templateData,\n message: textMessage,\n }),\n html: this.htmlTemplate\n ? interpolate(this.htmlTemplate, templateData)\n : await globalThis.view(this.ViewName, templateData),\n }\n const result = await this.driver.sendMail(payload)\n\n if (this.fileDirectory) {\n await this.storeFileMail(payload, result as Record<string, any>)\n }\n\n return result\n }\n\n private async storeFileMail (\n payload: Record<string, any>,\n result: Record<string, any>,\n ) {\n const now = new Date()\n const date = now.toISOString().slice(0, 10)\n const directory = join(this.fileDirectory as string, date)\n const id = String(result.messageId || now.getTime()).replace(\n /[^a-zA-Z0-9_.-]/g,\n '-',\n )\n const path = join(directory, now.getTime() + '-' + id + '.json')\n const message = result.message\n\n await mkdir(directory, { recursive: true })\n await writeFile(\n path,\n JSON.stringify(\n {\n id: result.messageId,\n date: now.toISOString(),\n envelope: result.envelope,\n accepted: result.accepted,\n rejected: result.rejected,\n pending: result.pending,\n response: result.response,\n message: {\n from: payload.from,\n to: payload.to,\n subject: payload.subject,\n text: payload.text,\n html: payload.html,\n raw: Buffer.isBuffer(message)\n ? message.toString('utf8')\n : String(message ?? ''),\n },\n },\n null,\n 2,\n ),\n 'utf8',\n )\n\n return path\n }\n\n private resolveRecipients (recipient?: MailRecipient) {\n const recipients = recipient ?? this.recipients\n const resolved = (\n Array.isArray(recipients)\n ? [...recipients]\n : recipients\n ? [recipients]\n : []\n ).flatMap((recipient) => this.normalizeRecipient(recipient) as unknown)\n\n const driver = configure('drivers.mail', {})\n const trpt = driver.transport ?? 'smtp'\n const transport = configure(\n `transports.${trpt}`,\n {},\n ) as MergedTransportConfig\n\n const testAddress =\n driver.test_address ??\n driver.test_address ??\n transport.test_address ??\n transport.test_address ??\n env<string | undefined>('MAIL_TEST_ADDRESS')\n\n if (env('NODE_ENV') !== 'production' && testAddress) {\n resolved.push(testAddress)\n }\n\n return resolved\n }\n\n private normalizeRecipient (recipient: string | MailRecipientAddress) {\n if (typeof recipient === 'string') {\n return [recipient]\n }\n\n return Object.entries(recipient).map(([address, name]) => ({\n address,\n name,\n }))\n }\n}\n","import { env } from '@arkstack/common'\nimport africastalking from 'africastalking'\n\nimport { interpolate } from '../../utils/template'\nimport type { NotificationData, NotificationRecipient, SmsDriverOptions } from '../../types'\n\nexport class AfricasTalkingSmsDriver {\n private driver: {\n send: (payload: { to: string; from?: string; message: string }) => Promise<unknown>\n sendBulk: (payload: { to: string[]; from?: string; message: string }) => Promise<unknown>\n }\n private senderId?: string\n\n constructor(options: SmsDriverOptions['africastalking'] = {}) {\n const username = options.username ?? env('AFRICASTALKING_USERNAME', 'sandbox')\n const apiKey = options.apiKey ?? env('AFRICASTALKING_API_KEY', 'sandbox')\n\n this.senderId = options.senderId ?? env('AFRICASTALKING_SENDER_ID', env('SMS_FROM', 'Arkstack'))\n this.driver = (africastalking as (config: { username: string; apiKey: string }) => {\n SMS: AfricasTalkingSmsDriver['driver']\n })({ username, apiKey }).SMS\n }\n\n async send (message: string, recipient: NotificationRecipient, data: NotificationData = {}) {\n const recipients = Array.isArray(recipient) ? recipient : [recipient]\n const resolvedMessage = interpolate(message, data)\n\n if (recipients.length > 1) {\n return await this.driver.sendBulk({\n to: recipients,\n from: this.senderId,\n message: resolvedMessage,\n })\n }\n\n return await this.driver.send({\n to: recipients[0],\n from: this.senderId,\n message: resolvedMessage,\n })\n }\n}\n","import type { NotificationData, NotificationRecipient, SmsDriverOptions } from '../../types'\n\nimport { env } from '@arkstack/common'\nimport { interpolate } from '../../utils/template'\nimport twilio from 'twilio'\n\nexport class TwilioSmsDriver {\n private client: ReturnType<typeof twilio>\n private fromNumber: string\n\n constructor(options: SmsDriverOptions['twilio'] = {}) {\n const accountSid = options.accountSid ?? env('TWILIO_ACCOUNT_SID', '')\n const authToken = options.authToken ?? env('TWILIO_AUTH_TOKEN', '')\n\n this.fromNumber = options.from ?? env('TWILIO_FROM', env('SMS_FROM', ''))\n this.client = twilio(accountSid, authToken)\n }\n\n async send (message: string, recipient: NotificationRecipient, data: NotificationData = {}) {\n const recipients = Array.isArray(recipient) ? recipient : [recipient]\n const resolvedMessage = interpolate(message, data)\n\n return await Promise.all(recipients.map(async to => await this.client.messages.create({\n body: resolvedMessage,\n from: this.fromNumber,\n to,\n })))\n }\n}\n","import type { NotificationData, NotificationRecipient, SmsDriverOptions } from '../types'\n\nimport { AfricasTalkingSmsDriver } from './sms/AfricasTalkingSmsDriver'\nimport { NotificationContract } from '../Contracts/NotificationContract'\nimport { TwilioSmsDriver } from './sms/TwilioSmsDriver'\nimport { configure } from '../config'\nimport { env } from '@arkstack/common'\n\ntype SmsProvider = AfricasTalkingSmsDriver | TwilioSmsDriver\n\nexport class SmsNotification extends NotificationContract {\n driver: SmsProvider\n private recipients?: NotificationRecipient\n private fromValue?: string\n\n constructor(options: SmsDriverOptions = {}) {\n super()\n\n const driverConfig = configure<SmsDriverOptions>('drivers.sms', {})\n const transport = options.transport ?? driverConfig.transport ?? 'twilio'\n const transportConfig = configure<Record<string, any>>(`transports.${transport}`, {})\n const legacySmsConfig = configure<SmsDriverOptions>('sms', {})\n const from = options.from ?? driverConfig.from ?? legacySmsConfig.from\n\n this.fromValue = from\n this.driver = transport === 'twilio'\n ? new TwilioSmsDriver({\n ...legacySmsConfig.twilio,\n ...transportConfig,\n ...options.twilio,\n from: options.twilio?.from ?? transportConfig.from ?? legacySmsConfig.twilio?.from ?? from,\n })\n : new AfricasTalkingSmsDriver({\n ...legacySmsConfig.africastalking,\n ...transportConfig,\n ...options.africastalking,\n senderId: options.africastalking?.senderId ?? transportConfig.senderId ?? legacySmsConfig.africastalking?.senderId ?? from,\n })\n }\n\n from (from: string): this {\n this.fromValue = from\n\n return this\n }\n\n subject (_subject: string): this {\n return this\n }\n\n recipient (recipient: NotificationRecipient): this {\n this.recipients = recipient\n\n return this\n }\n\n async send (\n message: string,\n _subject?: string,\n recipient?: NotificationRecipient,\n data?: NotificationData\n ) {\n const resolvedRecipient = recipient ?? this.recipients\n\n if (!resolvedRecipient) {\n throw new Error('No recipient provided for SMS notification')\n }\n\n return await this.driver.send(message, resolvedRecipient, {\n app_name: env('APP_NAME', 'Arkstack'),\n from: this.fromValue,\n ...this.mergeData(data),\n })\n }\n}\n","import type { MailDriverOptions, MailRecipient, NotificationChannel, NotificationData, NotificationRecipient, NotificationUser, SmsDriverOptions } from './types'\n\nimport { DbNotification } from './drivers/DbNotification'\nimport { DriverMap } from './Contracts/Maps'\nimport { MailNotification } from './drivers/MailNotification'\nimport { SmsNotification } from './drivers/SmsNotification'\nimport { configure } from './config'\n\nexport class Notification<D extends keyof DriverMap = keyof DriverMap> {\n private driver: DriverMap[D]\n\n constructor(driver: D, options: MailDriverOptions | SmsDriverOptions = {}) {\n this.driver = Notification.createDriver(driver, options) as DriverMap[D]\n }\n\n static mail (options?: MailDriverOptions) {\n return new MailNotification(options)\n }\n\n static email (options?: MailDriverOptions) {\n return this.mail(options)\n }\n\n static sms (options?: SmsDriverOptions) {\n return new SmsNotification(options)\n }\n\n static db () {\n return new DbNotification()\n }\n\n static channel (\n channel?: NotificationChannel | 'email',\n options?: MailDriverOptions | SmsDriverOptions\n ) {\n return Notification.createDriver(channel ?? configure('default_driver', 'mail'), options)\n }\n\n prepare (\n recipient?: null | MailRecipient | NotificationRecipient | NotificationUser,\n data: NotificationData = {}\n ) {\n this.driver.data(data)\n\n if (recipient && typeof recipient === 'object' && !Array.isArray(recipient) && 'id' in recipient) {\n if (this.driver instanceof MailNotification) {\n if (recipient.email) {\n this.driver.recipient(recipient.email)\n }\n } else if (this.driver instanceof SmsNotification) {\n if (recipient.phone) {\n this.driver.recipient(recipient.phone)\n }\n } else {\n this.driver.recipient(recipient as never)\n }\n\n return this.driver\n }\n\n if (recipient) {\n this.driver.recipient(recipient as NotificationRecipient)\n }\n\n return this.driver\n }\n\n private static createDriver (\n driver: NotificationChannel | 'email',\n options: MailDriverOptions | SmsDriverOptions = {}\n ) {\n switch (driver) {\n case 'mail':\n case 'email':\n return new MailNotification(options as MailDriverOptions)\n case 'sms':\n return new SmsNotification(options as SmsDriverOptions)\n case 'db':\n return new DbNotification()\n default:\n throw new Error(`Unsupported notification driver: ${driver}`)\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkstack/notifications",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "type": "module",
5
5
  "description": "Framework-agnostic notification module for Arkstack and Nodejs, providing support for multi-channel notification delivery.",
6
6
  "homepage": "https://arkstack.toneflix.net/guide/notifications",
@@ -34,10 +34,10 @@
34
34
  "africastalking": "^0.8.0",
35
35
  "nodemailer": "^8.0.7",
36
36
  "twilio": "^6.0.0",
37
- "@arkstack/common": "^0.8.0"
37
+ "@arkstack/common": "^0.9.1"
38
38
  },
39
39
  "peerDependencies": {
40
- "@arkstack/database": "^0.8.0"
40
+ "@arkstack/database": "^0.9.1"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/nodemailer": "^7.0.11"