@tyravel/notifications 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/channels/database-channel.d.ts +13 -0
  2. package/dist/channels/database-channel.d.ts.map +1 -0
  3. package/dist/channels/database-channel.js +26 -0
  4. package/dist/channels/database-channel.js.map +1 -0
  5. package/dist/channels/mail-channel.d.ts +9 -0
  6. package/dist/channels/mail-channel.d.ts.map +1 -0
  7. package/dist/channels/mail-channel.js +19 -0
  8. package/dist/channels/mail-channel.js.map +1 -0
  9. package/dist/index.d.ts +13 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +9 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/notification-manager.d.ts +23 -0
  14. package/dist/notification-manager.d.ts.map +1 -0
  15. package/dist/notification-manager.js +62 -0
  16. package/dist/notification-manager.js.map +1 -0
  17. package/dist/notification-registry.d.ts +9 -0
  18. package/dist/notification-registry.d.ts.map +1 -0
  19. package/dist/notification-registry.js +18 -0
  20. package/dist/notification-registry.js.map +1 -0
  21. package/dist/notification.d.ts +15 -0
  22. package/dist/notification.d.ts.map +1 -0
  23. package/dist/notification.js +10 -0
  24. package/dist/notification.js.map +1 -0
  25. package/dist/notifications.test.d.ts +2 -0
  26. package/dist/notifications.test.d.ts.map +1 -0
  27. package/dist/notifications.test.js +44 -0
  28. package/dist/notifications.test.js.map +1 -0
  29. package/dist/queue-bridge.d.ts +9 -0
  30. package/dist/queue-bridge.d.ts.map +1 -0
  31. package/dist/queue-bridge.js +2 -0
  32. package/dist/queue-bridge.js.map +1 -0
  33. package/dist/queued-notification-context.d.ts +13 -0
  34. package/dist/queued-notification-context.d.ts.map +1 -0
  35. package/dist/queued-notification-context.js +11 -0
  36. package/dist/queued-notification-context.js.map +1 -0
  37. package/dist/queued-notifications.test.d.ts +2 -0
  38. package/dist/queued-notifications.test.d.ts.map +1 -0
  39. package/dist/queued-notifications.test.js +65 -0
  40. package/dist/queued-notifications.test.js.map +1 -0
  41. package/dist/send-queued-notification.d.ts +13 -0
  42. package/dist/send-queued-notification.d.ts.map +1 -0
  43. package/dist/send-queued-notification.js +11 -0
  44. package/dist/send-queued-notification.js.map +1 -0
  45. package/dist/send.d.ts +6 -0
  46. package/dist/send.d.ts.map +1 -0
  47. package/dist/send.js +11 -0
  48. package/dist/send.js.map +1 -0
  49. package/dist/serialized-notifiable.d.ts +14 -0
  50. package/dist/serialized-notifiable.d.ts.map +1 -0
  51. package/dist/serialized-notifiable.js +21 -0
  52. package/dist/serialized-notifiable.js.map +1 -0
  53. package/dist/should-queue.d.ts +6 -0
  54. package/dist/should-queue.d.ts.map +1 -0
  55. package/dist/should-queue.js +4 -0
  56. package/dist/should-queue.js.map +1 -0
  57. package/dist/types.d.ts +15 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/types.js +2 -0
  60. package/dist/types.js.map +1 -0
  61. package/package.json +41 -0
