@gravito/signal 3.0.3 → 3.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.
- package/CHANGELOG.md +16 -0
- package/README.md +99 -59
- package/README.zh-TW.md +140 -9
- package/build.ts +133 -0
- package/dist/Mailable.d.ts +453 -0
- package/dist/OrbitSignal.d.ts +267 -0
- package/dist/Queueable.d.ts +9 -0
- package/dist/TypedMailable.d.ts +95 -0
- package/dist/dev/DevMailbox.d.ts +79 -0
- package/dist/dev/DevServer.d.ts +39 -0
- package/dist/dev/storage/FileMailboxStorage.d.ts +16 -0
- package/dist/dev/storage/MailboxStorage.d.ts +14 -0
- package/dist/dev/storage/MemoryMailboxStorage.d.ts +13 -0
- package/dist/dev/ui/mailbox.d.ts +11 -0
- package/dist/dev/ui/preview.d.ts +11 -0
- package/dist/dev/ui/shared.d.ts +15 -0
- package/dist/errors.d.ts +58 -0
- package/dist/events.d.ts +63 -0
- package/dist/index.cjs +88291 -0
- package/dist/index.cjs.map +712 -0
- package/dist/index.d.ts +31 -643
- package/dist/index.mjs +87748 -335
- package/dist/index.mjs.map +710 -0
- package/dist/renderers/HtmlRenderer.d.ts +34 -0
- package/dist/renderers/MjmlRenderer.d.ts +41 -0
- package/dist/renderers/ReactMjmlRenderer.d.ts +38 -0
- package/dist/renderers/ReactRenderer.d.ts +46 -0
- package/dist/renderers/Renderer.d.ts +66 -0
- package/dist/renderers/TemplateRenderer.d.ts +55 -0
- package/dist/renderers/VueMjmlRenderer.d.ts +39 -0
- package/dist/renderers/VueRenderer.d.ts +47 -0
- package/dist/renderers/mjml-templates.d.ts +11 -0
- package/dist/transports/BaseTransport.d.ts +111 -0
- package/dist/transports/LogTransport.d.ts +42 -0
- package/dist/transports/MemoryTransport.d.ts +51 -0
- package/dist/transports/SesTransport.d.ts +79 -0
- package/dist/transports/SmtpTransport.d.ts +124 -0
- package/dist/transports/Transport.d.ts +44 -0
- package/dist/types.d.ts +294 -0
- package/dist/utils/html.d.ts +29 -0
- package/dist/webhooks/SendGridWebhookDriver.d.ts +45 -0
- package/dist/webhooks/SesWebhookDriver.d.ts +23 -0
- package/doc/ADVANCED_RENDERING.md +71 -0
- package/doc/DISTRIBUTED_MESSAGING.md +79 -0
- package/doc/OPTIMIZATION_PLAN.md +496 -0
- package/package.json +14 -11
- package/package.json.bak +75 -0
- package/scripts/check-coverage.ts +64 -0
- package/src/Mailable.ts +340 -44
- package/src/OrbitSignal.ts +350 -50
- package/src/TypedMailable.ts +96 -0
- package/src/dev/DevMailbox.ts +89 -33
- package/src/dev/DevServer.ts +14 -14
- package/src/dev/storage/FileMailboxStorage.ts +66 -0
- package/src/dev/storage/MailboxStorage.ts +15 -0
- package/src/dev/storage/MemoryMailboxStorage.ts +36 -0
- package/src/dev/ui/mailbox.ts +1 -1
- package/src/dev/ui/preview.ts +4 -4
- package/src/errors.ts +69 -0
- package/src/events.ts +72 -0
- package/src/index.ts +20 -1
- package/src/renderers/HtmlRenderer.ts +20 -18
- package/src/renderers/MjmlRenderer.ts +73 -0
- package/src/renderers/ReactMjmlRenderer.ts +94 -0
- package/src/renderers/ReactRenderer.ts +26 -21
- package/src/renderers/Renderer.ts +43 -3
- package/src/renderers/TemplateRenderer.ts +48 -15
- package/src/renderers/VueMjmlRenderer.ts +99 -0
- package/src/renderers/VueRenderer.ts +26 -21
- package/src/renderers/mjml-templates.ts +50 -0
- package/src/transports/BaseTransport.ts +148 -0
- package/src/transports/LogTransport.ts +28 -6
- package/src/transports/MemoryTransport.ts +34 -6
- package/src/transports/SesTransport.ts +62 -17
- package/src/transports/SmtpTransport.ts +123 -27
- package/src/transports/Transport.ts +33 -4
- package/src/types.ts +172 -3
- package/src/utils/html.ts +43 -0
- package/src/webhooks/SendGridWebhookDriver.ts +80 -0
- package/src/webhooks/SesWebhookDriver.ts +44 -0
- package/tests/DevMailbox.test.ts +54 -0
- package/tests/FileMailboxStorage.test.ts +56 -0
- package/tests/MjmlLayout.test.ts +28 -0
- package/tests/MjmlRenderer.test.ts +53 -0
- package/tests/OrbitSignalWebhook.test.ts +56 -0
- package/tests/ReactMjmlRenderer.test.ts +33 -0
- package/tests/SendGridWebhookDriver.test.ts +69 -0
- package/tests/SesWebhookDriver.test.ts +46 -0
- package/tests/VueMjmlRenderer.test.ts +35 -0
- package/tests/dev-server.test.ts +1 -1
- package/tests/transports.test.ts +3 -3
- package/tsconfig.build.json +24 -0
- package/tsconfig.json +8 -25
- package/dist/OrbitMail-2Z7ZTKYA.mjs +0 -7
- package/dist/OrbitMail-BGV32HWN.mjs +0 -7
- package/dist/OrbitMail-FUYZQSAV.mjs +0 -7
- package/dist/OrbitMail-NAPCRK7B.mjs +0 -7
- package/dist/OrbitMail-REGJ276B.mjs +0 -7
- package/dist/OrbitMail-TCFBJWDT.mjs +0 -7
- package/dist/OrbitMail-XZZW6U4N.mjs +0 -7
- package/dist/OrbitSignal-IPSA2CDO.mjs +0 -7
- package/dist/OrbitSignal-MABW4DDW.mjs +0 -7
- package/dist/OrbitSignal-QSW5VQ5M.mjs +0 -7
- package/dist/OrbitSignal-R22QHWAA.mjs +0 -7
- package/dist/OrbitSignal-ZKKMEC27.mjs +0 -7
- package/dist/ReactRenderer-24SQ4KRU.mjs +0 -27
- package/dist/ReactRenderer-CMCAOEPH.mjs +0 -28
- package/dist/ReactRenderer-KYNA4WKE.mjs +0 -28
- package/dist/ReactRenderer-L5INVYKT.mjs +0 -27
- package/dist/VueRenderer-DWTCD2RF.mjs +0 -31
- package/dist/VueRenderer-IIR5SYTM.mjs +0 -31
- package/dist/VueRenderer-S65ZARRI.mjs +0 -37129
- package/dist/VueRenderer-SUP66ISX.mjs +0 -29
- package/dist/VueRenderer-Z5PRVBNH.mjs +0 -37298
- package/dist/chunk-3U2CYJO5.mjs +0 -367
- package/dist/chunk-3XFC4T6M.mjs +0 -392
- package/dist/chunk-456QRYFW.mjs +0 -401
- package/dist/chunk-6DZX6EAA.mjs +0 -37
- package/dist/chunk-DT3R2TNV.mjs +0 -367
- package/dist/chunk-EBO3CZXG.mjs +0 -15
- package/dist/chunk-F6MVTUCT.mjs +0 -421
- package/dist/chunk-GADWIVC4.mjs +0 -400
- package/dist/chunk-HHKFAMSE.mjs +0 -380
- package/dist/chunk-NEQCQSZI.mjs +0 -406
- package/dist/chunk-OKRNL6PN.mjs +0 -400
- package/dist/chunk-ULN3GMY2.mjs +0 -367
- package/dist/chunk-XAWO7RSP.mjs +0 -398
- package/dist/chunk-YLVDJSED.mjs +0 -431
- package/dist/index.d.mts +0 -644
- package/dist/index.js +0 -38251
- package/dist/server-renderer-4W4FI7YG.mjs +0 -37269
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @gravito/signal
|
|
2
2
|
|
|
3
|
+
## 3.0.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Convert all workspace:\* dependencies to version numbers for npm publishing
|
|
8
|
+
|
|
9
|
+
- Fixed 144 workspace:\* dependencies across 58 packages
|
|
10
|
+
- Ensures all packages work properly when installed from npm
|
|
11
|
+
- Resolves issues with bunx and npm installation of CLI tools
|
|
12
|
+
- All internal dependencies now use explicit version constraints
|
|
13
|
+
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
- @gravito/core@1.6.1
|
|
16
|
+
- @gravito/prism@3.1.1
|
|
17
|
+
- @gravito/stream@2.0.2
|
|
18
|
+
|
|
3
19
|
## 3.0.3
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,42 +1,69 @@
|
|
|
1
|
+
# @gravito/signal 🛰️
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
`@gravito/signal` is the powerful, multi-driver email framework for the Gravito ecosystem. It provides a clean, fluent API for building and sending emails with support for multiple rendering engines and transport drivers.
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
## ✨ Features
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
- 🚀 **Fluent API**: Expressive `Mailable` classes for building complex email messages with zero friction.
|
|
8
|
+
- 🌌 **Galaxy-Ready**: A native Gravito Orbit that integrates seamlessly into the IoC container and context.
|
|
9
|
+
- 🔌 **Multi-Driver Transport**: Support for SMTP, AWS SES, SendGrid, Log, and Memory drivers.
|
|
10
|
+
- 🎨 **Modern Rendering**: Design emails using **React**, **Vue**, **MJML**, or **Prism** templates.
|
|
11
|
+
- 📬 **Dev Mailbox UI**: Built-in visual interface at `/__mail` for local email inspection and debugging.
|
|
12
|
+
- ⚙️ **Distributed Queueing**: Automatic offloading to `@gravito/stream` for background delivery.
|
|
13
|
+
- 🛡️ **Webhook Processing**: Handle inbound delivery events (bounces, clicks) with ease.
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
- **Content Renderers**:
|
|
10
|
-
- Raw HTML
|
|
11
|
-
- OrbitPrism Templates
|
|
12
|
-
- React Components (lazy-loaded)
|
|
13
|
-
- Vue Components (lazy-loaded)
|
|
14
|
-
- **Development Mode**: Intercept emails and view them in a built-in UI (`/__mail`).
|
|
15
|
-
- **Flexible API**: Fluent interface for building emails.
|
|
16
|
-
- **Queue Support**: Built-in `Queueable` interface for async sending.
|
|
15
|
+
## 🌌 Role in Galaxy Architecture
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
In the **Gravito Galaxy Architecture**, Signal serves as the **Communication Nervous System**.
|
|
18
|
+
|
|
19
|
+
- **Outbound Signal**: Satellites (domain plugins) trigger `Signal` to communicate with the outside world (users, other systems).
|
|
20
|
+
- **Inbound Feedback**: Signal processes Webhooks from providers to update the state of the Galaxy (e.g., marking an email as invalid in the `Membership` Satellite).
|
|
21
|
+
- **Event-Driven**: Leverages `@gravito/core`'s lifecycle to ensure emails are sent only after transactions are committed.
|
|
22
|
+
|
|
23
|
+
```mermaid
|
|
24
|
+
graph LR
|
|
25
|
+
S[Satellite] -->|Trigger| Signal[Signal Orbit]
|
|
26
|
+
Signal -->|Render| MW[MJML/React/Vue]
|
|
27
|
+
MW -->|Send| Provider[SES/SMTP]
|
|
28
|
+
Provider -.->|Webhook| Signal
|
|
29
|
+
Signal -.->|Event| S
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 📦 Installation
|
|
19
33
|
|
|
20
34
|
```bash
|
|
21
35
|
bun add @gravito/signal
|
|
22
36
|
```
|
|
23
37
|
|
|
24
|
-
|
|
38
|
+
### Optional Dependencies
|
|
39
|
+
|
|
40
|
+
Depending on your transport or renderer choice, you may need additional packages:
|
|
41
|
+
|
|
25
42
|
```bash
|
|
43
|
+
# For AWS SES
|
|
26
44
|
bun add @aws-sdk/client-ses
|
|
45
|
+
|
|
46
|
+
# For React components
|
|
47
|
+
bun add react react-dom
|
|
48
|
+
|
|
49
|
+
# For Vue components
|
|
50
|
+
bun add vue @vue/server-renderer
|
|
27
51
|
```
|
|
28
52
|
|
|
29
|
-
##
|
|
53
|
+
## 🚀 Quick Start
|
|
30
54
|
|
|
31
|
-
###
|
|
55
|
+
### 1. Configure the Orbit
|
|
32
56
|
|
|
33
|
-
|
|
57
|
+
Register `OrbitSignal` in your Gravito application:
|
|
34
58
|
|
|
35
59
|
```typescript
|
|
60
|
+
import { PlanetCore } from '@gravito/core';
|
|
36
61
|
import { OrbitSignal, SmtpTransport } from '@gravito/signal';
|
|
37
62
|
|
|
38
|
-
const
|
|
39
|
-
|
|
63
|
+
const core = new PlanetCore();
|
|
64
|
+
|
|
65
|
+
const mail = new OrbitSignal({
|
|
66
|
+
from: { name: 'Gravito Support', address: 'support@example.com' },
|
|
40
67
|
transport: new SmtpTransport({
|
|
41
68
|
host: 'smtp.mailtrap.io',
|
|
42
69
|
port: 2525,
|
|
@@ -45,19 +72,18 @@ const mail = OrbitSignal.configure({
|
|
|
45
72
|
devMode: process.env.NODE_ENV === 'development',
|
|
46
73
|
});
|
|
47
74
|
|
|
48
|
-
// Install into PlanetCore
|
|
49
75
|
mail.install(core);
|
|
50
76
|
```
|
|
51
77
|
|
|
52
|
-
###
|
|
78
|
+
### 2. Create a Mailable
|
|
53
79
|
|
|
54
|
-
Extend the `Mailable` class to
|
|
80
|
+
Extend the `Mailable` class to define your email:
|
|
55
81
|
|
|
56
82
|
```typescript
|
|
57
83
|
import { Mailable } from '@gravito/signal';
|
|
58
84
|
|
|
59
85
|
export class WelcomeEmail extends Mailable {
|
|
60
|
-
constructor(private user:
|
|
86
|
+
constructor(private user: { name: string; email: string }) {
|
|
61
87
|
super();
|
|
62
88
|
}
|
|
63
89
|
|
|
@@ -70,60 +96,74 @@ export class WelcomeEmail extends Mailable {
|
|
|
70
96
|
}
|
|
71
97
|
```
|
|
72
98
|
|
|
73
|
-
###
|
|
99
|
+
### 3. Send the Email
|
|
74
100
|
|
|
75
|
-
|
|
76
|
-
import { WelcomeEmail } from './mail/WelcomeEmail';
|
|
101
|
+
Access the mail service via the Gravito context:
|
|
77
102
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
103
|
+
```typescript
|
|
104
|
+
// In your route handler
|
|
105
|
+
const mail = c.get('mail');
|
|
106
|
+
await mail.send(new WelcomeEmail(user));
|
|
81
107
|
```
|
|
82
108
|
|
|
83
|
-
|
|
109
|
+
## 🛠️ Advanced Usage
|
|
84
110
|
|
|
85
|
-
|
|
111
|
+
### React & Vue Rendering
|
|
112
|
+
|
|
113
|
+
You can use modern frontend frameworks to design your emails:
|
|
86
114
|
|
|
87
115
|
```typescript
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
116
|
+
// React example
|
|
117
|
+
export class MonthlyReport extends Mailable {
|
|
118
|
+
build() {
|
|
119
|
+
return this
|
|
120
|
+
.subject('Your Monthly Report')
|
|
121
|
+
.react(ReportComponent, { data: this.data });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
91
124
|
|
|
92
|
-
//
|
|
93
|
-
|
|
125
|
+
// Vue example
|
|
126
|
+
export class InvoiceEmail extends Mailable {
|
|
127
|
+
build() {
|
|
128
|
+
return this
|
|
129
|
+
.subject('Invoice #12345')
|
|
130
|
+
.vue(InvoiceTemplate, { total: 100 });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
94
133
|
```
|
|
95
134
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
### SMTP
|
|
99
|
-
Standard SMTP transport using `nodemailer`.
|
|
135
|
+
### Queueing Emails
|
|
100
136
|
|
|
101
|
-
|
|
102
|
-
Send via Amazon SES API.
|
|
137
|
+
For better performance, send emails asynchronously:
|
|
103
138
|
|
|
104
139
|
```typescript
|
|
105
|
-
|
|
140
|
+
const email = new WelcomeEmail(user)
|
|
141
|
+
.onQueue('notifications')
|
|
142
|
+
.delay(60); // Send after 60 seconds
|
|
106
143
|
|
|
107
|
-
|
|
108
|
-
region: 'us-east-1',
|
|
109
|
-
accessKeyId: '...', // Optional if using environment variables
|
|
110
|
-
secretAccessKey: '...'
|
|
111
|
-
});
|
|
144
|
+
await email.queue();
|
|
112
145
|
```
|
|
113
146
|
|
|
114
|
-
###
|
|
115
|
-
|
|
147
|
+
### Development Mailbox
|
|
148
|
+
|
|
149
|
+
When `devMode` is enabled, `OrbitSignal` intercepts all outgoing emails and stores them in memory. You can view them by navigating to `/__mail` (or your configured `devUiPrefix`) in your browser. This UI provides:
|
|
150
|
+
- A list of all intercepted emails.
|
|
151
|
+
- Preview of HTML and Plain Text content.
|
|
152
|
+
- Metadata view (Subject, To, From, etc.).
|
|
116
153
|
|
|
117
|
-
|
|
118
|
-
Stores emails in memory (used by Dev Mode).
|
|
154
|
+
## 🔧 Configuration Options
|
|
119
155
|
|
|
120
|
-
|
|
156
|
+
The `MailConfig` object supports the following options:
|
|
121
157
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
158
|
+
| Option | Type | Description |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| `from` | `Address` | Default sender address. |
|
|
161
|
+
| `transport` | `Transport` | The transport driver to use. |
|
|
162
|
+
| `devMode` | `boolean` | Enable/disable email interception. |
|
|
163
|
+
| `viewsDir` | `string` | Path to template directory. |
|
|
164
|
+
| `devUiPrefix`| `string` | URL prefix for Dev UI (default: `/__mail`). |
|
|
165
|
+
| `translator` | `Function` | I18n translation function. |
|
|
126
166
|
|
|
127
|
-
## License
|
|
167
|
+
## 📄 License
|
|
128
168
|
|
|
129
|
-
MIT
|
|
169
|
+
MIT © Carl Lee
|
package/README.zh-TW.md
CHANGED
|
@@ -1,27 +1,158 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @gravito/signal 🛰️
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@gravito/signal` 是 Gravito 生態系統中功能強大且支援多種驅動程式的電子郵件框架。它提供了一個簡潔、流暢的 API 來構建和發送電子郵件,並支援多種渲染引擎和傳輸驅動程式。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 🌟 特性
|
|
6
|
+
|
|
7
|
+
- **流暢的 API**:使用具表現力的 `Mailable` 類別來構建電子郵件內容。
|
|
8
|
+
- **多驅動傳輸**:支援 SMTP (Nodemailer)、AWS SES、Log (控制台日誌) 和 Memory (記憶體)。
|
|
9
|
+
- **靈活的渲染引擎**:支援使用以下方式渲染郵件內容:
|
|
10
|
+
- 原生 HTML 字串
|
|
11
|
+
- **Prism** (Edge 優化的模板引擎)
|
|
12
|
+
- **React** 組件 (透過 `react-dom/server`)
|
|
13
|
+
- **Vue** 組件 (透過 `@vue/server-renderer`)
|
|
14
|
+
- **開發體驗優化**:
|
|
15
|
+
- **開發模式 (Dev Mode)**:在本地開發時攔截電子郵件,而不實際發送。
|
|
16
|
+
- **信箱 UI (Mailbox UI)**:在開發期間透過 `/__mail` 存取攔截到的郵件預覽界面。
|
|
17
|
+
- **隊列整合**:內建支援透過 `@gravito/stream` 進行異步郵件發送。
|
|
18
|
+
- **國際化 (I18n)**:整合多語系支援,輕鬆發送在地化郵件。
|
|
19
|
+
- **類型安全**:完全使用 TypeScript 編寫,提供完善的配置與使用類型定義。
|
|
20
|
+
|
|
21
|
+
## 📦 安裝
|
|
6
22
|
|
|
7
23
|
```bash
|
|
8
24
|
bun add @gravito/signal
|
|
9
25
|
```
|
|
10
26
|
|
|
11
|
-
|
|
27
|
+
### 選用依賴
|
|
28
|
+
|
|
29
|
+
根據您選擇的傳輸方式或渲染引擎,您可能需要安裝額外的套件:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 若使用 AWS SES
|
|
33
|
+
bun add @aws-sdk/client-ses
|
|
34
|
+
|
|
35
|
+
# 若使用 React 組件
|
|
36
|
+
bun add react react-dom
|
|
37
|
+
|
|
38
|
+
# 若使用 Vue 組件
|
|
39
|
+
bun add vue @vue/server-renderer
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 🚀 快速上手
|
|
43
|
+
|
|
44
|
+
### 1. 配置 Orbit
|
|
45
|
+
|
|
46
|
+
在您的 Gravito 應用程式中註冊 `OrbitSignal`:
|
|
12
47
|
|
|
13
48
|
```typescript
|
|
14
|
-
import {
|
|
49
|
+
import { PlanetCore } from '@gravito/core';
|
|
50
|
+
import { OrbitSignal, SmtpTransport } from '@gravito/signal';
|
|
15
51
|
|
|
16
|
-
const
|
|
17
|
-
|
|
52
|
+
const core = new PlanetCore();
|
|
53
|
+
|
|
54
|
+
const mail = new OrbitSignal({
|
|
55
|
+
from: { name: 'Gravito 支援團隊', address: 'support@example.com' },
|
|
18
56
|
transport: new SmtpTransport({
|
|
19
57
|
host: 'smtp.mailtrap.io',
|
|
20
58
|
port: 2525,
|
|
21
59
|
auth: { user: '...', pass: '...' }
|
|
22
60
|
}),
|
|
23
61
|
devMode: process.env.NODE_ENV === 'development',
|
|
24
|
-
})
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
mail.install(core);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. 建立 Mailable
|
|
68
|
+
|
|
69
|
+
繼承 `Mailable` 類別來定義您的郵件內容:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { Mailable } from '@gravito/signal';
|
|
73
|
+
|
|
74
|
+
export class WelcomeEmail extends Mailable {
|
|
75
|
+
constructor(private user: { name: string; email: string }) {
|
|
76
|
+
super();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
build() {
|
|
80
|
+
return this
|
|
81
|
+
.to(this.user.email)
|
|
82
|
+
.subject('歡迎來到 Gravito!')
|
|
83
|
+
.view('emails/welcome', { name: this.user.name });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 3. 發送郵件
|
|
25
89
|
|
|
26
|
-
|
|
90
|
+
透過 Gravito Context 存取郵件服務:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// 在路由處理器中
|
|
94
|
+
const mail = c.get('mail');
|
|
95
|
+
await mail.send(new WelcomeEmail(user));
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 🛠️ 進階用法
|
|
99
|
+
|
|
100
|
+
### 使用 React 或 Vue 渲染
|
|
101
|
+
|
|
102
|
+
您可以使用現代前端框架來設計您的郵件:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// React 範例
|
|
106
|
+
export class MonthlyReport extends Mailable {
|
|
107
|
+
build() {
|
|
108
|
+
return this
|
|
109
|
+
.subject('您的每月報表')
|
|
110
|
+
.react(ReportComponent, { data: this.data });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Vue 範例
|
|
115
|
+
export class InvoiceEmail extends Mailable {
|
|
116
|
+
build() {
|
|
117
|
+
return this
|
|
118
|
+
.subject('發票單號 #12345')
|
|
119
|
+
.vue(InvoiceTemplate, { total: 100 });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 隊列發送 (Queueing)
|
|
125
|
+
|
|
126
|
+
為了提升應用程式效能,建議使用異步隊列發送郵件:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const email = new WelcomeEmail(user)
|
|
130
|
+
.onQueue('notifications')
|
|
131
|
+
.delay(60); // 60 秒後發送
|
|
132
|
+
|
|
133
|
+
await email.queue();
|
|
27
134
|
```
|
|
135
|
+
|
|
136
|
+
### 開發者信箱 (Dev Mailbox)
|
|
137
|
+
|
|
138
|
+
當啟用 `devMode` 時,`OrbitSignal` 會攔截所有外發郵件並將其存儲在記憶體中。您可以透過瀏覽器訪問 `/__mail` (或您配置的 `devUiPrefix`) 來查看。此界面提供:
|
|
139
|
+
- 所有已攔截郵件的列表。
|
|
140
|
+
- HTML 與純文字內容預覽。
|
|
141
|
+
- 中繼資料查看 (主旨、收件者、寄件者等)。
|
|
142
|
+
|
|
143
|
+
## 🔧 配置選項
|
|
144
|
+
|
|
145
|
+
`MailConfig` 物件支援以下選項:
|
|
146
|
+
|
|
147
|
+
| 選項 | 類型 | 描述 |
|
|
148
|
+
|---|---|---|
|
|
149
|
+
| `from` | `Address` | 預設寄件者地址。 |
|
|
150
|
+
| `transport` | `Transport` | 使用的傳輸驅動程式。 |
|
|
151
|
+
| `devMode` | `boolean` | 是否啟用郵件攔截開發模式。 |
|
|
152
|
+
| `viewsDir` | `string` | 模板檔案存放目錄。 |
|
|
153
|
+
| `devUiPrefix`| `string` | 開發界面 URL 前綴 (預設: `/__mail`)。 |
|
|
154
|
+
| `translator` | `Function` | I18n 翻譯函數。 |
|
|
155
|
+
|
|
156
|
+
## 📄 授權
|
|
157
|
+
|
|
158
|
+
MIT © Carl Lee
|
package/build.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { cp, type Dirent, mkdir, readdir, rm } from 'node:fs/promises'
|
|
2
|
+
import { basename } from 'node:path'
|
|
3
|
+
import { build } from 'bun'
|
|
4
|
+
|
|
5
|
+
const isDtsOnly = process.argv.includes('--dts-only')
|
|
6
|
+
const pkgName = basename(import.meta.dirname) // "signal"
|
|
7
|
+
|
|
8
|
+
console.log(
|
|
9
|
+
isDtsOnly ? 'Building @gravito/signal DTS...' : 'Building @gravito/signal in parallel...'
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// Clean dist
|
|
13
|
+
await rm('dist', { recursive: true, force: true })
|
|
14
|
+
|
|
15
|
+
// External dependencies(peer deps + framework 包)
|
|
16
|
+
const externalDeps = [
|
|
17
|
+
'@gravito/core',
|
|
18
|
+
'@gravito/stream',
|
|
19
|
+
'@gravito/prism',
|
|
20
|
+
'react',
|
|
21
|
+
'react-dom',
|
|
22
|
+
'vue',
|
|
23
|
+
'@vue/server-renderer',
|
|
24
|
+
'nodemailer',
|
|
25
|
+
'@aws-sdk/client-ses',
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
async function buildInParallel() {
|
|
29
|
+
const tasks: Promise<number>[] = []
|
|
30
|
+
const tempDir = isDtsOnly ? 'dist' : '.tsc-temp'
|
|
31
|
+
|
|
32
|
+
if (!isDtsOnly) {
|
|
33
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
34
|
+
|
|
35
|
+
// Task 1: bun build ESM
|
|
36
|
+
const esmPromise = (async () => {
|
|
37
|
+
const buildResult = await build({
|
|
38
|
+
entrypoints: ['src/index.ts'],
|
|
39
|
+
outdir: 'dist',
|
|
40
|
+
format: 'esm',
|
|
41
|
+
target: 'node',
|
|
42
|
+
splitting: false,
|
|
43
|
+
sourcemap: 'external',
|
|
44
|
+
external: externalDeps,
|
|
45
|
+
naming: '[dir]/[name].mjs',
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
if (!buildResult.success) {
|
|
49
|
+
console.error('❌ ESM build failed:', buildResult.logs)
|
|
50
|
+
return 1
|
|
51
|
+
}
|
|
52
|
+
return 0
|
|
53
|
+
})()
|
|
54
|
+
tasks.push(esmPromise)
|
|
55
|
+
|
|
56
|
+
// Task 2: bun build CJS
|
|
57
|
+
const cjsPromise = (async () => {
|
|
58
|
+
const cjsResult = await build({
|
|
59
|
+
entrypoints: ['src/index.ts'],
|
|
60
|
+
outdir: 'dist',
|
|
61
|
+
format: 'cjs',
|
|
62
|
+
target: 'node',
|
|
63
|
+
splitting: false,
|
|
64
|
+
sourcemap: 'external',
|
|
65
|
+
naming: '[dir]/[name].cjs',
|
|
66
|
+
external: externalDeps,
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
if (!cjsResult.success) {
|
|
70
|
+
console.error('❌ CJS build failed:', cjsResult.logs)
|
|
71
|
+
return 1
|
|
72
|
+
}
|
|
73
|
+
return 0
|
|
74
|
+
})()
|
|
75
|
+
tasks.push(cjsPromise)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Task 3: tsc 生成型別宣告(使用 tsconfig.build.json + noCheck)
|
|
79
|
+
const tscPromise = (async () => {
|
|
80
|
+
const tsc = Bun.spawn(['bunx', 'tsc', '-p', 'tsconfig.build.json', '--outDir', tempDir], {
|
|
81
|
+
stdout: 'inherit',
|
|
82
|
+
stderr: 'inherit',
|
|
83
|
+
cwd: import.meta.dirname,
|
|
84
|
+
})
|
|
85
|
+
return await tsc.exited
|
|
86
|
+
})()
|
|
87
|
+
tasks.push(tscPromise)
|
|
88
|
+
|
|
89
|
+
const results = await Promise.all(tasks)
|
|
90
|
+
for (const result of results) {
|
|
91
|
+
if (result !== 0) {
|
|
92
|
+
process.exit(1)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 遞迴複製 .d.ts 檔案
|
|
98
|
+
async function copyDtsFiles(src: string, dest: string) {
|
|
99
|
+
let entries: Dirent[]
|
|
100
|
+
try {
|
|
101
|
+
entries = await readdir(src, { withFileTypes: true })
|
|
102
|
+
} catch (_e) {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
for (const entry of entries) {
|
|
107
|
+
const srcPath = `${src}/${entry.name}`
|
|
108
|
+
const destPath = `${dest}/${entry.name}`
|
|
109
|
+
|
|
110
|
+
if (entry.isDirectory()) {
|
|
111
|
+
await mkdir(destPath, { recursive: true }).catch(() => {})
|
|
112
|
+
await copyDtsFiles(srcPath, destPath)
|
|
113
|
+
} else if (entry.isFile() && entry.name.endsWith('.d.ts')) {
|
|
114
|
+
await cp(srcPath, destPath)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await buildInParallel()
|
|
120
|
+
|
|
121
|
+
// 後處理:tsc 輸出到 .tsc-temp/{pkgName}/src/,需要移到 dist/
|
|
122
|
+
const tempDir = isDtsOnly ? 'dist' : '.tsc-temp'
|
|
123
|
+
if (!isDtsOnly) {
|
|
124
|
+
try {
|
|
125
|
+
const dtsSourceDir = `${tempDir}/${pkgName}/src`
|
|
126
|
+
await copyDtsFiles(dtsSourceDir, 'dist')
|
|
127
|
+
await rm(tempDir, { recursive: true, force: true })
|
|
128
|
+
} catch (e) {
|
|
129
|
+
console.warn('⚠️ Warning: Failed to copy type declarations:', e)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
console.log('✅ @gravito/signal build completed')
|