@gravito/flare 3.0.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -135
- package/README.zh-TW.md +126 -11
- package/dist/index.cjs +694 -92
- package/dist/index.d.cts +242 -17
- package/dist/index.d.ts +242 -17
- package/dist/index.js +680 -91
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
> Lightweight, high-performance notifications for Gravito with multi-channel delivery (mail, database, broadcast, Slack, SMS).
|
|
4
4
|
|
|
5
|
-
**Status**:
|
|
5
|
+
**Status**: v3.3.0 - Production ready with advanced features (Retries, Metrics, Batching).
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- **Zero runtime overhead**: Pure type wrappers that delegate to channel drivers
|
|
10
|
-
- **Multi-channel delivery**: Mail, database, broadcast, Slack, SMS
|
|
11
|
-
- **
|
|
10
|
+
- **Multi-channel delivery**: Mail, database, broadcast, Slack, SMS (Twilio & AWS SNS)
|
|
11
|
+
- **High Performance**: Parallel channel execution and batch sending capabilities
|
|
12
|
+
- **Reliability**: Built-in retry mechanism with exponential backoff
|
|
13
|
+
- **Observability**: Comprehensive metrics with Prometheus support
|
|
14
|
+
- **Developer Experience**: Strong typing, lifecycle hooks, and template system
|
|
12
15
|
- **Queue support**: Works with `@gravito/stream` for async delivery
|
|
13
|
-
- **AI-friendly**: Strong typing, clear JSDoc, and predictable APIs
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
|
@@ -20,208 +22,145 @@ bun add @gravito/flare
|
|
|
20
22
|
|
|
21
23
|
## Quick Start
|
|
22
24
|
|
|
23
|
-
### 1.
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
import { Notification } from '@gravito/flare'
|
|
27
|
-
import type { MailMessage, DatabaseNotification, Notifiable } from '@gravito/flare'
|
|
28
|
-
|
|
29
|
-
class InvoicePaid extends Notification {
|
|
30
|
-
constructor(private invoice: Invoice) {
|
|
31
|
-
super()
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
via(user: Notifiable): string[] {
|
|
35
|
-
return ['mail', 'database']
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
toMail(user: Notifiable): MailMessage {
|
|
39
|
-
return {
|
|
40
|
-
subject: 'Invoice Paid',
|
|
41
|
-
view: 'emails.invoice-paid',
|
|
42
|
-
data: { invoice: this.invoice },
|
|
43
|
-
to: user.email,
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
toDatabase(user: Notifiable): DatabaseNotification {
|
|
48
|
-
return {
|
|
49
|
-
type: 'invoice-paid',
|
|
50
|
-
data: {
|
|
51
|
-
invoice_id: this.invoice.id,
|
|
52
|
-
amount: this.invoice.amount,
|
|
53
|
-
},
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### 2. Configure OrbitFlare
|
|
25
|
+
### 1. Configure OrbitFlare
|
|
60
26
|
|
|
61
27
|
```typescript
|
|
62
28
|
import { PlanetCore } from '@gravito/core'
|
|
63
29
|
import { OrbitFlare } from '@gravito/flare'
|
|
64
|
-
import { OrbitSignal } from '@gravito/signal'
|
|
65
|
-
import { OrbitStream } from '@gravito/stream'
|
|
66
30
|
|
|
67
31
|
const core = await PlanetCore.boot({
|
|
68
32
|
orbits: [
|
|
69
|
-
OrbitSignal.configure({ /* ... */ }),
|
|
70
|
-
OrbitStream.configure({ /* ... */ }),
|
|
71
33
|
OrbitFlare.configure({
|
|
72
34
|
enableMail: true,
|
|
73
35
|
enableDatabase: true,
|
|
74
|
-
enableBroadcast: true,
|
|
75
36
|
channels: {
|
|
76
|
-
slack: {
|
|
77
|
-
|
|
78
|
-
|
|
37
|
+
slack: { webhookUrl: process.env.SLACK_WEBHOOK_URL },
|
|
38
|
+
sms: {
|
|
39
|
+
provider: 'aws-sns', // or 'twilio'
|
|
40
|
+
region: 'us-east-1'
|
|
41
|
+
}
|
|
79
42
|
},
|
|
43
|
+
// Optional: Global retry policy
|
|
44
|
+
retry: {
|
|
45
|
+
maxAttempts: 3,
|
|
46
|
+
backoff: 'exponential',
|
|
47
|
+
baseDelay: 1000
|
|
48
|
+
}
|
|
80
49
|
}),
|
|
81
50
|
],
|
|
82
51
|
})
|
|
83
52
|
```
|
|
84
53
|
|
|
85
|
-
###
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
// In a controller
|
|
89
|
-
const notifications = c.get('notifications') as NotificationManager
|
|
90
|
-
|
|
91
|
-
await notifications.send(user, new InvoicePaid(invoice))
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## Queueing Notifications
|
|
54
|
+
### 2. Create a notification
|
|
95
55
|
|
|
96
56
|
```typescript
|
|
97
|
-
import { Notification
|
|
57
|
+
import { Notification } from '@gravito/flare'
|
|
58
|
+
import type { MailMessage, Notifiable } from '@gravito/flare'
|
|
98
59
|
|
|
99
|
-
class
|
|
100
|
-
|
|
101
|
-
|
|
60
|
+
class WelcomeNotification extends Notification {
|
|
61
|
+
constructor(private name: string) {
|
|
62
|
+
super()
|
|
63
|
+
}
|
|
102
64
|
|
|
103
65
|
via(user: Notifiable): string[] {
|
|
104
|
-
return ['mail']
|
|
66
|
+
return ['mail', 'database']
|
|
105
67
|
}
|
|
106
68
|
|
|
107
69
|
toMail(user: Notifiable): MailMessage {
|
|
108
70
|
return {
|
|
109
71
|
subject: 'Welcome!',
|
|
110
|
-
to: user.email,
|
|
111
72
|
view: 'emails.welcome',
|
|
73
|
+
data: { name: this.name },
|
|
74
|
+
to: user.email,
|
|
112
75
|
}
|
|
113
76
|
}
|
|
114
|
-
}
|
|
115
77
|
|
|
116
|
-
|
|
78
|
+
// Define per-notification retry logic
|
|
79
|
+
retry = {
|
|
80
|
+
maxAttempts: 5,
|
|
81
|
+
backoff: 'linear' as const
|
|
82
|
+
}
|
|
83
|
+
}
|
|
117
84
|
```
|
|
118
85
|
|
|
119
|
-
|
|
86
|
+
### 3. Send a notification
|
|
120
87
|
|
|
121
|
-
|
|
88
|
+
```typescript
|
|
89
|
+
const notifications = c.get('notifications') as NotificationManager
|
|
122
90
|
|
|
123
|
-
|
|
91
|
+
// Simple send
|
|
92
|
+
const result = await notifications.send(user, new WelcomeNotification('Alice'))
|
|
124
93
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return ['mail']
|
|
94
|
+
if (result.failed.length > 0) {
|
|
95
|
+
console.error('Some channels failed:', result.failed)
|
|
128
96
|
}
|
|
129
97
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
subject: 'Subject',
|
|
133
|
-
view: 'emails.template',
|
|
134
|
-
data: { /* ... */ },
|
|
135
|
-
to: user.email,
|
|
136
|
-
}
|
|
137
|
-
}
|
|
98
|
+
// Batch send (high performance)
|
|
99
|
+
await notifications.sendBatch(users, new SystemUpdateNotification())
|
|
138
100
|
```
|
|
139
101
|
|
|
140
|
-
|
|
102
|
+
## Advanced Features
|
|
141
103
|
|
|
142
|
-
|
|
104
|
+
### Retries
|
|
105
|
+
Configure retries globally or per-notification:
|
|
143
106
|
|
|
144
107
|
```typescript
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
toDatabase(user: Notifiable): DatabaseNotification {
|
|
150
|
-
return {
|
|
151
|
-
type: 'notification-type',
|
|
152
|
-
data: { /* ... */ },
|
|
108
|
+
// Per-notification
|
|
109
|
+
class CriticalAlert extends Notification {
|
|
110
|
+
shouldRetry(attempt: number, error: Error): boolean {
|
|
111
|
+
return attempt < 5 && isRetryable(error)
|
|
153
112
|
}
|
|
154
113
|
}
|
|
155
114
|
```
|
|
156
115
|
|
|
157
|
-
###
|
|
158
|
-
|
|
159
|
-
Requires `@gravito/radiance`:
|
|
116
|
+
### Metrics
|
|
117
|
+
Enable metrics to track success rates and latency:
|
|
160
118
|
|
|
161
119
|
```typescript
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
120
|
+
const metrics = new NotificationMetricsCollector()
|
|
121
|
+
notifications.setMetricsCollector(metrics)
|
|
165
122
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
type: 'notification-type',
|
|
169
|
-
data: { /* ... */ },
|
|
170
|
-
}
|
|
171
|
-
}
|
|
123
|
+
// Export to Prometheus
|
|
124
|
+
const promData = toPrometheusFormat(metrics.getSummary())
|
|
172
125
|
```
|
|
173
126
|
|
|
174
|
-
###
|
|
127
|
+
### Hooks
|
|
128
|
+
Listen to lifecycle events:
|
|
175
129
|
|
|
176
130
|
```typescript
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
toSlack(user: Notifiable): SlackMessage {
|
|
182
|
-
return {
|
|
183
|
-
text: 'Notification message',
|
|
184
|
-
channel: '#notifications',
|
|
185
|
-
}
|
|
186
|
-
}
|
|
131
|
+
notifications.on('notification:failed', ({ notification, error }) => {
|
|
132
|
+
logger.error('Notification failed completely', error)
|
|
133
|
+
})
|
|
187
134
|
```
|
|
188
135
|
|
|
189
|
-
###
|
|
136
|
+
### Templates
|
|
137
|
+
Use `TemplatedNotification` for consistent messaging:
|
|
190
138
|
|
|
191
139
|
```typescript
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
toSms(user: Notifiable): SmsMessage {
|
|
197
|
-
return {
|
|
198
|
-
to: user.phone,
|
|
199
|
-
message: 'Notification message',
|
|
140
|
+
class OrderShipped extends TemplatedNotification {
|
|
141
|
+
constructor(order: Order) {
|
|
142
|
+
super('order-shipped', { orderId: order.id })
|
|
200
143
|
}
|
|
201
144
|
}
|
|
202
145
|
```
|
|
203
146
|
|
|
204
147
|
## API Reference
|
|
205
148
|
|
|
206
|
-
###
|
|
207
|
-
|
|
208
|
-
Every notification should extend `Notification`.
|
|
149
|
+
### NotificationManager
|
|
209
150
|
|
|
210
151
|
#### Methods
|
|
211
152
|
|
|
212
|
-
- `
|
|
213
|
-
- `
|
|
214
|
-
- `
|
|
215
|
-
- `toBroadcast(notifiable: Notifiable): BroadcastNotification` - Broadcast payload (optional)
|
|
216
|
-
- `toSlack(notifiable: Notifiable): SlackMessage` - Slack payload (optional)
|
|
217
|
-
- `toSms(notifiable: Notifiable): SmsMessage` - SMS payload (optional)
|
|
153
|
+
- `send(notifiable: Notifiable, notification: Notification, options?: SendOptions): Promise<NotificationResult>`
|
|
154
|
+
- `sendBatch(notifiables: Notifiable[], notification: Notification): Promise<BatchResult>`
|
|
155
|
+
- `sendBatchStream(iterator: AsyncIterator<Notifiable>, notification: Notification): Promise<BatchResult>`
|
|
218
156
|
|
|
219
|
-
###
|
|
157
|
+
### Notification
|
|
220
158
|
|
|
221
159
|
#### Methods
|
|
222
160
|
|
|
223
|
-
- `
|
|
224
|
-
- `
|
|
161
|
+
- `via(notifiable: Notifiable): string[]` - Choose delivery channels
|
|
162
|
+
- `toMail`, `toDatabase`, `toBroadcast`, `toSlack`, `toSms` - Channel payloads
|
|
163
|
+
- `shouldRetry(attempt: number, error: Error): boolean` - Custom retry logic
|
|
225
164
|
|
|
226
165
|
## License
|
|
227
166
|
|
package/README.zh-TW.md
CHANGED
|
@@ -1,35 +1,150 @@
|
|
|
1
|
-
# @gravito/flare
|
|
1
|
+
# @gravito/flare 🌌
|
|
2
2
|
|
|
3
|
-
> Gravito
|
|
3
|
+
> 輕量化、高效能的 Gravito 通知引擎,支援多通路發送(郵件、資料庫、廣播、Slack、SMS)。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`@gravito/flare` 是 Gravito 框架官方提供的通知引擎。它提供了一套簡潔且具表現力的 API,讓開發者能夠輕鬆地透過多種通路發送通知,並原生支援背景隊列處理。
|
|
6
|
+
|
|
7
|
+
**狀態**: v1.0.0 - 生產環境可用。
|
|
8
|
+
|
|
9
|
+
## 🌟 核心特性
|
|
10
|
+
|
|
11
|
+
- **零執行負擔 (Zero Runtime Overhead)**:純 TypeScript 實作,直接委派給高效的通路驅動程式。
|
|
12
|
+
- **多通路支援**:單一通知可同時透過多個通路(Mail, DB, Slack 等)發送。
|
|
13
|
+
- **背景隊列支援**:與 `@gravito/stream` (OrbitStream) 無縫整合,處理高流量通知發送而不阻塞請求。
|
|
14
|
+
- **類型安全 (Type-Safe)**:為每個通路提供嚴格的訊息結構定義,確保資料完整性。
|
|
15
|
+
- **易於擴充**:可輕鬆註冊自定義的通知通路。
|
|
16
|
+
- **Galaxy 架構相容**:設計為標準的 Gravito Orbit,支援零配置整合。
|
|
17
|
+
|
|
18
|
+
## 📦 安裝
|
|
6
19
|
|
|
7
20
|
```bash
|
|
8
21
|
bun add @gravito/flare
|
|
9
22
|
```
|
|
10
23
|
|
|
11
|
-
##
|
|
24
|
+
## 🚀 快速上手
|
|
25
|
+
|
|
26
|
+
### 1. 定義您的通知
|
|
27
|
+
|
|
28
|
+
建立一個繼承自 `Notification` 的類別。實作 `via` 方法來指定發送通路,以及實作 `to[Channel]` 方法來定義發送內容。
|
|
12
29
|
|
|
13
30
|
```typescript
|
|
14
31
|
import { Notification } from '@gravito/flare'
|
|
15
|
-
import type { MailMessage, Notifiable } from '@gravito/flare'
|
|
32
|
+
import type { MailMessage, DatabaseNotification, Notifiable } from '@gravito/flare'
|
|
16
33
|
|
|
17
|
-
class
|
|
34
|
+
class OrderShipped extends Notification {
|
|
35
|
+
constructor(private order: any) {
|
|
36
|
+
super()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 指定發送通路
|
|
18
40
|
via(user: Notifiable): string[] {
|
|
19
|
-
return ['mail']
|
|
41
|
+
return ['mail', 'database']
|
|
20
42
|
}
|
|
21
43
|
|
|
44
|
+
// 郵件內容
|
|
22
45
|
toMail(user: Notifiable): MailMessage {
|
|
23
46
|
return {
|
|
24
|
-
subject:
|
|
25
|
-
view: 'emails.
|
|
47
|
+
subject: `訂單 #${this.order.id} 已出貨!`,
|
|
48
|
+
view: 'emails.order-shipped',
|
|
49
|
+
data: { order: this.order },
|
|
26
50
|
to: user.email,
|
|
27
51
|
}
|
|
28
52
|
}
|
|
53
|
+
|
|
54
|
+
// 資料庫通知內容
|
|
55
|
+
toDatabase(user: Notifiable): DatabaseNotification {
|
|
56
|
+
return {
|
|
57
|
+
type: 'order_shipped',
|
|
58
|
+
data: {
|
|
59
|
+
order_id: this.order.id,
|
|
60
|
+
tracking_number: this.order.tracking,
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
}
|
|
29
64
|
}
|
|
30
65
|
```
|
|
31
66
|
|
|
67
|
+
### 2. 配置 OrbitFlare
|
|
68
|
+
|
|
69
|
+
在 `PlanetCore` 啟動程序中註冊 `OrbitFlare`。
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { PlanetCore } from '@gravito/core'
|
|
73
|
+
import { OrbitFlare } from '@gravito/flare'
|
|
74
|
+
|
|
75
|
+
const core = await PlanetCore.boot({
|
|
76
|
+
orbits: [
|
|
77
|
+
OrbitFlare.configure({
|
|
78
|
+
enableMail: true,
|
|
79
|
+
enableDatabase: true,
|
|
80
|
+
channels: {
|
|
81
|
+
slack: {
|
|
82
|
+
webhookUrl: process.env.SLACK_WEBHOOK_URL,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
],
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. 發送通知
|
|
91
|
+
|
|
92
|
+
透過核心容器或上下文變數取得 `notifications` 管理器。
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// 在業務邏輯或控制器中
|
|
96
|
+
const notifications = core.container.make('notifications')
|
|
97
|
+
|
|
98
|
+
await notifications.send(user, new OrderShipped(order))
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## ⏳ 非同步隊列
|
|
102
|
+
|
|
103
|
+
若要將通知放入背景發送,只需在通知類別中實作 `ShouldQueue` 介面。
|
|
104
|
+
|
|
32
105
|
```typescript
|
|
33
|
-
|
|
34
|
-
|
|
106
|
+
import { Notification, ShouldQueue } from '@gravito/flare'
|
|
107
|
+
|
|
108
|
+
class WeeklyReport extends Notification implements ShouldQueue {
|
|
109
|
+
queue = 'notifications' // 可選:指定隊列名稱
|
|
110
|
+
delay = 3600 // 可選:延遲秒數
|
|
111
|
+
|
|
112
|
+
via(user: Notifiable): string[] {
|
|
113
|
+
return ['mail']
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ... toMail 實作
|
|
117
|
+
}
|
|
35
118
|
```
|
|
119
|
+
|
|
120
|
+
## 🛠️ 支援的通路
|
|
121
|
+
|
|
122
|
+
| 通路 | 依賴模組 | 描述 |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| **Mail** | `@gravito/signal` | 透過配置的郵件驅動發送電子郵件。 |
|
|
125
|
+
| **Database** | `@gravito/atlas` | 將通知儲存至 `notifications` 資料表。 |
|
|
126
|
+
| **Broadcast**| `@gravito/radiance`| 透過 WebSockets 推送即時更新。 |
|
|
127
|
+
| **Slack** | 無 | 透過 Webhooks 發送訊息至 Slack。 |
|
|
128
|
+
| **SMS** | 供應商配置 | 透過配置的簡訊供應商發送簡訊。 |
|
|
129
|
+
|
|
130
|
+
## 🧩 API 參考
|
|
131
|
+
|
|
132
|
+
### `Notification` 基礎類別
|
|
133
|
+
- `via(notifiable)`:回傳通路名稱陣列。
|
|
134
|
+
- `toMail(notifiable)`:回傳 `MailMessage`。
|
|
135
|
+
- `toDatabase(notifiable)`:回傳 `DatabaseNotification`。
|
|
136
|
+
- `toBroadcast(notifiable)`:回傳 `BroadcastNotification`。
|
|
137
|
+
- `toSlack(notifiable)`:回傳 `SlackMessage`。
|
|
138
|
+
- `toSms(notifiable)`:回傳 `SmsMessage`。
|
|
139
|
+
|
|
140
|
+
### `NotificationManager`
|
|
141
|
+
- `send(notifiable, notification)`:將通知發送至所有指定的通路。
|
|
142
|
+
- `channel(name, implementation)`:註冊自定義的發送通路。
|
|
143
|
+
|
|
144
|
+
## 🤝 參與貢獻
|
|
145
|
+
|
|
146
|
+
我們歡迎任何形式的貢獻!詳細資訊請參閱 [貢獻指南](../../CONTRIBUTING.md)。
|
|
147
|
+
|
|
148
|
+
## 📄 開源授權
|
|
149
|
+
|
|
150
|
+
MIT © Carl Lee
|