ts-mailkit 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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
@@ -261,12 +288,14 @@ pnpm run prepublishOnly
261
288
 
262
289
  ## Deprecated API (Scheduled for Removal in v1.0.0)
263
290
 
264
- The following legacy migration bridges are marked `@deprecated` and scheduled for removal in `v1.0.0`:
291
+ The following legacy migration bridges and aliases are marked `@deprecated` and scheduled for removal in `v1.0.0`:
265
292
 
266
293
  | Deprecated | Replacement | Notes |
267
294
  | :--- | :--- | :--- |
268
- | `mailer.send(to, template, toName)` | `mailer.send({ to, template, ... })` | Use the type-safe options object. |
295
+ | `ZeptoMailSender` | `ZeptoMailHttpSender` | Renamed to explicitly signify HTTP transport adapter. |
296
+ | `ZeptoMailConfig` | `ZeptoMailHttpConfig` | Configuration interface rename. |
269
297
  | `sender.sendEmail(...)` | `sender.send({ to, subject, html, text, ... })` | Standardized on `SendMailOptions`. |
298
+ | `mailer.send(to, template, toName)` | `mailer.send({ to, template, ... })` | Use the type-safe options object. |
270
299
  | `mailer.sendSimpleMessage(...)` | `mailer.sendRaw({ to, subject, text, html })` | Direct raw email dispatch. |
271
300
  | `mailer.buildPlainTextMessage(template)` | `mailer.render(template)` or `buildPlainText(dto)` | Standalone or client renderer methods. |
272
301
  | `EmailService` | `MailKit` | Main client class. |
@@ -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
  /**
@@ -94,44 +94,48 @@ export declare class MailKit {
94
94
  */
95
95
  render(template: EmailTemplateDto): Promise<RenderedEmail>;
96
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.
97
+ * Legacy positional send signature for backwards compatibility with 0.1.x:
98
+ * `send(to: string, template: EmailTemplateDto, toName?: string): Promise<SendResult>`
99
+ *
100
+ * @deprecated Use `send({ to, template, ... })` instead. This overload will be removed in v1.0.0.
99
101
  */
100
- buildPlainTextMessage(template: EmailTemplateDto): string;
102
+ send(to: string, template: EmailTemplateDto, toName?: string): Promise<SendResult>;
101
103
  /**
102
104
  * 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')`
105
+ *
106
+ * Example:
107
+ * ```ts
108
+ * await mailer.send({
109
+ * to: 'user@example.com',
110
+ * template: myDto,
111
+ * });
112
+ * ```
107
113
  */
108
114
  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
115
  /**
114
116
  * Dispatches a raw, pre-rendered email directly through the transport.
117
+ * If `options.text` is omitted, plain text is automatically derived from `options.html`.
115
118
  */
116
119
  sendRaw(options: SendMailOptions): Promise<SendResult>;
117
120
  /**
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
+ * Helper method matching legacy 0.1.x `sendSimpleMessage(to, subject, text, fromName?)`.
122
+ * @deprecated Use `sendRaw({ to, subject, text, ... })` instead. This method will be removed in v1.0.0.
121
123
  */
122
- sendSimpleMessage(to: string, subject: string, text: string, toName?: string): Promise<SendResult>;
124
+ sendSimpleMessage(to: string, subject: string, text: string, fromName?: string): Promise<SendResult>;
125
+ /**
126
+ * Legacy helper method to construct plain text from a template.
127
+ * @deprecated Use `render(template)` or `buildPlainText(dto)` instead. This method will be removed in v1.0.0.
128
+ */
129
+ buildPlainTextMessage(template: EmailTemplateDto): string;
123
130
  }
124
131
  /**
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.
132
+ * Convenience factory to create a MailKit client instance.
127
133
  */
128
- export declare const EmailService: typeof MailKit;
134
+ export declare function createMailKit(options: MailKitOptions): MailKit;
129
135
  /**
136
+ * Backward-compatibility alias for `MailKit`.
130
137
  * @deprecated Use `MailKit` instead. This alias will be removed in v1.0.0.
131
138
  */
139
+ export declare const EmailService: typeof MailKit;
132
140
  export type EmailService = MailKit;