@@ -0,0 +1,13 @@
1
+ import type { DatabaseConnection } from '@tyravel/database';
2
+ import type { Notification } from '../notification.js';
3
+ import type { Notifiable } from '../types.js';
4
+ export interface DatabaseChannelOptions {
5
+ connection: DatabaseConnection;
6
+ table?: string;
7
+ }
8
+ export declare class DatabaseChannel {
9
+ private readonly options;
10
+ constructor(options: DatabaseChannelOptions);
11
+ send(notifiable: Notifiable, notification: Notification): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=database-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-channel.d.ts","sourceRoot":"","sources":["../../src/channels/database-channel.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAaD,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,sBAAsB;IAEtD,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAiB9E"}
@@ -0,0 +1,26 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { QueryBuilder } from '@tyravel/database';
3
+ export class DatabaseChannel {
4
+ options;
5
+ constructor(options) {
6
+ this.options = options;
7
+ }
8
+ async send(notifiable, notification) {
9
+ if (!notification.toDatabase) {
10
+ throw new Error(`Notification ${notification.id()} does not implement toDatabase().`);
11
+ }
12
+ const data = await notification.toDatabase(notifiable);
13
+ const table = this.options.table ?? 'notifications';
14
+ const now = new Date().toISOString();
15
+ await new QueryBuilder(this.options.connection, table).insert({
16
+ id: randomUUID(),
17
+ type: notification.id(),
18
+ notifiable_type: notifiable.constructor.name,
19
+ notifiable_id: String(notifiable.getKey()),
20
+ data: JSON.stringify(data),
21
+ read_at: null,
22
+ created_at: now,
23
+ });
24
+ }
25
+ }
26
+ //# sourceMappingURL=database-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-channel.js","sourceRoot":"","sources":["../../src/channels/database-channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAoBjD,MAAM,OAAO,eAAe;IACG;IAA7B,YAA6B,OAA+B;QAA/B,YAAO,GAAP,OAAO,CAAwB;IAAG,CAAC;IAEhE,KAAK,CAAC,IAAI,CAAC,UAAsB,EAAE,YAA0B;QAC3D,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,gBAAgB,YAAY,CAAC,EAAE,EAAE,mCAAmC,CAAC,CAAC;QACxF,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,eAAe,CAAC;QACpD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,IAAI,YAAY,CAAkB,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC;YAC7E,EAAE,EAAE,UAAU,EAAE;YAChB,IAAI,EAAE,YAAY,CAAC,EAAE,EAAE;YACvB,eAAe,EAAE,UAAU,CAAC,WAAW,CAAC,IAAI;YAC5C,aAAa,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YAC1C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,GAAG;SAChB,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ import type { MailManager } from '@tyravel/mail';
2
+ import type { Notification } from '../notification.js';
3
+ import type { Notifiable } from '../types.js';
4
+ export declare class MailChannel {
5
+ private readonly mail;
6
+ constructor(mail: MailManager);
7
+ send(notifiable: Notifiable, notification: Notification): Promise<void>;
8
+ }
9
+ //# sourceMappingURL=mail-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail-channel.d.ts","sourceRoot":"","sources":["../../src/channels/mail-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,WAAW;IAExC,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAa9E"}
@@ -0,0 +1,19 @@
1
+ export class MailChannel {
2
+ mail;
3
+ constructor(mail) {
4
+ this.mail = mail;
5
+ }
6
+ async send(notifiable, notification) {
7
+ if (!notification.toMail) {
8
+ throw new Error(`Notification ${notification.id()} does not implement toMail().`);
9
+ }
10
+ const message = await notification.toMail(notifiable);
11
+ const address = notifiable.routeNotificationForMail?.();
12
+ const recipient = typeof address === 'string' ? address : address?.address;
13
+ if (!recipient) {
14
+ throw new Error('Notifiable is missing routeNotificationForMail().');
15
+ }
16
+ await this.mail.mailer().to(recipient).send(message);
17
+ }
18
+ }
19
+ //# sourceMappingURL=mail-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail-channel.js","sourceRoot":"","sources":["../../src/channels/mail-channel.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,WAAW;IACO;IAA7B,YAA6B,IAAiB;QAAjB,SAAI,GAAJ,IAAI,CAAa;IAAG,CAAC;IAElD,KAAK,CAAC,IAAI,CAAC,UAAsB,EAAE,YAA0B;QAC3D,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,gBAAgB,YAAY,CAAC,EAAE,EAAE,+BAA+B,CAAC,CAAC;QACpF,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,UAAU,CAAC,wBAAwB,EAAE,EAAE,CAAC;QACxD,MAAM,SAAS,GACb,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC;QAC3D,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;QACD,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvD,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ export type { Notifiable, NotificationChannel, NotificationsConfig } from './types.js';
2
+ export { Notification } from './notification.js';
3
+ export { NotificationManager } from './notification-manager.js';
4
+ export { NotificationRegistry } from './notification-registry.js';
5
+ export { notify, setNotificationSender } from './send.js';
6
+ export type { ShouldQueue } from './should-queue.js';
7
+ export { shouldQueue } from './should-queue.js';
8
+ export { SendQueuedNotification } from './send-queued-notification.js';
9
+ export type { SendQueuedNotificationData } from './send-queued-notification.js';
10
+ export { setQueuedNotificationContext } from './queued-notification-context.js';
11
+ export { SerializedNotifiable, serializeNotifiable } from './serialized-notifiable.js';
12
+ export type { NotificationQueueBridge } from './queue-bridge.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,YAAY,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACvF,YAAY,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { Notification } from './notification.js';
2
+ export { NotificationManager } from './notification-manager.js';
3
+ export { NotificationRegistry } from './notification-registry.js';
4
+ export { notify, setNotificationSender } from './send.js';
5
+ export { shouldQueue } from './should-queue.js';
6
+ export { SendQueuedNotification } from './send-queued-notification.js';
7
+ export { setQueuedNotificationContext } from './queued-notification-context.js';
8
+ export { SerializedNotifiable, serializeNotifiable } from './serialized-notifiable.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAE1D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAEvE,OAAO,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { MailManager } from '@tyravel/mail';
2
+ import type { Notification } from './notification.js';
3
+ import type { Notifiable } from './types.js';
4
+ import type { DatabaseChannelOptions } from './channels/database-channel.js';
5
+ import type { NotificationQueueBridge } from './queue-bridge.js';
6
+ import type { NotificationRegistry } from './notification-registry.js';
7
+ export declare class NotificationManager {
8
+ private readonly queue?;
9
+ private readonly registry?;
10
+ private readonly mailChannel;
11
+ private readonly databaseChannel?;
12
+ private queueDefaults;
13
+ constructor(mail: MailManager, database?: DatabaseChannelOptions, queue?: NotificationQueueBridge | undefined, registry?: NotificationRegistry | undefined);
14
+ setQueueDefaults(options: {
15
+ connection?: string;
16
+ queue?: string;
17
+ }): void;
18
+ send(notifiable: Notifiable, notification: Notification): Promise<void>;
19
+ sendNow(notifiable: Notifiable, notification: Notification): Promise<void>;
20
+ private resolveQueueOptions;
21
+ private sendOnChannel;
22
+ }
23
+ //# sourceMappingURL=notification-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-manager.d.ts","sourceRoot":"","sources":["../src/notification-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAKvE,qBAAa,mBAAmB;IAQ5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAR5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAkB;IACnD,OAAO,CAAC,aAAa,CAA+C;gBAGlE,IAAI,EAAE,WAAW,EACjB,QAAQ,CAAC,EAAE,sBAAsB,EAChB,KAAK,CAAC,EAAE,uBAAuB,YAAA,EAC/B,QAAQ,CAAC,EAAE,oBAAoB,YAAA;IAMlD,gBAAgB,CAAC,OAAO,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAIlE,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBvE,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhF,OAAO,CAAC,mBAAmB;YAiBb,aAAa;CAmB5B"}
@@ -0,0 +1,62 @@
1
+ import { MailChannel } from './channels/mail-channel.js';
2
+ import { DatabaseChannel } from './channels/database-channel.js';
3
+ import { shouldQueue } from './should-queue.js';
4
+ import { SendQueuedNotification } from './send-queued-notification.js';
5
+ import { serializeNotifiable } from './serialized-notifiable.js';
6
+ export class NotificationManager {
7
+ queue;
8
+ registry;
9
+ mailChannel;
10
+ databaseChannel;
11
+ queueDefaults = {};
12
+ constructor(mail, database, queue, registry) {
13
+ this.queue = queue;
14
+ this.registry = registry;
15
+ this.mailChannel = new MailChannel(mail);
16
+ this.databaseChannel = database ? new DatabaseChannel(database) : undefined;
17
+ }
18
+ setQueueDefaults(options) {
19
+ this.queueDefaults = options;
20
+ }
21
+ async send(notifiable, notification) {
22
+ this.registry?.registerInstance(notification);
23
+ if (shouldQueue(notification) && this.queue) {
24
+ const job = new SendQueuedNotification({
25
+ notification: notification.id(),
26
+ notifiable: serializeNotifiable(notifiable),
27
+ });
28
+ const options = this.resolveQueueOptions(notification);
29
+ await this.queue.dispatch(job, options);
30
+ return;
31
+ }
32
+ await this.sendNow(notifiable, notification);
33
+ }
34
+ async sendNow(notifiable, notification) {
35
+ const channels = notification.via(notifiable);
36
+ await Promise.all(channels.map((channel) => this.sendOnChannel(notifiable, notification, channel)));
37
+ }
38
+ resolveQueueOptions(notification) {
39
+ const queued = notification;
40
+ return {
41
+ connection: queued.connection ?? this.queueDefaults.connection,
42
+ queue: queued.queue ?? this.queueDefaults.queue,
43
+ delaySeconds: queued.delaySeconds,
44
+ };
45
+ }
46
+ async sendOnChannel(notifiable, notification, channel) {
47
+ switch (channel) {
48
+ case 'mail':
49
+ await this.mailChannel.send(notifiable, notification);
50
+ return;
51
+ case 'database':
52
+ if (!this.databaseChannel) {
53
+ throw new Error('Database notification channel is not configured.');
54
+ }
55
+ await this.databaseChannel.send(notifiable, notification);
56
+ return;
57
+ default:
58
+ throw new Error(`Unknown notification channel [${channel}].`);
59
+ }
60
+ }
61
+ }
62
+ //# sourceMappingURL=notification-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-manager.js","sourceRoot":"","sources":["../src/notification-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Notification } from './notification.js';
2
+ export type NotificationConstructor = new () => Notification;
3
+ export declare class NotificationRegistry {
4
+ private readonly notifications;
5
+ register(constructor: NotificationConstructor): this;
6
+ registerInstance(notification: Notification): this;
7
+ create(name: string): Notification;
8
+ }
9
+ //# sourceMappingURL=notification-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-registry.d.ts","sourceRoot":"","sources":["../src/notification-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,MAAM,uBAAuB,GAAG,UAAU,YAAY,CAAC;AAE7D,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8C;IAE5E,QAAQ,CAAC,WAAW,EAAE,uBAAuB,GAAG,IAAI;IAKpD,gBAAgB,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI;IAIlD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;CAOnC"}
@@ -0,0 +1,18 @@
1
+ export class NotificationRegistry {
2
+ notifications = new Map();
3
+ register(constructor) {
4
+ this.notifications.set(constructor.name, constructor);
5
+ return this;
6
+ }
7
+ registerInstance(notification) {
8
+ return this.register(notification.constructor);
9
+ }
10
+ create(name) {
11
+ const constructor = this.notifications.get(name);
12
+ if (!constructor) {
13
+ throw new Error(`Notification class not registered: ${name}`);
14
+ }
15
+ return new constructor();
16
+ }
17
+ }
18
+ //# sourceMappingURL=notification-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-registry.js","sourceRoot":"","sources":["../src/notification-registry.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,oBAAoB;IACd,aAAa,GAAG,IAAI,GAAG,EAAmC,CAAC;IAE5E,QAAQ,CAAC,WAAoC;QAC3C,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gBAAgB,CAAC,YAA0B;QACzC,OAAO,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,WAAsC,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,CAAC,IAAY;QACjB,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,OAAO,IAAI,WAAW,EAAE,CAAC;IAC3B,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ import type { MailMessage } from '@tyravel/mail';
2
+ import type { NotificationChannel, Notifiable } from './types.js';
3
+ export declare abstract class Notification {
4
+ abstract via(notifiable: Notifiable): NotificationChannel[];
5
+ /** When true (via ShouldQueue + override), notification is pushed to the queue. */
6
+ shouldQueue?(): boolean;
7
+ /** Optional queue routing (Laravel-style). */
8
+ connection?: string;
9
+ queue?: string;
10
+ delaySeconds?: number;
11
+ toMail?(notifiable: Notifiable): MailMessage | Promise<MailMessage>;
12
+ toDatabase?(notifiable: Notifiable): Record<string, unknown> | Promise<Record<string, unknown>>;
13
+ id(): string;
14
+ }
15
+ //# sourceMappingURL=notification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification.d.ts","sourceRoot":"","sources":["../src/notification.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAElE,8BAAsB,YAAY;IAChC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,UAAU,GAAG,mBAAmB,EAAE;IAE3D,mFAAmF;IACnF,WAAW,CAAC,IAAI,OAAO;IAEvB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,MAAM,CAAC,CAAC,UAAU,EAAE,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAEnE,UAAU,CAAC,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE/F,EAAE,IAAI,MAAM;CAGb"}
@@ -0,0 +1,10 @@
1
+ export class Notification {
2
+ /** Optional queue routing (Laravel-style). */
3
+ connection;
4
+ queue;
5
+ delaySeconds;
6
+ id() {
7
+ return this.constructor.name;
8
+ }
9
+ }
10
+ //# sourceMappingURL=notification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification.js","sourceRoot":"","sources":["../src/notification.ts"],"names":[],"mappings":"AAGA,MAAM,OAAgB,YAAY;IAMhC,8CAA8C;IAC9C,UAAU,CAAU;IACpB,KAAK,CAAU;IACf,YAAY,CAAU;IAMtB,EAAE;QACA,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=notifications.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications.test.d.ts","sourceRoot":"","sources":["../src/notifications.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { MailManager } from '@tyravel/mail';
3
+ import { Notification } from './notification.js';
4
+ import { NotificationManager } from './notification-manager.js';
5
+ class PingNotification extends Notification {
6
+ via() {
7
+ return ['mail'];
8
+ }
9
+ toMail() {
10
+ return {
11
+ subject: 'Ping',
12
+ to: [],
13
+ text: 'pong',
14
+ };
15
+ }
16
+ }
17
+ class User {
18
+ id;
19
+ email;
20
+ constructor(id, email) {
21
+ this.id = id;
22
+ this.email = email;
23
+ }
24
+ getKey() {
25
+ return this.id;
26
+ }
27
+ routeNotificationForMail() {
28
+ return this.email;
29
+ }
30
+ }
31
+ describe('NotificationManager', () => {
32
+ it('sends mail channel notifications', async () => {
33
+ const mail = new MailManager({
34
+ default: 'array',
35
+ from: { address: 'noreply@example.com' },
36
+ connections: { array: { driver: 'array' } },
37
+ });
38
+ const manager = new NotificationManager(mail);
39
+ await manager.send(new User(1, 'a@b.com'), new PingNotification());
40
+ const transport = mail.transport('array');
41
+ expect(transport.messages[0]?.subject).toBe('Ping');
42
+ });
43
+ });
44
+ //# sourceMappingURL=notifications.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications.test.js","sourceRoot":"","sources":["../src/notifications.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAGhE,MAAM,gBAAiB,SAAQ,YAAY;IAChC,GAAG;QACV,OAAO,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAEQ,MAAM;QACb,OAAO;YACL,OAAO,EAAE,MAAM;YACf,EAAE,EAAE,EAAE;YACN,IAAI,EAAE,MAAM;SACb,CAAC;IACJ,CAAC;CACF;AAED,MAAM,IAAI;IAEC;IACA;IAFT,YACS,EAAU,EACV,KAAa;QADb,OAAE,GAAF,EAAE,CAAQ;QACV,UAAK,GAAL,KAAK,CAAQ;IACnB,CAAC;IAEJ,MAAM;QACJ,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,wBAAwB;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF;AAED,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC;YAC3B,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,EAAE,OAAO,EAAE,qBAAqB,EAAE;YACxC,WAAW,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;SAC5C,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,IAAI,gBAAgB,EAAE,CAAC,CAAC;QACnE,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAuB,CAAC;QAChE,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Job } from '@tyravel/queue';
2
+ export interface NotificationQueueBridge {
3
+ dispatch(job: Job, options?: {
4
+ connection?: string;
5
+ queue?: string;
6
+ delaySeconds?: number;
7
+ }): Promise<void>;
8
+ }
9
+ //# sourceMappingURL=queue-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-bridge.d.ts","sourceRoot":"","sources":["../src/queue-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7G"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=queue-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-bridge.js","sourceRoot":"","sources":["../src/queue-bridge.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import type { Container } from '@tyravel/container';
2
+ import type { NotificationManager } from './notification-manager.js';
3
+ import type { NotificationRegistry } from './notification-registry.js';
4
+ import { SerializedNotifiable } from './serialized-notifiable.js';
5
+ export interface QueuedNotificationContext {
6
+ manager: NotificationManager;
7
+ registry: NotificationRegistry;
8
+ SerializedNotifiable: typeof SerializedNotifiable;
9
+ container?: Container;
10
+ }
11
+ export declare function setQueuedNotificationContext(value: QueuedNotificationContext): void;
12
+ export declare function getQueuedNotificationContext(): QueuedNotificationContext;
13
+ //# sourceMappingURL=queued-notification-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queued-notification-context.d.ts","sourceRoot":"","sources":["../src/queued-notification-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAElE,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;IAClD,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAID,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,yBAAyB,GAAG,IAAI,CAEnF;AAED,wBAAgB,4BAA4B,IAAI,yBAAyB,CAKxE"}
@@ -0,0 +1,11 @@
1
+ let context;
2
+ export function setQueuedNotificationContext(value) {
3
+ context = value;
4
+ }
5
+ export function getQueuedNotificationContext() {
6
+ if (!context) {
7
+ throw new Error('Queued notification context is not configured.');
8
+ }
9
+ return context;
10
+ }
11
+ //# sourceMappingURL=queued-notification-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queued-notification-context.js","sourceRoot":"","sources":["../src/queued-notification-context.ts"],"names":[],"mappings":"AAYA,IAAI,OAA8C,CAAC;AAEnD,MAAM,UAAU,4BAA4B,CAAC,KAAgC;IAC3E,OAAO,GAAG,KAAK,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,4BAA4B;IAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=queued-notifications.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queued-notifications.test.d.ts","sourceRoot":"","sources":["../src/queued-notifications.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,65 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { MailManager } from '@tyravel/mail';
3
+ import { JobRegistry, QueueManager, QueueWorker } from '@tyravel/queue';
4
+ import { Notification } from './notification.js';
5
+ import { NotificationManager } from './notification-manager.js';
6
+ import { NotificationRegistry } from './notification-registry.js';
7
+ import { SendQueuedNotification } from './send-queued-notification.js';
8
+ import { SerializedNotifiable, serializeNotifiable } from './serialized-notifiable.js';
9
+ import { setQueuedNotificationContext } from './queued-notification-context.js';
10
+ class QueuedPing extends Notification {
11
+ shouldQueue() {
12
+ return true;
13
+ }
14
+ via() {
15
+ return ['mail'];
16
+ }
17
+ toMail() {
18
+ return { subject: 'Queued', to: [], text: 'async' };
19
+ }
20
+ }
21
+ class User {
22
+ id;
23
+ email;
24
+ constructor(id, email) {
25
+ this.id = id;
26
+ this.email = email;
27
+ }
28
+ getKey() {
29
+ return this.id;
30
+ }
31
+ routeNotificationForMail() {
32
+ return this.email;
33
+ }
34
+ }
35
+ describe('queued notifications', () => {
36
+ it('dispatches SendQueuedNotification on sync queue', async () => {
37
+ const mail = new MailManager({
38
+ default: 'array',
39
+ from: { address: 'noreply@example.com' },
40
+ connections: { array: { driver: 'array' } },
41
+ });
42
+ const registry = new NotificationRegistry();
43
+ const jobs = new JobRegistry();
44
+ jobs.register(SendQueuedNotification);
45
+ const worker = new QueueWorker(jobs);
46
+ const queueManager = new QueueManager({ default: 'sync', connections: { sync: { driver: 'sync' } } }, worker);
47
+ const manager = new NotificationManager(mail, undefined, {
48
+ dispatch: async (job) => {
49
+ await queueManager.connection('sync').push(job);
50
+ },
51
+ }, registry);
52
+ setQueuedNotificationContext({
53
+ manager,
54
+ registry,
55
+ SerializedNotifiable,
56
+ });
57
+ const user = new User(9, 'queued@example.com');
58
+ registry.register(QueuedPing);
59
+ await manager.send(user, new QueuedPing());
60
+ const transport = mail.transport('array');
61
+ expect(transport.messages[0]?.subject).toBe('Queued');
62
+ expect(serializeNotifiable(user).mail).toBe('queued@example.com');
63
+ });
64
+ });
65
+ //# sourceMappingURL=queued-notifications.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queued-notifications.test.js","sourceRoot":"","sources":["../src/queued-notifications.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { Job } from '@tyravel/queue';
2
+ export interface SendQueuedNotificationData extends Record<string, unknown> {
3
+ notification: string;
4
+ notifiable: {
5
+ type: string;
6
+ id: string | number;
7
+ mail?: string;
8
+ };
9
+ }
10
+ export declare class SendQueuedNotification extends Job<SendQueuedNotificationData> {
11
+ handle(): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=send-queued-notification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-queued-notification.d.ts","sourceRoot":"","sources":["../src/send-queued-notification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAGrC,MAAM,WAAW,0BAA2B,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED,qBAAa,sBAAuB,SAAQ,GAAG,CAAC,0BAA0B,CAAC;IAC1D,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAMvC"}
@@ -0,0 +1,11 @@
1
+ import { Job } from '@tyravel/queue';
2
+ import { getQueuedNotificationContext } from './queued-notification-context.js';
3
+ export class SendQueuedNotification extends Job {
4
+ async handle() {
5
+ const context = getQueuedNotificationContext();
6
+ const notification = context.registry.create(this.data.notification);
7
+ const notifiable = new context.SerializedNotifiable(this.data.notifiable);
8
+ await context.manager.sendNow(notifiable, notification);
9
+ }
10
+ }
11
+ //# sourceMappingURL=send-queued-notification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-queued-notification.js","sourceRoot":"","sources":["../src/send-queued-notification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAWhF,MAAM,OAAO,sBAAuB,SAAQ,GAA+B;IAChE,KAAK,CAAC,MAAM;QACnB,MAAM,OAAO,GAAG,4BAA4B,EAAE,CAAC;QAC/C,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACrE,MAAM,UAAU,GAAG,IAAI,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1E,MAAM,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC1D,CAAC;CACF"}
package/dist/send.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { Notification } from './notification.js';
2
+ import type { NotificationManager } from './notification-manager.js';
3
+ import type { Notifiable } from './types.js';
4
+ export declare function setNotificationSender(manager: NotificationManager): void;
5
+ export declare function notify(notifiable: Notifiable, notification: Notification): Promise<void>;
6
+ //# sourceMappingURL=send.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAExE;AAED,wBAAsB,MAAM,CAC1B,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,IAAI,CAAC,CAKf"}
package/dist/send.js ADDED
@@ -0,0 +1,11 @@
1
+ let notifier;
2
+ export function setNotificationSender(manager) {
3
+ notifier = manager;
4
+ }
5
+ export async function notify(notifiable, notification) {
6
+ if (!notifier) {
7
+ throw new Error('Notification manager is not configured.');
8
+ }
9
+ await notifier.send(notifiable, notification);
10
+ }
11
+ //# sourceMappingURL=send.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.js","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAIA,IAAI,QAAyC,CAAC;AAE9C,MAAM,UAAU,qBAAqB,CAAC,OAA4B;IAChE,QAAQ,GAAG,OAAO,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,UAAsB,EACtB,YAA0B;IAE1B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IACD,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Notifiable } from './types.js';
2
+ export interface SerializedNotifiableData {
3
+ type: string;
4
+ id: string | number;
5
+ mail?: string;
6
+ }
7
+ export declare class SerializedNotifiable implements Notifiable {
8
+ private readonly data;
9
+ constructor(data: SerializedNotifiableData);
10
+ getKey(): string | number;
11
+ routeNotificationForMail(): string;
12
+ }
13
+ export declare function serializeNotifiable(notifiable: Notifiable): SerializedNotifiableData;
14
+ //# sourceMappingURL=serialized-notifiable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialized-notifiable.d.ts","sourceRoot":"","sources":["../src/serialized-notifiable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,oBAAqB,YAAW,UAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,wBAAwB;IAE3D,MAAM,IAAI,MAAM,GAAG,MAAM;IAIzB,wBAAwB,IAAI,MAAM;CAGnC;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,wBAAwB,CAOpF"}
@@ -0,0 +1,21 @@
1
+ export class SerializedNotifiable {
2
+ data;
3
+ constructor(data) {
4
+ this.data = data;
5
+ }
6
+ getKey() {
7
+ return this.data.id;
8
+ }
9
+ routeNotificationForMail() {
10
+ return this.data.mail ?? '';
11
+ }
12
+ }
13
+ export function serializeNotifiable(notifiable) {
14
+ const mail = notifiable.routeNotificationForMail?.();
15
+ return {
16
+ type: notifiable.constructor.name,
17
+ id: notifiable.getKey(),
18
+ mail: typeof mail === 'string' ? mail : mail?.address,
19
+ };
20
+ }
21
+ //# sourceMappingURL=serialized-notifiable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialized-notifiable.js","sourceRoot":"","sources":["../src/serialized-notifiable.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,oBAAoB;IACF;IAA7B,YAA6B,IAA8B;QAA9B,SAAI,GAAJ,IAAI,CAA0B;IAAG,CAAC;IAE/D,MAAM;QACJ,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IACtB,CAAC;IAED,wBAAwB;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,UAAU,mBAAmB,CAAC,UAAsB;IACxD,MAAM,IAAI,GAAG,UAAU,CAAC,wBAAwB,EAAE,EAAE,CAAC;IACrD,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,IAAI;QACjC,EAAE,EAAE,UAAU,CAAC,MAAM,EAAE;QACvB,IAAI,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO;KACtD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Notification } from './notification.js';
2
+ /** Marker interface — notifications that implement this are queued. */
3
+ export interface ShouldQueue {
4
+ }
5
+ export declare function shouldQueue(notification: Notification): boolean;
6
+ //# sourceMappingURL=should-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"should-queue.d.ts","sourceRoot":"","sources":["../src/should-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,uEAAuE;AACvE,MAAM,WAAW,WAAW;CAAG;AAE/B,wBAAgB,WAAW,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAE/D"}
@@ -0,0 +1,4 @@
1
+ export function shouldQueue(notification) {
2
+ return notification.shouldQueue?.() === true;
3
+ }
4
+ //# sourceMappingURL=should-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"should-queue.js","sourceRoot":"","sources":["../src/should-queue.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,WAAW,CAAC,YAA0B;IACpD,OAAO,YAAY,CAAC,WAAW,EAAE,EAAE,KAAK,IAAI,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,15 @@
1
+ export type NotificationChannel = 'mail' | 'database';
2
+ export interface Notifiable {
3
+ getKey(): string | number;
4
+ routeNotificationForMail?(): string | {
5
+ address: string;
6
+ name?: string;
7
+ };
8
+ }
9
+ export interface NotificationsConfig {
10
+ table?: string;
11
+ connection?: string;
12
+ queue?: string;
13
+ queueConnection?: string;
14
+ }
15
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,UAAU,CAAC;AAEtD,MAAM,WAAW,UAAU;IACzB,MAAM,IAAI,MAAM,GAAG,MAAM,CAAC;IAC1B,wBAAwB,CAAC,IAAI,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1E;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@tyravel/notifications",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsc -p tsconfig.json",
15
+ "typecheck": "tsc -p tsconfig.json --noEmit"
16
+ },
17
+ "dependencies": {
18
+ "@tyravel/database": "0.1.0",
19
+ "@tyravel/mail": "0.1.0",
20
+ "@tyravel/queue": "0.1.0"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "engines": {
26
+ "node": ">=22"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^22.15.30"
30
+ },
31
+ "description": "Multi-channel notifications for Tyravel",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/thesimonharms/tyravel.git",
36
+ "directory": "packages/notifications"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ }
41
+ }