@gravito/flare 1.0.0-alpha.5 → 1.0.0-beta.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.
@@ -0,0 +1,383 @@
1
+ import { PlanetCore, GravitoOrbit } from 'gravito-core';
2
+
3
+ /**
4
+ * Notification system type definitions.
5
+ */
6
+
7
+ /**
8
+ * Notification channel interface.
9
+ */
10
+ interface NotificationChannel {
11
+ /**
12
+ * Send a notification.
13
+ * @param notification - Notification instance
14
+ * @param notifiable - Recipient
15
+ */
16
+ send(notification: Notification, notifiable: Notifiable): Promise<void>;
17
+ }
18
+ /**
19
+ * Notifiable (notification recipient) interface.
20
+ */
21
+ interface Notifiable {
22
+ /**
23
+ * Recipient identifier (usually an ID).
24
+ */
25
+ getNotifiableId(): string | number;
26
+ /**
27
+ * Recipient type (optional, for polymorphic relations).
28
+ */
29
+ getNotifiableType?(): string;
30
+ /**
31
+ * Preferred channels (optional).
32
+ */
33
+ preferredNotificationChannels?(): string[];
34
+ }
35
+ /**
36
+ * Mail message payload.
37
+ */
38
+ interface MailMessage {
39
+ subject: string;
40
+ view?: string;
41
+ data?: Record<string, unknown>;
42
+ html?: string;
43
+ text?: string;
44
+ from?: string;
45
+ to?: string | string[];
46
+ cc?: string | string[];
47
+ bcc?: string | string[];
48
+ }
49
+ /**
50
+ * Database notification payload.
51
+ */
52
+ interface DatabaseNotification {
53
+ type: string;
54
+ data: Record<string, unknown>;
55
+ readAt?: Date | null;
56
+ }
57
+ /**
58
+ * Broadcast notification payload.
59
+ */
60
+ interface BroadcastNotification {
61
+ type: string;
62
+ data: Record<string, unknown>;
63
+ }
64
+ /**
65
+ * Slack message payload.
66
+ */
67
+ interface SlackMessage {
68
+ text: string;
69
+ channel?: string;
70
+ username?: string;
71
+ iconEmoji?: string;
72
+ attachments?: Array<{
73
+ color?: string;
74
+ title?: string;
75
+ text?: string;
76
+ fields?: Array<{
77
+ title: string;
78
+ value: string;
79
+ short?: boolean;
80
+ }>;
81
+ }>;
82
+ }
83
+ /**
84
+ * SMS message payload.
85
+ */
86
+ interface SmsMessage {
87
+ to: string;
88
+ message: string;
89
+ from?: string;
90
+ }
91
+
92
+ /**
93
+ * Marker interface for notifications that should be queued.
94
+ */
95
+ interface ShouldQueue {
96
+ /**
97
+ * Queue name (optional).
98
+ */
99
+ queue?: string | undefined;
100
+ connection?: string | undefined;
101
+ delay?: number | undefined;
102
+ }
103
+ /**
104
+ * Base Notification class.
105
+ *
106
+ * All notifications should extend this class.
107
+ * Notifications can be delivered via multiple channels (mail, database, broadcast, Slack, SMS, etc.).
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * class InvoicePaid extends Notification {
112
+ * constructor(private invoice: Invoice) {
113
+ * super()
114
+ * }
115
+ *
116
+ * via(user: User): string[] {
117
+ * return ['mail', 'database']
118
+ * }
119
+ *
120
+ * toMail(user: User): MailMessage {
121
+ * return new MailMessage()
122
+ * .subject('Invoice Paid')
123
+ * .view('emails.invoice-paid', { invoice: this.invoice })
124
+ * }
125
+ *
126
+ * toDatabase(user: User): DatabaseNotification {
127
+ * return {
128
+ * type: 'invoice-paid',
129
+ * data: { invoice_id: this.invoice.id }
130
+ * }
131
+ * }
132
+ * }
133
+ * ```
134
+ */
135
+ declare abstract class Notification {
136
+ /**
137
+ * Specify which channels should be used for delivery.
138
+ * @param notifiable - Recipient
139
+ * @returns Channel names
140
+ */
141
+ abstract via(notifiable: Notifiable): string[];
142
+ /**
143
+ * Get mail message (optional).
144
+ * Implement this if the notification will be sent via the mail channel.
145
+ */
146
+ toMail?(_notifiable: Notifiable): MailMessage;
147
+ /**
148
+ * Get database notification (optional).
149
+ * Implement this if the notification will be stored via the database channel.
150
+ */
151
+ toDatabase?(_notifiable: Notifiable): DatabaseNotification;
152
+ /**
153
+ * Get broadcast notification (optional).
154
+ * Implement this if the notification will be sent via the broadcast channel.
155
+ */
156
+ toBroadcast?(_notifiable: Notifiable): BroadcastNotification;
157
+ /**
158
+ * Get Slack message (optional).
159
+ * Implement this if the notification will be sent via the Slack channel.
160
+ */
161
+ toSlack?(_notifiable: Notifiable): SlackMessage;
162
+ /**
163
+ * Get SMS message (optional).
164
+ * Implement this if the notification will be sent via the SMS channel.
165
+ */
166
+ toSms?(_notifiable: Notifiable): SmsMessage;
167
+ /**
168
+ * Check whether this notification should be queued.
169
+ */
170
+ shouldQueue(): boolean;
171
+ /**
172
+ * Get queue configuration.
173
+ */
174
+ getQueueConfig(): {
175
+ queue?: string | undefined;
176
+ connection?: string | undefined;
177
+ delay?: number | undefined;
178
+ };
179
+ }
180
+
181
+ /**
182
+ * Broadcast channel.
183
+ *
184
+ * Sends notifications via a broadcast service.
185
+ */
186
+ declare class BroadcastChannel implements NotificationChannel {
187
+ private broadcastService;
188
+ constructor(broadcastService: {
189
+ broadcast(channel: string, event: string, data: Record<string, unknown>): Promise<void>;
190
+ });
191
+ send(notification: Notification, notifiable: Notifiable): Promise<void>;
192
+ }
193
+
194
+ /**
195
+ * Database channel.
196
+ *
197
+ * Persists notifications to a database.
198
+ */
199
+ declare class DatabaseChannel implements NotificationChannel {
200
+ private dbService;
201
+ constructor(dbService: {
202
+ insertNotification(data: {
203
+ notifiableId: string | number;
204
+ notifiableType: string;
205
+ type: string;
206
+ data: Record<string, unknown>;
207
+ }): Promise<void>;
208
+ });
209
+ send(notification: Notification, notifiable: Notifiable): Promise<void>;
210
+ }
211
+
212
+ /**
213
+ * Mail channel.
214
+ *
215
+ * Sends notifications via the mail service.
216
+ */
217
+ declare class MailChannel implements NotificationChannel {
218
+ private mailService;
219
+ constructor(mailService: {
220
+ send(message: MailMessage): Promise<void>;
221
+ });
222
+ send(notification: Notification, notifiable: Notifiable): Promise<void>;
223
+ }
224
+
225
+ /**
226
+ * Slack channel configuration.
227
+ */
228
+ interface SlackChannelConfig {
229
+ webhookUrl: string;
230
+ defaultChannel?: string;
231
+ }
232
+ /**
233
+ * Slack channel.
234
+ *
235
+ * Sends notifications via a Slack webhook.
236
+ */
237
+ declare class SlackChannel implements NotificationChannel {
238
+ private config;
239
+ constructor(config: SlackChannelConfig);
240
+ send(notification: Notification, notifiable: Notifiable): Promise<void>;
241
+ }
242
+
243
+ /**
244
+ * SMS channel configuration.
245
+ */
246
+ interface SmsChannelConfig {
247
+ provider: string;
248
+ apiKey?: string;
249
+ apiSecret?: string;
250
+ from?: string;
251
+ }
252
+ /**
253
+ * SMS channel.
254
+ *
255
+ * Sends notifications via an SMS provider.
256
+ * Only a basic interface is provided; real implementations should be extended per provider.
257
+ */
258
+ declare class SmsChannel implements NotificationChannel {
259
+ private config;
260
+ constructor(config: SmsChannelConfig);
261
+ send(notification: Notification, notifiable: Notifiable): Promise<void>;
262
+ /**
263
+ * Send SMS via Twilio.
264
+ */
265
+ private sendViaTwilio;
266
+ /**
267
+ * Send SMS via AWS SNS.
268
+ */
269
+ private sendViaAwsSns;
270
+ }
271
+
272
+ /**
273
+ * Notification manager.
274
+ *
275
+ * Responsible for managing notification channels and delivering notifications.
276
+ */
277
+ declare class NotificationManager {
278
+ private core;
279
+ /**
280
+ * Channel registry.
281
+ */
282
+ private channels;
283
+ /**
284
+ * Queue manager (optional, injected by `orbit-queue`).
285
+ */
286
+ private queueManager?;
287
+ constructor(core: PlanetCore);
288
+ /**
289
+ * Register a notification channel.
290
+ *
291
+ * @param name - The name of the channel.
292
+ * @param channel - The channel instance.
293
+ */
294
+ channel(name: string, channel: NotificationChannel): void;
295
+ /**
296
+ * Register the queue manager (called by `orbit-queue`).
297
+ *
298
+ * @param manager - The queue manager implementation.
299
+ */
300
+ setQueueManager(manager: NotificationManager['queueManager']): void;
301
+ /**
302
+ * Send a notification.
303
+ *
304
+ * @param notifiable - The recipient of the notification.
305
+ * @param notification - The notification instance.
306
+ * @returns A promise that resolves when the notification is sent or queued.
307
+ *
308
+ * @example
309
+ * ```typescript
310
+ * await notificationManager.send(user, new InvoicePaid(invoice))
311
+ * ```
312
+ */
313
+ send(notifiable: Notifiable, notification: Notification): Promise<void>;
314
+ /**
315
+ * Send immediately (without queue).
316
+ *
317
+ * @param notifiable - The recipient.
318
+ * @param notification - The notification.
319
+ * @param channels - The list of channels to send via.
320
+ */
321
+ private sendNow;
322
+ /**
323
+ * Serialize notification (for queuing).
324
+ *
325
+ * @param notification - The notification to serialize.
326
+ * @returns A plain object representation of the notification.
327
+ */
328
+ private serializeNotification;
329
+ }
330
+
331
+ /**
332
+ * OrbitFlare options.
333
+ */
334
+ interface OrbitFlareOptions {
335
+ /**
336
+ * Enable mail channel.
337
+ */
338
+ enableMail?: boolean;
339
+ /**
340
+ * Enable database channel.
341
+ */
342
+ enableDatabase?: boolean;
343
+ /**
344
+ * Enable broadcast channel.
345
+ */
346
+ enableBroadcast?: boolean;
347
+ /**
348
+ * Enable Slack channel.
349
+ */
350
+ enableSlack?: boolean;
351
+ /**
352
+ * Enable SMS channel.
353
+ */
354
+ enableSms?: boolean;
355
+ /**
356
+ * Custom channel configuration.
357
+ */
358
+ channels?: Record<string, unknown>;
359
+ }
360
+ /**
361
+ * Notifications Orbit
362
+ *
363
+ * Provides notifications with multiple channels (mail, database, broadcast, Slack, SMS).
364
+ */
365
+ declare class OrbitFlare implements GravitoOrbit {
366
+ private options;
367
+ constructor(options?: OrbitFlareOptions);
368
+ /**
369
+ * Configure OrbitFlare.
370
+ *
371
+ * @param options - The OrbitFlare configuration options.
372
+ * @returns A new OrbitFlare instance.
373
+ */
374
+ static configure(options?: OrbitFlareOptions): OrbitFlare;
375
+ /**
376
+ * Install OrbitFlare into PlanetCore.
377
+ *
378
+ * @param core - The PlanetCore instance.
379
+ */
380
+ install(core: PlanetCore): Promise<void>;
381
+ }
382
+
383
+ export { BroadcastChannel, type BroadcastNotification, DatabaseChannel, type DatabaseNotification, MailChannel, type MailMessage, type Notifiable, Notification, type NotificationChannel, NotificationManager, OrbitFlare, type OrbitFlareOptions, type ShouldQueue, SlackChannel, type SlackChannelConfig, type SlackMessage, SmsChannel, type SmsChannelConfig, type SmsMessage };
@@ -1,6 +1,5 @@
1
1
  // src/channels/BroadcastChannel.ts