133
- /**
134
- * Convenience factory to create a MailKit client instance.
135
- */
136
- export declare function createMailKit(options: MailKitOptions): MailKit;
137
141
  //# sourceMappingURL=client.d.ts.map
@@ -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":"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"}
@@ -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
@@ -30,18 +30,35 @@ __export(index_exports, {
30
30
  MemoryEmailSender: () => MemoryEmailSender,
31
31
  TemplateError: () => TemplateError,
32
32
  TransportError: () => TransportError,
33
+ ZeptoMailHttpSender: () => ZeptoMailHttpSender,
33
34
  ZeptoMailSender: () => ZeptoMailSender,
34
35
  buildHtml: () => buildHtml,
35
36
  buildPlainText: () => buildPlainText,
36
37
  createMailKit: () => createMailKit,
37
38
  decodeHtmlEntities: () => decodeHtmlEntities,
38
39
  htmlToPlainText: () => htmlToPlainText,
40
+ isValidHttpUrl: () => isValidHttpUrl,
39
41
  normalizeRecipient: () => normalizeRecipient,
40
- normalizeRecipients: () => normalizeRecipients
42
+ normalizeRecipients: () => normalizeRecipients,
43
+ parseEmailAddress: () => parseEmailAddress
41
44
  });
42
45
  module.exports = __toCommonJS(index_exports);
43
46
 
44
47
  // src/core/types.ts
