ts-mailkit 2.0.1 → 2.1.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.
Files changed (36) hide show
  1. package/README.md +114 -63
  2. package/dist/core/bulk-types.d.ts +196 -0
  3. package/dist/core/bulk-types.d.ts.map +1 -0
  4. package/dist/core/client.d.ts +38 -6
  5. package/dist/core/client.d.ts.map +1 -1
  6. package/dist/core/errors.d.ts +3 -0
  7. package/dist/core/errors.d.ts.map +1 -1
  8. package/dist/core/types.d.ts +5 -0
  9. package/dist/core/types.d.ts.map +1 -1
  10. package/dist/index.cjs +338 -67
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts +19 -13
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +335 -67
  15. package/dist/index.js.map +1 -1
  16. package/dist/renderers/default/default-renderer.d.ts +2 -2
  17. package/dist/renderers/renderer.interface.d.ts +1 -1
  18. package/dist/templates/factory.d.ts +1 -1
  19. package/dist/templates/factory.d.ts.map +1 -1
  20. package/dist/templates/template-builder.d.ts +36 -0
  21. package/dist/templates/template-builder.d.ts.map +1 -0
  22. package/dist/transports/console/console-sender.d.ts +7 -2
  23. package/dist/transports/console/console-sender.d.ts.map +1 -1
  24. package/dist/transports/cpaas/cpaas-sender.d.ts +51 -0
  25. package/dist/transports/cpaas/cpaas-sender.d.ts.map +1 -0
  26. package/dist/transports/cpaas/types.d.ts +133 -0
  27. package/dist/transports/cpaas/types.d.ts.map +1 -0
  28. package/dist/transports/memory/memory-sender.d.ts +26 -6
  29. package/dist/transports/memory/memory-sender.d.ts.map +1 -1
  30. package/dist/transports/sender.interface.d.ts +34 -3
  31. package/dist/transports/sender.interface.d.ts.map +1 -1
  32. package/dist/transports/zeptomail/types.d.ts +5 -46
  33. package/dist/transports/zeptomail/types.d.ts.map +1 -1
  34. package/dist/transports/zeptomail/zeptomail-sender.d.ts +4 -19
  35. package/dist/transports/zeptomail/zeptomail-sender.d.ts.map +1 -1
  36. package/package.json +39 -37
