ts-mailkit 0.2.0 → 1.0.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 +34 -22
- package/dist/core/client.d.ts +10 -29
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/types.d.ts +8 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.cjs +87 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +83 -76
- package/dist/index.js.map +1 -1
- package/dist/renderers/default/default-renderer.d.ts +9 -0
- package/dist/renderers/default/default-renderer.d.ts.map +1 -1
- package/dist/templates/dto/index.d.ts +8 -4
- package/dist/templates/dto/index.d.ts.map +1 -1
- package/dist/templates/factory.d.ts +7 -2
- package/dist/templates/factory.d.ts.map +1 -1
- package/dist/transports/zeptomail/types.d.ts +1 -1
- package/dist/transports/zeptomail/types.d.ts.map +1 -1
- package/dist/transports/zeptomail/zeptomail-sender.d.ts +4 -10
- package/dist/transports/zeptomail/zeptomail-sender.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Works seamlessly in **Node.js**, **Cloudflare Workers** (`workerd`), **Bun**, **
|
|
|
10
10
|
|
|
11
11
|
- **Type-Safe DTOs**: Strongly-typed Data Transfer Objects for transactional emails (verification, password reset, 2FA, magic links, newsletters).
|
|
12
12
|
- **Pluggable Transports (`IEmailSender`)**: Decoupled transport adapter interface with built-in senders:
|
|
13
|
-
- `
|
|
13
|
+
- `ZeptoMailHttpSender` (Zoho Mail transactional API via native `fetch`).
|
|
14
14
|
- `MemoryEmailSender` (In-memory mock transport for unit and integration testing).
|
|
15
15
|
- `ConsoleEmailSender` (Formatted terminal logger for local development).
|
|
16
16
|
- **Zero Runtime Dependencies**: Core uses standard web APIs (global `fetch`), ensuring zero heavy dependencies and complete edge compatibility.
|
|
@@ -18,7 +18,11 @@ Works seamlessly in **Node.js**, **Cloudflare Workers** (`workerd`), **Bun**, **
|
|
|
18
18
|
1. DTO schema and validation.
|
|
19
19
|
2. Template building and responsive HTML rendering.
|
|
20
20
|
3. Dispatch via the chosen transport adapter.
|
|
21
|
-
- **Multitenant & Dynamic Branding**: Configure global branding or pass a dynamic brand name resolver for multi-tenant applications.
|
|
21
|
+
- **Multitenant & Dynamic Branding**: Configure global branding, top-center logos, or pass a dynamic brand name resolver for multi-tenant applications.
|
|
22
|
+
- **Top-Center Brand Logo**: Display your brand or company logo top-center with automatic HTTP(S) URL validation.
|
|
23
|
+
- **Auto Plain-Text Fallbacks**: When sending custom or raw HTML, accessible plain-text equivalents are cleanly generated automatically if not explicitly provided.
|
|
24
|
+
- **RFC 5322 Address Parser**: Optional utility to safely parse `"Display Name <email@example.com>"` strings into structured address objects.
|
|
25
|
+
- **Template Preview**: Inspect [`demo-newsletter.html`](demo-newsletter.html) directly in the root folder to view the responsive default email template before installing.
|
|
22
26
|
- **Dual ESM & CommonJS**: Full support for `import` and `require` with first-class TypeScript declaration maps.
|
|
23
27
|
|
|
24
28
|
---
|
|
@@ -48,10 +52,10 @@ bun add ts-mailkit
|
|
|
48
52
|
### 1. Initialize Transport & Client
|
|
49
53
|
|
|
50
54
|
```ts
|
|
51
|
-
import { createMailKit,
|
|
55
|
+
import { createMailKit, ZeptoMailHttpSender, EmailTemplateFactory } from 'ts-mailkit';
|
|
52
56
|
|
|
53
57
|
// Configure the Zoho ZeptoMail HTTP transport
|
|
54
|
-
const transport = new
|
|
58
|
+
const transport = new ZeptoMailHttpSender({
|
|
55
59
|
apiToken: process.env.ZEPTO_MAIL_TOKEN!,
|
|
56
60
|
apiHost: 'api.zeptomail.com', // or 'api.zeptomail.eu', 'api.zeptomail.in'
|
|
57
61
|
from: { address: 'noreply@yourdomain.com', name: 'Acme App' },
|
|
@@ -137,6 +141,7 @@ You can instantiate `EmailTemplateFactory` with brand-level defaults:
|
|
|
137
141
|
```ts
|
|
138
142
|
const tenantFactory = new EmailTemplateFactory({
|
|
139
143
|
brandName: 'St. Patrick High School',
|
|
144
|
+
logoUrl: 'https://school.edu/assets/logo.png',
|
|
140
145
|
securityMessage: 'If you did not make this request, contact our school IT administration immediately.',
|
|
141
146
|
footer: (brand) => `© ${new Date().getFullYear()} ${brand}. Authorized educational portal.`,
|
|
142
147
|
});
|
|
@@ -198,14 +203,14 @@ describe('User Registration Email', () => {
|
|
|
198
203
|
|
|
199
204
|
### 4. Sending Raw Emails (No Template DTO)
|
|
200
205
|
|
|
201
|
-
For custom HTML or plain-text messages
|
|
206
|
+
For custom HTML or plain-text messages. When `text` is omitted, an accessible plain-text fallback is automatically extracted from the `html`:
|
|
202
207
|
|
|
203
208
|
```ts
|
|
204
209
|
await mailer.sendRaw({
|
|
205
210
|
to: [{ address: 'support@example.com', name: 'Support Team' }],
|
|
206
211
|
subject: 'Urgent System Alert',
|
|
207
212
|
html: '<h1>System Alert</h1><p>Database failover completed successfully.</p>',
|
|
208
|
-
text
|
|
213
|
+
// text is optional — auto-converted from html if omitted
|
|
209
214
|
attachments: [
|
|
210
215
|
{
|
|
211
216
|
filename: 'report.txt',
|
|
@@ -216,7 +221,21 @@ await mailer.sendRaw({
|
|
|
216
221
|
});
|
|
217
222
|
```
|
|
218
223
|
|
|
219
|
-
### 5.
|
|
224
|
+
### 5. Parsing RFC 5322 Email Addresses
|
|
225
|
+
|
|
226
|
+
If your application receives combined address strings like `"Jane Doe <jane@example.com>"`, use the standalone, tree-shakeable `parseEmailAddress` helper:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { parseEmailAddress } from 'ts-mailkit';
|
|
230
|
+
|
|
231
|
+
const recipient = parseEmailAddress('Jane Doe <jane@example.com>');
|
|
232
|
+
// => { address: 'jane@example.com', name: 'Jane Doe' }
|
|
233
|
+
|
|
234
|
+
const simple = parseEmailAddress('jane@example.com');
|
|
235
|
+
// => { address: 'jane@example.com' }
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 6. Pluggable Transports (`IEmailSender`)
|
|
220
239
|
|
|
221
240
|
You can implement custom transports (e.g. SMTP, Resend, SendGrid, Postmark) by satisfying the `IEmailSender` interface:
|
|
222
241
|
|
|
@@ -238,6 +257,14 @@ export class CustomSmtpSender implements IEmailSender {
|
|
|
238
257
|
|
|
239
258
|
---
|
|
240
259
|
|
|
260
|
+
## Template Preview
|
|
261
|
+
|
|
262
|
+
You can inspect what the default responsive email template looks like without writing code or installing the library:
|
|
263
|
+
|
|
264
|
+
- 📄 **[demo-newsletter.html](demo-newsletter.html)** — Standalone, fully pre-rendered HTML newsletter showcasing top-center brand logo, hero banner, typography, call-to-action button, and footer. Generated automatically during the test suite.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
241
268
|
## Development & Testing
|
|
242
269
|
|
|
243
270
|
```bash
|
|
@@ -259,21 +286,6 @@ pnpm run prepublishOnly
|
|
|
259
286
|
|
|
260
287
|
---
|
|
261
288
|
|
|
262
|
-
## Deprecated API (Scheduled for Removal in v1.0.0)
|
|
263
|
-
|
|
264
|
-
The following legacy migration bridges are marked `@deprecated` and scheduled for removal in `v1.0.0`:
|
|
265
|
-
|
|
266
|
-
| Deprecated | Replacement | Notes |
|
|
267
|
-
| :--- | :--- | :--- |
|
|
268
|
-
| `mailer.send(to, template, toName)` | `mailer.send({ to, template, ... })` | Use the type-safe options object. |
|
|
269
|
-
| `sender.sendEmail(...)` | `sender.send({ to, subject, html, text, ... })` | Standardized on `SendMailOptions`. |
|
|
270
|
-
| `mailer.sendSimpleMessage(...)` | `mailer.sendRaw({ to, subject, text, html })` | Direct raw email dispatch. |
|
|
271
|
-
| `mailer.buildPlainTextMessage(template)` | `mailer.render(template)` or `buildPlainText(dto)` | Standalone or client renderer methods. |
|
|
272
|
-
| `EmailService` | `MailKit` | Main client class. |
|
|
273
|
-
| `BaseEmailDto` | `EmailTemplateDto` | Core DTO interface. |
|
|
274
|
-
|
|
275
|
-
---
|
|
276
|
-
|
|
277
289
|
## License
|
|
278
290
|
|
|
279
291
|
[MIT](LICENSE)
|
package/dist/core/client.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export interface SendTemplateOptions {
|
|
|
54
54
|
}
|
|
55
55
|
export interface MailKitOptions {
|
|
56
56
|
/**
|
|
57
|
-
* The transport adapter used to dispatch emails (e.g.
|
|
57
|
+
* The transport adapter used to dispatch emails (e.g. ZeptoMailHttpSender, MemoryEmailSender).
|
|
58
58
|
*/
|
|
59
59
|
transport: IEmailSender;
|
|
60
60
|
/**
|
|
@@ -93,43 +93,24 @@ export declare class MailKit {
|
|
|
93
93
|
* Renders an email DTO to HTML and plain text without dispatching.
|
|
94
94
|
*/
|
|
95
95
|
render(template: EmailTemplateDto): Promise<RenderedEmail>;
|
|
96
|
-
/**
|
|
97
|
-
* Returns the plain-text fallback for an email DTO without sending.
|
|
98
|
-
* @deprecated Use `mailer.render(template)` or `buildPlainText(template)` instead. This method will be removed in v1.0.0.
|
|
99
|
-
*/
|
|
100
|
-
buildPlainTextMessage(template: EmailTemplateDto): string;
|
|
101
96
|
/**
|
|
102
97
|
* Sends an email using a strongly-typed template DTO.
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
98
|
+
*
|
|
99
|
+
* Example:
|
|
100
|
+
* ```ts
|
|
101
|
+
* await mailer.send({
|
|
102
|
+
* to: 'user@example.com',
|
|
103
|
+
* template: myDto,
|
|
104
|
+
* });
|
|
105
|
+
* ```
|
|
107
106
|
*/
|
|
108
107
|
send(options: SendTemplateOptions): Promise<SendResult>;
|
|
109
|
-
/**
|
|
110
|
-
* @deprecated Use the options object signature `mailer.send({ to, template, ... })` instead. Positional signature will be removed in v1.0.0.
|
|
111
|
-
*/
|
|
112
|
-
send(to: string, template: EmailTemplateDto, toName?: string): Promise<SendResult>;
|
|
113
108
|
/**
|
|
114
109
|
* Dispatches a raw, pre-rendered email directly through the transport.
|
|
110
|
+
* If `options.text` is omitted, plain text is automatically derived from `options.html`.
|
|
115
111
|
*/
|
|
116
112
|
sendRaw(options: SendMailOptions): Promise<SendResult>;
|
|
117
|
-
/**
|
|
118
|
-
* Sends a raw plain-text email with basic HTML pre-formatting.
|
|
119
|
-
* Useful for plain notifications, alerts, or simple text messages.
|
|
120
|
-
* @deprecated Use `mailer.sendRaw({ to, subject, text, html })` instead. This method will be removed in v1.0.0.
|
|
121
|
-
*/
|
|
122
|
-
sendSimpleMessage(to: string, subject: string, text: string, toName?: string): Promise<SendResult>;
|
|
123
113
|
}
|
|
124
|
-
/**
|
|
125
|
-
* Drop-in alias for MailKit to maintain compatibility with existing EmailService code.
|
|
126
|
-
* @deprecated Use `MailKit` instead. This alias will be removed in v1.0.0.
|
|
127
|
-
*/
|
|
128
|
-
export declare const EmailService: typeof MailKit;
|
|
129
|
-
/**
|
|
130
|
-
* @deprecated Use `MailKit` instead. This alias will be removed in v1.0.0.
|
|
131
|
-
*/
|
|
132
|
-
export type EmailService = MailKit;
|
|
133
114
|
/**
|
|
134
115
|
* Convenience factory to create a MailKit client instance.
|
|
135
116
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAGxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,EAAE,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAC5B;;OAEG;IACH,QAAQ,EAAE,gBAAgB,CAAC;IAC3B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,EAAE,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAC7B;;OAEG;IACH,GAAG,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAC9B;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAClC;;OAEG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC,aAAa,CAAC,CAAC;IAC7C;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,SAAS,EAAE,YAAY,CAAC;IACxB;;OAEG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;OAEG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CACpD;AAED;;GAEG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAY;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAiC;gBAExD,OAAO,EAAE,cAAc;IAWnC;;OAEG;IACH,YAAY,IAAI,YAAY;IAI5B;;OAEG;IACG,MAAM,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC;IAIhE;;;;;;;;;;OAUG;IACG,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC;IAgD7D;;;OAGG;IACG,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC;CAS7D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAE9D"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -14,6 +14,14 @@ export type Recipient = string | EmailAddress | {
|
|
|
14
14
|
email: string;
|
|
15
15
|
name?: string;
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Parses an RFC 5322 formatted address string into an EmailAddress.
|
|
19
|
+
* Supports formats such as:
|
|
20
|
+
* - "user@example.com" -> { address: "user@example.com" }
|
|
21
|
+
* - "Sender Name <sender@example.com>" -> { name: "Sender Name", address: "sender@example.com" }
|
|
22
|
+
* - "\"Sender Name\" <sender@example.com>" -> { name: "Sender Name", address: "sender@example.com" }
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseEmailAddress(input: string): EmailAddress;
|
|
17
25
|
/**
|
|
18
26
|
* Normalizes a Recipient value into a standardized EmailAddress.
|
|
19
27
|
*/
|
package/dist/core/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,YAAY,GACZ;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,YAAY,CAcrE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,GACnC,YAAY,EAAE,CAIhB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAC7B,GAAG,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAClC,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,YAAY,GACZ;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAa7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,YAAY,CAcrE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,GACnC,YAAY,EAAE,CAIhB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAC7B,GAAG,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAClC,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
|
package/dist/index.cjs
CHANGED
|
@@ -23,25 +23,40 @@ __export(index_exports, {
|
|
|
23
23
|
ConfigurationError: () => ConfigurationError,
|
|
24
24
|
ConsoleEmailSender: () => ConsoleEmailSender,
|
|
25
25
|
DefaultEmailRenderer: () => DefaultEmailRenderer,
|
|
26
|
-
EmailService: () => EmailService,
|
|
27
26
|
EmailTemplateFactory: () => EmailTemplateFactory,
|
|
28
27
|
MailKit: () => MailKit,
|
|
29
28
|
MailKitError: () => MailKitError,
|
|
30
29
|
MemoryEmailSender: () => MemoryEmailSender,
|
|
31
30
|
TemplateError: () => TemplateError,
|
|
32
31
|
TransportError: () => TransportError,
|
|
33
|
-
|
|
32
|
+
ZeptoMailHttpSender: () => ZeptoMailHttpSender,
|
|
34
33
|
buildHtml: () => buildHtml,
|
|
35
34
|
buildPlainText: () => buildPlainText,
|
|
36
35
|
createMailKit: () => createMailKit,
|
|
37
36
|
decodeHtmlEntities: () => decodeHtmlEntities,
|
|
38
37
|
htmlToPlainText: () => htmlToPlainText,
|
|
38
|
+
isValidHttpUrl: () => isValidHttpUrl,
|
|
39
39
|
normalizeRecipient: () => normalizeRecipient,
|
|
40
|
-
normalizeRecipients: () => normalizeRecipients
|
|
40
|
+
normalizeRecipients: () => normalizeRecipients,
|
|
41
|
+
parseEmailAddress: () => parseEmailAddress
|
|
41
42
|
});
|
|
42
43
|
module.exports = __toCommonJS(index_exports);
|
|
43
44
|
|
|
44
45
|
// src/core/types.ts
|
|
46
|
+
function parseEmailAddress(input) {
|
|
47
|
+
if (!input || typeof input !== "string") {
|
|
48
|
+
return { address: "" };
|
|
49
|
+
}
|
|
50
|
+
const trimmed = input.trim();
|
|
51
|
+
const match = trimmed.match(/^(?:["']?([^"']+)["']?\s+)?<?([^\s<>@]+@[^\s<>@]+)>?$/);
|
|
52
|
+
if (!match) {
|
|
53
|
+
return { address: trimmed };
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
address: match[2].trim(),
|
|
57
|
+
name: match[1]?.trim() || void 0
|
|
58
|
+
};
|
|
59
|
+
}
|
|
45
60
|
function normalizeRecipient(recipient) {
|
|
46
61
|
if (typeof recipient === "string") {
|
|
47
62
|
return { address: recipient.trim() };
|
|
@@ -113,6 +128,15 @@ function htmlToPlainText(html) {
|
|
|
113
128
|
}
|
|
114
129
|
|
|
115
130
|
// src/renderers/default/default-renderer.ts
|
|
131
|
+
function isValidHttpUrl(urlString) {
|
|
132
|
+
if (!urlString || typeof urlString !== "string") return false;
|
|
133
|
+
try {
|
|
134
|
+
const url = new URL(urlString.trim());
|
|
135
|
+
return (url.protocol === "http:" || url.protocol === "https:") && url.hostname.length > 0;
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
116
140
|
function buildPlainText(dto) {
|
|
117
141
|
if (dto.noHtmlMessage) {
|
|
118
142
|
return dto.noHtmlMessage;
|
|
@@ -155,6 +179,15 @@ function buildHtml(dto, options = {}) {
|
|
|
155
179
|
`<body style="margin:0;padding:24px 16px;background-color:#f9fafb;font-family:${fontFamily};color:#1f2937;line-height:1.6;">`,
|
|
156
180
|
`<div style="max-width:${maxWidth};margin:0 auto;background-color:#ffffff;padding:32px 28px;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,0.1);border:1px solid #f3f4f6;">`
|
|
157
181
|
];
|
|
182
|
+
const logoCandidate = dto.logoUrl ?? options.logoUrl;
|
|
183
|
+
if (logoCandidate && isValidHttpUrl(logoCandidate)) {
|
|
184
|
+
const validLogo = logoCandidate.trim();
|
|
185
|
+
parts.push(
|
|
186
|
+
`<div style="margin-bottom:24px;text-align:center;">`,
|
|
187
|
+
` <img src="${validLogo}" alt="${dto.fromName || "Logo"}" style="max-height:48px;max-width:200px;height:auto;width:auto;display:inline-block;border:0;outline:none;text-decoration:none;" />`,
|
|
188
|
+
`</div>`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
158
191
|
if (dto.imageUrl) {
|
|
159
192
|
parts.push(
|
|
160
193
|
`<div style="margin-bottom:24px;text-align:center;">`,
|
|
@@ -240,29 +273,20 @@ var MailKit = class {
|
|
|
240
273
|
return await this.renderer.render(template);
|
|
241
274
|
}
|
|
242
275
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
276
|
+
* Sends an email using a strongly-typed template DTO.
|
|
277
|
+
*
|
|
278
|
+
* Example:
|
|
279
|
+
* ```ts
|
|
280
|
+
* await mailer.send({
|
|
281
|
+
* to: 'user@example.com',
|
|
282
|
+
* template: myDto,
|
|
283
|
+
* });
|
|
284
|
+
* ```
|
|
245
285
|
*/
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
let opts;
|
|
251
|
-
if (typeof optionsOrTo === "string") {
|
|
252
|
-
if (!maybeTemplate) {
|
|
253
|
-
throw new ConfigurationError("MailKit.send: Missing required template DTO argument.");
|
|
254
|
-
}
|
|
255
|
-
opts = {
|
|
256
|
-
to: optionsOrTo,
|
|
257
|
-
template: maybeTemplate,
|
|
258
|
-
toName: maybeToName
|
|
259
|
-
};
|
|
260
|
-
} else {
|
|
261
|
-
opts = optionsOrTo;
|
|
262
|
-
}
|
|
263
|
-
const template = opts.template;
|
|
264
|
-
const recipientName = opts.toName ?? template.userName;
|
|
265
|
-
let resolvedFromName = opts.fromName ?? template.fromName;
|
|
286
|
+
async send(options) {
|
|
287
|
+
const template = options.template;
|
|
288
|
+
const recipientName = options.toName ?? template.userName;
|
|
289
|
+
let resolvedFromName = options.fromName ?? template.fromName;
|
|
266
290
|
if (!resolvedFromName && this.brandNameResolver) {
|
|
267
291
|
resolvedFromName = await this.brandNameResolver();
|
|
268
292
|
}
|
|
@@ -272,12 +296,12 @@ var MailKit = class {
|
|
|
272
296
|
const rendered = await this.renderer.render(template);
|
|
273
297
|
const plaintextBody = template.noHtmlMessage ?? rendered.text;
|
|
274
298
|
let toRecipients;
|
|
275
|
-
if (typeof
|
|
276
|
-
toRecipients = { address:
|
|
299
|
+
if (typeof options.to === "string" && recipientName) {
|
|
300
|
+
toRecipients = { address: options.to, name: recipientName };
|
|
277
301
|
} else {
|
|
278
|
-
toRecipients =
|
|
302
|
+
toRecipients = options.to;
|
|
279
303
|
}
|
|
280
|
-
let from =
|
|
304
|
+
let from = options.from ?? this.defaultFrom;
|
|
281
305
|
if (resolvedFromName && from) {
|
|
282
306
|
const normalizedFrom = normalizeRecipient(from);
|
|
283
307
|
from = { address: normalizedFrom.address, name: resolvedFromName };
|
|
@@ -288,47 +312,35 @@ var MailKit = class {
|
|
|
288
312
|
subject: template.subject,
|
|
289
313
|
html: rendered.html,
|
|
290
314
|
text: plaintextBody,
|
|
291
|
-
cc:
|
|
292
|
-
bcc:
|
|
293
|
-
replyTo:
|
|
294
|
-
attachments:
|
|
295
|
-
headers:
|
|
296
|
-
tags:
|
|
297
|
-
metadata:
|
|
315
|
+
cc: options.cc,
|
|
316
|
+
bcc: options.bcc,
|
|
317
|
+
replyTo: options.replyTo,
|
|
318
|
+
attachments: options.attachments,
|
|
319
|
+
headers: options.headers,
|
|
320
|
+
tags: options.tags,
|
|
321
|
+
metadata: options.metadata
|
|
298
322
|
});
|
|
299
323
|
}
|
|
300
324
|
/**
|
|
301
325
|
* Dispatches a raw, pre-rendered email directly through the transport.
|
|
326
|
+
* If `options.text` is omitted, plain text is automatically derived from `options.html`.
|
|
302
327
|
*/
|
|
303
328
|
async sendRaw(options) {
|
|
304
329
|
const from = options.from ?? this.defaultFrom;
|
|
330
|
+
const text = options.text ?? (options.html ? htmlToPlainText(options.html) : void 0);
|
|
305
331
|
return await this.transport.send({
|
|
306
332
|
...options,
|
|
307
|
-
from
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* Sends a raw plain-text email with basic HTML pre-formatting.
|
|
312
|
-
* Useful for plain notifications, alerts, or simple text messages.
|
|
313
|
-
* @deprecated Use `mailer.sendRaw({ to, subject, text, html })` instead. This method will be removed in v1.0.0.
|
|
314
|
-
*/
|
|
315
|
-
async sendSimpleMessage(to, subject, text, toName = "") {
|
|
316
|
-
const html = `<pre style="font-family:monospace;white-space:pre-wrap;">${text}</pre>`;
|
|
317
|
-
return await this.sendRaw({
|
|
318
|
-
to: { address: to, name: toName },
|
|
319
|
-
subject,
|
|
320
|
-
html,
|
|
333
|
+
from,
|
|
321
334
|
text
|
|
322
335
|
});
|
|
323
336
|
}
|
|
324
337
|
};
|
|
325
|
-
var EmailService = MailKit;
|
|
326
338
|
function createMailKit(options) {
|
|
327
339
|
return new MailKit(options);
|
|
328
340
|
}
|
|
329
341
|
|
|
330
342
|
// src/transports/zeptomail/zeptomail-sender.ts
|
|
331
|
-
var
|
|
343
|
+
var ZeptoMailHttpSender = class {
|
|
332
344
|
name = "zeptomail";
|
|
333
345
|
apiUrl;
|
|
334
346
|
authHeader;
|
|
@@ -336,10 +348,10 @@ var ZeptoMailSender = class {
|
|
|
336
348
|
fetchImpl;
|
|
337
349
|
constructor(config) {
|
|
338
350
|
if (!config.apiToken || typeof config.apiToken !== "string") {
|
|
339
|
-
throw new ConfigurationError("
|
|
351
|
+
throw new ConfigurationError("ZeptoMailHttpSender: Missing or invalid apiToken.");
|
|
340
352
|
}
|
|
341
353
|
if (!config.from) {
|
|
342
|
-
throw new ConfigurationError("
|
|
354
|
+
throw new ConfigurationError("ZeptoMailHttpSender: Missing required default from address.");
|
|
343
355
|
}
|
|
344
356
|
const host = (config.apiHost || "api.zeptomail.com").replace(/^https?:\/\//, "").replace(/\/+$/, "");
|
|
345
357
|
this.apiUrl = `https://${host}/v1.1/email`;
|
|
@@ -347,12 +359,12 @@ var ZeptoMailSender = class {
|
|
|
347
359
|
this.authHeader = token.startsWith("Zoho-") ? token : `Zoho-enczapikey ${token}`;
|
|
348
360
|
this.defaultFrom = normalizeRecipient(config.from);
|
|
349
361
|
if (!this.defaultFrom.address) {
|
|
350
|
-
throw new ConfigurationError("
|
|
362
|
+
throw new ConfigurationError("ZeptoMailHttpSender: Configured from address must not be empty.");
|
|
351
363
|
}
|
|
352
364
|
this.fetchImpl = config.fetch ?? globalThis.fetch;
|
|
353
365
|
if (typeof this.fetchImpl !== "function") {
|
|
354
366
|
throw new ConfigurationError(
|
|
355
|
-
"
|
|
367
|
+
"ZeptoMailHttpSender: No global fetch implementation found. Pass a custom fetch to ZeptoMailHttpConfig if running on Node < 18."
|
|
356
368
|
);
|
|
357
369
|
}
|
|
358
370
|
}
|
|
@@ -362,7 +374,7 @@ var ZeptoMailSender = class {
|
|
|
362
374
|
async send(options) {
|
|
363
375
|
const toRecipients = normalizeRecipients(options.to);
|
|
364
376
|
if (toRecipients.length === 0) {
|
|
365
|
-
throw new TransportError("
|
|
377
|
+
throw new TransportError("ZeptoMailHttpSender: At least one recipient (to) must be specified.", {
|
|
366
378
|
transportName: this.name
|
|
367
379
|
});
|
|
368
380
|
}
|
|
@@ -444,7 +456,7 @@ var ZeptoMailSender = class {
|
|
|
444
456
|
body: JSON.stringify(payload)
|
|
445
457
|
});
|
|
446
458
|
} catch (err) {
|
|
447
|
-
throw new TransportError(`
|
|
459
|
+
throw new TransportError(`ZeptoMailHttpSender: Network request failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
448
460
|
transportName: this.name,
|
|
449
461
|
cause: err
|
|
450
462
|
});
|
|
@@ -458,7 +470,7 @@ var ZeptoMailSender = class {
|
|
|
458
470
|
}
|
|
459
471
|
if (!response.ok) {
|
|
460
472
|
const errorMessage = responseData?.error?.message || responseData?.message || `HTTP ${response.status}: ${responseText || response.statusText}`;
|
|
461
|
-
throw new TransportError(`
|
|
473
|
+
throw new TransportError(`ZeptoMailHttpSender: Failed to send email - ${errorMessage}`, {
|
|
462
474
|
transportName: this.name,
|
|
463
475
|
statusCode: response.status,
|
|
464
476
|
responseBody: responseText
|
|
@@ -471,20 +483,6 @@ var ZeptoMailSender = class {
|
|
|
471
483
|
response: responseData
|
|
472
484
|
};
|
|
473
485
|
}
|
|
474
|
-
/**
|
|
475
|
-
* Backward-compatible helper method matching legacy signature:
|
|
476
|
-
* `sendEmail(to, htmlBody, subject, toName?, plaintextBody?, fromName?)`
|
|
477
|
-
* @deprecated Use `send({ to, subject, html, text, ... })` instead. This method will be removed in v1.0.0.
|
|
478
|
-
*/
|
|
479
|
-
async sendEmail(to, htmlBody, subject, toName = "", plaintextBody, fromName) {
|
|
480
|
-
await this.send({
|
|
481
|
-
to: { address: to, name: toName },
|
|
482
|
-
from: fromName ? { address: this.defaultFrom.address, name: fromName } : this.defaultFrom,
|
|
483
|
-
subject,
|
|
484
|
-
html: htmlBody,
|
|
485
|
-
text: plaintextBody
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
486
|
};
|
|
489
487
|
|
|
490
488
|
// src/transports/memory/memory-sender.ts
|
|
@@ -584,9 +582,11 @@ var EmailTemplateFactory = class _EmailTemplateFactory {
|
|
|
584
582
|
defaultBrandName;
|
|
585
583
|
defaultSecurityMessage;
|
|
586
584
|
footerResolver;
|
|
585
|
+
defaultLogoUrl;
|
|
587
586
|
constructor(config = {}) {
|
|
588
587
|
this.defaultBrandName = config.brandName || "Application";
|
|
589
588
|
this.defaultSecurityMessage = config.securityMessage || DEFAULT_SECURITY_MESSAGE;
|
|
589
|
+
this.defaultLogoUrl = config.logoUrl;
|
|
590
590
|
if (typeof config.footer === "function") {
|
|
591
591
|
this.footerResolver = config.footer;
|
|
592
592
|
} else if (typeof config.footer === "string") {
|
|
@@ -612,6 +612,7 @@ var EmailTemplateFactory = class _EmailTemplateFactory {
|
|
|
612
612
|
subject: "Verify Your Email Address",
|
|
613
613
|
header: `Welcome to ${brand}!`,
|
|
614
614
|
body,
|
|
615
|
+
logoUrl: this.defaultLogoUrl,
|
|
615
616
|
otpCode: otp,
|
|
616
617
|
actionUrl: verificationUrl,
|
|
617
618
|
actionText: "Verify Email",
|
|
@@ -640,6 +641,7 @@ Your OTP code is: ${otp}` : "";
|
|
|
640
641
|
subject: "Password Reset Request",
|
|
641
642
|
header: "Reset Your Password",
|
|
642
643
|
body,
|
|
644
|
+
logoUrl: this.defaultLogoUrl,
|
|
643
645
|
otpCode: otp || void 0,
|
|
644
646
|
actionUrl: resetUrl,
|
|
645
647
|
actionText: "Reset Password",
|
|
@@ -662,6 +664,7 @@ ${resetUrl}`
|
|
|
662
664
|
subject,
|
|
663
665
|
header,
|
|
664
666
|
body,
|
|
667
|
+
logoUrl: this.defaultLogoUrl,
|
|
665
668
|
footer: this.buildFooter(brand),
|
|
666
669
|
fromName: brand,
|
|
667
670
|
noHtmlMessage: htmlToPlainText(body)
|
|
@@ -670,7 +673,7 @@ ${resetUrl}`
|
|
|
670
673
|
/**
|
|
671
674
|
* Newsletter / announcement email with an optional hero image and CTA button.
|
|
672
675
|
*/
|
|
673
|
-
createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName) {
|
|
676
|
+
createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName, logoUrl) {
|
|
674
677
|
const brand = this.resolveBrand(brandName);
|
|
675
678
|
let noHtmlMessage = htmlToPlainText(body);
|
|
676
679
|
if (actionUrl) {
|
|
@@ -682,6 +685,7 @@ ${actionText || "Learn more"}: ${actionUrl}`;
|
|
|
682
685
|
subject,
|
|
683
686
|
header,
|
|
684
687
|
body,
|
|
688
|
+
logoUrl: logoUrl || this.defaultLogoUrl,
|
|
685
689
|
imageUrl,
|
|
686
690
|
actionUrl,
|
|
687
691
|
actionText,
|
|
@@ -704,6 +708,7 @@ ${magicLink}` : "";
|
|
|
704
708
|
subject: "Your Login Verification Code",
|
|
705
709
|
header: "Two-Factor Authentication",
|
|
706
710
|
body,
|
|
711
|
+
logoUrl: this.defaultLogoUrl,
|
|
707
712
|
otpCode: otp,
|
|
708
713
|
actionUrl: magicLink || void 0,
|
|
709
714
|
actionText: magicLink ? "Verify Login" : void 0,
|
|
@@ -726,6 +731,7 @@ Your verification code is: ${otp}` + plainLink
|
|
|
726
731
|
subject: "Your Magic Link",
|
|
727
732
|
header: `Sign In to ${brand}`,
|
|
728
733
|
body,
|
|
734
|
+
logoUrl: this.defaultLogoUrl,
|
|
729
735
|
actionUrl: magicLink,
|
|
730
736
|
actionText: "Sign In",
|
|
731
737
|
extraMessage: this.defaultSecurityMessage,
|
|
@@ -755,7 +761,7 @@ ${magicLink}`
|
|
|
755
761
|
static createSimpleMessage(subject, header, body, brandName) {
|
|
756
762
|
return this.defaultInstance.createSimpleMessage(subject, header, body, brandName);
|
|
757
763
|
}
|
|
758
|
-
static createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName) {
|
|
764
|
+
static createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName, logoUrl) {
|
|
759
765
|
return this.defaultInstance.createNewsLetter(
|
|
760
766
|
subject,
|
|
761
767
|
header,
|
|
@@ -763,7 +769,8 @@ ${magicLink}`
|
|
|
763
769
|
imageUrl,
|
|
764
770
|
actionUrl,
|
|
765
771
|
actionText,
|
|
766
|
-
brandName
|
|
772
|
+
brandName,
|
|
773
|
+
logoUrl
|
|
767
774
|
);
|
|
768
775
|
}
|
|
769
776
|
static create2FAEmail(username, otp, magicLink, brandName) {
|
|
@@ -778,20 +785,21 @@ ${magicLink}`
|
|
|
778
785
|
ConfigurationError,
|
|
779
786
|
ConsoleEmailSender,
|
|
780
787
|
DefaultEmailRenderer,
|
|
781
|
-
EmailService,
|
|
782
788
|
EmailTemplateFactory,
|
|
783
789
|
MailKit,
|
|
784
790
|
MailKitError,
|
|
785
791
|
MemoryEmailSender,
|
|
786
792
|
TemplateError,
|
|
787
793
|
TransportError,
|
|
788
|
-
|
|
794
|
+
ZeptoMailHttpSender,
|
|
789
795
|
buildHtml,
|
|
790
796
|
buildPlainText,
|
|
791
797
|
createMailKit,
|
|
792
798
|
decodeHtmlEntities,
|
|
793
799
|
htmlToPlainText,
|
|
800
|
+
isValidHttpUrl,
|
|
794
801
|
normalizeRecipient,
|
|
795
|
-
normalizeRecipients
|
|
802
|
+
normalizeRecipients,
|
|
803
|
+
parseEmailAddress
|
|
796
804
|
});
|
|
797
805
|
//# sourceMappingURL=index.cjs.map
|