2
- class BroadcastChannel {
3
- broadcastService;
2
+ var BroadcastChannel = class {
4
3
  constructor(broadcastService) {
5
4
  this.broadcastService = broadcastService;
6
5
  }
@@ -12,12 +11,16 @@ class BroadcastChannel {
12
11
  const notifiableId = notifiable.getNotifiableId();
13
12
  const notifiableType = notifiable.getNotifiableType?.() || "user";
14
13
  const channel = `private-${notifiableType}.${notifiableId}`;
15
- await this.broadcastService.broadcast(channel, broadcastNotification.type, broadcastNotification.data);
14
+ await this.broadcastService.broadcast(
15
+ channel,
16
+ broadcastNotification.type,
17
+ broadcastNotification.data
18
+ );
16
19
  }
17
- }
20
+ };
21
+
18
22
  // src/channels/DatabaseChannel.ts
19
- class DatabaseChannel {
20
- dbService;
23
+ var DatabaseChannel = class {
21
24
  constructor(dbService) {
22
25
  this.dbService = dbService;
23
26
  }
@@ -33,10 +36,10 @@ class DatabaseChannel {
33
36
  data: dbNotification.data
34
37
  });
35
38
  }
36
- }
39
+ };
40
+
37
41
  // src/channels/MailChannel.ts
