ts-mailkit 0.1.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
@@ -259,6 +286,23 @@ pnpm run prepublishOnly
259
286
 
260
287
  ---
261
288
 
289
+ ## Deprecated API (Scheduled for Removal in v1.0.0)
290
+
291
+ The following legacy migration bridges and aliases are marked `@deprecated` and scheduled for removal in `v1.0.0`:
292
+
293
+ | Deprecated | Replacement | Notes |
294
+ | :--- | :--- | :--- |
295
+ | `ZeptoMailSender` | `ZeptoMailHttpSender` | Renamed to explicitly signify HTTP transport adapter. |
296
+ | `ZeptoMailConfig` | `ZeptoMailHttpConfig` | Configuration interface rename. |
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. |
299
+ | `mailer.sendSimpleMessage(...)` | `mailer.sendRaw({ to, subject, text, html })` | Direct raw email dispatch. |
300
+ | `mailer.buildPlainTextMessage(template)` | `mailer.render(template)` or `buildPlainText(dto)` | Standalone or client renderer methods. |
301
+ | `EmailService` | `MailKit` | Main client class. |
302
+ | `BaseEmailDto` | `EmailTemplateDto` | Core DTO interface. |
303
+
304
+ ---
305
+
262
306
  ## License
263
307
 
264
308
  [MIT](LICENSE)
@@ -1,6 +1,6 @@
1
1
  import type { IEmailSender } from '../transports/sender.interface.js';
2
2
  import type { IEmailRenderer, RenderedEmail } from '../renderers/renderer.interface.js';
3
- import type { BaseEmailDto } from '../templates/dto/index.js';
3
+ import type { EmailTemplateDto } from '../templates/dto/index.js';
4
4
  import { type Recipient, type SendMailOptions, type SendResult } from './types.js';
5
5
  export interface SendTemplateOptions {
6
6
  /**
@@ -10,7 +10,7 @@ export interface SendTemplateOptions {
10
10
  /**
11
11
  * Templated email DTO to render and send.
12
12
  */
13
- template: BaseEmailDto;
13
+ template: EmailTemplateDto;
14
14
  /**
15
15
  * Optional recipient display name (used when `to` is a plain string).
16
16
  */
@@ -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
  /**
@@ -92,37 +92,50 @@ export declare class MailKit {
92
92
  /**
93
93
  * Renders an email DTO to HTML and plain text without dispatching.
94
94
  */
95
- render(template: BaseEmailDto): Promise<RenderedEmail>;
95
+ render(template: EmailTemplateDto): Promise<RenderedEmail>;
96
96
  /**
97
- * Returns the plain-text fallback for an email DTO without sending.
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.
98
101
  */
99
- buildPlainTextMessage(template: BaseEmailDto): string;
102
+ send(to: string, template: EmailTemplateDto, toName?: string): Promise<SendResult>;
100
103
  /**
101
104
  * Sends an email using a strongly-typed template DTO.
102
- * Supports both modern options object:
103
- * `mailer.send({ to: 'user@example.com', template: myDto })`
104
- * and legacy positional arguments for seamless migration:
105
- * `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
+ * ```
106
113
  */
107
114
  send(options: SendTemplateOptions): Promise<SendResult>;
108
- send(to: string, template: BaseEmailDto, toName?: string): Promise<SendResult>;
109
115
  /**
110
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`.
111
118
  */
112
119
  sendRaw(options: SendMailOptions): Promise<SendResult>;
113
120
  /**
114
- * Sends a raw plain-text email with basic HTML pre-formatting.
115
- * Useful for plain notifications, alerts, or simple text messages.
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.
116
123
  */
117
- 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;
118
130
  }
119
- /**
120
- * Drop-in alias for MailKit to maintain compatibility with existing EmailService code.
121
- */
122
- export declare const EmailService: typeof MailKit;
123
- export type EmailService = MailKit;
124
131
  /**
125
132
  * Convenience factory to create a MailKit client instance.
126
133
  */
127
134
  export declare function createMailKit(options: MailKitOptions): MailKit;
