ts-mailkit 0.2.0 → 0.3.1

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,14 +376,15 @@ 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
- this.fetchImpl = config.fetch ?? globalThis.fetch;
353
- if (typeof this.fetchImpl !== "function") {
381
+ const fetchImpl = config.fetch ?? globalThis.fetch;
382
+ if (typeof 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
  }
387
+ this.fetchImpl = fetchImpl.bind(globalThis);
358
388
  }
359
389
  /**
360
390
  * Dispatches an email using Zoho ZeptoMail API v1.1.
@@ -362,7 +392,7 @@ var ZeptoMailSender = class {
362
392
  async send(options) {
363
393
  const toRecipients = normalizeRecipients(options.to);
364
394
  if (toRecipients.length === 0) {
365
- throw new TransportError("ZeptoMailSender: At least one recipient (to) must be specified.", {
395
+ throw new TransportError("ZeptoMailHttpSender: At least one recipient (to) must be specified.", {
366
396
  transportName: this.name
367
397
  });
368
398
  }
@@ -444,7 +474,7 @@ var ZeptoMailSender = class {
444
474
  body: JSON.stringify(payload)
445
475
  });
446
476
  } catch (err) {
447
- throw new TransportError(`ZeptoMailSender: Network request failed: ${err instanceof Error ? err.message : String(err)}`, {
477
+ throw new TransportError(`ZeptoMailHttpSender: Network request failed: ${err instanceof Error ? err.message : String(err)}`, {
448
478
  transportName: this.name,
449
479
  cause: err
450
480
  });
@@ -458,7 +488,7 @@ var ZeptoMailSender = class {
458
488
  }
459
489
  if (!response.ok) {
460
490
  const errorMessage = responseData?.error?.message || responseData?.message || `HTTP ${response.status}: ${responseText || response.statusText}`;
461
- throw new TransportError(`ZeptoMailSender: Failed to send email - ${errorMessage}`, {
491
+ throw new TransportError(`ZeptoMailHttpSender: Failed to send email - ${errorMessage}`, {
462
492
  transportName: this.name,
463
493
  statusCode: response.status,
464
494
  responseBody: responseText
@@ -486,6 +516,7 @@ var ZeptoMailSender = class {
486
516
  });
487
517
  }
488
518
  };
519
+ var ZeptoMailSender = ZeptoMailHttpSender;
489
520
 
490
521
  // src/transports/memory/memory-sender.ts
491
522
  var MemoryEmailSender = class {
@@ -584,9 +615,11 @@ var EmailTemplateFactory = class _EmailTemplateFactory {
584
615
  defaultBrandName;
585
616
  defaultSecurityMessage;
586
617
  footerResolver;
618
+ defaultLogoUrl;
587
619
  constructor(config = {}) {
588
620
  this.defaultBrandName = config.brandName || "Application";
589
621
  this.defaultSecurityMessage = config.securityMessage || DEFAULT_SECURITY_MESSAGE;
622
+ this.defaultLogoUrl = config.logoUrl;
590
623
  if (typeof config.footer === "function") {
591
624
  this.footerResolver = config.footer;
592
625
  } else if (typeof config.footer === "string") {
@@ -612,6 +645,7 @@ var EmailTemplateFactory = class _EmailTemplateFactory {
612
645
  subject: "Verify Your Email Address",
613
646
  header: `Welcome to ${brand}!`,
614
647
  body,
648
+ logoUrl: this.defaultLogoUrl,
615
649
  otpCode: otp,
616
650
  actionUrl: verificationUrl,
617
651
  actionText: "Verify Email",
@@ -640,6 +674,7 @@ Your OTP code is: ${otp}` : "";
640
674
  subject: "Password Reset Request",
641
675
  header: "Reset Your Password",
642
676
  body,
677
+ logoUrl: this.defaultLogoUrl,
643
678
  otpCode: otp || void 0,
644
679
  actionUrl: resetUrl,
645
680
  actionText: "Reset Password",
@@ -662,6 +697,7 @@ ${resetUrl}`
662
697
  subject,
663
698
  header,
664
699
  body,
700
+ logoUrl: this.defaultLogoUrl,
665
701
  footer: this.buildFooter(brand),
666
702
  fromName: brand,
667
703
  noHtmlMessage: htmlToPlainText(body)
@@ -670,7 +706,7 @@ ${resetUrl}`
670
706
  /**
671
707
  * Newsletter / announcement email with an optional hero image and CTA button.
672
708
  */
673
- createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName) {
709
+ createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName, logoUrl) {
674
710
  const brand = this.resolveBrand(brandName);
675
711
  let noHtmlMessage = htmlToPlainText(body);
676
712
  if (actionUrl) {
@@ -682,6 +718,7 @@ ${actionText || "Learn more"}: ${actionUrl}`;
682
718
  subject,
683
719
  header,
684
720
  body,
721
+ logoUrl: logoUrl || this.defaultLogoUrl,
685
722
  imageUrl,
686
723
  actionUrl,
687
724
  actionText,
@@ -704,6 +741,7 @@ ${magicLink}` : "";
704
741
  subject: "Your Login Verification Code",
705
742
  header: "Two-Factor Authentication",
706
743
  body,
744
+ logoUrl: this.defaultLogoUrl,
707
745
  otpCode: otp,
708
746
  actionUrl: magicLink || void 0,
709
747
  actionText: magicLink ? "Verify Login" : void 0,
@@ -726,6 +764,7 @@ Your verification code is: ${otp}` + plainLink
726
764
  subject: "Your Magic Link",
727
765
  header: `Sign In to ${brand}`,
728
766
  body,
767
+ logoUrl: this.defaultLogoUrl,
729
768
  actionUrl: magicLink,
730
769
  actionText: "Sign In",
731
770
  extraMessage: this.defaultSecurityMessage,
@@ -755,7 +794,7 @@ ${magicLink}`
755
794
  static createSimpleMessage(subject, header, body, brandName) {
756
795
  return this.defaultInstance.createSimpleMessage(subject, header, body, brandName);
757
796
  }
758
- static createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName) {
797
+ static createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName, logoUrl) {
759
798
  return this.defaultInstance.createNewsLetter(
760
799
  subject,
761
800
  header,
@@ -763,7 +802,8 @@ ${magicLink}`
763
802
  imageUrl,
764
803
  actionUrl,
765
804
  actionText,
766
- brandName
805
+ brandName,
806
+ logoUrl
767
807
  );
768
808
  }
769
809
  static create2FAEmail(username, otp, magicLink, brandName) {
@@ -785,13 +825,16 @@ ${magicLink}`
785
825
  MemoryEmailSender,
786
826
  TemplateError,
787
827
  TransportError,
828
+ ZeptoMailHttpSender,
788
829
  ZeptoMailSender,
789
830
  buildHtml,
790
831
  buildPlainText,
791
832
  createMailKit,
792
833
  decodeHtmlEntities,
793
834
  htmlToPlainText,
835
+ isValidHttpUrl,
794
836
  normalizeRecipient,
795
- normalizeRecipients
837
+ normalizeRecipients,
838
+ parseEmailAddress
796
839
  });
797
840
  //# sourceMappingURL=index.cjs.map