package/dist/index.js CHANGED
@@ -67,6 +67,8 @@ var TransportError = class extends MailKitError {
67
67
  transportName;
68
68
  statusCode;
69
69
  responseBody;
70
+ /** Parsed raw provider response, when available. */
71
+ rawResponse;
70
72
  cause;
71
73
  constructor(message, options) {
72
74
  super(message);
@@ -74,6 +76,7 @@ var TransportError = class extends MailKitError {
74
76
  this.transportName = options.transportName;
75
77
  this.statusCode = options.statusCode;
76
78
  this.responseBody = options.responseBody;
79
+ this.rawResponse = options.rawResponse;
77
80
  this.cause = options.cause;
78
81
  Object.setPrototypeOf(this, new.target.prototype);
79
82
  }
@@ -220,18 +223,16 @@ var MailKit = class {
220
223
  *
221
224
  * Example:
222
225
  * ```ts
223
- * await mailer.send({
226
+ * await mailer.sendMail({
224
227
  * to: 'user@example.com',
225
228
  * template: myDto,
226
229
  * });
227
230
  * ```
228
231
  */
229
- async send(options) {
232
+ async sendMail(options) {
230
233
  const template = options.template;
231
234
  const recipientName = options.toName ?? template.userName;
232
- let resolvedFromName = options.fromName ?? template.fromName;
233
- if (!resolvedFromName && this.brandNameResolver) resolvedFromName = await this.brandNameResolver();
234
- if (!resolvedFromName) resolvedFromName = this.defaultBrandName;
235
+ const resolvedFromName = await this.resolveFromName(template, options.fromName);
235
236
  const rendered = await this.renderer.render(template);
236
237
  const plaintextBody = template.noHtmlMessage ?? rendered.text;
237
238
  let toRecipients;
@@ -240,11 +241,7 @@ var MailKit = class {
240
241
  name: recipientName
241
242
  };
242
243
  else toRecipients = options.to;
243
- let from = options.from ?? this.defaultFrom;
244
- if (resolvedFromName && from) from = {
245
- address: normalizeRecipient(from).address,
246
- name: resolvedFromName
247
- };
244
+ const from = this.applyFromName(options.from ?? this.defaultFrom, resolvedFromName);
248
245
  return await this.sendRaw({
249
246
  to: toRecipients,
250
247
  from,
@@ -261,6 +258,88 @@ var MailKit = class {
261
258
  });
262
259
  }
263
260
  /**
261
+ * @deprecated Use {@link sendMail} instead.
262
+ */
263
+ async send(options) {
264
+ return await this.sendMail(options);
265
+ }
266
+ /**
267
+ * Sends a bulk email using a strongly-typed template DTO.
268
+ *
269
+ * The transport delivers an individual copy to each recipient, so
270
+ * recipients are not exposed to one another. Per-recipient personalization
271
+ * is available via {@link sendBulkKnownTemplate}.
272
+ *
273
+ * Example:
274
+ * ```ts
275
+ * await mailer.sendBulkMail({
276
+ * to: ['a@example.com', 'b@example.com'],
277
+ * template: myDto,
278
+ * });
279
+ * ```
280
+ */
281
+ async sendBulkMail(options) {
282
+ const transport = this.transport;
283
+ if (typeof transport.sendBulk !== "function") throw new ConfigurationError("MailKit: The configured transport does not support bulk sending (sendBulk).");
284
+ const template = options.template;
285
+ const resolvedFromName = await this.resolveFromName(template);
286
+ const rendered = await this.renderer.render(template);
287
+ const plaintextBody = template.noHtmlMessage ?? rendered.text;
288
+ const from = this.applyFromName(options.from ?? this.defaultFrom, resolvedFromName);
289
+ return await transport.sendBulk({
290
+ to: options.to,
291
+ from,
292
+ subject: template.subject,
293
+ html: rendered.html,
294
+ text: plaintextBody,
295
+ cc: options.cc,
296
+ bcc: options.bcc,
297
+ replyTo: options.replyTo,
298
+ attachments: options.attachments,
299
+ headers: options.headers,
300
+ tags: options.tags,
301
+ metadata: options.metadata,
302
+ trackClicks: options.trackClicks,
303
+ trackOpens: options.trackOpens
304
+ });
305
+ }
306
+ /**
307
+ * Sends a single email using a provider-side template.
308
+ */
309
+ async sendKnownTemplate(options) {
310
+ const transport = this.transport;
311
+ if (typeof transport.sendKnownTemplate !== "function") throw new ConfigurationError("MailKit: The configured transport does not support template sending (sendKnownTemplate).");
312
+ return await transport.sendKnownTemplate({
313
+ ...options,
314
+ from: options.from ?? this.defaultFrom
315
+ });
316
+ }
317
+ /**
318
+ * Sends a bulk email using a provider-side template with optional
319
+ * per-recipient merge data.
320
+ */
321
+ async sendBulkKnownTemplate(options) {
322
+ const transport = this.transport;
323
+ if (typeof transport.sendBulkKnownTemplate !== "function") throw new ConfigurationError("MailKit: The configured transport does not support bulk template sending (sendBulkKnownTemplate).");
324
+ return await transport.sendBulkKnownTemplate({
325
+ ...options,
326
+ from: options.from ?? this.defaultFrom
327
+ });
328
+ }
329
+ async resolveFromName(template, explicitFromName) {
330
+ let resolved = explicitFromName ?? template.fromName;
331
+ if (!resolved && this.brandNameResolver) resolved = await this.brandNameResolver();
332
+ if (!resolved) resolved = this.defaultBrandName;
333
+ return resolved;
334
+ }
335
+ applyFromName(from, fromName) {
336
+ if (fromName && from) return {
337
+ address: normalizeRecipient(from).address,
338
+ name: fromName
339
+ };
340
+ return from;
341
+ }
342
+ /**
264
343
  * Dispatches a raw, pre-rendered email directly through the transport.
265
344
  * If `options.text` is omitted, plain text is automatically derived from `options.html`.
266
345
  */
@@ -282,38 +361,73 @@ function createMailKit(options) {
282
361
  }
283
362
 
284
363
  //#endregion
285
- //#region src/transports/zeptomail/zeptomail-sender.ts
364
+ //#region src/transports/cpaas/cpaas-sender.ts
286
365
  /**
287
- * ZeptoMail (Zoho Mail transactional email HTTP API) transport adapter.
366
+ * Zoho CPaaS (formerly ZeptoMail) transactional email HTTP transport adapter.
288
367
  *
289
- * Designed for zero dependencies and universal edge compatibility (Node.js, Bun, Deno, Cloudflare Workers)
290
- * by utilizing the standard global `fetch` API.
368
+ * Uses the standard global `fetch` API for zero-dependency, universal edge
369
+ * compatibility (Node.js, Bun, Deno, Cloudflare Workers).
370
+ *
371
+ * Supports:
372
+ * - `send` — single raw email (`POST /v1.1/email`)
373
+ * - `sendBulk` — batch raw email (`POST /v1.1/email/batch`)
374
+ * - `sendKnownTemplate` — single template email (`POST /v1.1/email/template`)
375
+ * - `sendBulkKnownTemplate` — batch template email (`POST /v1.1/email/template/batch`)
291
376
  */
292
- var ZeptoMailHttpSender = class {
293
- name = "zeptomail";
294
- apiUrl;
377
+ var CpaasHttpSender = class {
378
+ name = "cpaas";
379
+ apiBase;
295
380
  authHeader;
296
381
  defaultFrom;
382
+ preferTemplateAlias;
297
383
  fetchImpl;
298
384
  constructor(config) {
299
- if (!config.apiToken || typeof config.apiToken !== "string") throw new ConfigurationError("ZeptoMailHttpSender: Missing or invalid apiToken.");
300
- if (!config.from) throw new ConfigurationError("ZeptoMailHttpSender: Missing required default from address.");
301
- const host = (config.apiHost || "api.zeptomail.com").replace(/^https?:\/\//, "").replace(/\/+$/, "");
302
- this.apiUrl = `https://${host}/v1.1/email`;
385
+ if (!config.apiToken || typeof config.apiToken !== "string") throw new ConfigurationError("CpaasHttpSender: Missing or invalid apiToken.");
386
+ if (!config.from) throw new ConfigurationError("CpaasHttpSender: Missing required default from address.");
387
+ const host = (config.apiHost || "cpaas.zoho.com").replace(/^https?:\/\//, "").replace(/\/+$/, "");
388
+ this.apiBase = `https://${host}/v1.1`;
303
389
  const token = config.apiToken.trim();
304
390
  this.authHeader = token.startsWith("Zoho-") ? token : `Zoho-enczapikey ${token}`;
305
391
  this.defaultFrom = normalizeRecipient(config.from);
306
- if (!this.defaultFrom.address) throw new ConfigurationError("ZeptoMailHttpSender: Configured from address must not be empty.");
392
+ if (!this.defaultFrom.address) throw new ConfigurationError("CpaasHttpSender: Configured from address must not be empty.");
393
+ this.preferTemplateAlias = config.preferTemplateAlias ?? false;
307
394
  const fetchImpl = config.fetch ?? globalThis.fetch;
308
- if (typeof fetchImpl !== "function") throw new ConfigurationError("ZeptoMailHttpSender: No global fetch implementation found. Pass a custom fetch to ZeptoMailHttpConfig if running on Node < 18.");
395
+ if (typeof fetchImpl !== "function") throw new ConfigurationError("CpaasHttpSender: No global fetch implementation found. Pass a custom fetch to CpaasHttpConfig if running on Node < 18.");
309
396
  this.fetchImpl = fetchImpl.bind(globalThis);
310
397
  }
311
398
  /**
312
- * Dispatches an email using Zoho ZeptoMail API v1.1.
399
+ * Dispatches a single raw email using Zoho CPaaS API.
313
400
  */
314
401
  async send(options) {
402
+ const payload = this.buildSendPayload(options);
403
+ return await this.post(`${this.apiBase}/email`, payload);
404
+ }
405
+ /**
406
+ * Dispatches a batch of a single raw email content to many recipients.
407
+ * The provider delivers an individual copy to each recipient.
408
+ */
409
+ async sendBulk(options) {
410
+ const payload = this.buildSendPayload(options);
411
+ return await this.post(`${this.apiBase}/email/batch`, payload);
412
+ }
413
+ /**
414
+ * Dispatches a single email using a provider-side template.
415
+ */
416
+ async sendKnownTemplate(options) {
417
+ const payload = this.buildTemplatePayload(options);
418
+ return await this.post(`${this.apiBase}/email/template`, payload);
419
+ }
420
+ /**
421
+ * Dispatches a batch of emails using a provider-side template, with
422
+ * optional per-recipient merge data.
423
+ */
424
+ async sendBulkKnownTemplate(options) {
425
+ const payload = this.buildBatchTemplatePayload(options);
426
+ return await this.post(`${this.apiBase}/email/template/batch`, payload);
427
+ }
428
+ buildSendPayload(options) {
315
429
  const toRecipients = normalizeRecipients(options.to);
316
- if (toRecipients.length === 0) throw new TransportError("ZeptoMailHttpSender: At least one recipient (to) must be specified.", { transportName: this.name });
430
+ if (toRecipients.length === 0) throw new TransportError("CpaasHttpSender: At least one recipient (to) must be specified.", { transportName: this.name });
317
431
  const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;
318
432
  const payload = {
319
433
  from: {
@@ -328,6 +442,52 @@ var ZeptoMailHttpSender = class {
328
442
  htmlbody: options.html,
329
443
  textbody: options.text
330
444
  };
445
+ this.applyCommon(payload, options);
446
+ return payload;
447
+ }
448
+ buildTemplatePayload(options) {
449
+ const toRecipients = normalizeRecipients(options.to);
450
+ if (toRecipients.length === 0) throw new TransportError("CpaasHttpSender: At least one recipient (to) must be specified.", { transportName: this.name });
451
+ const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;
452
+ const payload = {
453
+ ...this.templateRef(options.templateId),
454
+ from: {
455
+ address: resolvedFrom.address,
456
+ name: resolvedFrom.name
457
+ },
458
+ to: toRecipients.map((r) => ({ email_address: {
459
+ address: r.address,
460
+ name: r.name || ""
461
+ } }))
462
+ };
463
+ if (options.mergeInfo) payload.merge_info = options.mergeInfo;
464
+ this.applyCommon(payload, options);
465
+ return payload;
466
+ }
467
+ buildBatchTemplatePayload(options) {
468
+ if (!options.to || options.to.length === 0) throw new TransportError("CpaasHttpSender: At least one recipient (to) must be specified.", { transportName: this.name });
469
+ const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;
470
+ const payload = {
471
+ ...this.templateRef(options.templateId),
472
+ from: {
473
+ address: resolvedFrom.address,
474
+ name: resolvedFrom.name
475
+ },
476
+ to: options.to.map((r) => ({
477
+ email_address: {
478
+ address: r.address,
479
+ name: r.name || ""
480
+ },
481
+ ...r.mergeInfo ? { merge_info: r.mergeInfo } : {}
482
+ }))
483
+ };
484
+ this.applyCommon(payload, options);
485
+ return payload;
486
+ }
487
+ templateRef(templateId) {
488
+ return this.preferTemplateAlias ? { template_alias: templateId } : { template_key: templateId };
489
+ }
490
+ applyCommon(payload, options) {
331
491
  if (options.cc) {
332
492
  const ccList = normalizeRecipients(options.cc);
333
493
  if (ccList.length > 0) payload.cc = ccList.map((r) => ({ email_address: {
@@ -349,26 +509,33 @@ var ZeptoMailHttpSender = class {
349
509
  name: r.name
350
510
  }));
351
511
  }
352
- if (options.attachments && options.attachments.length > 0) payload.attachments = options.attachments.map((att) => {
353
- let contentBase64;
354
- if (typeof att.content === "string") contentBase64 = att.content;
355
- else if (typeof Buffer !== "undefined") contentBase64 = Buffer.from(att.content).toString("base64");
356
- else {
357
- let binary = "";
358
- const bytes = new Uint8Array(att.content);
359
- for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
360
- contentBase64 = btoa(binary);
361
- }
362
- return {
363
- name: att.filename,
364
- content: contentBase64,
365
- mime_type: att.mimeType || att.contentType || "application/octet-stream",
366
- cid: att.cid
367
- };
368
- });
512
+ if (options.attachments && options.attachments.length > 0) payload.attachments = options.attachments.map((att) => this.toAttachmentPayload(att));
513
+ if (options.trackClicks !== void 0) payload.track_clicks = options.trackClicks;
514
+ if (options.trackOpens !== void 0) payload.track_opens = options.trackOpens;
515
+ if (options.bounceAddress !== void 0) payload.bounce_address = options.bounceAddress;
516
+ if (options.clientReference !== void 0) payload.client_reference = options.clientReference;
517
+ }
518
+ toAttachmentPayload(att) {
519
+ let contentBase64;
520
+ if (typeof att.content === "string") contentBase64 = att.content;
521
+ else if (typeof Buffer !== "undefined") contentBase64 = Buffer.from(att.content).toString("base64");
522
+ else {
523
+ let binary = "";
524
+ const bytes = new Uint8Array(att.content);
525
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
526
+ contentBase64 = btoa(binary);
527
+ }
528
+ return {
529
+ name: att.filename,
530
+ content: contentBase64,
531
+ mime_type: att.mimeType || att.contentType || "application/octet-stream",
532
+ cid: att.cid
533
+ };
534
+ }
535
+ async post(url, payload) {
369
536
  let response;
370
537
  try {
371
- response = await this.fetchImpl(this.apiUrl, {
538
+ response = await this.fetchImpl(url, {
372
539
  method: "POST",
373
540
  headers: {
374
541
  Accept: "application/json",
@@ -378,7 +545,7 @@ var ZeptoMailHttpSender = class {
378
545
  body: JSON.stringify(payload)
379
546
  });
380
547
  } catch (err) {
381
- throw new TransportError(`ZeptoMailHttpSender: Network request failed: ${err instanceof Error ? err.message : String(err)}`, {
548
+ throw new TransportError(`CpaasHttpSender: Network request failed: ${err instanceof Error ? err.message : String(err)}`, {
382
549
  transportName: this.name,
383
550
  cause: err
384
551
  });
@@ -391,16 +558,18 @@ var ZeptoMailHttpSender = class {
391
558
  responseData = { raw: responseText };
392
559
  }
393
560
  if (!response.ok) {
394
- const errorMessage = responseData?.error?.message || responseData?.message || `HTTP ${response.status}: ${responseText || response.statusText}`;
395
- throw new TransportError(`ZeptoMailHttpSender: Failed to send email - ${errorMessage}`, {
561
+ const errorMessage = responseData?.data?.message || responseData?.error?.message || responseData?.message || `HTTP ${response.status}: ${responseText || response.statusText}`;
562
+ throw new TransportError(`CpaasHttpSender: Failed to send email - ${errorMessage}`, {
396
563
  transportName: this.name,
397
564
  statusCode: response.status,
398
- responseBody: responseText
565
+ responseBody: responseText,
566
+ rawResponse: responseData
399
567
  });
400
568
  }
401
569
  return {
402
570
  success: true,
403
571
  messageId: responseData?.data?.[0]?.message_id,
572
+ rawResponse: responseData,
404
573
  response: responseData
405
574
  };
406
575
  }
@@ -415,36 +584,78 @@ var ZeptoMailHttpSender = class {
415
584
  var MemoryEmailSender = class {
416
585
  name = "memory";
417
586
  sentMails = [];
587
+ sentBulkMails = [];
588
+ sentTemplateMails = [];
589
+ sentBulkTemplateMails = [];
418
590
  nextId = 1;
419
- async send(options) {
420
- this.sentMails.push({ ...options });
421
- const messageId = `mem-${this.nextId++}-${Date.now()}`;
591
+ result(prefix) {
592
+ const id = this.nextId++;
593
+ const messageId = prefix ? `mem-${prefix}-${id}-${Date.now()}` : `mem-${id}-${Date.now()}`;
594
+ const rawResponse = { id: messageId };
422
595
  return {
423
596
  success: true,
424
597
  messageId,
425
- response: { id: messageId }
598
+ rawResponse,
599
+ response: rawResponse
426
600
  };
427
601
  }
602
+ async send(options) {
603
+ this.sentMails.push({ ...options });
604
+ return this.result();
605
+ }
606
+ async sendBulk(options) {
607
+ this.sentBulkMails.push({ ...options });
608
+ return this.result("bulk");
609
+ }
610
+ async sendKnownTemplate(options) {
611
+ this.sentTemplateMails.push({ ...options });
612
+ return this.result("tpl");
613
+ }
614
+ async sendBulkKnownTemplate(options) {
615
+ this.sentBulkTemplateMails.push({ ...options });
616
+ return this.result("bulk-tpl");
617
+ }
428
618
  /**
429
- * Returns a copy of all emails sent through this transport instance.
619
+ * Returns a copy of all single emails sent through this transport instance.
430
620
  */
431
621
  getSentMails() {
432
622
  return [...this.sentMails];
433
623
  }
434
624
  /**
435
- * Returns the most recent email sent, or undefined if no emails have been sent.
625
+ * Returns the most recent single email sent, or undefined if none have been sent.
436
626
  */
437
627
  getLastMail() {
438
628
  return this.sentMails.length > 0 ? { ...this.sentMails[this.sentMails.length - 1] } : void 0;
439
629
  }
440
630
  /**
441
- * Clears all recorded sent emails.
631
+ * Returns a copy of all bulk emails sent through this transport instance.
632
+ */
633
+ getSentBulkMails() {
634
+ return [...this.sentBulkMails];
635
+ }
636
+ /**
637
+ * Returns a copy of all single template emails sent through this transport instance.
638
+ */
639
+ getSentTemplateMails() {
640
+ return [...this.sentTemplateMails];
641
+ }
642
+ /**
643
+ * Returns a copy of all bulk template emails sent through this transport instance.
644
+ */
645
+ getSentBulkTemplateMails() {
646
+ return [...this.sentBulkTemplateMails];
647
+ }
648
+ /**
649
+ * Clears all recorded emails.
442
650
  */
443
651
  clear() {
444
652
  this.sentMails = [];
653
+ this.sentBulkMails = [];
654
+ this.sentTemplateMails = [];
655
+ this.sentBulkTemplateMails = [];
445
656
  }
446
657
  /**
447
- * Returns the total count of sent emails.
658
+ * Returns the total count of single emails sent.
448
659
  */
449
660
  count() {
450
661
  return this.sentMails.length;
@@ -467,33 +678,72 @@ var ConsoleEmailSender = class {
467
678
  this.includeHtml = options.includeHtml ?? false;
468
679
  }
469
680
  async send(options) {
470
- const to = normalizeRecipients(options.to).map((r) => r.name ? `${r.name} <${r.address}>` : r.address).join(", ");
471
- const from = options.from ? JSON.stringify(options.from) : "(default)";
681
+ return this.logDispatch({
682
+ kind: "EMAIL",
683
+ to: options.to,
684
+ from: options.from,
685
+ subject: options.subject,
686
+ text: options.text,
687
+ html: options.html
688
+ });
689
+ }
690
+ async sendBulk(options) {
691
+ return this.logDispatch({
692
+ kind: "BULK EMAIL",
693
+ to: options.to,
694
+ from: options.from,
695
+ subject: options.subject,
696
+ text: options.text,
697
+ html: options.html
698
+ });
699
+ }
700
+ async sendKnownTemplate(options) {
701
+ return this.logDispatch({
702
+ kind: "TEMPLATE EMAIL",
703
+ to: options.to,
704
+ from: options.from,
705
+ templateId: options.templateId
706
+ });
707
+ }
708
+ async sendBulkKnownTemplate(options) {
709
+ return this.logDispatch({
710
+ kind: "BULK TEMPLATE EMAIL",
711
+ to: options.to,
712
+ from: options.from,
713
+ templateId: options.templateId
714
+ });
715
+ }
716
+ logDispatch(entry) {
717
+ const to = normalizeRecipients(entry.to).map((r) => r.name ? `${r.name} <${r.address}>` : r.address).join(", ");
718
+ const from = entry.from ? JSON.stringify(entry.from) : "(default)";
472
719
  const messageId = `console-${this.nextId++}-${Date.now()}`;
473
720
  const separator = "─".repeat(60);
474
721
  const lines = [
475
722
  `\n┌${separator}┐`,
476
- `│ [ts-mailkit:console] DISPATCHED EMAIL (${messageId})`,
723
+ `│ [ts-mailkit:console] DISPATCHED ${entry.kind} (${messageId})`,
477
724
  `├${separator}┤`,
478
725
  `│ From: ${from}`,
479
- `│ To: ${to}`,
480
- `│ Subject: ${options.subject}`,
481
- `├${separator}┤`
726
+ `│ To: ${to}`
482
727
  ];
483
- if (options.text) {
728
+ if (entry.subject !== void 0) lines.push(`│ Subject: ${entry.subject}`);
729
+ if (entry.templateId !== void 0) lines.push(`│ Template: ${entry.templateId}`);
730
+ lines.push(`├${separator}┤`);
731
+ if (entry.text) {
484
732
  lines.push("│ Text Body:");
485
- for (const line of options.text.split("\n")) lines.push(`│ ${line}`);
733
+ for (const line of entry.text.split("\n")) lines.push(`│ ${line}`);
486
734
  }
487
- if (this.includeHtml && options.html) {
735
+ if (this.includeHtml && entry.html) {
488
736
  lines.push("│ HTML Body:");
489
- for (const line of options.html.split("\n")) lines.push(`│ ${line}`);
737
+ for (const line of entry.html.split("\n")) lines.push(`│ ${line}`);
490
738
  }
491
739
  lines.push(`└${separator}┘\n`);
492
740
  this.log(lines.join("\n"));
741
+ const rawResponse = { logged: true };
493
742
  return {
494
743
  success: true,
495
744
  messageId,
496
- response: { logged: true }
745
+ rawResponse,
746
+ response: rawResponse
497
747
  };
498
748
  }
499
749
  };
@@ -680,5 +930,23 @@ var EmailTemplateFactory = class EmailTemplateFactory {
680
930
  };
681
931
 
682
932
  //#endregion
683
- export { ConfigurationError, ConsoleEmailSender, DefaultEmailRenderer, EmailTemplateFactory, MailKit, MailKitError, MemoryEmailSender, TemplateError, TransportError, ZeptoMailHttpSender, buildHtml, buildPlainText, createMailKit, decodeHtmlEntities, htmlToPlainText, isValidHttpUrl, normalizeRecipient, normalizeRecipients, parseEmailAddress };
933
+ //#region src/templates/template-builder.ts
934
+ /**
935
+ * Builds an upload-ready HTML template from the canonical {@link EmailTemplateDto}.
936
+ *
937
+ * The output can be uploaded to a messaging platform (Zoho CPaaS, SendGrid, etc.)
938
+ * as a provider-side template.
939
+ */
940
+ function buildHTMLTemplate(dto, options = {}) {
941
+ return buildHtml(dto, options);
942
+ }
943
+ /**
944
+ * Builds an upload-ready plain-text template from the canonical {@link EmailTemplateDto}.
945
+ */
946
+ function buildPlainTextTemplate(dto, _options) {
947
+ return buildPlainText(dto);
948
+ }
949
+
950
+ //#endregion
951
+ export { ConfigurationError, ConsoleEmailSender, CpaasHttpSender, CpaasHttpSender as ZeptoMailHttpSender, DefaultEmailRenderer, EmailTemplateFactory, MailKit, MailKitError, MemoryEmailSender, TemplateError, TransportError, buildHTMLTemplate, buildHtml, buildPlainText, buildPlainTextTemplate, createMailKit, decodeHtmlEntities, htmlToPlainText, isValidHttpUrl, normalizeRecipient, normalizeRecipients, parseEmailAddress };
684
952
  //# sourceMappingURL=index.js.map