135
+ /**
136
+ * Backward-compatibility alias for `MailKit`.
137
+ * @deprecated Use `MailKit` instead. This alias will be removed in v1.0.0.
138
+ */
139
+ export declare const EmailService: typeof MailKit;
140
+ export type EmailService = MailKit;
128
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,29 +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
- */
245
- buildPlainTextMessage(template) {
246
- return template.noHtmlMessage ?? buildPlainText(template);
247
- }
248
- async send(optionsOrTo, maybeTemplate, maybeToName) {
249
- let opts;
250
- if (typeof optionsOrTo === "string") {
251
- if (!maybeTemplate) {
252
- throw new ConfigurationError("MailKit.send: Missing required template DTO argument.");
253
- }
254
- opts = {
255
- to: optionsOrTo,
256
- template: maybeTemplate,
257
- toName: maybeToName
258
- };
259
- } else {
260
- opts = optionsOrTo;
261
- }
262
- const template = opts.template;
263
- const recipientName = opts.toName ?? template.userName;
264
- 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;
265
286
  if (!resolvedFromName && this.brandNameResolver) {
266
287
  resolvedFromName = await this.brandNameResolver();
267
288
  }
@@ -271,12 +292,12 @@ var MailKit = class {
271
292
  const rendered = await this.renderer.render(template);
272
293
  const plaintextBody = template.noHtmlMessage ?? rendered.text;
273
294
  let toRecipients;
274
- if (typeof opts.to === "string" && recipientName) {
275
- toRecipients = { address: opts.to, name: recipientName };
295
+ if (typeof options.to === "string" && recipientName) {
296
+ toRecipients = { address: options.to, name: recipientName };
276
297
  } else {
277
- toRecipients = opts.to;
298
+ toRecipients = options.to;
278
299
  }
279
- let from = opts.from ?? this.defaultFrom;
300
+ let from = options.from ?? this.defaultFrom;
280
301
  if (resolvedFromName && from) {
281
302
  const normalizedFrom = normalizeRecipient(from);
282
303
  from = { address: normalizedFrom.address, name: resolvedFromName };
@@ -287,46 +308,56 @@ var MailKit = class {
287
308
  subject: template.subject,
288
309
  html: rendered.html,
289
310
  text: plaintextBody,
290
- cc: opts.cc,
291
- bcc: opts.bcc,
292
- replyTo: opts.replyTo,
293
- attachments: opts.attachments,
294
- headers: opts.headers,
295
- tags: opts.tags,
296
- 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
297
318
  });
298
319
  }
299
320
  /**
300
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`.
301
323
  */
302
324
  async sendRaw(options) {
303
325
  const from = options.from ?? this.defaultFrom;
326
+ const text = options.text ?? (options.html ? htmlToPlainText(options.html) : void 0);
304
327
  return await this.transport.send({
305
328
  ...options,
306
- from
329
+ from,
330
+ text
307
331
  });
308
332
  }
309
333
  /**
310
- * Sends a raw plain-text email with basic HTML pre-formatting.
311
- * Useful for plain notifications, alerts, or simple text messages.
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.
312
336
  */
313
- async sendSimpleMessage(to, subject, text, toName = "") {
314
- const html = `<pre style="font-family:monospace;white-space:pre-wrap;">${text}</pre>`;
337
+ async sendSimpleMessage(to, subject, text, fromName) {
315
338
  return await this.sendRaw({
316
- to: { address: to, name: toName },
339
+ to,
317
340
  subject,
318
- html,
319
- 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
320
344
  });
321
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
+ }
322
353
  };
323
- var EmailService = MailKit;
324
354
  function createMailKit(options) {
325
355
  return new MailKit(options);
326
356
  }
357
+ var EmailService = MailKit;
327
358
 
328
359
  // src/transports/zeptomail/zeptomail-sender.ts
329
- var ZeptoMailSender = class {
360
+ var ZeptoMailHttpSender = class {
330
361
  name = "zeptomail";
331
362
  apiUrl;
332
363
  authHeader;
@@ -334,10 +365,10 @@ var ZeptoMailSender = class {
334
365
  fetchImpl;
335
366
  constructor(config) {
336
367
  if (!config.apiToken || typeof config.apiToken !== "string") {
337
- throw new ConfigurationError("ZeptoMailSender: Missing or invalid apiToken.");
368
+ throw new ConfigurationError("ZeptoMailHttpSender: Missing or invalid apiToken.");
338
369
  }
339
370
  if (!config.from) {
340
- throw new ConfigurationError("ZeptoMailSender: Missing required default from address.");
371
+ throw new ConfigurationError("ZeptoMailHttpSender: Missing required default from address.");
341
372
  }
342
373
  const host = (config.apiHost || "api.zeptomail.com").replace(/^https?:\/\//, "").replace(/\/+$/, "");
343
374
  this.apiUrl = `https://${host}/v1.1/email`;
@@ -345,12 +376,12 @@ var ZeptoMailSender = class {
345
376
  this.authHeader = token.startsWith("Zoho-") ? token : `Zoho-enczapikey ${token}`;
346
377
  this.defaultFrom = normalizeRecipient(config.from);
347
378
  if (!this.defaultFrom.address) {
348
- throw new ConfigurationError("ZeptoMailSender: Configured from address must not be empty.");
379
+ throw new ConfigurationError("ZeptoMailHttpSender: Configured from address must not be empty.");
349
380
  }
350
381
  this.fetchImpl = config.fetch ?? globalThis.fetch;
351
382
  if (typeof this.fetchImpl !== "function") {
352
383
  throw new ConfigurationError(
353
- "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."
354
385
  );
355
386
  }
356
387
  }
@@ -360,7 +391,7 @@ var ZeptoMailSender = class {
360
391
  async send(options) {
361
392
  const toRecipients = normalizeRecipients(options.to);
362
393
  if (toRecipients.length === 0) {
363
- 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.", {
364
395
  transportName: this.name
365
396
  });
366
397
  }
@@ -442,7 +473,7 @@ var ZeptoMailSender = class {
442
473
  body: JSON.stringify(payload)
443
474
  });
444
475
  } catch (err) {
445
- 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)}`, {
446
477
  transportName: this.name,
447
478
  cause: err
448
479
  });
@@ -456,7 +487,7 @@ var ZeptoMailSender = class {
456
487
  }
457
488
  if (!response.ok) {
458
489
  const errorMessage = responseData?.error?.message || responseData?.message || `HTTP ${response.status}: ${responseText || response.statusText}`;
459
- throw new TransportError(`ZeptoMailSender: Failed to send email - ${errorMessage}`, {
490
+ throw new TransportError(`ZeptoMailHttpSender: Failed to send email - ${errorMessage}`, {
460
491
  transportName: this.name,
461
492
  statusCode: response.status,
462
493
  responseBody: responseText
@@ -472,6 +503,7 @@ var ZeptoMailSender = class {
472
503
  /**
473
504
  * Backward-compatible helper method matching legacy signature:
474
505
  * `sendEmail(to, htmlBody, subject, toName?, plaintextBody?, fromName?)`
506
+ * @deprecated Use `send({ to, subject, html, text, ... })` instead. This method will be removed in v1.0.0.
475
507
  */
476
508
  async sendEmail(to, htmlBody, subject, toName = "", plaintextBody, fromName) {
477
509
  await this.send({
@@ -483,6 +515,7 @@ var ZeptoMailSender = class {
483
515
  });
484
516
  }
485
517
  };
518
+ var ZeptoMailSender = ZeptoMailHttpSender;
486
519
 
487
520
  // src/transports/memory/memory-sender.ts
488
521
  var MemoryEmailSender = class {
@@ -581,9 +614,11 @@ var EmailTemplateFactory = class _EmailTemplateFactory {
581
614
  defaultBrandName;
582
615
  defaultSecurityMessage;
583
616
  footerResolver;
617
+ defaultLogoUrl;
584
618
  constructor(config = {}) {
585
619
  this.defaultBrandName = config.brandName || "Application";
586
620
  this.defaultSecurityMessage = config.securityMessage || DEFAULT_SECURITY_MESSAGE;
621
+ this.defaultLogoUrl = config.logoUrl;
587
622
  if (typeof config.footer === "function") {
588
623
  this.footerResolver = config.footer;
589
624
  } else if (typeof config.footer === "string") {
@@ -609,6 +644,7 @@ var EmailTemplateFactory = class _EmailTemplateFactory {
609
644
  subject: "Verify Your Email Address",
610
645
  header: `Welcome to ${brand}!`,
611
646
  body,
647
+ logoUrl: this.defaultLogoUrl,
612
648
  otpCode: otp,
613
649
  actionUrl: verificationUrl,
614
650
  actionText: "Verify Email",
@@ -637,6 +673,7 @@ Your OTP code is: ${otp}` : "";
637
673
  subject: "Password Reset Request",
638
674
  header: "Reset Your Password",
639
675
  body,
676
+ logoUrl: this.defaultLogoUrl,
640
677
  otpCode: otp || void 0,
641
678
  actionUrl: resetUrl,
642
679
  actionText: "Reset Password",
@@ -659,6 +696,7 @@ ${resetUrl}`
659
696
  subject,
660
697
  header,
661
698
  body,
699
+ logoUrl: this.defaultLogoUrl,
662
700
  footer: this.buildFooter(brand),
663
701
  fromName: brand,
664
702
  noHtmlMessage: htmlToPlainText(body)
@@ -667,7 +705,7 @@ ${resetUrl}`
667
705
  /**
668
706
  * Newsletter / announcement email with an optional hero image and CTA button.
669
707
  */
670
- createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName) {
708
+ createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName, logoUrl) {
671
709
  const brand = this.resolveBrand(brandName);
672
710
  let noHtmlMessage = htmlToPlainText(body);
673
711
  if (actionUrl) {
@@ -679,6 +717,7 @@ ${actionText || "Learn more"}: ${actionUrl}`;
679
717
  subject,
680
718
  header,
681
719
  body,
720
+ logoUrl: logoUrl || this.defaultLogoUrl,
682
721
  imageUrl,
683
722
  actionUrl,
684
723
  actionText,
@@ -701,6 +740,7 @@ ${magicLink}` : "";
701
740
  subject: "Your Login Verification Code",
702
741
  header: "Two-Factor Authentication",
703
742
  body,
743
+ logoUrl: this.defaultLogoUrl,
704
744
  otpCode: otp,
705
745
  actionUrl: magicLink || void 0,
706
746
  actionText: magicLink ? "Verify Login" : void 0,
@@ -723,6 +763,7 @@ Your verification code is: ${otp}` + plainLink
723
763
  subject: "Your Magic Link",
724
764
  header: `Sign In to ${brand}`,
725
765
  body,
766
+ logoUrl: this.defaultLogoUrl,
726
767
  actionUrl: magicLink,
727
768
  actionText: "Sign In",
728
769
  extraMessage: this.defaultSecurityMessage,
@@ -752,7 +793,7 @@ ${magicLink}`
752
793
  static createSimpleMessage(subject, header, body, brandName) {
753
794
  return this.defaultInstance.createSimpleMessage(subject, header, body, brandName);
754
795
  }
755
- static createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName) {
796
+ static createNewsLetter(subject, header, body, imageUrl, actionUrl, actionText, brandName, logoUrl) {
756
797
  return this.defaultInstance.createNewsLetter(
757
798
  subject,
758
799
  header,
@@ -760,7 +801,8 @@ ${magicLink}`
760
801
  imageUrl,
761
802
  actionUrl,
762
803
  actionText,
763
- brandName
804
+ brandName,
805
+ logoUrl
764
806
  );
765
807
  }
766
808
  static create2FAEmail(username, otp, magicLink, brandName) {
@@ -782,13 +824,16 @@ ${magicLink}`
782
824
  MemoryEmailSender,
783
825
  TemplateError,
784
826
  TransportError,
827
+ ZeptoMailHttpSender,
785
828
  ZeptoMailSender,
786
829
  buildHtml,
787
830
  buildPlainText,
788
831
  createMailKit,
789
832
  decodeHtmlEntities,
790
833
  htmlToPlainText,
834
+ isValidHttpUrl,
791
835
  normalizeRecipient,
792
- normalizeRecipients
836
+ normalizeRecipients,
837
+ parseEmailAddress
793
838
  });
794
839
  //# sourceMappingURL=index.cjs.map