38
- class MailChannel {
39
- mailService;
42
+ var MailChannel = class {
40
43
  constructor(mailService) {
41
44
  this.mailService = mailService;
42
45
  }
@@ -47,10 +50,10 @@ class MailChannel {
47
50
  const message = notification.toMail(notifiable);
48
51
  await this.mailService.send(message);
49
52
  }
50
- }
53
+ };
54
+
51
55
  // src/channels/SlackChannel.ts
52
- class SlackChannel {
53
- config;
56
+ var SlackChannel = class {
54
57
  constructor(config) {
55
58
  this.config = config;
56
59
  }
@@ -76,10 +79,10 @@ class SlackChannel {
76
79
  throw new Error(`Failed to send Slack notification: ${response.statusText}`);
77
80
  }
78
81
  }
79
- }
82
+ };
83
+
80
84
  // src/channels/SmsChannel.ts
81
- class SmsChannel {
82
- config;
85
+ var SmsChannel = class {
83
86
  constructor(config) {
84
87
  this.config = config;
85
88
  }
@@ -99,53 +102,89 @@ class SmsChannel {
99
102
  throw new Error(`Unsupported SMS provider: ${this.config.provider}`);
100
103
  }
101
104
  }
105
+ /**
106
+ * Send SMS via Twilio.
107
+ */
102
108
  async sendViaTwilio(message) {
103
109
  if (!this.config.apiKey || !this.config.apiSecret) {
104
110
  throw new Error("Twilio API key and secret are required");
105
111
  }
106
112
  const accountSid = this.config.apiKey;
107
113
  const authToken = this.config.apiSecret;
108
- const response = await fetch(`https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json`, {
109
- method: "POST",
110
- headers: {
111
- Authorization: `Basic ${btoa(`${accountSid}:${authToken}`)}`,
112
- "Content-Type": "application/x-www-form-urlencoded"
113
- },
114
- body: new URLSearchParams({
115
- From: this.config.from || "",
116
- To: message.to,
117
- Body: message.message
118
- })
119
- });
114
+ const response = await fetch(
115
+ `https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json`,
116
+ {
117
+ method: "POST",
118
+ headers: {
119
+ Authorization: `Basic ${btoa(`${accountSid}:${authToken}`)}`,
120
+ "Content-Type": "application/x-www-form-urlencoded"
121
+ },
122
+ body: new URLSearchParams({
123
+ From: this.config.from || "",
124
+ To: message.to,
125
+ Body: message.message
126
+ })
127
+ }
128
+ );
120
129
  if (!response.ok) {
121
130
  const error = await response.text();
122
131
  throw new Error(`Failed to send SMS via Twilio: ${error}`);
123
132
  }
124
133
  }
134
+ /**
135
+ * Send SMS via AWS SNS.
136
+ */
125
137
  async sendViaAwsSns(_message) {
126
138
  throw new Error("AWS SNS SMS provider not yet implemented. Please install @aws-sdk/client-sns");
127
139
  }
128
- }
140
+ };
141
+
129
142
  // src/Notification.ts
