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 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
- - `ZeptoMailSender` (Zoho Mail transactional API via native `fetch`).
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, ZeptoMailSender, EmailTemplateFactory } from 'ts-mailkit';
55
+ import { createMailKit, ZeptoMailHttpSender, EmailTemplateFactory } from 'ts-mailkit';
52
56
 
53
57
  // Configure the Zoho ZeptoMail HTTP transport
54
- const transport = new ZeptoMailSender({
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: 'System Alert: Database failover completed successfully.',
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. Pluggable Transports (`IEmailSender`)
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)** &mdash; 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)
@@ -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. ZeptoMailSender, MemoryEmailSender).
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
- * Supports both modern options object:
104
- * `mailer.send({ to: 'user@example.com', template: myDto })`
105
- * and legacy positional arguments for seamless migration:
106
- * `mailer.send('user@example.com', myDto, 'User Name')`
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":"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"}
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"}
@@ -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
  */
@@ -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
- ZeptoMailSender: () => ZeptoMailSender,
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
- * Returns the plain-text fallback for an email DTO without sending.
244
- * @deprecated Use `mailer.render(template)` or `buildPlainText(template)` instead. This method will be removed in v1.0.0.
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
- buildPlainTextMessage(template) {
247
- return template.noHtmlMessage ?? buildPlainText(template);
248
- }
249
- async send(optionsOrTo, maybeTemplate, maybeToName) {
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 opts.to === "string" && recipientName) {
276
- toRecipients = { address: opts.to, name: recipientName };
299
+ if (typeof options.to === "string" && recipientName) {
300
+ toRecipients = { address: options.to, name: recipientName };
277
301
  } else {
278
- toRecipients = opts.to;
302
+ toRecipients = options.to;
279
303
  }
280
- let from = opts.from ?? this.defaultFrom;
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: opts.cc,
292
- bcc: opts.bcc,
293
- replyTo: opts.replyTo,
294
- attachments: opts.attachments,
295
- headers: opts.headers,
296
- tags: opts.tags,
297
- metadata: opts.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 ZeptoMailSender = class {
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("ZeptoMailSender: Missing or invalid apiToken.");
351
+ throw new ConfigurationError("ZeptoMailHttpSender: Missing or invalid apiToken.");
340
352
  }
341
353
  if (!config.from) {
342
- throw new ConfigurationError("ZeptoMailSender: Missing required default from address.");
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("ZeptoMailSender: Configured from address must not be empty.");
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
- "ZeptoMailSender: No global fetch implementation found. Pass a custom fetch to ZeptoMailConfig if running on Node < 18."
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("ZeptoMailSender: At least one recipient (to) must be specified.", {
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(`ZeptoMailSender: Network request failed: ${err instanceof Error ? err.message : String(err)}`, {
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(`ZeptoMailSender: Failed to send email - ${errorMessage}`, {
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
- ZeptoMailSender,
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