48
+ function parseEmailAddress(input) {
49
+ if (!input || typeof input !== "string") {
50
+ return { address: "" };
51
+ }
52
+ const trimmed = input.trim();
53
+ const match = trimmed.match(/^(?:["']?([^"']+)["']?\s+)?<?([^\s<>@]+@[^\s<>@]+)>?$/);
54
+ if (!match) {
55
+ return { address: trimmed };
56
+ }
57
+ return {
58
+ address: match[2].trim(),
59
+ name: match[1]?.trim() || void 0
60
+ };
61
+ }
45
62
  function normalizeRecipient(recipient) {
46
63
  if (typeof recipient === "string") {
47
64
  return { address: recipient.trim() };
@@ -113,6 +130,15 @@ function htmlToPlainText(html) {
113
130
  }
114
131
 
115
132
  // src/renderers/default/default-renderer.ts
133
+ function isValidHttpUrl(urlString) {
134
+ if (!urlString || typeof urlString !== "string") return false;
135
+ try {
136
+ const url = new URL(urlString.trim());
137
+ return (url.protocol === "http:" || url.protocol === "https:") && url.hostname.length > 0;
138
+ } catch {
139
+ return false;
140
+ }
141
+ }
116
142
  function buildPlainText(dto) {
117
143
  if (dto.noHtmlMessage) {
118
144
  return dto.noHtmlMessage;
@@ -155,6 +181,15 @@ function buildHtml(dto, options = {}) {
155
181
  `<body style="margin:0;padding:24px 16px;background-color:#f9fafb;font-family:${fontFamily};color:#1f2937;line-height:1.6;">`,
156
182
  `<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
183
  ];
184
+ const logoCandidate = dto.logoUrl ?? options.logoUrl;
185
+ if (logoCandidate && isValidHttpUrl(logoCandidate)) {
186
+ const validLogo = logoCandidate.trim();
187
+ parts.push(
188
+ `<div style="margin-bottom:24px;text-align:center;">`,
189
+ ` <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;" />`,
190
+ `</div>`
191
+ );
192
+ }
158
193
  if (dto.imageUrl) {
159
194
  parts.push(
160
195
  `<div style="margin-bottom:24px;text-align:center;">`,
@@ -239,30 +274,15 @@ var MailKit = class {
239
274
  async render(template) {
240
275
  return await this.renderer.render(template);
241
276
  }
242
- /**
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.
245
- */
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;
277
+ async send(toOrOptions, maybeTemplate, maybeToName) {
278
+ const options = typeof toOrOptions === "string" ? {
279
+ to: toOrOptions,
280
+ template: maybeTemplate,
281
+ toName: maybeToName
282
+ } : toOrOptions;
283
+ const template = options.template;
284
+ const recipientName = options.toName ?? template.userName;
285
+ let resolvedFromName = options.fromName ?? template.fromName;
266
286
  if (!resolvedFromName && this.brandNameResolver) {
267
287
  resolvedFromName = await this.brandNameResolver();
268
288
  }
@@ -272,12 +292,12 @@ var MailKit = class {
272
292
  const rendered = await this.renderer.render(template);
273
293
  const plaintextBody = template.noHtmlMessage ?? rendered.text;
274
294
  let toRecipients;
275
- if (typeof opts.to === "string" && recipientName) {
276
- toRecipients = { address: opts.to, name: recipientName };
295
+ if (typeof options.to === "string" && recipientName) {
296
+ toRecipients = { address: options.to, name: recipientName };
277
297
  } else {
278
- toRecipients = opts.to;
298
+ toRecipients = options.to;
279
299
  }
280
- let from = opts.from ?? this.defaultFrom;
300
+ let from = options.from ?? this.defaultFrom;
281
301
  if (resolvedFromName && from) {
282
302
  const normalizedFrom = normalizeRecipient(from);
283
303
  from = { address: normalizedFrom.address, name: resolvedFromName };
@@ -288,47 +308,56 @@ var MailKit = class {
288
308
  subject: template.subject,
289
309
  html: rendered.html,
290
310
  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
311
+ cc: options.cc,
312
+ bcc: options.bcc,
313
+ replyTo: options.replyTo,
314
+ attachments: options.attachments,
315
+ headers: options.headers,
316
+ tags: options.tags,
317
+ metadata: options.metadata
298
318
  });
299
319
  }
300
320
  /**
301
321
  * Dispatches a raw, pre-rendered email directly through the transport.
322
+ * If `options.text` is omitted, plain text is automatically derived from `options.html`.
302
323
  */
303
324
  async sendRaw(options) {
304
325
  const from = options.from ?? this.defaultFrom;
326
+ const text = options.text ?? (options.html ? htmlToPlainText(options.html) : void 0);
305
327
  return await this.transport.send({
306
328
  ...options,
307
- from
329
+ from,
330
+ text
308
331
  });
309
332
  }
310
333
  /**
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.
334
+ * Helper method matching legacy 0.1.x `sendSimpleMessage(to, subject, text, fromName?)`.
335
+ * @deprecated Use `sendRaw({ to, subject, text, ... })` instead. This method will be removed in v1.0.0.
314
336
  */
315
- async sendSimpleMessage(to, subject, text, toName = "") {
316
- const html = `<pre style="font-family:monospace;white-space:pre-wrap;">${text}</pre>`;
337
+ async sendSimpleMessage(to, subject, text, fromName) {
317
338
  return await this.sendRaw({
318
- to: { address: to, name: toName },
339
+ to,
319
340
  subject,
320
- html,
321
- text
341
+ text,
342
+ html: `<pre style="font-family:monospace;white-space:pre-wrap;">${text}</pre>`,
343
+ from: fromName && this.defaultFrom ? { address: normalizeRecipient(this.defaultFrom).address, name: fromName } : this.defaultFrom
322
344
  });
323
345
  }
346
+ /**
347
+ * Legacy helper method to construct plain text from a template.
348
+ * @deprecated Use `render(template)` or `buildPlainText(dto)` instead. This method will be removed in v1.0.0.
349
+ */
350
+ buildPlainTextMessage(template) {
351
+ return template.noHtmlMessage ?? buildPlainText(template);
352
+ }
324
353
  };
325
- var EmailService = MailKit;
326
354
  function createMailKit(options) {
327
355
  return new MailKit(options);
328
356
  }
357
+ var EmailService = MailKit;
329
358
 
330
359
  // src/transports/zeptomail/zeptomail-sender.ts
331
- var ZeptoMailSender = class {
360
+ var ZeptoMailHttpSender = class {
332
361
  name = "zeptomail";
333
362
  apiUrl;
334
363
  authHeader;
@@ -336,10 +365,10 @@ var ZeptoMailSender = class {
336
365
  fetchImpl;
337
366
  constructor(config) {
338
367
  if (!config.apiToken || typeof config.apiToken !== "string") {
339
- throw new ConfigurationError("ZeptoMailSender: Missing or invalid apiToken.");
368
+ throw new ConfigurationError("ZeptoMailHttpSender: Missing or invalid apiToken.");
340
369
  }
341
370
  if (!config.from) {
342
- throw new ConfigurationError("ZeptoMailSender: Missing required default from address.");
371
+ throw new ConfigurationError("ZeptoMailHttpSender: Missing required default from address.");
343
372
  }
344
373
  const host = (config.apiHost || "api.zeptomail.com").replace(/^https?:\/\//, "").replace(/\/+$/, "");
345
374
  this.apiUrl = `https://${host}/v1.1/email`;
@@ -347,12 +376,12 @@ var ZeptoMailSender = class {
347
376
  this.authHeader = token.startsWith("Zoho-") ? token : `Zoho-enczapikey ${token}`;
348
377
  this.defaultFrom = normalizeRecipient(config.from);
349
378
  if (!this.defaultFrom.address) {
350
- throw new ConfigurationError("ZeptoMailSender: Configured from address must not be empty.");
379
+ throw new ConfigurationError("ZeptoMailHttpSender: Configured from address must not be empty.");
351
380
  }
352
381
  this.fetchImpl = config.fetch ?? globalThis.fetch;
353
382
  if (typeof this.fetchImpl !== "function") {
354
383
  throw new ConfigurationError(
355
- "ZeptoMailSender: No global fetch implementation found. Pass a custom fetch to ZeptoMailConfig if running on Node < 18."
384
+ "ZeptoMailHttpSender: No global fetch implementation found. Pass a custom fetch to ZeptoMailHttpConfig if running on Node < 18."
356
385
  );
357
386
  }
358
387
  }
@@ -362,7 +391,7 @@ var ZeptoMailSender = class {
362
391
  async send(options) {
363
392
  const toRecipients = normalizeRecipients(options.to);
364
393
  if (toRecipients.length === 0) {
365
- throw new TransportError("ZeptoMailSender: At least one recipient (to) must be specified.", {
394
+ throw new TransportError("ZeptoMailHttpSender: At least one recipient (to) must be specified.", {
366
395
  transportName: this.name
367
396
  });
368
397
  }
@@ -444,7 +473,7 @@ var ZeptoMailSender = class {
444
473
  body: JSON.stringify(payload)
445
474
  });
446
475
  } catch (err) {
447
- throw new TransportError(`ZeptoMailSender: Network request failed: ${err instanceof Error ? err.message : String(err)}`, {
476
+ throw new TransportError(`ZeptoMailHttpSender: Network request failed: ${err instanceof Error ? err.message : String(err)}`, {
448
477
  transportName: this.name,
449
478
  cause: err
450
479
  });
@@ -458,7 +487,7 @@ var ZeptoMailSender = class {
458
487
  }
459
488
  if (!response.ok) {
460
489
  const errorMessage = responseData?.error?.message || responseData?.message || `HTTP ${response.status}: ${responseText || response.statusText}`;
461
- throw new TransportError(`ZeptoMailSender: Failed to send email - ${errorMessage}`, {
490
+ throw new TransportError(`ZeptoMailHttpSender: Failed to send email - ${errorMessage}`, {
462
491
  transportName: this.name,
463
492
  statusCode: response.status,
464
493
  responseBody: responseText
@@ -486,6 +515,7 @@ var ZeptoMailSender = class {
486
515
  });
487
516
  }
488
517
  };
518
+ var ZeptoMailSender = ZeptoMailHttpSender;
489
519
 
490
520
  // src/transports/memory/memory-sender.ts
491
521
  var MemoryEmailSender = class {
@@ -584,9 +614,11 @@ var EmailTemplateFactory = class _EmailTemplateFactory {
584
614
  defaultBrandName;
585
615
  defaultSecurityMessage;
586
616
  footerResolver;
617
+ defaultLogoUrl;
587
618
  constructor(config = {}) {
588
619
  this.defaultBrandName = config.brandName || "Application";
589
620
  this.defaultSecurityMessage = config.securityMessage || DEFAULT_SECURITY_MESSAGE;
621
+ this.defaultLogoUrl = config.logoUrl;
590
622
  if (typeof config.footer === "function") {
591
623
  this.footerResolver = config.footer;
592
624
  } else if (typeof config.footer === "string") {
@@ -612,6 +644,7 @@ var EmailTemplateFactory = class _EmailTemplateFactory {
612
644
  subject: "Verify Your Email Address",
613
645
  header: `Welcome to ${brand}!`,
614
646
  body,
647
+ logoUrl: this.defaultLogoUrl,
615
648
  otpCode: otp,
616
649
  actionUrl: verificationUrl,
617
650
  actionText: "Verify Email",
@@ -640,6 +673,7 @@ Your OTP code is: ${otp}` : "";
640
673
  subject: "Password Reset Request",
641
674
  header: "Reset Your Password",
642
675
  body,
676
+ logoUrl: this.defaultLogoUrl,
643
677
  otpCode: otp || void 0,
644
678
  actionUrl: resetUrl,
645
679
  actionText: "Reset Password",
@@ -662,6 +696,7 @@ ${resetUrl}`
662
696
  subject,
663
697
  header,
664
698
  body,
699
+ logoUrl: this.defaultLogoUrl,
665
700
  footer: this.buildFooter(brand),
666
701
  fromName: brand,
667
702
  noHtmlMessage: htmlToPlainText(body)
@@ -670,7 +705,7 @@ ${resetUrl}`
670
705
  /**
671
706
  * Newsletter / announcement email with an optional hero image and CTA button.
672
707
  */
673
- createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName) {
708
+ createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName, logoUrl) {
674
709
  const brand = this.resolveBrand(brandName);
675
710
  let noHtmlMessage = htmlToPlainText(body);
676
711
  if (actionUrl) {
@@ -682,6 +717,7 @@ ${actionText || "Learn more"}: ${actionUrl}`;
682
717
  subject,
683
718
  header,
684
719
  body,
720
+ logoUrl: logoUrl || this.defaultLogoUrl,
685
721
  imageUrl,
686
722
  actionUrl,
687
723
  actionText,
@@ -704,6 +740,7 @@ ${magicLink}` : "";
704
740
  subject: "Your Login Verification Code",
705
741
  header: "Two-Factor Authentication",
706
742
  body,
743
+ logoUrl: this.defaultLogoUrl,
707
744
  otpCode: otp,
708
745
  actionUrl: magicLink || void 0,
709
746
  actionText: magicLink ? "Verify Login" : void 0,
@@ -726,6 +763,7 @@ Your verification code is: ${otp}` + plainLink
726
763
  subject: "Your Magic Link",
727
764
  header: `Sign In to ${brand}`,
728
765
  body,
766
+ logoUrl: this.defaultLogoUrl,
729
767
  actionUrl: magicLink,
730
768
  actionText: "Sign In",
731
769
  extraMessage: this.defaultSecurityMessage,
@@ -755,7 +793,7 @@ ${magicLink}`
755
793
  static createSimpleMessage(subject, header, body, brandName) {
756
794
  return this.defaultInstance.createSimpleMessage(subject, header, body, brandName);
757
795
  }
758
- static createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName) {
796
+ static createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName, logoUrl) {
759
797
  return this.defaultInstance.createNewsLetter(
760
798
  subject,
761
799
  header,
@@ -763,7 +801,8 @@ ${magicLink}`
763
801
  imageUrl,
764
802
  actionUrl,
765
803
  actionText,
766
- brandName
804
+ brandName,
805
+ logoUrl
767
806
  );
768
807
  }
769
808
  static create2FAEmail(username, otp, magicLink, brandName) {
@@ -785,13 +824,16 @@ ${magicLink}`
785
824
  MemoryEmailSender,
786
825
  TemplateError,
787
826
  TransportError,
827
+ ZeptoMailHttpSender,
788
828
  ZeptoMailSender,
789
829
  buildHtml,
790
830
  buildPlainText,
791
831
  createMailKit,
792
832
  decodeHtmlEntities,
793
833
  htmlToPlainText,
834
+ isValidHttpUrl,
794
835
  normalizeRecipient,
795
- normalizeRecipients
836
+ normalizeRecipients,
837
+ parseEmailAddress
796
838
  });
797
839
  //# sourceMappingURL=index.cjs.map