@pearl-framework/mail 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.
- package/.turbo/turbo-build.log +5 -0
- package/LICENSE +21 -0
- package/README.md +65 -0
- package/dist/Mailer.d.ts +14 -0
- package/dist/Mailer.d.ts.map +1 -0
- package/dist/Mailer.js +19 -0
- package/dist/Mailer.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/mail/Mailable.d.ts +73 -0
- package/dist/mail/Mailable.d.ts.map +1 -0
- package/dist/mail/Mailable.js +89 -0
- package/dist/mail/Mailable.js.map +1 -0
- package/dist/providers/MailServiceProvider.d.ts +32 -0
- package/dist/providers/MailServiceProvider.d.ts.map +1 -0
- package/dist/providers/MailServiceProvider.js +50 -0
- package/dist/providers/MailServiceProvider.js.map +1 -0
- package/dist/transports/index.d.ts +38 -0
- package/dist/transports/index.d.ts.map +1 -0
- package/dist/transports/index.js +80 -0
- package/dist/transports/index.js.map +1 -0
- package/package.json +47 -0
- package/src/Mailer.ts +28 -0
- package/src/index.ts +11 -0
- package/src/mail/Mailable.ts +143 -0
- package/src/providers/MailServiceProvider.ts +67 -0
- package/src/transports/index.ts +96 -0
- package/tsconfig.json +9 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Sharvari Divekar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @pearl-framework/mail
|
|
2
|
+
|
|
3
|
+
> Mailable classes and multi-transport email sending for Pearl.js
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @pearl-framework/mail @pearl-framework/core nodemailer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Define a mailable
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { Mailable } from '@pearl-framework/mail'
|
|
17
|
+
|
|
18
|
+
export class WelcomeEmail extends Mailable {
|
|
19
|
+
constructor(private readonly user: User) { super() }
|
|
20
|
+
|
|
21
|
+
build(): this {
|
|
22
|
+
return this
|
|
23
|
+
.to(this.user.email)
|
|
24
|
+
.from({ name: 'Pearl App', address: 'hi@pearl.dev' })
|
|
25
|
+
.subject('Welcome to Pearl!')
|
|
26
|
+
.html(`<h1>Hi ${this.user.name}, welcome aboard!</h1>`)
|
|
27
|
+
.text(`Hi ${this.user.name}, welcome aboard!`)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Send mail
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
const mailer = app.make(Mailer)
|
|
36
|
+
await mailer.send(new WelcomeEmail(user))
|
|
37
|
+
await mailer.sendBulk([new WelcomeEmail(u1), new WelcomeEmail(u2)])
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### MailServiceProvider
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
export class AppMailServiceProvider extends MailServiceProvider {
|
|
44
|
+
protected config = {
|
|
45
|
+
driver: process.env.MAIL_DRIVER as MailDriver,
|
|
46
|
+
from: { name: 'Pearl App', address: process.env.MAIL_FROM! },
|
|
47
|
+
smtp: {
|
|
48
|
+
host: process.env.MAIL_HOST!,
|
|
49
|
+
port: Number(process.env.MAIL_PORT),
|
|
50
|
+
auth: { user: process.env.MAIL_USER!, pass: process.env.MAIL_PASS! },
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Transports
|
|
57
|
+
|
|
58
|
+
| Transport | Use case |
|
|
59
|
+
|-----------|---------|
|
|
60
|
+
| `SmtpTransport` | Production SMTP (Mailgun, Postmark, etc.) |
|
|
61
|
+
| `SesTransport` | AWS SES |
|
|
62
|
+
| `LogTransport` | Development ā logs to console |
|
|
63
|
+
| `ArrayTransport` | Testing ā captures emails in memory |
|
|
64
|
+
|
|
65
|
+
---
|
package/dist/Mailer.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MailTransport } from './transports/index.js';
|
|
2
|
+
import type { Mailable, MailAddress } from './mail/Mailable.js';
|
|
3
|
+
export interface MailerConfig {
|
|
4
|
+
from?: MailAddress | string;
|
|
5
|
+
transport: MailTransport;
|
|
6
|
+
}
|
|
7
|
+
export declare class Mailer {
|
|
8
|
+
private readonly config;
|
|
9
|
+
constructor(config: MailerConfig);
|
|
10
|
+
send(mailable: Mailable): Promise<void>;
|
|
11
|
+
sendBulk(mailables: Mailable[]): Promise<void>;
|
|
12
|
+
get transport(): MailTransport;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=Mailer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Mailer.d.ts","sourceRoot":"","sources":["../src/Mailer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAE/D,MAAM,WAAW,YAAY;IACzB,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IAC3B,SAAS,EAAE,aAAa,CAAA;CAC3B;AAED,qBAAa,MAAM;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,YAAY;IAI3C,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvC,QAAQ,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpD,IAAI,SAAS,IAAI,aAAa,CAE7B;CACJ"}
|
package/dist/Mailer.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class Mailer {
|
|
2
|
+
config;
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.config = config;
|
|
5
|
+
}
|
|
6
|
+
// āāā Sending āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
7
|
+
async send(mailable) {
|
|
8
|
+
const mail = await mailable.compile(this.config.from);
|
|
9
|
+
await this.config.transport.send(mail);
|
|
10
|
+
}
|
|
11
|
+
async sendBulk(mailables) {
|
|
12
|
+
await Promise.all(mailables.map((m) => this.send(m)));
|
|
13
|
+
}
|
|
14
|
+
// āāā Transport access āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
15
|
+
get transport() {
|
|
16
|
+
return this.config.transport;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=Mailer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Mailer.js","sourceRoot":"","sources":["../src/Mailer.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,MAAM;IACc;IAA7B,YAA6B,MAAoB;QAApB,WAAM,GAAN,MAAM,CAAc;IAAG,CAAC;IAErD,6EAA6E;IAE7E,KAAK,CAAC,IAAI,CAAC,QAAkB;QACzB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACrD,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC1C,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,SAAqB;QAChC,MAAM,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACzD,CAAC;IAED,6EAA6E;IAE7E,IAAI,SAAS;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAA;IAChC,CAAC;CACJ"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { Mailable } from './mail/Mailable.js';
|
|
2
|
+
export type { MailAddress, MailEnvelope, MailContent, BuiltMail } from './mail/Mailable.js';
|
|
3
|
+
export { Mailer } from './Mailer.js';
|
|
4
|
+
export type { MailerConfig } from './Mailer.js';
|
|
5
|
+
export { SmtpTransport, LogTransport, ArrayTransport, SesTransport } from './transports/index.js';
|
|
6
|
+
export type { MailTransport, SmtpConfig, SesConfig } from './transports/index.js';
|
|
7
|
+
export { MailServiceProvider } from './providers/MailServiceProvider.js';
|
|
8
|
+
export type { MailServiceConfig, MailDriver } from './providers/MailServiceProvider.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAE3F,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACjG,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAEjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AACxE,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { Mailable } from './mail/Mailable.js';
|
|
2
|
+
export { Mailer } from './Mailer.js';
|
|
3
|
+
export { SmtpTransport, LogTransport, ArrayTransport, SesTransport } from './transports/index.js';
|
|
4
|
+
export { MailServiceProvider } from './providers/MailServiceProvider.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAG7C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGpC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAGjG,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Attachment } from 'nodemailer/lib/mailer/index.js';
|
|
2
|
+
export interface MailAddress {
|
|
3
|
+
name?: string;
|
|
4
|
+
address: string;
|
|
5
|
+
}
|
|
6
|
+
export interface MailEnvelope {
|
|
7
|
+
from?: MailAddress | string;
|
|
8
|
+
to: Array<MailAddress | string>;
|
|
9
|
+
cc?: Array<MailAddress | string>;
|
|
10
|
+
bcc?: Array<MailAddress | string>;
|
|
11
|
+
replyTo?: MailAddress | string;
|
|
12
|
+
subject: string;
|
|
13
|
+
}
|
|
14
|
+
export interface MailContent {
|
|
15
|
+
html?: string;
|
|
16
|
+
text?: string;
|
|
17
|
+
attachments?: Attachment[];
|
|
18
|
+
}
|
|
19
|
+
export interface BuiltMail extends MailContent {
|
|
20
|
+
from?: MailAddress | string;
|
|
21
|
+
to: Array<MailAddress | string>;
|
|
22
|
+
cc?: Array<MailAddress | string>;
|
|
23
|
+
bcc?: Array<MailAddress | string>;
|
|
24
|
+
replyTo?: MailAddress | string;
|
|
25
|
+
subject: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Base class for all Pearl mailables.
|
|
29
|
+
*
|
|
30
|
+
* Usage:
|
|
31
|
+
* export class WelcomeEmail extends Mailable {
|
|
32
|
+
* constructor(private readonly user: User) {
|
|
33
|
+
* super()
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* build(): this {
|
|
37
|
+
* return this
|
|
38
|
+
* .to(this.user.email)
|
|
39
|
+
* .subject('Welcome to Pearl!')
|
|
40
|
+
* .html(`<h1>Hi ${this.user.name}!</h1>`)
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
*/
|
|
44
|
+
export declare abstract class Mailable {
|
|
45
|
+
private _from?;
|
|
46
|
+
private _to;
|
|
47
|
+
private _cc;
|
|
48
|
+
private _bcc;
|
|
49
|
+
private _replyTo?;
|
|
50
|
+
private _subject;
|
|
51
|
+
private _html?;
|
|
52
|
+
private _text?;
|
|
53
|
+
private _attachments;
|
|
54
|
+
from(address: MailAddress | string): this;
|
|
55
|
+
to(...addresses: Array<MailAddress | string>): this;
|
|
56
|
+
cc(...addresses: Array<MailAddress | string>): this;
|
|
57
|
+
bcc(...addresses: Array<MailAddress | string>): this;
|
|
58
|
+
replyTo(address: MailAddress | string): this;
|
|
59
|
+
subject(value: string): this;
|
|
60
|
+
html(content: string): this;
|
|
61
|
+
text(content: string): this;
|
|
62
|
+
attach(attachment: Attachment): this;
|
|
63
|
+
/**
|
|
64
|
+
* Implement this to configure the email.
|
|
65
|
+
* Call this.to(), this.subject(), this.html() etc.
|
|
66
|
+
*/
|
|
67
|
+
abstract build(): this | Promise<this>;
|
|
68
|
+
compile(defaultFrom?: MailAddress | string): Promise<BuiltMail>;
|
|
69
|
+
/** Override to send via queue instead of synchronously */
|
|
70
|
+
shouldQueue(): boolean;
|
|
71
|
+
get queue(): string;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=Mailable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Mailable.d.ts","sourceRoot":"","sources":["../../src/mail/Mailable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAA;AAEhE,MAAM,WAAW,WAAW;IACxB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IAC3B,EAAE,EAAE,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,CAAA;IAC/B,EAAE,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,CAAA;IAChC,GAAG,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,CAAA;IACjC,OAAO,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IAC9B,OAAO,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,UAAU,EAAE,CAAA;CAC7B;AAED,MAAM,WAAW,SAAU,SAAQ,WAAW;IAC1C,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IAC3B,EAAE,EAAE,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,CAAA;IAC/B,EAAE,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,CAAA;IAChC,GAAG,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,CAAA;IACjC,OAAO,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IAC9B,OAAO,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,8BAAsB,QAAQ;IAC1B,OAAO,CAAC,KAAK,CAAC,CAAsB;IACpC,OAAO,CAAC,GAAG,CAAkC;IAC7C,OAAO,CAAC,GAAG,CAAkC;IAC7C,OAAO,CAAC,IAAI,CAAkC;IAC9C,OAAO,CAAC,QAAQ,CAAC,CAAsB;IACvC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,KAAK,CAAC,CAAQ;IACtB,OAAO,CAAC,KAAK,CAAC,CAAQ;IACtB,OAAO,CAAC,YAAY,CAAmB;IAIvC,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI;IAKzC,EAAE,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,GAAG,IAAI;IAKnD,EAAE,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,GAAG,IAAI;IAKnD,GAAG,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,GAAG,IAAI;IAKpD,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI;IAK5C,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3B,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;IAOpC;;;OAGG;IACH,QAAQ,CAAC,KAAK,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAEhC,OAAO,CAAC,WAAW,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAoBrE,0DAA0D;IAC1D,WAAW,IAAI,OAAO;IAItB,IAAI,KAAK,IAAI,MAAM,CAElB;CACJ"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for all Pearl mailables.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* export class WelcomeEmail extends Mailable {
|
|
6
|
+
* constructor(private readonly user: User) {
|
|
7
|
+
* super()
|
|
8
|
+
* }
|
|
9
|
+
*
|
|
10
|
+
* build(): this {
|
|
11
|
+
* return this
|
|
12
|
+
* .to(this.user.email)
|
|
13
|
+
* .subject('Welcome to Pearl!')
|
|
14
|
+
* .html(`<h1>Hi ${this.user.name}!</h1>`)
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
export class Mailable {
|
|
19
|
+
_from;
|
|
20
|
+
_to = [];
|
|
21
|
+
_cc = [];
|
|
22
|
+
_bcc = [];
|
|
23
|
+
_replyTo;
|
|
24
|
+
_subject = '';
|
|
25
|
+
_html;
|
|
26
|
+
_text;
|
|
27
|
+
_attachments = [];
|
|
28
|
+
// āāā Builder methods āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
29
|
+
from(address) {
|
|
30
|
+
this._from = address;
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
to(...addresses) {
|
|
34
|
+
this._to.push(...addresses);
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
cc(...addresses) {
|
|
38
|
+
this._cc.push(...addresses);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
bcc(...addresses) {
|
|
42
|
+
this._bcc.push(...addresses);
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
replyTo(address) {
|
|
46
|
+
this._replyTo = address;
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
subject(value) {
|
|
50
|
+
this._subject = value;
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
html(content) {
|
|
54
|
+
this._html = content;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
text(content) {
|
|
58
|
+
this._text = content;
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
attach(attachment) {
|
|
62
|
+
this._attachments.push(attachment);
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
async compile(defaultFrom) {
|
|
66
|
+
await this.build();
|
|
67
|
+
const from = this._from ?? defaultFrom;
|
|
68
|
+
return {
|
|
69
|
+
...(from !== undefined && { from }),
|
|
70
|
+
to: this._to,
|
|
71
|
+
...(this._cc.length && { cc: this._cc }),
|
|
72
|
+
...(this._bcc.length && { bcc: this._bcc }),
|
|
73
|
+
...(this._replyTo !== undefined && { replyTo: this._replyTo }),
|
|
74
|
+
subject: this._subject,
|
|
75
|
+
...(this._html !== undefined && { html: this._html }),
|
|
76
|
+
...(this._text !== undefined && { text: this._text }),
|
|
77
|
+
...(this._attachments.length && { attachments: this._attachments }),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// āāā Queue support āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
81
|
+
/** Override to send via queue instead of synchronously */
|
|
82
|
+
shouldQueue() {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
get queue() {
|
|
86
|
+
return 'mail';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=Mailable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Mailable.js","sourceRoot":"","sources":["../../src/mail/Mailable.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ServiceProvider } from '@pearl-framework/core';
|
|
2
|
+
import { type SmtpConfig } from '../transports/index.js';
|
|
3
|
+
import type { MailAddress } from '../mail/Mailable.js';
|
|
4
|
+
export type MailDriver = 'smtp' | 'log' | 'array';
|
|
5
|
+
export interface MailServiceConfig {
|
|
6
|
+
driver: MailDriver;
|
|
7
|
+
from?: MailAddress | string;
|
|
8
|
+
smtp?: SmtpConfig;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* MailServiceProvider registers the Mailer into the container.
|
|
12
|
+
*
|
|
13
|
+
* Usage ā extend this in your app:
|
|
14
|
+
*
|
|
15
|
+
* export class AppMailServiceProvider extends MailServiceProvider {
|
|
16
|
+
* protected config: MailServiceConfig = {
|
|
17
|
+
* driver: env('MAIL_DRIVER') as MailDriver,
|
|
18
|
+
* from: { name: 'Pearl App', address: env('MAIL_FROM') },
|
|
19
|
+
* smtp: {
|
|
20
|
+
* host: env('MAIL_HOST'),
|
|
21
|
+
* port: env.number('MAIL_PORT'),
|
|
22
|
+
* auth: { user: env('MAIL_USER'), pass: env('MAIL_PASS') },
|
|
23
|
+
* },
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
*/
|
|
27
|
+
export declare class MailServiceProvider extends ServiceProvider {
|
|
28
|
+
protected config: MailServiceConfig;
|
|
29
|
+
register(): void;
|
|
30
|
+
private resolveTransport;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=MailServiceProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MailServiceProvider.d.ts","sourceRoot":"","sources":["../../src/providers/MailServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,EAKH,KAAK,UAAU,EAClB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEtD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAA;AAEjD,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IAC3B,IAAI,CAAC,EAAE,UAAU,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,mBAAoB,SAAQ,eAAe;IACpD,SAAS,CAAC,MAAM,EAAE,iBAAiB,CAElC;IAED,QAAQ,IAAI,IAAI;IAUhB,OAAO,CAAC,gBAAgB;CAe3B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ServiceProvider } from '@pearl-framework/core';
|
|
2
|
+
import { Mailer } from '../Mailer.js';
|
|
3
|
+
import { SmtpTransport, LogTransport, ArrayTransport, } from '../transports/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* MailServiceProvider registers the Mailer into the container.
|
|
6
|
+
*
|
|
7
|
+
* Usage ā extend this in your app:
|
|
8
|
+
*
|
|
9
|
+
* export class AppMailServiceProvider extends MailServiceProvider {
|
|
10
|
+
* protected config: MailServiceConfig = {
|
|
11
|
+
* driver: env('MAIL_DRIVER') as MailDriver,
|
|
12
|
+
* from: { name: 'Pearl App', address: env('MAIL_FROM') },
|
|
13
|
+
* smtp: {
|
|
14
|
+
* host: env('MAIL_HOST'),
|
|
15
|
+
* port: env.number('MAIL_PORT'),
|
|
16
|
+
* auth: { user: env('MAIL_USER'), pass: env('MAIL_PASS') },
|
|
17
|
+
* },
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
export class MailServiceProvider extends ServiceProvider {
|
|
22
|
+
config = {
|
|
23
|
+
driver: 'log',
|
|
24
|
+
};
|
|
25
|
+
register() {
|
|
26
|
+
this.container.singleton(Mailer, () => {
|
|
27
|
+
const transport = this.resolveTransport();
|
|
28
|
+
return new Mailer({
|
|
29
|
+
transport,
|
|
30
|
+
...(this.config.from !== undefined && { from: this.config.from }),
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
resolveTransport() {
|
|
35
|
+
switch (this.config.driver) {
|
|
36
|
+
case 'smtp': {
|
|
37
|
+
if (!this.config.smtp) {
|
|
38
|
+
throw new Error('SMTP config is required when using the smtp mail driver.');
|
|
39
|
+
}
|
|
40
|
+
return new SmtpTransport(this.config.smtp);
|
|
41
|
+
}
|
|
42
|
+
case 'array':
|
|
43
|
+
return new ArrayTransport();
|
|
44
|
+
case 'log':
|
|
45
|
+
default:
|
|
46
|
+
return new LogTransport();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=MailServiceProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MailServiceProvider.js","sourceRoot":"","sources":["../../src/providers/MailServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EACH,aAAa,EACb,YAAY,EACZ,cAAc,GAGjB,MAAM,wBAAwB,CAAA;AAW/B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,mBAAoB,SAAQ,eAAe;IAC1C,MAAM,GAAsB;QAClC,MAAM,EAAE,KAAK;KAChB,CAAA;IAED,QAAQ;QACJ,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE;YACtC,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAA;YACzC,OAAO,IAAI,MAAM,CAAC;gBACd,SAAS;gBACT,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;aACpE,CAAC,CAAA;QACF,CAAC,CAAC,CAAA;IACN,CAAC;IAEO,gBAAgB;QACpB,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAC7B,KAAK,MAAM,CAAC,CAAC,CAAC;gBACV,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;oBACxB,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;gBAC3E,CAAC;gBACD,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAC9C,CAAC;YACD,KAAK,OAAO;gBACR,OAAO,IAAI,cAAc,EAAE,CAAA;YAC/B,KAAK,KAAK,CAAC;YACX;gBACI,OAAO,IAAI,YAAY,EAAE,CAAA;QAC7B,CAAC;IACL,CAAC;CACJ"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { BuiltMail } from '../mail/Mailable.js';
|
|
2
|
+
export interface MailTransport {
|
|
3
|
+
send(mail: BuiltMail): Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
export interface SmtpConfig {
|
|
6
|
+
host: string;
|
|
7
|
+
port: number;
|
|
8
|
+
secure?: boolean;
|
|
9
|
+
auth?: {
|
|
10
|
+
user: string;
|
|
11
|
+
pass: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare class SmtpTransport implements MailTransport {
|
|
15
|
+
private readonly config;
|
|
16
|
+
constructor(config: SmtpConfig);
|
|
17
|
+
send(mail: BuiltMail): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export interface SesConfig {
|
|
20
|
+
region: string;
|
|
21
|
+
accessKeyId: string;
|
|
22
|
+
secretAccessKey: string;
|
|
23
|
+
}
|
|
24
|
+
export declare class SesTransport implements MailTransport {
|
|
25
|
+
private readonly config;
|
|
26
|
+
constructor(config: SesConfig);
|
|
27
|
+
send(mail: BuiltMail): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export declare class LogTransport implements MailTransport {
|
|
30
|
+
send(mail: BuiltMail): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export declare class ArrayTransport implements MailTransport {
|
|
33
|
+
readonly sent: BuiltMail[];
|
|
34
|
+
send(mail: BuiltMail): Promise<void>;
|
|
35
|
+
clear(): void;
|
|
36
|
+
last(): BuiltMail | undefined;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transports/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,qBAAqB,CAAA;AAEjE,MAAM,WAAW,aAAa;IAC1B,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACvC;AAOD,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CACxC;AAED,qBAAa,aAAc,YAAW,aAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,UAAU;IAEzC,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAqB7C;AAED,MAAM,WAAW,SAAS;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;CAC1B;AAED,qBAAa,YAAa,YAAW,aAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAExC,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAuB7C;AAED,qBAAa,YAAa,YAAW,aAAa;IACxC,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAS7C;AAEG,qBAAa,cAAe,YAAW,aAAa;IACpD,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,CAAK;IACzB,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1C,KAAK,IAAI,IAAI;IACb,IAAI,IAAI,SAAS,GAAG,SAAS;CAChC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
function toNodemailerAddress(addr) {
|
|
2
|
+
if (typeof addr === 'string')
|
|
3
|
+
return addr;
|
|
4
|
+
return addr.name ? `"${addr.name}" <${addr.address}>` : addr.address;
|
|
5
|
+
}
|
|
6
|
+
export class SmtpTransport {
|
|
7
|
+
config;
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
async send(mail) {
|
|
12
|
+
const nodemailer = await import('nodemailer');
|
|
13
|
+
const transporter = nodemailer.createTransport({
|
|
14
|
+
host: this.config.host,
|
|
15
|
+
port: this.config.port,
|
|
16
|
+
secure: this.config.secure ?? this.config.port === 465,
|
|
17
|
+
...(this.config.auth !== undefined && { auth: this.config.auth }),
|
|
18
|
+
});
|
|
19
|
+
await transporter.sendMail({
|
|
20
|
+
...(mail.from !== undefined && { from: toNodemailerAddress(mail.from) }),
|
|
21
|
+
to: mail.to.map(toNodemailerAddress).join(', '),
|
|
22
|
+
...(mail.cc !== undefined && { cc: mail.cc.map(toNodemailerAddress).join(', ') }),
|
|
23
|
+
...(mail.bcc !== undefined && { bcc: mail.bcc.map(toNodemailerAddress).join(', ') }),
|
|
24
|
+
...(mail.replyTo !== undefined && { replyTo: toNodemailerAddress(mail.replyTo) }),
|
|
25
|
+
subject: mail.subject,
|
|
26
|
+
...(mail.html !== undefined && { html: mail.html }),
|
|
27
|
+
...(mail.text !== undefined && { text: mail.text }),
|
|
28
|
+
...(mail.attachments !== undefined && { attachments: mail.attachments }),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class SesTransport {
|
|
33
|
+
config;
|
|
34
|
+
constructor(config) {
|
|
35
|
+
this.config = config;
|
|
36
|
+
}
|
|
37
|
+
async send(mail) {
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
+
const aws = await import('@aws-sdk/client-ses');
|
|
40
|
+
const client = new aws.SESClient({
|
|
41
|
+
region: this.config.region,
|
|
42
|
+
credentials: {
|
|
43
|
+
accessKeyId: this.config.accessKeyId,
|
|
44
|
+
secretAccessKey: this.config.secretAccessKey,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
const toAddresses = mail.to.map((a) => typeof a === 'string' ? a : a.address);
|
|
48
|
+
await client.send(new aws.SendEmailCommand({
|
|
49
|
+
Source: mail.from ? toNodemailerAddress(mail.from) : '',
|
|
50
|
+
Destination: { ToAddresses: toAddresses },
|
|
51
|
+
Message: {
|
|
52
|
+
Subject: { Data: mail.subject },
|
|
53
|
+
Body: {
|
|
54
|
+
...(mail.html !== undefined && { Html: { Data: mail.html } }),
|
|
55
|
+
...(mail.text !== undefined && { Text: { Data: mail.text } }),
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export class LogTransport {
|
|
62
|
+
async send(mail) {
|
|
63
|
+
console.log('\nš§ [Pearl Mail ā LogTransport]');
|
|
64
|
+
console.log(' From: ', mail.from);
|
|
65
|
+
console.log(' To: ', mail.to);
|
|
66
|
+
console.log(' Subject:', mail.subject);
|
|
67
|
+
if (mail.html)
|
|
68
|
+
console.log(' HTML: ', mail.html.slice(0, 100) + '...');
|
|
69
|
+
if (mail.text)
|
|
70
|
+
console.log(' Text: ', mail.text.slice(0, 100) + '...');
|
|
71
|
+
console.log('');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export class ArrayTransport {
|
|
75
|
+
sent = [];
|
|
76
|
+
async send(mail) { this.sent.push(mail); }
|
|
77
|
+
clear() { this.sent.length = 0; }
|
|
78
|
+
last() { return this.sent[this.sent.length - 1]; }
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transports/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pearl-framework/mail",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pearl.js mail ā Nodemailer-powered mailable classes, transports, and queue support",
|
|
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
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"typesVersions": {
|
|
15
|
+
"*": {
|
|
16
|
+
"*": [
|
|
17
|
+
"./dist/index.d.ts"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"nodemailer": "^6.9.0",
|
|
23
|
+
"@pearl-framework/core": "0.1.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^20.0.0",
|
|
27
|
+
"@types/nodemailer": "^6.4.0",
|
|
28
|
+
"typescript": "^5.4.0",
|
|
29
|
+
"vitest": "^1.6.0"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public",
|
|
33
|
+
"registry": "https://registry.npmjs.org"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/skd09/pearl.js.git",
|
|
38
|
+
"directory": "packages/mail"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc",
|
|
42
|
+
"dev": "tsc --watch",
|
|
43
|
+
"test": "vitest run",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"clean": "rm -rf dist"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/Mailer.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MailTransport } from './transports/index.js'
|
|
2
|
+
import type { Mailable, MailAddress } from './mail/Mailable.js'
|
|
3
|
+
|
|
4
|
+
export interface MailerConfig {
|
|
5
|
+
from?: MailAddress | string
|
|
6
|
+
transport: MailTransport
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class Mailer {
|
|
10
|
+
constructor(private readonly config: MailerConfig) {}
|
|
11
|
+
|
|
12
|
+
// āāā Sending āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
13
|
+
|
|
14
|
+
async send(mailable: Mailable): Promise<void> {
|
|
15
|
+
const mail = await mailable.compile(this.config.from)
|
|
16
|
+
await this.config.transport.send(mail)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async sendBulk(mailables: Mailable[]): Promise<void> {
|
|
20
|
+
await Promise.all(mailables.map((m) => this.send(m)))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// āāā Transport access āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
24
|
+
|
|
25
|
+
get transport(): MailTransport {
|
|
26
|
+
return this.config.transport
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { Mailable } from './mail/Mailable.js'
|
|
2
|
+
export type { MailAddress, MailEnvelope, MailContent, BuiltMail } from './mail/Mailable.js'
|
|
3
|
+
|
|
4
|
+
export { Mailer } from './Mailer.js'
|
|
5
|
+
export type { MailerConfig } from './Mailer.js'
|
|
6
|
+
|
|
7
|
+
export { SmtpTransport, LogTransport, ArrayTransport, SesTransport } from './transports/index.js'
|
|
8
|
+
export type { MailTransport, SmtpConfig, SesConfig } from './transports/index.js'
|
|
9
|
+
|
|
10
|
+
export { MailServiceProvider } from './providers/MailServiceProvider.js'
|
|
11
|
+
export type { MailServiceConfig, MailDriver } from './providers/MailServiceProvider.js'
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { Attachment } from 'nodemailer/lib/mailer/index.js'
|
|
2
|
+
|
|
3
|
+
export interface MailAddress {
|
|
4
|
+
name?: string
|
|
5
|
+
address: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface MailEnvelope {
|
|
9
|
+
from?: MailAddress | string
|
|
10
|
+
to: Array<MailAddress | string>
|
|
11
|
+
cc?: Array<MailAddress | string>
|
|
12
|
+
bcc?: Array<MailAddress | string>
|
|
13
|
+
replyTo?: MailAddress | string
|
|
14
|
+
subject: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface MailContent {
|
|
18
|
+
html?: string
|
|
19
|
+
text?: string
|
|
20
|
+
attachments?: Attachment[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface BuiltMail extends MailContent {
|
|
24
|
+
from?: MailAddress | string
|
|
25
|
+
to: Array<MailAddress | string>
|
|
26
|
+
cc?: Array<MailAddress | string>
|
|
27
|
+
bcc?: Array<MailAddress | string>
|
|
28
|
+
replyTo?: MailAddress | string
|
|
29
|
+
subject: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Base class for all Pearl mailables.
|
|
34
|
+
*
|
|
35
|
+
* Usage:
|
|
36
|
+
* export class WelcomeEmail extends Mailable {
|
|
37
|
+
* constructor(private readonly user: User) {
|
|
38
|
+
* super()
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* build(): this {
|
|
42
|
+
* return this
|
|
43
|
+
* .to(this.user.email)
|
|
44
|
+
* .subject('Welcome to Pearl!')
|
|
45
|
+
* .html(`<h1>Hi ${this.user.name}!</h1>`)
|
|
46
|
+
* }
|
|
47
|
+
* }
|
|
48
|
+
*/
|
|
49
|
+
export abstract class Mailable {
|
|
50
|
+
private _from?: MailAddress | string
|
|
51
|
+
private _to: Array<MailAddress | string> = []
|
|
52
|
+
private _cc: Array<MailAddress | string> = []
|
|
53
|
+
private _bcc: Array<MailAddress | string> = []
|
|
54
|
+
private _replyTo?: MailAddress | string
|
|
55
|
+
private _subject = ''
|
|
56
|
+
private _html?: string
|
|
57
|
+
private _text?: string
|
|
58
|
+
private _attachments: Attachment[] = []
|
|
59
|
+
|
|
60
|
+
// āāā Builder methods āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
61
|
+
|
|
62
|
+
from(address: MailAddress | string): this {
|
|
63
|
+
this._from = address
|
|
64
|
+
return this
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
to(...addresses: Array<MailAddress | string>): this {
|
|
68
|
+
this._to.push(...addresses)
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
cc(...addresses: Array<MailAddress | string>): this {
|
|
73
|
+
this._cc.push(...addresses)
|
|
74
|
+
return this
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
bcc(...addresses: Array<MailAddress | string>): this {
|
|
78
|
+
this._bcc.push(...addresses)
|
|
79
|
+
return this
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
replyTo(address: MailAddress | string): this {
|
|
83
|
+
this._replyTo = address
|
|
84
|
+
return this
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
subject(value: string): this {
|
|
88
|
+
this._subject = value
|
|
89
|
+
return this
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
html(content: string): this {
|
|
93
|
+
this._html = content
|
|
94
|
+
return this
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
text(content: string): this {
|
|
98
|
+
this._text = content
|
|
99
|
+
return this
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
attach(attachment: Attachment): this {
|
|
103
|
+
this._attachments.push(attachment)
|
|
104
|
+
return this
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// āāā Build āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Implement this to configure the email.
|
|
111
|
+
* Call this.to(), this.subject(), this.html() etc.
|
|
112
|
+
*/
|
|
113
|
+
abstract build(): this | Promise<this>
|
|
114
|
+
|
|
115
|
+
async compile(defaultFrom?: MailAddress | string): Promise<BuiltMail> {
|
|
116
|
+
await this.build()
|
|
117
|
+
|
|
118
|
+
const from = this._from ?? defaultFrom
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
...(from !== undefined && { from }),
|
|
122
|
+
to: this._to,
|
|
123
|
+
...(this._cc.length && { cc: this._cc }),
|
|
124
|
+
...(this._bcc.length && { bcc: this._bcc }),
|
|
125
|
+
...(this._replyTo !== undefined && { replyTo: this._replyTo }),
|
|
126
|
+
subject: this._subject,
|
|
127
|
+
...(this._html !== undefined && { html: this._html }),
|
|
128
|
+
...(this._text !== undefined && { text: this._text }),
|
|
129
|
+
...(this._attachments.length && { attachments: this._attachments }),
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// āāā Queue support āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
134
|
+
|
|
135
|
+
/** Override to send via queue instead of synchronously */
|
|
136
|
+
shouldQueue(): boolean {
|
|
137
|
+
return false
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
get queue(): string {
|
|
141
|
+
return 'mail'
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ServiceProvider } from '@pearl-framework/core'
|
|
2
|
+
import { Mailer } from '../Mailer.js'
|
|
3
|
+
import {
|
|
4
|
+
SmtpTransport,
|
|
5
|
+
LogTransport,
|
|
6
|
+
ArrayTransport,
|
|
7
|
+
type MailTransport,
|
|
8
|
+
type SmtpConfig,
|
|
9
|
+
} from '../transports/index.js'
|
|
10
|
+
import type { MailAddress } from '../mail/Mailable.js'
|
|
11
|
+
|
|
12
|
+
export type MailDriver = 'smtp' | 'log' | 'array'
|
|
13
|
+
|
|
14
|
+
export interface MailServiceConfig {
|
|
15
|
+
driver: MailDriver
|
|
16
|
+
from?: MailAddress | string
|
|
17
|
+
smtp?: SmtpConfig
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* MailServiceProvider registers the Mailer into the container.
|
|
22
|
+
*
|
|
23
|
+
* Usage ā extend this in your app:
|
|
24
|
+
*
|
|
25
|
+
* export class AppMailServiceProvider extends MailServiceProvider {
|
|
26
|
+
* protected config: MailServiceConfig = {
|
|
27
|
+
* driver: env('MAIL_DRIVER') as MailDriver,
|
|
28
|
+
* from: { name: 'Pearl App', address: env('MAIL_FROM') },
|
|
29
|
+
* smtp: {
|
|
30
|
+
* host: env('MAIL_HOST'),
|
|
31
|
+
* port: env.number('MAIL_PORT'),
|
|
32
|
+
* auth: { user: env('MAIL_USER'), pass: env('MAIL_PASS') },
|
|
33
|
+
* },
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
*/
|
|
37
|
+
export class MailServiceProvider extends ServiceProvider {
|
|
38
|
+
protected config: MailServiceConfig = {
|
|
39
|
+
driver: 'log',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
register(): void {
|
|
43
|
+
this.container.singleton(Mailer, () => {
|
|
44
|
+
const transport = this.resolveTransport()
|
|
45
|
+
return new Mailer({
|
|
46
|
+
transport,
|
|
47
|
+
...(this.config.from !== undefined && { from: this.config.from }),
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private resolveTransport(): MailTransport {
|
|
53
|
+
switch (this.config.driver) {
|
|
54
|
+
case 'smtp': {
|
|
55
|
+
if (!this.config.smtp) {
|
|
56
|
+
throw new Error('SMTP config is required when using the smtp mail driver.')
|
|
57
|
+
}
|
|
58
|
+
return new SmtpTransport(this.config.smtp)
|
|
59
|
+
}
|
|
60
|
+
case 'array':
|
|
61
|
+
return new ArrayTransport()
|
|
62
|
+
case 'log':
|
|
63
|
+
default:
|
|
64
|
+
return new LogTransport()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { BuiltMail, MailAddress } from '../mail/Mailable.js'
|
|
2
|
+
|
|
3
|
+
export interface MailTransport {
|
|
4
|
+
send(mail: BuiltMail): Promise<void>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function toNodemailerAddress(addr: MailAddress | string): string {
|
|
8
|
+
if (typeof addr === 'string') return addr
|
|
9
|
+
return addr.name ? `"${addr.name}" <${addr.address}>` : addr.address
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SmtpConfig {
|
|
13
|
+
host: string
|
|
14
|
+
port: number
|
|
15
|
+
secure?: boolean
|
|
16
|
+
auth?: { user: string; pass: string }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class SmtpTransport implements MailTransport {
|
|
20
|
+
constructor(private readonly config: SmtpConfig) {}
|
|
21
|
+
|
|
22
|
+
async send(mail: BuiltMail): Promise<void> {
|
|
23
|
+
const nodemailer = await import('nodemailer')
|
|
24
|
+
const transporter = nodemailer.createTransport({
|
|
25
|
+
host: this.config.host,
|
|
26
|
+
port: this.config.port,
|
|
27
|
+
secure: this.config.secure ?? this.config.port === 465,
|
|
28
|
+
...(this.config.auth !== undefined && { auth: this.config.auth }),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
await transporter.sendMail({
|
|
32
|
+
...(mail.from !== undefined && { from: toNodemailerAddress(mail.from) }),
|
|
33
|
+
to: mail.to.map(toNodemailerAddress).join(', '),
|
|
34
|
+
...(mail.cc !== undefined && { cc: mail.cc.map(toNodemailerAddress).join(', ') }),
|
|
35
|
+
...(mail.bcc !== undefined && { bcc: mail.bcc.map(toNodemailerAddress).join(', ') }),
|
|
36
|
+
...(mail.replyTo !== undefined && { replyTo: toNodemailerAddress(mail.replyTo) }),
|
|
37
|
+
subject: mail.subject,
|
|
38
|
+
...(mail.html !== undefined && { html: mail.html }),
|
|
39
|
+
...(mail.text !== undefined && { text: mail.text }),
|
|
40
|
+
...(mail.attachments !== undefined && { attachments: mail.attachments }),
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface SesConfig {
|
|
46
|
+
region: string
|
|
47
|
+
accessKeyId: string
|
|
48
|
+
secretAccessKey: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class SesTransport implements MailTransport {
|
|
52
|
+
constructor(private readonly config: SesConfig) {}
|
|
53
|
+
|
|
54
|
+
async send(mail: BuiltMail): Promise<void> {
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
const aws = await import('@aws-sdk/client-ses' as string) as any
|
|
57
|
+
const client = new aws.SESClient({
|
|
58
|
+
region: this.config.region,
|
|
59
|
+
credentials: {
|
|
60
|
+
accessKeyId: this.config.accessKeyId,
|
|
61
|
+
secretAccessKey: this.config.secretAccessKey,
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
const toAddresses = mail.to.map((a) => typeof a === 'string' ? a : a.address)
|
|
65
|
+
await client.send(new aws.SendEmailCommand({
|
|
66
|
+
Source: mail.from ? toNodemailerAddress(mail.from) : '',
|
|
67
|
+
Destination: { ToAddresses: toAddresses },
|
|
68
|
+
Message: {
|
|
69
|
+
Subject: { Data: mail.subject },
|
|
70
|
+
Body: {
|
|
71
|
+
...(mail.html !== undefined && { Html: { Data: mail.html } }),
|
|
72
|
+
...(mail.text !== undefined && { Text: { Data: mail.text } }),
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
}))
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export class LogTransport implements MailTransport {
|
|
80
|
+
async send(mail: BuiltMail): Promise<void> {
|
|
81
|
+
console.log('\nš§ [Pearl Mail ā LogTransport]')
|
|
82
|
+
console.log(' From: ', mail.from)
|
|
83
|
+
console.log(' To: ', mail.to)
|
|
84
|
+
console.log(' Subject:', mail.subject)
|
|
85
|
+
if (mail.html) console.log(' HTML: ', mail.html.slice(0, 100) + '...')
|
|
86
|
+
if (mail.text) console.log(' Text: ', mail.text.slice(0, 100) + '...')
|
|
87
|
+
console.log('')
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export class ArrayTransport implements MailTransport {
|
|
92
|
+
readonly sent: BuiltMail[] = []
|
|
93
|
+
async send(mail: BuiltMail): Promise<void> { this.sent.push(mail) }
|
|
94
|
+
clear(): void { this.sent.length = 0 }
|
|
95
|
+
last(): BuiltMail | undefined { return this.sent[this.sent.length - 1] }
|
|
96
|
+
}
|