130
- class Notification {
143
+ var Notification = class {
144
+ /**
145
+ * Get mail message (optional).
146
+ * Implement this if the notification will be sent via the mail channel.
147
+ */
131
148
  toMail(_notifiable) {
132
149
  throw new Error("toMail method not implemented");
133
150
  }
151
+ /**
152
+ * Get database notification (optional).
153
+ * Implement this if the notification will be stored via the database channel.
154
+ */
134
155
  toDatabase(_notifiable) {
135
156
  throw new Error("toDatabase method not implemented");
136
157
  }
158
+ /**
159
+ * Get broadcast notification (optional).
160
+ * Implement this if the notification will be sent via the broadcast channel.
161
+ */
137
162
  toBroadcast(_notifiable) {
138
163
  throw new Error("toBroadcast method not implemented");
139
164
  }
165
+ /**
166
+ * Get Slack message (optional).
167
+ * Implement this if the notification will be sent via the Slack channel.
168
+ */
140
169
  toSlack(_notifiable) {
141
170
  throw new Error("toSlack method not implemented");
142
171
  }
172
+ /**
173
+ * Get SMS message (optional).
174
+ * Implement this if the notification will be sent via the SMS channel.
175
+ */
143
176
  toSms(_notifiable) {
144
177
  throw new Error("toSms method not implemented");
145
178
  }
179
+ /**
180
+ * Check whether this notification should be queued.
181
+ */
146
182
  shouldQueue() {
147
183
  return "queue" in this || "connection" in this || "delay" in this;
148
184
  }
185
+ /**
186
+ * Get queue configuration.
187
+ */
149
188
  getQueueConfig() {
150
189
  if (this.shouldQueue()) {
151
190
  const queueable = this;
@@ -157,21 +196,50 @@ class Notification {
157
196
  }
158
197
  return {};
159
198
  }
160
- }
199
+ };
200
+
161
201
  // src/NotificationManager.ts
162
- class NotificationManager {
163
- core;
164
- channels = new Map;
165
- queueManager;
202
+ var NotificationManager = class {
166
203
  constructor(core) {
167
204
  this.core = core;
168
205
  }
206
+ /**
207
+ * Channel registry.
208
+ */
209
+ channels = /* @__PURE__ */ new Map();
210
+ /**
211
+ * Queue manager (optional, injected by `orbit-queue`).
212
+ */
213
+ queueManager;
214
+ /**
215
+ * Register a notification channel.
216
+ *
217
+ * @param name - The name of the channel.
218
+ * @param channel - The channel instance.
219
+ */
169
220
  channel(name, channel) {
170
221
  this.channels.set(name, channel);
171
222
  }
223
+ /**
224
+ * Register the queue manager (called by `orbit-queue`).
225
+ *
226
+ * @param manager - The queue manager implementation.
227
+ */
172
228
  setQueueManager(manager) {
173
229
  this.queueManager = manager;
174
230
  }
231
+ /**
232
+ * Send a notification.
233
+ *
234
+ * @param notifiable - The recipient of the notification.
235
+ * @param notification - The notification instance.
236
+ * @returns A promise that resolves when the notification is sent or queued.
237
+ *
238
+ * @example
239
+ * ```typescript
240
+ * await notificationManager.send(user, new InvoicePaid(invoice))
241
+ * ```
242
+ */
175
243
  async send(notifiable, notification) {
176
244
  const channels = notification.via(notifiable);
177
245
  if (notification.shouldQueue() && this.queueManager) {
@@ -187,11 +255,23 @@ class NotificationManager {
187
255
  await this.sendNow(notifiable, notification, channels);
188
256
  }
189
257
  };
190
- await this.queueManager.push(queueJob, queueConfig.queue, queueConfig.connection, queueConfig.delay);
258
+ await this.queueManager.push(
259
+ queueJob,
260
+ queueConfig.queue,
261
+ queueConfig.connection,
262
+ queueConfig.delay
263
+ );
191
264
  return;
192
265
  }
193
266
  await this.sendNow(notifiable, notification, channels);
194
267
  }
268
+ /**
269
+ * Send immediately (without queue).
270
+ *
271
+ * @param notifiable - The recipient.
272
+ * @param notification - The notification.
273
+ * @param channels - The list of channels to send via.
274
+ */
195
275
  async sendNow(notifiable, notification, channels) {
196
276
  for (const channelName of channels) {
197
277
  const channel = this.channels.get(channelName);
@@ -202,10 +282,19 @@ class NotificationManager {
202
282
  try {
203
283
  await channel.send(notification, notifiable);
204
284
  } catch (error) {
205
- this.core.logger.error(`[NotificationManager] Failed to send notification via '${channelName}':`, error);
285
+ this.core.logger.error(
286
+ `[NotificationManager] Failed to send notification via '${channelName}':`,
287
+ error
288
+ );
206
289
  }
207
290
  }
208
291
  }
292
+ /**
293
+ * Serialize notification (for queuing).
294
+ *
295
+ * @param notification - The notification to serialize.
296
+ * @returns A plain object representation of the notification.
297
+ */
209
298
  serializeNotification(notification) {
210
299
  const data = {};
211
300
  for (const [key, value] of Object.entries(notification)) {
@@ -215,9 +304,10 @@ class NotificationManager {
215
304
  }
216
305
  return data;
217
306
  }
218
- }
307
+ };
308
+
219
309
  // src/OrbitFlare.ts
220
- class OrbitFlare {
310
+ var OrbitFlare = class _OrbitFlare {
221
311
  options;
222
312
  constructor(options = {}) {
223
313
  this.options = {
@@ -229,9 +319,20 @@ class OrbitFlare {
229
319
  ...options
230
320
  };
231
321
  }
322
+ /**
323
+ * Configure OrbitFlare.
324
+ *
325
+ * @param options - The OrbitFlare configuration options.
326
+ * @returns A new OrbitFlare instance.
327
+ */
232
328
  static configure(options = {}) {
233
- return new OrbitFlare(options);
329
+ return new _OrbitFlare(options);
234
330
  }
331
+ /**
332
+ * Install OrbitFlare into PlanetCore.
333
+ *
334
+ * @param core - The PlanetCore instance.
335
+ */
235
336
  async install(core) {
236
337
  const manager = new NotificationManager(core);
237
338
  if (this.options.enableMail) {
@@ -285,14 +386,14 @@ class OrbitFlare {
285
386
  }
286
387
  core.logger.info("[OrbitFlare] Installed");
287
388
  }
288
- }
389
+ };
289
390
  export {
290
- SmsChannel,
291
- SlackChannel,
292
- OrbitFlare,
293
- NotificationManager,
294
- Notification,
295
- MailChannel,
391
+ BroadcastChannel,
296
392
  DatabaseChannel,
297
- BroadcastChannel
393
+ MailChannel,
394
+ Notification,
395
+ NotificationManager,
396
+ OrbitFlare,
397
+ SlackChannel,
398
+ SmsChannel
298
399
  };