ts-mailkit 3.0.0 → 3.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.
- package/README.md +6 -2
- package/dist/core/client.d.ts +4 -3
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/types.d.ts +4 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.cjs +75 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +75 -33
- package/dist/index.js.map +1 -1
- package/dist/renderers/default/default-renderer.d.ts +5 -22
- package/dist/renderers/default/default-renderer.d.ts.map +1 -1
- package/dist/renderers/renderer.interface.d.ts +25 -0
- package/dist/renderers/renderer.interface.d.ts.map +1 -1
- package/dist/templates/dto/index.d.ts +13 -24
- package/dist/templates/dto/index.d.ts.map +1 -1
- package/dist/templates/factory.d.ts +1 -0
- package/dist/templates/factory.d.ts.map +1 -1
- package/dist/templates/template-builder.d.ts +2 -19
- package/dist/templates/template-builder.d.ts.map +1 -1
- package/dist/transports/cpaas/cpaas-sender.d.ts +8 -0
- package/dist/transports/cpaas/cpaas-sender.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/core/types.ts","../src/core/errors.ts","../src/renderers/utils/html-to-plaintext.ts","../src/renderers/default/default-renderer.ts","../src/core/client.ts","../src/transports/cpaas/cpaas-sender.ts","../src/transports/memory/memory-sender.ts","../src/transports/console/console-sender.ts","../src/templates/factory.ts","../src/templates/template-builder.ts"],"sourcesContent":["import type { EmailTemplateDto } from \"../templates/dto/index.js\";\n\n/**\n * Standard email address representation with optional display name.\n */\nexport interface EmailAddress {\n address: string;\n name?: string;\n}\n\n/**\n * A recipient with optional provider-side merge variables.\n */\nexport interface BulkRecipient extends EmailAddress {\n /**\n * Provider-side merge variables (e.g. Zoho CPaaS `merge_info`).\n */\n mergeInfo?: Record<string, string>;\n}\n\n/**\n * Flexible recipient representation allowing string ('user@example.com'),\n * EmailAddress ({ address: 'user@example.com', name: 'User' }),\n * or legacy/standard object ({ email: 'user@example.com', name: 'User' }).\n */\nexport type Recipient = string | EmailAddress | { email: string; name?: string };\n\n/**\n * Parses an RFC 5322 formatted address string into an EmailAddress.\n * Supports formats such as:\n * - \"user@example.com\" -> { address: \"user@example.com\" }\n * - \"Sender Name <sender@example.com>\" -> { name: \"Sender Name\", address: \"sender@example.com\" }\n * - \"\\\"Sender Name\\\" <sender@example.com>\" -> { name: \"Sender Name\", address: \"sender@example.com\" }\n */\nexport function parseEmailAddress(input: string): EmailAddress {\n if (!input || typeof input !== \"string\") {\n return { address: \"\" };\n }\n const trimmed = input.trim();\n const match = trimmed.match(/^(?:[\"']?([^\"']+)[\"']?\\s+)?<?([^\\s<>@]+@[^\\s<>@]+)>?$/);\n if (!match) {\n return { address: trimmed };\n }\n return {\n address: match[2].trim(),\n name: match[1]?.trim() || undefined,\n };\n}\n\n/**\n * Normalizes a Recipient value into a standardized EmailAddress.\n */\nexport function normalizeRecipient(recipient: Recipient): EmailAddress {\n if (typeof recipient === \"string\") {\n return { address: recipient.trim() };\n }\n if (\"email\" in recipient) {\n return {\n address: (recipient.email ?? \"\").trim(),\n name: recipient.name?.trim(),\n };\n }\n return {\n address: (recipient.address ?? \"\").trim(),\n name: recipient.name?.trim(),\n };\n}\n\n/**\n * Normalizes a single or array of Recipients into an array of EmailAddress.\n */\nexport function normalizeRecipients(recipients?: Recipient | Recipient[]): EmailAddress[] {\n if (!recipients) return [];\n const list = Array.isArray(recipients) ? recipients : [recipients];\n return list.map(normalizeRecipient).filter((r) => r.address.length > 0);\n}\n\n/**\n * Email attachment representation.\n */\nexport interface EmailAttachment {\n filename: string;\n /**\n * Content can be base64-encoded string, plain text, or binary Buffer/Uint8Array.\n */\n content: string | Uint8Array;\n mimeType?: string;\n contentType?: string;\n cid?: string;\n}\n\n/**\n * Common sender and recipient addressing options.\n */\nexport interface MailAddressOptions {\n /**\n * Optional sender address override.\n */\n from?: Recipient;\n /**\n * Optional CC recipients.\n */\n cc?: Recipient | Recipient[];\n /**\n * Optional BCC recipients.\n */\n bcc?: Recipient | Recipient[];\n /**\n * Optional Reply-To recipients.\n */\n replyTo?: Recipient | Recipient[];\n /**\n * Optional file attachments.\n */\n attachments?: EmailAttachment[];\n}\n\n/**\n * Common custom header, tag, and metadata options.\n */\nexport interface MailMetadataOptions {\n /**\n * Optional custom headers.\n */\n headers?: Record<string, string>;\n /**\n * Optional tags for tracking.\n */\n tags?: string[];\n /**\n * Optional metadata.\n */\n metadata?: Record<string, string>;\n}\n\n/**\n * Common provider tracking and reference options.\n */\nexport interface TrackingOptions {\n /**\n * Enable or disable click tracking.\n */\n trackClicks?: boolean;\n /**\n * Enable or disable open tracking.\n */\n trackOpens?: boolean;\n /**\n * Verified bounce (Return-Path) address.\n */\n bounceAddress?: string;\n /**\n * Custom reference string to identify the email in your system.\n */\n clientReference?: string;\n}\n\n/**\n * Options for sending an email through a transport adapter.\n */\nexport interface SendMailOptions extends MailAddressOptions, MailMetadataOptions {\n to: Recipient | Recipient[];\n subject: string;\n html: string;\n text?: string;\n}\n\n/**\n * Transport-level batch of a single raw email content to many recipients.\n *\n * The provider delivers an individual copy to each recipient, so recipients\n * are not exposed to one another.\n */\nexport type BulkSendOptions = SendMailOptions & TrackingOptions;\n\n/**\n * Options for sending a single email using a provider-side template.\n */\nexport interface KnownTemplateSendOptions extends MailAddressOptions, TrackingOptions {\n /**\n * Provider-side template identifier (e.g. Zoho `template_key`).\n */\n templateId: string;\n /**\n * Recipient email address.\n */\n to: Recipient;\n /**\n * Template variable substitutions.\n */\n mergeInfo?: Record<string, string>;\n}\n\n/**\n * Options for sending a batch of emails using a provider-side template.\n *\n * Each recipient may carry its own `mergeInfo` so the provider can\n * personalize the template per recipient.\n */\nexport interface BulkKnownTemplateSendOptions extends MailAddressOptions, TrackingOptions {\n /**\n * Provider-side template identifier (e.g. Zoho `template_key`).\n */\n templateId: string;\n /**\n * Recipients, each with optional per-recipient merge variables.\n */\n to: BulkRecipient[];\n}\n\n/**\n * Client-level options for sending a bulk email using the in-built template DTO.\n */\nexport interface SendBulkMailOptions\n extends MailAddressOptions, MailMetadataOptions, TrackingOptions {\n /**\n * Templated email DTO to render and send to every recipient.\n */\n template: EmailTemplateDto;\n /**\n * One or more recipient email addresses.\n */\n to: Recipient | Recipient[];\n}\n\n/**\n * Result returned by an IEmailSender upon dispatch.\n */\nexport interface SendResult {\n success: boolean;\n messageId?: string;\n /** Raw provider response (e.g. Zoho CPaaS JSON body). */\n rawResponse?: unknown;\n}\n","/**\n * Base error class for all ts-mailkit errors.\n */\nexport class MailKitError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MailKitError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when transport configuration is missing or invalid.\n */\nexport class ConfigurationError extends MailKitError {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigurationError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when an email transport fails to dispatch an email.\n */\nexport class TransportError extends MailKitError {\n readonly transportName: string;\n readonly statusCode?: number;\n readonly responseBody?: string;\n /** Parsed raw provider response, when available. */\n readonly rawResponse?: unknown;\n readonly cause?: unknown;\n\n constructor(\n message: string,\n options: {\n transportName: string;\n statusCode?: number;\n responseBody?: string;\n rawResponse?: unknown;\n cause?: unknown;\n },\n ) {\n super(message);\n this.name = \"TransportError\";\n this.transportName = options.transportName;\n this.statusCode = options.statusCode;\n this.responseBody = options.responseBody;\n this.rawResponse = options.rawResponse;\n this.cause = options.cause;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when template rendering or DTO validation fails.\n */\nexport class TemplateError extends MailKitError {\n constructor(message: string) {\n super(message);\n this.name = \"TemplateError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","/**\n * Decodes common HTML entities into their plain text equivalents.\n */\nexport function decodeHtmlEntities(text: string): string {\n return text\n .replace(/ /gi, \" \")\n .replace(/&/gi, \"&\")\n .replace(/</gi, \"<\")\n .replace(/>/gi, \">\")\n .replace(/"/gi, '\"')\n .replace(/'/gi, \"'\")\n .replace(/©/gi, \"©\")\n .replace(/®/gi, \"®\")\n .replace(/™/gi, \"™\");\n}\n\n/**\n * Strips HTML tags and normalizes whitespace into readable plain text.\n * Converts <br>, <p>, and <div> into appropriate line breaks.\n */\nexport function htmlToPlainText(html: string): string {\n if (!html) return \"\";\n\n return decodeHtmlEntities(\n html\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\\n\")\n .replace(/<\\/div>/gi, \"\\n\")\n .replace(/<\\/h[1-6]>/gi, \"\\n\\n\")\n .replace(/<[^>]*>/g, \"\"),\n )\n .replace(/[ \\t]+/g, \" \")\n .replace(/\\n\\s*\\n\\s*\\n+/g, \"\\n\\n\")\n .trim();\n}\n","import type { EmailTemplateDto } from \"../../templates/dto/index.js\";\nimport type { IEmailRenderer, RenderedEmail } from \"../renderer.interface.js\";\nimport { htmlToPlainText } from \"../utils/html-to-plaintext.js\";\n\nexport interface DefaultRendererOptions {\n /**\n * Primary brand color for buttons and accents.\n * @default '#4f46e5'\n */\n primaryColor?: string;\n\n /**\n * Font stack used in the HTML wrapper.\n * @default \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif\"\n */\n fontFamily?: string;\n\n /**\n * Container maximum width.\n * @default '600px'\n */\n maxWidth?: string;\n\n /**\n * Default logo URL displayed at the top-center of the email if not provided in the DTO.\n * Must be a valid http(s) URL.\n */\n logoUrl?: string;\n}\n\n/**\n * Validates whether a string is a valid HTTP or HTTPS URL.\n */\nexport function isValidHttpUrl(urlString?: string): boolean {\n if (!urlString || typeof urlString !== \"string\") return false;\n try {\n const url = new URL(urlString.trim());\n return (url.protocol === \"http:\" || url.protocol === \"https:\") && url.hostname.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Builds a plain-text fallback string from an email DTO.\n */\nexport function buildPlainText(dto: EmailTemplateDto): string {\n if (dto.noHtmlMessage) {\n return dto.noHtmlMessage;\n }\n\n const divider = \"──────────────────────────────\";\n const parts: string[] = [];\n\n if (dto.header) {\n parts.push(dto.header, divider);\n }\n\n if (dto.body) {\n parts.push(htmlToPlainText(dto.body));\n }\n\n if (dto.otpCode) {\n parts.push(`Verification Code: ${dto.otpCode}`);\n }\n\n if (dto.actionText && dto.actionUrl) {\n parts.push(`${dto.actionText}: ${dto.actionUrl}`);\n }\n\n if (dto.extraMessage) {\n parts.push(htmlToPlainText(dto.extraMessage));\n }\n\n if (dto.footer) {\n parts.push(`${divider}\\n${htmlToPlainText(dto.footer)}`);\n }\n\n return parts.join(\"\\n\\n\");\n}\n\n/**\n * Builds a responsive, styled HTML email document from an email DTO.\n */\nexport function buildHtml(dto: EmailTemplateDto, options: DefaultRendererOptions = {}): string {\n const primaryColor = options.primaryColor || \"#4f46e5\";\n const fontFamily =\n options.fontFamily ||\n \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif\";\n const maxWidth = options.maxWidth || \"600px\";\n\n const parts: string[] = [\n `<!DOCTYPE html>`,\n `<html lang=\"en\">`,\n `<head>`,\n `<meta charset=\"UTF-8\">`,\n `<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">`,\n `<title>${dto.subject}</title>`,\n `</head>`,\n `<body style=\"margin:0;padding:24px 16px;background-color:#f9fafb;font-family:${fontFamily};color:#1f2937;line-height:1.6;\">`,\n `<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;\">`,\n ];\n\n const logoCandidate = dto.logoUrl ?? options.logoUrl;\n if (logoCandidate && isValidHttpUrl(logoCandidate)) {\n const validLogo = logoCandidate.trim();\n parts.push(\n `<div style=\"margin-bottom:24px;text-align:center;\">`,\n ` <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;\" />`,\n `</div>`,\n );\n }\n\n if (dto.imageUrl) {\n parts.push(\n `<div style=\"margin-bottom:24px;text-align:center;\">`,\n ` <img src=\"${dto.imageUrl}\" alt=\"Header\" style=\"max-width:100%;height:auto;border-radius:6px;\" />`,\n `</div>`,\n );\n }\n\n if (dto.header) {\n parts.push(\n `<h2 style=\"margin:0 0 16px;font-size:1.5rem;font-weight:700;color:#111827;letter-spacing:-0.025em;\">${dto.header}</h2>`,\n );\n }\n\n if (dto.body) {\n parts.push(`<div style=\"margin:0 0 20px;font-size:1rem;color:#374151;\">${dto.body}</div>`);\n }\n\n if (dto.otpCode) {\n parts.push(\n `<div style=\"margin:24px 0;padding:20px;background-color:#f3f4f6;border-radius:8px;text-align:center;\">`,\n ` <span style=\"font-size:2rem;letter-spacing:0.5rem;font-weight:700;color:#111827;font-family:monospace;\">${dto.otpCode}</span>`,\n `</div>`,\n );\n }\n\n if (dto.actionUrl && dto.actionText) {\n parts.push(\n `<div style=\"margin:24px 0;text-align:center;\">`,\n ` <a href=\"${dto.actionUrl}\" style=\"display:inline-block;padding:12px 28px;background-color:${primaryColor};color:#ffffff;border-radius:6px;text-decoration:none;font-weight:600;font-size:1rem;text-align:center;box-sizing:border-box;\">${dto.actionText}</a>`,\n `</div>`,\n );\n }\n\n if (dto.extraMessage) {\n parts.push(\n `<div style=\"margin:20px 0 0;font-size:0.875rem;color:#6b7280;line-height:1.5;\">${dto.extraMessage}</div>`,\n );\n }\n\n if (dto.footer) {\n parts.push(\n `<div style=\"margin-top:32px;padding-top:20px;border-top:1px solid #e5e7eb;font-size:0.75rem;color:#9ca3af;text-align:center;\">${dto.footer}</div>`,\n );\n }\n\n parts.push(`</div>`, `</body>`, `</html>`);\n return parts.join(\"\\n\");\n}\n\n/**\n * Default HTML and plain-text email renderer for ts-mailkit.\n */\nexport class DefaultEmailRenderer implements IEmailRenderer {\n private readonly options: DefaultRendererOptions;\n\n constructor(options: DefaultRendererOptions = {}) {\n this.options = options;\n }\n\n render(dto: EmailTemplateDto): RenderedEmail {\n return {\n html: buildHtml(dto, this.options),\n text: buildPlainText(dto),\n };\n }\n}\n","import type { IEmailSender } from \"../transports/sender.interface.js\";\nimport type { IEmailRenderer, RenderedEmail } from \"../renderers/renderer.interface.js\";\nimport { DefaultEmailRenderer } from \"../renderers/default/default-renderer.js\";\nimport { htmlToPlainText } from \"../renderers/utils/html-to-plaintext.js\";\nimport type { EmailTemplateDto } from \"../templates/dto/index.js\";\nimport {\n normalizeRecipient,\n type BulkKnownTemplateSendOptions,\n type KnownTemplateSendOptions,\n type MailAddressOptions,\n type MailMetadataOptions,\n type Recipient,\n type SendBulkMailOptions,\n type SendMailOptions,\n type SendResult,\n} from \"./types.js\";\nimport { ConfigurationError } from \"./errors.js\";\n\nexport interface SendTemplateOptions extends MailAddressOptions, MailMetadataOptions {\n /**\n * One or more recipient email addresses.\n */\n to: Recipient | Recipient[];\n /**\n * Templated email DTO to render and send.\n */\n template: EmailTemplateDto;\n /**\n * Optional recipient display name (used when `to` is a plain string).\n */\n toName?: string;\n /**\n * Optional sender display name override for this specific email.\n */\n fromName?: string;\n}\n\nexport interface MailKitOptions {\n /**\n * The transport adapter used to dispatch emails (e.g. CpaasHttpSender, MemoryEmailSender).\n */\n transport: IEmailSender;\n /**\n * Custom renderer implementation. Defaults to DefaultEmailRenderer.\n */\n renderer?: IEmailRenderer;\n /**\n * Default sender address or Recipient.\n */\n defaultFrom?: Recipient;\n /**\n * Default brand or application name.\n */\n defaultBrandName?: string;\n /**\n * Optional asynchronous or synchronous resolver to dynamically resolve brand/tenant name.\n * Useful for multitenant applications.\n */\n brandNameResolver?: () => Promise<string> | string;\n}\n\n/**\n * Main email client and orchestration service for ts-mailkit.\n */\nexport class MailKit {\n private readonly transport: IEmailSender;\n private readonly renderer: IEmailRenderer;\n private readonly defaultFrom?: Recipient;\n private readonly defaultBrandName?: string;\n private readonly brandNameResolver?: () => Promise<string> | string;\n\n constructor(options: MailKitOptions) {\n if (!options.transport) {\n throw new ConfigurationError(\"MailKit: A transport adapter (IEmailSender) is required.\");\n }\n this.transport = options.transport;\n this.renderer = options.renderer ?? new DefaultEmailRenderer();\n this.defaultFrom = options.defaultFrom;\n this.defaultBrandName = options.defaultBrandName;\n this.brandNameResolver = options.brandNameResolver;\n }\n\n /**\n * Returns the underlying transport instance.\n */\n getTransport(): IEmailSender {\n return this.transport;\n }\n\n /**\n * Renders an email DTO to HTML and plain text without dispatching.\n */\n async render(template: EmailTemplateDto): Promise<RenderedEmail> {\n return await this.renderer.render(template);\n }\n\n /**\n * Sends an email using a strongly-typed template DTO.\n *\n * Example:\n * ```ts\n * await mailer.sendMail({\n * to: 'user@example.com',\n * template: myDto,\n * });\n * ```\n */\n async sendMail(options: SendTemplateOptions): Promise<SendResult> {\n const template = options.template;\n const recipientName = options.toName ?? template.userName;\n const resolvedFromName = await this.resolveFromName(template, options.fromName);\n\n // Render template DTO\n const rendered = await this.renderer.render(template);\n const plaintextBody = template.noHtmlMessage ?? rendered.text;\n\n // Format recipients\n let toRecipients: Recipient | Recipient[];\n if (typeof options.to === \"string\" && recipientName) {\n toRecipients = { address: options.to, name: recipientName };\n } else {\n toRecipients = options.to;\n }\n\n // Resolve From address\n const from = this.applyFromName(options.from ?? this.defaultFrom, resolvedFromName);\n\n return await this.sendRaw({\n to: toRecipients,\n from,\n subject: template.subject,\n html: rendered.html,\n text: plaintextBody,\n cc: options.cc,\n bcc: options.bcc,\n replyTo: options.replyTo,\n attachments: options.attachments,\n headers: options.headers,\n tags: options.tags,\n metadata: options.metadata,\n });\n }\n\n /**\n * Sends a bulk email using a strongly-typed template DTO.\n *\n * The transport delivers an individual copy to each recipient, so\n * recipients are not exposed to one another. Per-recipient personalization\n * is available via {@link sendBulkKnownTemplate}.\n *\n * Example:\n * ```ts\n * await mailer.sendBulkMail({\n * to: ['a@example.com', 'b@example.com'],\n * template: myDto,\n * });\n * ```\n */\n async sendBulkMail(options: SendBulkMailOptions): Promise<SendResult> {\n const transport = this.transport;\n if (typeof transport.sendBulk !== \"function\") {\n throw new ConfigurationError(\n \"MailKit: The configured transport does not support bulk sending (sendBulk).\",\n );\n }\n\n const template = options.template;\n const resolvedFromName = await this.resolveFromName(template);\n\n const rendered = await this.renderer.render(template);\n const plaintextBody = template.noHtmlMessage ?? rendered.text;\n const from = this.applyFromName(options.from ?? this.defaultFrom, resolvedFromName);\n\n return await transport.sendBulk({\n to: options.to,\n from,\n subject: template.subject,\n html: rendered.html,\n text: plaintextBody,\n cc: options.cc,\n bcc: options.bcc,\n replyTo: options.replyTo,\n attachments: options.attachments,\n headers: options.headers,\n tags: options.tags,\n metadata: options.metadata,\n trackClicks: options.trackClicks,\n trackOpens: options.trackOpens,\n bounceAddress: options.bounceAddress,\n clientReference: options.clientReference,\n });\n }\n\n /**\n * Sends a single email using a provider-side template.\n */\n async sendKnownTemplate(options: KnownTemplateSendOptions): Promise<SendResult> {\n const transport = this.transport;\n if (typeof transport.sendKnownTemplate !== \"function\") {\n throw new ConfigurationError(\n \"MailKit: The configured transport does not support template sending (sendKnownTemplate).\",\n );\n }\n\n return await transport.sendKnownTemplate({\n ...options,\n from: options.from ?? this.defaultFrom,\n });\n }\n\n /**\n * Sends a bulk email using a provider-side template with optional\n * per-recipient merge data.\n */\n async sendBulkKnownTemplate(options: BulkKnownTemplateSendOptions): Promise<SendResult> {\n const transport = this.transport;\n if (typeof transport.sendBulkKnownTemplate !== \"function\") {\n throw new ConfigurationError(\n \"MailKit: The configured transport does not support bulk template sending (sendBulkKnownTemplate).\",\n );\n }\n\n return await transport.sendBulkKnownTemplate({\n ...options,\n from: options.from ?? this.defaultFrom,\n });\n }\n\n private async resolveFromName(\n template: EmailTemplateDto,\n explicitFromName?: string,\n ): Promise<string | undefined> {\n let resolved = explicitFromName ?? template.fromName;\n if (!resolved && this.brandNameResolver) {\n resolved = await this.brandNameResolver();\n }\n if (!resolved) {\n resolved = this.defaultBrandName;\n }\n return resolved;\n }\n\n private applyFromName(\n from: Recipient | undefined,\n fromName: string | undefined,\n ): Recipient | undefined {\n if (fromName && from) {\n const normalizedFrom = normalizeRecipient(from);\n return { address: normalizedFrom.address, name: fromName };\n }\n return from;\n }\n\n /**\n * Dispatches a raw, pre-rendered email directly through the transport.\n * If `options.text` is omitted, plain text is automatically derived from `options.html`.\n */\n async sendRaw(options: SendMailOptions): Promise<SendResult> {\n const from = options.from ?? this.defaultFrom;\n const text = options.text ?? (options.html ? htmlToPlainText(options.html) : undefined);\n return await this.transport.send({\n ...options,\n from,\n text,\n });\n }\n}\n\n/**\n * Convenience factory to create a MailKit client instance.\n */\nexport function createMailKit(options: MailKitOptions): MailKit {\n return new MailKit(options);\n}\n","import { ConfigurationError, TransportError } from \"../../core/errors.js\";\nimport {\n normalizeRecipient,\n normalizeRecipients,\n type EmailAttachment,\n type Recipient,\n type SendMailOptions,\n type SendResult,\n} from \"../../core/types.js\";\nimport type {\n BulkKnownTemplateSendOptions,\n BulkSendOptions,\n KnownTemplateSendOptions,\n} from \"../../core/types.js\";\nimport type { IEmailSender } from \"../sender.interface.js\";\nimport type {\n CpaasAddressObject,\n CpaasAttachmentPayload,\n CpaasBatchSendPayload,\n CpaasBatchTemplateSendPayload,\n CpaasHttpConfig,\n CpaasRecipientWrapper,\n CpaasSendPayload,\n CpaasTemplateSendPayload,\n} from \"./types.js\";\n\n/** Fields shared by every CPaaS request payload. */\ninterface CommonPayloadFields {\n cc?: CpaasRecipientWrapper[];\n bcc?: CpaasRecipientWrapper[];\n reply_to?: CpaasAddressObject[];\n attachments?: CpaasAttachmentPayload[];\n track_clicks?: boolean;\n track_opens?: boolean;\n bounce_address?: string;\n client_reference?: string;\n}\n\n/** Options accepted by {@link CpaasHttpSender.applyCommon}. */\ninterface CommonOptions {\n cc?: Recipient | Recipient[];\n bcc?: Recipient | Recipient[];\n replyTo?: Recipient | Recipient[];\n attachments?: EmailAttachment[];\n trackClicks?: boolean;\n trackOpens?: boolean;\n bounceAddress?: string;\n clientReference?: string;\n}\n\n/**\n * Zoho CPaaS transactional email HTTP transport adapter.\n *\n * Uses the standard global `fetch` API for zero-dependency, universal edge\n * compatibility (Node.js, Bun, Deno, Cloudflare Workers).\n *\n * Supports:\n * - `send` — single raw email (`POST /v1.1/email`)\n * - `sendBulk` — batch raw email (`POST /v1.1/email/batch`)\n * - `sendKnownTemplate` — single template email (`POST /v1.1/email/template`)\n * - `sendBulkKnownTemplate` — batch template email (`POST /v1.1/email/template/batch`)\n */\nexport class CpaasHttpSender implements IEmailSender {\n readonly name = \"cpaas\";\n\n private readonly apiBase: string;\n private readonly authHeader: string;\n private readonly defaultFrom: { address: string; name?: string };\n private readonly preferTemplateAlias: boolean;\n private readonly fetchImpl: typeof fetch;\n\n constructor(config: CpaasHttpConfig) {\n if (!config.apiToken || typeof config.apiToken !== \"string\") {\n throw new ConfigurationError(\"CpaasHttpSender: Missing or invalid apiToken.\");\n }\n if (!config.from) {\n throw new ConfigurationError(\"CpaasHttpSender: Missing required default from address.\");\n }\n\n const host = (config.apiHost || \"cpaas.zoho.com\")\n .replace(/^https?:\\/\\//, \"\")\n .replace(/\\/+$/, \"\");\n this.apiBase = `https://${host}/v1.1`;\n\n // Normalize auth token header. If the caller already included \"Zoho-\", use as is.\n const token = config.apiToken.trim();\n this.authHeader = token.startsWith(\"Zoho-\") ? token : `Zoho-enczapikey ${token}`;\n\n this.defaultFrom = normalizeRecipient(config.from);\n if (!this.defaultFrom.address) {\n throw new ConfigurationError(\"CpaasHttpSender: Configured from address must not be empty.\");\n }\n\n this.preferTemplateAlias = config.preferTemplateAlias ?? false;\n\n const fetchImpl = config.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new ConfigurationError(\n \"CpaasHttpSender: No global fetch implementation found. Pass a custom fetch to CpaasHttpConfig if running on Node < 18.\",\n );\n }\n // Bind: detached fetch throws \"Illegal invocation\" on strict-this runtimes (undici/workerd).\n this.fetchImpl = fetchImpl.bind(globalThis);\n }\n\n /**\n * Dispatches a single raw email using Zoho CPaaS API.\n */\n async send(options: SendMailOptions): Promise<SendResult> {\n const payload = this.buildSendPayload(options);\n return await this.post(`${this.apiBase}/email`, payload);\n }\n\n /**\n * Dispatches a batch of a single raw email content to many recipients.\n * The provider delivers an individual copy to each recipient.\n */\n async sendBulk(options: BulkSendOptions): Promise<SendResult> {\n const payload: CpaasBatchSendPayload = this.buildSendPayload(options);\n return await this.post(`${this.apiBase}/email/batch`, payload);\n }\n\n /**\n * Dispatches a single email using a provider-side template.\n */\n async sendKnownTemplate(options: KnownTemplateSendOptions): Promise<SendResult> {\n const payload = this.buildTemplatePayload(options);\n return await this.post(`${this.apiBase}/email/template`, payload);\n }\n\n /**\n * Dispatches a batch of emails using a provider-side template, with\n * optional per-recipient merge data.\n */\n async sendBulkKnownTemplate(options: BulkKnownTemplateSendOptions): Promise<SendResult> {\n const payload = this.buildBatchTemplatePayload(options);\n return await this.post(`${this.apiBase}/email/template/batch`, payload);\n }\n\n // ─── Payload builders ──────────────────────────────────────────────────────\n\n private buildSendPayload(options: SendMailOptions): CpaasSendPayload {\n const toRecipients = normalizeRecipients(options.to);\n if (toRecipients.length === 0) {\n throw new TransportError(\"CpaasHttpSender: At least one recipient (to) must be specified.\", {\n transportName: this.name,\n });\n }\n\n const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;\n\n const payload: CpaasSendPayload = {\n from: {\n address: resolvedFrom.address,\n name: resolvedFrom.name,\n },\n to: toRecipients.map((r) => ({\n email_address: { address: r.address, name: r.name || \"\" },\n })),\n subject: options.subject,\n htmlbody: options.html,\n textbody: options.text,\n };\n\n this.applyCommon(payload, options);\n return payload;\n }\n\n private buildTemplatePayload(options: KnownTemplateSendOptions): CpaasTemplateSendPayload {\n const toRecipients = normalizeRecipients(options.to);\n if (toRecipients.length === 0) {\n throw new TransportError(\"CpaasHttpSender: At least one recipient (to) must be specified.\", {\n transportName: this.name,\n });\n }\n\n const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;\n\n const payload: CpaasTemplateSendPayload = {\n ...this.templateRef(options.templateId),\n from: {\n address: resolvedFrom.address,\n name: resolvedFrom.name,\n },\n to: toRecipients.map((r) => ({\n email_address: { address: r.address, name: r.name || \"\" },\n })),\n };\n\n if (options.mergeInfo) {\n payload.merge_info = options.mergeInfo;\n }\n\n this.applyCommon(payload, options);\n return payload;\n }\n\n private buildBatchTemplatePayload(\n options: BulkKnownTemplateSendOptions,\n ): CpaasBatchTemplateSendPayload {\n if (!options.to || options.to.length === 0) {\n throw new TransportError(\"CpaasHttpSender: At least one recipient (to) must be specified.\", {\n transportName: this.name,\n });\n }\n\n const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;\n\n const payload: CpaasBatchTemplateSendPayload = {\n ...this.templateRef(options.templateId),\n from: {\n address: resolvedFrom.address,\n name: resolvedFrom.name,\n },\n to: options.to.map((r) => ({\n email_address: { address: r.address, name: r.name || \"\" },\n ...(r.mergeInfo ? { merge_info: r.mergeInfo } : {}),\n })),\n };\n\n this.applyCommon(payload, options);\n return payload;\n }\n\n private templateRef(templateId: string): { template_key?: string; template_alias?: string } {\n return this.preferTemplateAlias ? { template_alias: templateId } : { template_key: templateId };\n }\n\n private applyCommon(payload: CommonPayloadFields, options: CommonOptions): void {\n if (options.cc) {\n const ccList = normalizeRecipients(options.cc);\n if (ccList.length > 0) {\n payload.cc = ccList.map((r) => ({\n email_address: { address: r.address, name: r.name || \"\" },\n }));\n }\n }\n\n if (options.bcc) {\n const bccList = normalizeRecipients(options.bcc);\n if (bccList.length > 0) {\n payload.bcc = bccList.map((r) => ({\n email_address: { address: r.address, name: r.name || \"\" },\n }));\n }\n }\n\n if (options.replyTo) {\n const replyToList = normalizeRecipients(options.replyTo);\n if (replyToList.length > 0) {\n payload.reply_to = replyToList.map((r) => ({\n address: r.address,\n name: r.name,\n }));\n }\n }\n\n if (options.attachments && options.attachments.length > 0) {\n payload.attachments = options.attachments.map((att) => this.toAttachmentPayload(att));\n }\n\n if (options.trackClicks !== undefined) payload.track_clicks = options.trackClicks;\n if (options.trackOpens !== undefined) payload.track_opens = options.trackOpens;\n if (options.bounceAddress !== undefined) payload.bounce_address = options.bounceAddress;\n if (options.clientReference !== undefined) payload.client_reference = options.clientReference;\n }\n\n private toAttachmentPayload(att: EmailAttachment): CpaasAttachmentPayload {\n let contentBase64: string;\n if (typeof att.content === \"string\") {\n contentBase64 = att.content;\n } else if (typeof Buffer !== \"undefined\") {\n contentBase64 = Buffer.from(att.content).toString(\"base64\");\n } else {\n // Browser / Edge fallback for Uint8Array\n let binary = \"\";\n const bytes = new Uint8Array(att.content);\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n contentBase64 = btoa(binary);\n }\n\n return {\n name: att.filename,\n content: contentBase64,\n mime_type: att.mimeType || att.contentType || \"application/octet-stream\",\n cid: att.cid,\n };\n }\n\n // ─── HTTP ──────────────────────────────────────────────────────────────────\n\n private async post(url: string, payload: unknown): Promise<SendResult> {\n let response: Response;\n try {\n response = await this.fetchImpl(url, {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n Authorization: this.authHeader,\n },\n body: JSON.stringify(payload),\n });\n } catch (err) {\n throw new TransportError(\n `CpaasHttpSender: Network request failed: ${err instanceof Error ? err.message : String(err)}`,\n {\n transportName: this.name,\n cause: err,\n },\n );\n }\n\n const responseText = await response.text();\n let responseData: any;\n try {\n responseData = responseText ? JSON.parse(responseText) : {};\n } catch {\n responseData = { raw: responseText };\n }\n\n if (!response.ok) {\n const errorMessage =\n responseData?.data?.message ||\n responseData?.error?.message ||\n responseData?.message ||\n `HTTP ${response.status}: ${responseText || response.statusText}`;\n\n throw new TransportError(`CpaasHttpSender: Failed to send email - ${errorMessage}`, {\n transportName: this.name,\n statusCode: response.status,\n responseBody: responseText,\n rawResponse: responseData,\n });\n }\n\n // Single-send responses carry `message_id`; batch/template responses carry\n // `code`/`message` entries.\n const messageId = responseData?.data?.[0]?.message_id;\n\n return {\n success: true,\n messageId,\n rawResponse: responseData,\n };\n }\n}\n","import type { SendMailOptions, SendResult } from \"../../core/types.js\";\nimport type {\n BulkKnownTemplateSendOptions,\n BulkSendOptions,\n KnownTemplateSendOptions,\n} from \"../../core/types.js\";\nimport type { IEmailSender } from \"../sender.interface.js\";\n\n/**\n * In-memory email sender for automated testing, local development, and CI environments.\n * Captures all dispatched emails and provides inspection methods.\n */\nexport class MemoryEmailSender implements IEmailSender {\n readonly name = \"memory\";\n\n private sentMails: SendMailOptions[] = [];\n private sentBulkMails: BulkSendOptions[] = [];\n private sentTemplateMails: KnownTemplateSendOptions[] = [];\n private sentBulkTemplateMails: BulkKnownTemplateSendOptions[] = [];\n private nextId = 1;\n\n private result(prefix?: string): SendResult {\n const id = this.nextId++;\n const messageId = prefix ? `mem-${prefix}-${id}-${Date.now()}` : `mem-${id}-${Date.now()}`;\n const rawResponse = { id: messageId };\n return {\n success: true,\n messageId,\n rawResponse,\n };\n }\n\n async send(options: SendMailOptions): Promise<SendResult> {\n this.sentMails.push({ ...options });\n return this.result();\n }\n\n async sendBulk(options: BulkSendOptions): Promise<SendResult> {\n this.sentBulkMails.push({ ...options });\n return this.result(\"bulk\");\n }\n\n async sendKnownTemplate(options: KnownTemplateSendOptions): Promise<SendResult> {\n this.sentTemplateMails.push({ ...options });\n return this.result(\"tpl\");\n }\n\n async sendBulkKnownTemplate(options: BulkKnownTemplateSendOptions): Promise<SendResult> {\n this.sentBulkTemplateMails.push({ ...options });\n return this.result(\"bulk-tpl\");\n }\n\n /**\n * Returns a copy of all single emails sent through this transport instance.\n */\n getSentMails(): SendMailOptions[] {\n return [...this.sentMails];\n }\n\n /**\n * Returns the most recent single email sent, or undefined if none have been sent.\n */\n getLastMail(): SendMailOptions | undefined {\n return this.sentMails.length > 0 ? { ...this.sentMails[this.sentMails.length - 1] } : undefined;\n }\n\n /**\n * Returns a copy of all bulk emails sent through this transport instance.\n */\n getSentBulkMails(): BulkSendOptions[] {\n return [...this.sentBulkMails];\n }\n\n /**\n * Returns a copy of all single template emails sent through this transport instance.\n */\n getSentTemplateMails(): KnownTemplateSendOptions[] {\n return [...this.sentTemplateMails];\n }\n\n /**\n * Returns a copy of all bulk template emails sent through this transport instance.\n */\n getSentBulkTemplateMails(): BulkKnownTemplateSendOptions[] {\n return [...this.sentBulkTemplateMails];\n }\n\n /**\n * Clears all recorded emails.\n */\n clear(): void {\n this.sentMails = [];\n this.sentBulkMails = [];\n this.sentTemplateMails = [];\n this.sentBulkTemplateMails = [];\n }\n\n /**\n * Returns the total count of single emails sent.\n */\n count(): number {\n return this.sentMails.length;\n }\n}\n","import {\n normalizeRecipients,\n type Recipient,\n type SendMailOptions,\n type SendResult,\n} from \"../../core/types.js\";\nimport type {\n BulkKnownTemplateSendOptions,\n BulkSendOptions,\n KnownTemplateSendOptions,\n} from \"../../core/types.js\";\nimport type { IEmailSender } from \"../sender.interface.js\";\n\nexport interface ConsoleEmailSenderOptions {\n /**\n * Log level or custom log printer. Defaults to `console.log`.\n */\n logger?: (message: string) => void;\n /**\n * Whether to include the full HTML body in console output.\n * Defaults to false (prints plain text or a summary).\n */\n includeHtml?: boolean;\n}\n\ninterface DispatchLog {\n kind: string;\n to: Recipient | Recipient[];\n from?: Recipient;\n subject?: string;\n templateId?: string;\n text?: string;\n html?: string;\n}\n\n/**\n * Console logger transport that prints dispatched emails to stdout/stderr.\n * Ideal for development and debugging.\n */\nexport class ConsoleEmailSender implements IEmailSender {\n readonly name = \"console\";\n\n private readonly log: (message: string) => void;\n private readonly includeHtml: boolean;\n private nextId = 1;\n\n constructor(options: ConsoleEmailSenderOptions = {}) {\n this.log = options.logger ?? console.log;\n this.includeHtml = options.includeHtml ?? false;\n }\n\n async send(options: SendMailOptions): Promise<SendResult> {\n return this.logDispatch({\n kind: \"EMAIL\",\n to: options.to,\n from: options.from,\n subject: options.subject,\n text: options.text,\n html: options.html,\n });\n }\n\n async sendBulk(options: BulkSendOptions): Promise<SendResult> {\n return this.logDispatch({\n kind: \"BULK EMAIL\",\n to: options.to,\n from: options.from,\n subject: options.subject,\n text: options.text,\n html: options.html,\n });\n }\n\n async sendKnownTemplate(options: KnownTemplateSendOptions): Promise<SendResult> {\n return this.logDispatch({\n kind: \"TEMPLATE EMAIL\",\n to: options.to,\n from: options.from,\n templateId: options.templateId,\n });\n }\n\n async sendBulkKnownTemplate(options: BulkKnownTemplateSendOptions): Promise<SendResult> {\n return this.logDispatch({\n kind: \"BULK TEMPLATE EMAIL\",\n to: options.to,\n from: options.from,\n templateId: options.templateId,\n });\n }\n\n private logDispatch(entry: DispatchLog): SendResult {\n const to = normalizeRecipients(entry.to)\n .map((r) => (r.name ? `${r.name} <${r.address}>` : r.address))\n .join(\", \");\n const from = entry.from ? JSON.stringify(entry.from) : \"(default)\";\n const messageId = `console-${this.nextId++}-${Date.now()}`;\n\n const separator = \"─\".repeat(60);\n const lines = [\n `\\n┌${separator}┐`,\n `│ [ts-mailkit:console] DISPATCHED ${entry.kind} (${messageId})`,\n `├${separator}┤`,\n `│ From: ${from}`,\n `│ To: ${to}`,\n ];\n\n if (entry.subject !== undefined) {\n lines.push(`│ Subject: ${entry.subject}`);\n }\n if (entry.templateId !== undefined) {\n lines.push(`│ Template: ${entry.templateId}`);\n }\n lines.push(`├${separator}┤`);\n\n if (entry.text) {\n lines.push(\"│ Text Body:\");\n for (const line of entry.text.split(\"\\n\")) {\n lines.push(`│ ${line}`);\n }\n }\n\n if (this.includeHtml && entry.html) {\n lines.push(\"│ HTML Body:\");\n for (const line of entry.html.split(\"\\n\")) {\n lines.push(`│ ${line}`);\n }\n }\n\n lines.push(`└${separator}┘\\n`);\n\n this.log(lines.join(\"\\n\"));\n\n const rawResponse = { logged: true };\n return {\n success: true,\n messageId,\n rawResponse,\n };\n }\n}\n","import type {\n EmailTemplateDto,\n EmailVerificationParams,\n MagicLinkParams,\n NewsletterParams,\n PasswordResetParams,\n SimpleMessageParams,\n TwoFactorParams,\n} from \"./dto/index.js\";\nimport { htmlToPlainText } from \"../renderers/utils/html-to-plaintext.js\";\n\nexport interface BrandingConfig {\n /**\n * Default brand, application, or company name.\n * @default 'Application'\n */\n brandName?: string;\n\n /**\n * Optional custom security / disclaimer advisory message.\n */\n securityMessage?: string;\n\n /**\n * Optional custom footer generator or string.\n */\n footer?: string | ((brandName: string) => string);\n\n /**\n * Optional default logo URL to display top-center in templated emails.\n */\n logoUrl?: string;\n}\n\nconst DEFAULT_SECURITY_MESSAGE =\n \"If you did not request this, please ignore this email or contact support if you have concerns.\";\n\nfunction defaultFooter(brandName: string): string {\n return `© ${new Date().getFullYear()} ${brandName}. All rights reserved.`;\n}\n\n/**\n * Factory for creating pre-populated, type-safe email template DTOs.\n */\nexport class EmailTemplateFactory {\n private readonly defaultBrandName: string;\n private readonly defaultSecurityMessage: string;\n private readonly footerResolver: (brandName: string) => string;\n private readonly defaultLogoUrl?: string;\n\n constructor(config: BrandingConfig = {}) {\n this.defaultBrandName = config.brandName || \"Application\";\n this.defaultSecurityMessage = config.securityMessage || DEFAULT_SECURITY_MESSAGE;\n this.defaultLogoUrl = config.logoUrl;\n if (typeof config.footer === \"function\") {\n this.footerResolver = config.footer;\n } else if (typeof config.footer === \"string\") {\n const fixedFooter = config.footer;\n this.footerResolver = () => fixedFooter;\n } else {\n this.footerResolver = defaultFooter;\n }\n }\n\n private resolveBrand(override?: string): string {\n return override || this.defaultBrandName;\n }\n\n private buildFooter(brandName: string): string {\n return this.footerResolver(brandName);\n }\n\n /**\n * Welcome / email-verification email sent after registration.\n */\n createEmailVerification(params: EmailVerificationParams): EmailTemplateDto {\n const { username, verificationUrl, otp, brandName } = params;\n const brand = this.resolveBrand(brandName);\n const body = `Hi ${username},<br/>Thank you for signing up. Use the code below or click the button to verify your email address.`;\n return {\n subject: \"Verify Your Email Address\",\n header: `Welcome to ${brand}!`,\n body,\n logoUrl: this.defaultLogoUrl,\n otpCode: otp,\n actionUrl: verificationUrl,\n actionText: \"Verify Email\",\n extraMessage: this.defaultSecurityMessage,\n footer: this.buildFooter(brand),\n userName: username,\n fromName: brand,\n noHtmlMessage:\n htmlToPlainText(body) +\n `\\n\\nYour verification code is: ${otp}\\n\\nAlternatively, verify here:\\n${verificationUrl}`,\n };\n }\n\n /**\n * Password-reset email with an optional OTP code and action link.\n */\n createPasswordReset(params: PasswordResetParams): EmailTemplateDto {\n const { username, resetUrl, otp = \"\", brandName } = params;\n const brand = this.resolveBrand(brandName);\n const body = `Hi ${username},<br/>We received a request to reset your password. Use the OTP code below or click the button to proceed.`;\n const plainTextOtp = otp ? `\\n\\nYour OTP code is: ${otp}` : \"\";\n return {\n subject: \"Password Reset Request\",\n header: \"Reset Your Password\",\n body,\n logoUrl: this.defaultLogoUrl,\n otpCode: otp || undefined,\n actionUrl: resetUrl,\n actionText: \"Reset Password\",\n extraMessage: this.defaultSecurityMessage,\n footer: this.buildFooter(brand),\n userName: username,\n fromName: brand,\n noHtmlMessage:\n htmlToPlainText(body) +\n plainTextOtp +\n `\\n\\nAlternatively, you can use the following link to reset your password:\\n${resetUrl}`,\n };\n }\n\n /**\n * Generic plain message email (no action button, no OTP).\n */\n createSimpleMessage(params: SimpleMessageParams): EmailTemplateDto {\n const { subject, header, body, brandName } = params;\n const brand = this.resolveBrand(brandName);\n return {\n subject,\n header,\n body,\n logoUrl: this.defaultLogoUrl,\n footer: this.buildFooter(brand),\n fromName: brand,\n noHtmlMessage: htmlToPlainText(body),\n };\n }\n\n /**\n * Newsletter / announcement email with an optional hero image and CTA button.\n */\n createNewsLetter(params: NewsletterParams): EmailTemplateDto {\n const { subject, header, body, imageUrl, actionUrl, actionText, brandName, logoUrl } = params;\n const brand = this.resolveBrand(brandName);\n let noHtmlMessage = htmlToPlainText(body);\n if (actionUrl) {\n noHtmlMessage += `\\n\\n${actionText || \"Learn more\"}: ${actionUrl}`;\n }\n return {\n subject,\n header,\n body,\n logoUrl: logoUrl || this.defaultLogoUrl,\n imageUrl,\n actionUrl,\n actionText,\n footer: this.buildFooter(brand),\n fromName: brand,\n noHtmlMessage,\n };\n }\n\n /**\n * Two-factor authentication email with OTP code and optional magic link.\n */\n create2FAEmail(params: TwoFactorParams): EmailTemplateDto {\n const { username, otp, magicLink = \"\", brandName } = params;\n const brand = this.resolveBrand(brandName);\n const body = `Hi ${username},<br/>Use the code below or click the button to complete your login.`;\n const plainLink = magicLink ? `\\n\\nAlternatively, verify here:\\n${magicLink}` : \"\";\n return {\n subject: \"Your Login Verification Code\",\n header: \"Two-Factor Authentication\",\n body,\n logoUrl: this.defaultLogoUrl,\n otpCode: otp,\n actionUrl: magicLink || undefined,\n actionText: magicLink ? \"Verify Login\" : undefined,\n extraMessage: this.defaultSecurityMessage,\n footer: this.buildFooter(brand),\n userName: username,\n fromName: brand,\n noHtmlMessage: htmlToPlainText(body) + `\\n\\nYour verification code is: ${otp}` + plainLink,\n };\n }\n\n /**\n * Magic link sign-in email (no OTP, button only).\n */\n createMagicLinkEmail(params: MagicLinkParams): EmailTemplateDto {\n const { username, magicLink, brandName } = params;\n const brand = this.resolveBrand(brandName);\n const body = `Hi ${username},<br/>Click the button below to sign in. No password needed.`;\n return {\n subject: \"Your Magic Link\",\n header: `Sign In to ${brand}`,\n body,\n logoUrl: this.defaultLogoUrl,\n actionUrl: magicLink,\n actionText: \"Sign In\",\n extraMessage: this.defaultSecurityMessage,\n footer: this.buildFooter(brand),\n userName: username,\n fromName: brand,\n noHtmlMessage: htmlToPlainText(body) + `\\n\\nSign in here:\\n${magicLink}`,\n };\n }\n\n // ─── Default Static Instance ──────────────────────────────────────────────\n private static _defaultInstance?: EmailTemplateFactory;\n\n private static get defaultInstance(): EmailTemplateFactory {\n if (!this._defaultInstance) {\n this._defaultInstance = new EmailTemplateFactory();\n }\n return this._defaultInstance;\n }\n\n static createEmailVerification(params: EmailVerificationParams): EmailTemplateDto {\n return this.defaultInstance.createEmailVerification(params);\n }\n\n static createPasswordReset(params: PasswordResetParams): EmailTemplateDto {\n return this.defaultInstance.createPasswordReset(params);\n }\n\n static createSimpleMessage(params: SimpleMessageParams): EmailTemplateDto {\n return this.defaultInstance.createSimpleMessage(params);\n }\n\n static createNewsLetter(params: NewsletterParams): EmailTemplateDto {\n return this.defaultInstance.createNewsLetter(params);\n }\n\n static create2FAEmail(params: TwoFactorParams): EmailTemplateDto {\n return this.defaultInstance.create2FAEmail(params);\n }\n\n static createMagicLinkEmail(params: MagicLinkParams): EmailTemplateDto {\n return this.defaultInstance.createMagicLinkEmail(params);\n }\n}\n","import type { EmailTemplateDto } from \"./dto/index.js\";\nimport {\n buildHtml,\n buildPlainText,\n type DefaultRendererOptions,\n} from \"../renderers/default/default-renderer.js\";\n\n/**\n * Styling options for building upload-ready templates.\n */\nexport interface TemplateBuildOptions {\n /**\n * Primary brand color for buttons and accents.\n * @default '#4f46e5'\n */\n primaryColor?: string;\n\n /**\n * Font stack used in the HTML wrapper.\n */\n fontFamily?: string;\n\n /**\n * Container maximum width.\n * @default '600px'\n */\n maxWidth?: string;\n\n /**\n * Default logo URL displayed at the top-center of the template.\n */\n logoUrl?: string;\n}\n\n/**\n * Builds an upload-ready HTML template from the canonical {@link EmailTemplateDto}.\n *\n * The output can be uploaded to a messaging platform (Zoho CPaaS, SendGrid, etc.)\n * as a provider-side template.\n */\nexport function buildHTMLTemplate(\n dto: EmailTemplateDto,\n options: TemplateBuildOptions = {},\n): string {\n return buildHtml(dto, options as DefaultRendererOptions);\n}\n\n/**\n * Builds an upload-ready plain-text template from the canonical {@link EmailTemplateDto}.\n */\nexport function buildPlainTextTemplate(\n dto: EmailTemplateDto,\n _options?: TemplateBuildOptions,\n): string {\n return buildPlainText(dto);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/core/types.ts","../src/core/errors.ts","../src/renderers/utils/html-to-plaintext.ts","../src/renderers/default/default-renderer.ts","../src/core/client.ts","../src/transports/cpaas/cpaas-sender.ts","../src/transports/memory/memory-sender.ts","../src/transports/console/console-sender.ts","../src/templates/factory.ts","../src/templates/template-builder.ts"],"sourcesContent":["import type { EmailTemplateDto } from \"../templates/dto/index.js\";\n\n/**\n * Standard email address representation with optional display name.\n */\nexport interface EmailAddress {\n address: string;\n name?: string;\n}\n\n/**\n * A recipient with optional provider-side merge variables.\n */\nexport interface BulkRecipient extends EmailAddress {\n /**\n * Provider-side merge variables (e.g. Zoho CPaaS `merge_info`).\n */\n mergeInfo?: Record<string, string>;\n}\n\n/**\n * Flexible recipient representation allowing string ('user@example.com'),\n * EmailAddress ({ address: 'user@example.com', name: 'User' }),\n * or legacy/standard object ({ email: 'user@example.com', name: 'User' }).\n */\nexport type Recipient = string | EmailAddress | { email: string; name?: string };\n\n/**\n * Parses an RFC 5322 formatted address string into an EmailAddress.\n * Supports formats such as:\n * - \"user@example.com\" -> { address: \"user@example.com\" }\n * - \"Sender Name <sender@example.com>\" -> { name: \"Sender Name\", address: \"sender@example.com\" }\n * - \"\\\"Sender Name\\\" <sender@example.com>\" -> { name: \"Sender Name\", address: \"sender@example.com\" }\n */\nexport function parseEmailAddress(input: string): EmailAddress {\n if (!input || typeof input !== \"string\") {\n return { address: \"\" };\n }\n const trimmed = input.trim();\n const match = trimmed.match(/^(?:[\"']?([^\"']+)[\"']?\\s+)?<?([^\\s<>@]+@[^\\s<>@]+)>?$/);\n if (!match) {\n return { address: trimmed };\n }\n return {\n address: match[2].trim(),\n name: match[1]?.trim() || undefined,\n };\n}\n\n/**\n * Normalizes a Recipient value into a standardized EmailAddress.\n */\nexport function normalizeRecipient(recipient: Recipient): EmailAddress {\n if (typeof recipient === \"string\") {\n return { address: recipient.trim() };\n }\n if (\"email\" in recipient) {\n return {\n address: (recipient.email ?? \"\").trim(),\n name: recipient.name?.trim(),\n };\n }\n return {\n address: (recipient.address ?? \"\").trim(),\n name: recipient.name?.trim(),\n };\n}\n\n/**\n * Normalizes a single or array of Recipients into an array of EmailAddress.\n */\nexport function normalizeRecipients(recipients?: Recipient | Recipient[]): EmailAddress[] {\n if (!recipients) return [];\n const list = Array.isArray(recipients) ? recipients : [recipients];\n return list.map(normalizeRecipient).filter((r) => r.address.length > 0);\n}\n\n/**\n * Email attachment representation.\n */\nexport interface EmailAttachment {\n filename: string;\n /**\n * Content can be base64-encoded string, plain text, or binary Buffer/Uint8Array.\n */\n content: string | Uint8Array;\n mimeType?: string;\n contentType?: string;\n cid?: string;\n}\n\n/**\n * Common sender and recipient addressing options.\n */\nexport interface MailAddressOptions {\n /**\n * Optional sender address override.\n */\n from?: Recipient;\n /**\n * Optional CC recipients.\n */\n cc?: Recipient | Recipient[];\n /**\n * Optional BCC recipients.\n */\n bcc?: Recipient | Recipient[];\n /**\n * Optional Reply-To recipients.\n */\n replyTo?: Recipient | Recipient[];\n /**\n * Optional file attachments.\n */\n attachments?: EmailAttachment[];\n}\n\n/**\n * Common custom header, tag, and metadata options.\n */\nexport interface MailMetadataOptions {\n /**\n * Optional custom headers.\n */\n headers?: Record<string, string>;\n /**\n * Optional tags for tracking.\n */\n tags?: string[];\n /**\n * Optional metadata.\n */\n metadata?: Record<string, string>;\n}\n\n/**\n * Common provider tracking and reference options.\n */\nexport interface TrackingOptions {\n /**\n * Enable or disable click tracking.\n */\n trackClicks?: boolean;\n /**\n * Enable or disable open tracking.\n */\n trackOpens?: boolean;\n /**\n * Verified bounce (Return-Path) address.\n */\n bounceAddress?: string;\n /**\n * Custom reference string to identify the email in your system.\n */\n clientReference?: string;\n}\n\n/**\n * Options for sending an email through a transport adapter.\n */\nexport interface SendMailOptions extends MailAddressOptions, MailMetadataOptions {\n to: Recipient | Recipient[];\n subject: string;\n html: string;\n text?: string;\n}\n\n/**\n * Transport-level batch of a single raw email content to many recipients.\n *\n * The provider delivers an individual copy to each recipient, so recipients\n * are not exposed to one another.\n */\nexport type BulkSendOptions = SendMailOptions & TrackingOptions;\n\n/**\n * Options for sending a single email using a provider-side template.\n */\nexport interface KnownTemplateSendOptions extends MailAddressOptions, TrackingOptions {\n /**\n * Provider-side template identifier (e.g. Zoho `template_key`).\n */\n templateId: string;\n /**\n * Recipient email address.\n */\n to: Recipient;\n /**\n * Template variable substitutions.\n */\n mergeInfo?: Record<string, string>;\n}\n\n/**\n * Options for sending a batch of emails using a provider-side template.\n *\n * Each recipient may carry its own `mergeInfo` so the provider can\n * personalize the template per recipient.\n */\nexport interface BulkKnownTemplateSendOptions extends MailAddressOptions, TrackingOptions {\n /**\n * Provider-side template identifier (e.g. Zoho `template_key`).\n */\n templateId: string;\n /**\n * Recipients, each with optional per-recipient merge variables.\n */\n to: BulkRecipient[];\n}\n\n/**\n * Client-level options for sending the same in-built template DTO to many\n * recipients. For per-recipient content, use `sendBulkKnownTemplate` with a\n * provider-side template and `mergeInfo`.\n */\nexport interface SendBulkMailOptions\n extends MailAddressOptions, MailMetadataOptions, TrackingOptions {\n /**\n * Templated email DTO rendered once and sent to every recipient.\n */\n template: EmailTemplateDto;\n /**\n * One or more recipient email addresses.\n */\n to: Recipient | Recipient[];\n}\n\n/**\n * Result returned by an IEmailSender upon dispatch.\n */\nexport interface SendResult {\n success: boolean;\n messageId?: string;\n /** Raw provider response (e.g. Zoho CPaaS JSON body). */\n rawResponse?: unknown;\n}\n","/**\n * Base error class for all ts-mailkit errors.\n */\nexport class MailKitError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MailKitError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when transport configuration is missing or invalid.\n */\nexport class ConfigurationError extends MailKitError {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigurationError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when an email transport fails to dispatch an email.\n */\nexport class TransportError extends MailKitError {\n readonly transportName: string;\n readonly statusCode?: number;\n readonly responseBody?: string;\n /** Parsed raw provider response, when available. */\n readonly rawResponse?: unknown;\n readonly cause?: unknown;\n\n constructor(\n message: string,\n options: {\n transportName: string;\n statusCode?: number;\n responseBody?: string;\n rawResponse?: unknown;\n cause?: unknown;\n },\n ) {\n super(message);\n this.name = \"TransportError\";\n this.transportName = options.transportName;\n this.statusCode = options.statusCode;\n this.responseBody = options.responseBody;\n this.rawResponse = options.rawResponse;\n this.cause = options.cause;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when template rendering or DTO validation fails.\n */\nexport class TemplateError extends MailKitError {\n constructor(message: string) {\n super(message);\n this.name = \"TemplateError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","/**\n * Decodes common HTML entities into their plain text equivalents.\n */\nexport function decodeHtmlEntities(text: string): string {\n return text\n .replace(/ /gi, \" \")\n .replace(/&/gi, \"&\")\n .replace(/</gi, \"<\")\n .replace(/>/gi, \">\")\n .replace(/"/gi, '\"')\n .replace(/'/gi, \"'\")\n .replace(/©/gi, \"©\")\n .replace(/®/gi, \"®\")\n .replace(/™/gi, \"™\");\n}\n\n/**\n * Strips HTML tags and normalizes whitespace into readable plain text.\n * Converts <br>, <p>, and <div> into appropriate line breaks.\n */\nexport function htmlToPlainText(html: string): string {\n if (!html) return \"\";\n\n return decodeHtmlEntities(\n html\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\\n\")\n .replace(/<\\/div>/gi, \"\\n\")\n .replace(/<\\/h[1-6]>/gi, \"\\n\\n\")\n .replace(/<[^>]*>/g, \"\"),\n )\n .replace(/[ \\t]+/g, \" \")\n .replace(/\\n\\s*\\n\\s*\\n+/g, \"\\n\\n\")\n .trim();\n}\n","import type { EmailTemplateDto } from \"../../templates/dto/index.js\";\nimport type { IEmailRenderer, RenderedEmail, RendererOptions } from \"../renderer.interface.js\";\nimport { htmlToPlainText } from \"../utils/html-to-plaintext.js\";\n\n/**\n * Options for the default HTML/plain-text renderer.\n */\nexport interface DefaultRendererOptions extends RendererOptions {}\n\n/**\n * Validates whether a string is a valid HTTP or HTTPS URL.\n */\nexport function isValidHttpUrl(urlString?: string): boolean {\n if (!urlString || typeof urlString !== \"string\") return false;\n try {\n const url = new URL(urlString.trim());\n return (url.protocol === \"http:\" || url.protocol === \"https:\") && url.hostname.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Builds a plain-text fallback string from an email DTO.\n */\nexport function buildPlainText(dto: EmailTemplateDto): string {\n if (dto.noHtmlMessage) {\n return dto.noHtmlMessage;\n }\n\n const divider = \"──────────────────────────────\";\n const parts: string[] = [];\n\n if (dto.header) {\n parts.push(dto.header, divider);\n }\n\n if (dto.body) {\n parts.push(htmlToPlainText(dto.body));\n }\n\n if (dto.otpCode) {\n parts.push(`Verification Code: ${dto.otpCode}`);\n }\n\n if (dto.actionText && dto.actionUrl) {\n parts.push(`${dto.actionText}: ${dto.actionUrl}`);\n }\n\n if (dto.extraMessage) {\n parts.push(htmlToPlainText(dto.extraMessage));\n }\n\n if (dto.footer) {\n parts.push(`${divider}\\n${htmlToPlainText(dto.footer)}`);\n }\n\n return parts.join(\"\\n\\n\");\n}\n\n/**\n * Builds a responsive, styled HTML email document from an email DTO.\n */\nexport function buildHtml(dto: EmailTemplateDto, options: DefaultRendererOptions = {}): string {\n const primaryColor = options.primaryColor || \"#4f46e5\";\n const fontFamily =\n options.fontFamily ||\n \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif\";\n const maxWidth = options.maxWidth || \"600px\";\n\n const parts: string[] = [\n `<!DOCTYPE html>`,\n `<html lang=\"en\">`,\n `<head>`,\n `<meta charset=\"UTF-8\">`,\n `<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">`,\n `<title>${dto.subject}</title>`,\n `</head>`,\n `<body style=\"margin:0;padding:24px 16px;background-color:#f9fafb;font-family:${fontFamily};color:#1f2937;line-height:1.6;\">`,\n `<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;\">`,\n ];\n\n const logoCandidate = dto.logoUrl ?? options.logoUrl;\n if (logoCandidate && isValidHttpUrl(logoCandidate)) {\n const validLogo = logoCandidate.trim();\n parts.push(\n `<div style=\"margin-bottom:24px;text-align:center;\">`,\n ` <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;\" />`,\n `</div>`,\n );\n }\n\n if (dto.imageUrl) {\n parts.push(\n `<div style=\"margin-bottom:24px;text-align:center;\">`,\n ` <img src=\"${dto.imageUrl}\" alt=\"Header\" style=\"max-width:100%;height:auto;border-radius:6px;\" />`,\n `</div>`,\n );\n }\n\n if (dto.header) {\n parts.push(\n `<h2 style=\"margin:0 0 16px;font-size:1.5rem;font-weight:700;color:#111827;letter-spacing:-0.025em;\">${dto.header}</h2>`,\n );\n }\n\n if (dto.body) {\n parts.push(`<div style=\"margin:0 0 20px;font-size:1rem;color:#374151;\">${dto.body}</div>`);\n }\n\n if (dto.otpCode) {\n parts.push(\n `<div style=\"margin:24px 0;padding:20px;background-color:#f3f4f6;border-radius:8px;text-align:center;\">`,\n ` <span style=\"font-size:2rem;letter-spacing:0.5rem;font-weight:700;color:#111827;font-family:monospace;\">${dto.otpCode}</span>`,\n `</div>`,\n );\n }\n\n if (dto.actionUrl && dto.actionText) {\n parts.push(\n `<div style=\"margin:24px 0;text-align:center;\">`,\n ` <a href=\"${dto.actionUrl}\" style=\"display:inline-block;padding:12px 28px;background-color:${primaryColor};color:#ffffff;border-radius:6px;text-decoration:none;font-weight:600;font-size:1rem;text-align:center;box-sizing:border-box;\">${dto.actionText}</a>`,\n `</div>`,\n );\n }\n\n if (dto.extraMessage) {\n parts.push(\n `<div style=\"margin:20px 0 0;font-size:0.875rem;color:#6b7280;line-height:1.5;\">${dto.extraMessage}</div>`,\n );\n }\n\n if (dto.footer) {\n parts.push(\n `<div style=\"margin-top:32px;padding-top:20px;border-top:1px solid #e5e7eb;font-size:0.75rem;color:#9ca3af;text-align:center;\">${dto.footer}</div>`,\n );\n }\n\n parts.push(`</div>`, `</body>`, `</html>`);\n return parts.join(\"\\n\");\n}\n\n/**\n * Default HTML and plain-text email renderer for ts-mailkit.\n */\nexport class DefaultEmailRenderer implements IEmailRenderer {\n private readonly options: DefaultRendererOptions;\n\n constructor(options: DefaultRendererOptions = {}) {\n this.options = options;\n }\n\n render(dto: EmailTemplateDto): RenderedEmail {\n return {\n html: buildHtml(dto, this.options),\n text: buildPlainText(dto),\n };\n }\n}\n","import type { IEmailSender } from \"../transports/sender.interface.js\";\nimport type { IEmailRenderer, RenderedEmail } from \"../renderers/renderer.interface.js\";\nimport { DefaultEmailRenderer } from \"../renderers/default/default-renderer.js\";\nimport { htmlToPlainText } from \"../renderers/utils/html-to-plaintext.js\";\nimport type { EmailTemplateDto } from \"../templates/dto/index.js\";\nimport {\n normalizeRecipient,\n type BulkKnownTemplateSendOptions,\n type KnownTemplateSendOptions,\n type MailAddressOptions,\n type MailMetadataOptions,\n type Recipient,\n type SendBulkMailOptions,\n type SendMailOptions,\n type SendResult,\n} from \"./types.js\";\nimport { ConfigurationError } from \"./errors.js\";\n\nexport interface SendTemplateOptions extends MailAddressOptions, MailMetadataOptions {\n /**\n * One or more recipient email addresses.\n */\n to: Recipient | Recipient[];\n /**\n * Templated email DTO to render and send.\n */\n template: EmailTemplateDto;\n /**\n * Optional recipient display name (used when `to` is a plain string).\n */\n toName?: string;\n /**\n * Optional sender display name override for this specific email.\n */\n fromName?: string;\n}\n\nexport interface MailKitOptions {\n /**\n * The transport adapter used to dispatch emails (e.g. CpaasHttpSender, MemoryEmailSender).\n */\n transport: IEmailSender;\n /**\n * Custom renderer implementation. Defaults to DefaultEmailRenderer.\n */\n renderer?: IEmailRenderer;\n /**\n * Default sender address or Recipient.\n */\n defaultFrom?: Recipient;\n /**\n * Default brand or application name.\n */\n defaultBrandName?: string;\n /**\n * Optional asynchronous or synchronous resolver to dynamically resolve brand/tenant name.\n * Useful for multitenant applications.\n */\n brandNameResolver?: () => Promise<string> | string;\n}\n\n/**\n * Main email client and orchestration service for ts-mailkit.\n */\nexport class MailKit {\n private readonly transport: IEmailSender;\n private readonly renderer: IEmailRenderer;\n private readonly defaultFrom?: Recipient;\n private readonly defaultBrandName?: string;\n private readonly brandNameResolver?: () => Promise<string> | string;\n\n constructor(options: MailKitOptions) {\n if (!options.transport) {\n throw new ConfigurationError(\"MailKit: A transport adapter (IEmailSender) is required.\");\n }\n this.transport = options.transport;\n this.renderer = options.renderer ?? new DefaultEmailRenderer();\n this.defaultFrom = options.defaultFrom;\n this.defaultBrandName = options.defaultBrandName;\n this.brandNameResolver = options.brandNameResolver;\n }\n\n /**\n * Returns the underlying transport instance.\n */\n getTransport(): IEmailSender {\n return this.transport;\n }\n\n /**\n * Renders an email DTO to HTML and plain text without dispatching.\n */\n async render(template: EmailTemplateDto): Promise<RenderedEmail> {\n return await this.renderer.render(template);\n }\n\n /**\n * Sends an email using a strongly-typed template DTO.\n *\n * Example:\n * ```ts\n * await mailer.sendMail({\n * to: 'user@example.com',\n * template: myDto,\n * });\n * ```\n */\n async sendMail(options: SendTemplateOptions): Promise<SendResult> {\n const template = options.template;\n const recipientName = options.toName ?? template.userName;\n const resolvedFromName = await this.resolveFromName(template, options.fromName);\n\n // Render template DTO\n const rendered = await this.renderer.render(template);\n const plaintextBody = template.noHtmlMessage ?? rendered.text;\n\n // Format recipients\n let toRecipients: Recipient | Recipient[];\n if (typeof options.to === \"string\" && recipientName) {\n toRecipients = { address: options.to, name: recipientName };\n } else {\n toRecipients = options.to;\n }\n\n // Resolve From address\n const from = this.applyFromName(options.from ?? this.defaultFrom, resolvedFromName);\n\n return await this.sendRaw({\n to: toRecipients,\n from,\n subject: template.subject,\n html: rendered.html,\n text: plaintextBody,\n cc: options.cc,\n bcc: options.bcc,\n replyTo: options.replyTo,\n attachments: options.attachments,\n headers: options.headers,\n tags: options.tags,\n metadata: options.metadata,\n });\n }\n\n /**\n * Sends the same rendered template DTO to every recipient in a single batch.\n *\n * The transport delivers an individual copy to each recipient, so\n * recipients are not exposed to one another. The content is identical for\n * everyone; for per-recipient content, use {@link sendBulkKnownTemplate}\n * with a provider-side template and `mergeInfo`.\n *\n * Example:\n * ```ts\n * await mailer.sendBulkMail({\n * to: ['a@example.com', 'b@example.com'],\n * template: myDto,\n * });\n * ```\n */\n async sendBulkMail(options: SendBulkMailOptions): Promise<SendResult> {\n const transport = this.transport;\n if (typeof transport.sendBulk !== \"function\") {\n throw new ConfigurationError(\n \"MailKit: The configured transport does not support bulk sending (sendBulk).\",\n );\n }\n\n const template = options.template;\n const resolvedFromName = await this.resolveFromName(template);\n\n const rendered = await this.renderer.render(template);\n const plaintextBody = template.noHtmlMessage ?? rendered.text;\n const from = this.applyFromName(options.from ?? this.defaultFrom, resolvedFromName);\n\n return await transport.sendBulk({\n to: options.to,\n from,\n subject: template.subject,\n html: rendered.html,\n text: plaintextBody,\n cc: options.cc,\n bcc: options.bcc,\n replyTo: options.replyTo,\n attachments: options.attachments,\n headers: options.headers,\n tags: options.tags,\n metadata: options.metadata,\n trackClicks: options.trackClicks,\n trackOpens: options.trackOpens,\n bounceAddress: options.bounceAddress,\n clientReference: options.clientReference,\n });\n }\n\n /**\n * Sends a single email using a provider-side template.\n */\n async sendKnownTemplate(options: KnownTemplateSendOptions): Promise<SendResult> {\n const transport = this.transport;\n if (typeof transport.sendKnownTemplate !== \"function\") {\n throw new ConfigurationError(\n \"MailKit: The configured transport does not support template sending (sendKnownTemplate).\",\n );\n }\n\n return await transport.sendKnownTemplate({\n ...options,\n from: options.from ?? this.defaultFrom,\n });\n }\n\n /**\n * Sends a bulk email using a provider-side template with optional\n * per-recipient merge data.\n */\n async sendBulkKnownTemplate(options: BulkKnownTemplateSendOptions): Promise<SendResult> {\n const transport = this.transport;\n if (typeof transport.sendBulkKnownTemplate !== \"function\") {\n throw new ConfigurationError(\n \"MailKit: The configured transport does not support bulk template sending (sendBulkKnownTemplate).\",\n );\n }\n\n return await transport.sendBulkKnownTemplate({\n ...options,\n from: options.from ?? this.defaultFrom,\n });\n }\n\n private async resolveFromName(\n template: EmailTemplateDto,\n explicitFromName?: string,\n ): Promise<string | undefined> {\n let resolved = explicitFromName ?? template.fromName;\n if (!resolved && this.brandNameResolver) {\n resolved = await this.brandNameResolver();\n }\n if (!resolved) {\n resolved = this.defaultBrandName;\n }\n return resolved;\n }\n\n private applyFromName(\n from: Recipient | undefined,\n fromName: string | undefined,\n ): Recipient | undefined {\n if (fromName && from) {\n const normalizedFrom = normalizeRecipient(from);\n return { address: normalizedFrom.address, name: fromName };\n }\n return from;\n }\n\n /**\n * Dispatches a raw, pre-rendered email directly through the transport.\n * If `options.text` is omitted, plain text is automatically derived from `options.html`.\n */\n async sendRaw(options: SendMailOptions): Promise<SendResult> {\n const from = options.from ?? this.defaultFrom;\n const text = options.text ?? (options.html ? htmlToPlainText(options.html) : undefined);\n return await this.transport.send({\n ...options,\n from,\n text,\n });\n }\n}\n\n/**\n * Convenience factory to create a MailKit client instance.\n */\nexport function createMailKit(options: MailKitOptions): MailKit {\n return new MailKit(options);\n}\n","import { ConfigurationError, TransportError } from \"../../core/errors.js\";\nimport {\n normalizeRecipient,\n normalizeRecipients,\n type EmailAttachment,\n type Recipient,\n type SendMailOptions,\n type SendResult,\n} from \"../../core/types.js\";\nimport type {\n BulkKnownTemplateSendOptions,\n BulkSendOptions,\n KnownTemplateSendOptions,\n} from \"../../core/types.js\";\nimport type { IEmailSender } from \"../sender.interface.js\";\nimport type {\n CpaasAddressObject,\n CpaasAttachmentPayload,\n CpaasBatchSendPayload,\n CpaasBatchTemplateSendPayload,\n CpaasHttpConfig,\n CpaasRecipientWrapper,\n CpaasSendPayload,\n CpaasTemplateSendPayload,\n} from \"./types.js\";\n\n/**\n * Zoho CPaaS accepts at most 500 recipients per request, counted across\n * `to`, `cc`, and `bcc`.\n */\nconst CPAAS_MAX_RECIPIENTS = 500;\n\n/** Fields shared by every CPaaS request payload. */\ninterface CommonPayloadFields {\n cc?: CpaasRecipientWrapper[];\n bcc?: CpaasRecipientWrapper[];\n reply_to?: CpaasAddressObject[];\n attachments?: CpaasAttachmentPayload[];\n track_clicks?: boolean;\n track_opens?: boolean;\n bounce_address?: string;\n client_reference?: string;\n}\n\n/** Options accepted by {@link CpaasHttpSender.applyCommon}. */\ninterface CommonOptions {\n cc?: Recipient | Recipient[];\n bcc?: Recipient | Recipient[];\n replyTo?: Recipient | Recipient[];\n attachments?: EmailAttachment[];\n trackClicks?: boolean;\n trackOpens?: boolean;\n bounceAddress?: string;\n clientReference?: string;\n}\n\n/**\n * Zoho CPaaS transactional email HTTP transport adapter.\n *\n * Uses the standard global `fetch` API for zero-dependency, universal edge\n * compatibility (Node.js, Bun, Deno, Cloudflare Workers).\n *\n * Supports:\n * - `send` — single raw email (`POST /v1.1/email`)\n * - `sendBulk` — batch raw email (`POST /v1.1/email/batch`)\n * - `sendKnownTemplate` — single template email (`POST /v1.1/email/template`)\n * - `sendBulkKnownTemplate` — batch template email (`POST /v1.1/email/template/batch`)\n *\n * Requests exceeding Zoho's 500-recipient limit (across `to`, `cc`, and `bcc`)\n * are rejected before any network call is made.\n */\nexport class CpaasHttpSender implements IEmailSender {\n readonly name = \"cpaas\";\n\n private readonly apiBase: string;\n private readonly authHeader: string;\n private readonly defaultFrom: { address: string; name?: string };\n private readonly preferTemplateAlias: boolean;\n private readonly fetchImpl: typeof fetch;\n\n constructor(config: CpaasHttpConfig) {\n if (!config.apiToken || typeof config.apiToken !== \"string\") {\n throw new ConfigurationError(\"CpaasHttpSender: Missing or invalid apiToken.\");\n }\n if (!config.from) {\n throw new ConfigurationError(\"CpaasHttpSender: Missing required default from address.\");\n }\n\n const host = (config.apiHost || \"cpaas.zoho.com\")\n .replace(/^https?:\\/\\//, \"\")\n .replace(/\\/+$/, \"\");\n this.apiBase = `https://${host}/v1.1`;\n\n // Normalize auth token header. If the caller already included \"Zoho-\", use as is.\n const token = config.apiToken.trim();\n this.authHeader = token.startsWith(\"Zoho-\") ? token : `Zoho-enczapikey ${token}`;\n\n this.defaultFrom = normalizeRecipient(config.from);\n if (!this.defaultFrom.address) {\n throw new ConfigurationError(\"CpaasHttpSender: Configured from address must not be empty.\");\n }\n\n this.preferTemplateAlias = config.preferTemplateAlias ?? false;\n\n const fetchImpl = config.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new ConfigurationError(\n \"CpaasHttpSender: No global fetch implementation found. Pass a custom fetch to CpaasHttpConfig if running on Node < 18.\",\n );\n }\n // Bind: detached fetch throws \"Illegal invocation\" on strict-this runtimes (undici/workerd).\n this.fetchImpl = fetchImpl.bind(globalThis);\n }\n\n /**\n * Dispatches a single raw email using Zoho CPaaS API.\n */\n async send(options: SendMailOptions): Promise<SendResult> {\n const payload = this.buildSendPayload(options);\n return await this.post(`${this.apiBase}/email`, payload);\n }\n\n /**\n * Dispatches a batch of a single raw email content to many recipients.\n * The provider delivers an individual copy to each recipient.\n */\n async sendBulk(options: BulkSendOptions): Promise<SendResult> {\n const payload: CpaasBatchSendPayload = this.buildSendPayload(options);\n return await this.post(`${this.apiBase}/email/batch`, payload);\n }\n\n /**\n * Dispatches a single email using a provider-side template.\n */\n async sendKnownTemplate(options: KnownTemplateSendOptions): Promise<SendResult> {\n const payload = this.buildTemplatePayload(options);\n return await this.post(`${this.apiBase}/email/template`, payload);\n }\n\n /**\n * Dispatches a batch of emails using a provider-side template, with\n * optional per-recipient merge data.\n */\n async sendBulkKnownTemplate(options: BulkKnownTemplateSendOptions): Promise<SendResult> {\n const payload = this.buildBatchTemplatePayload(options);\n return await this.post(`${this.apiBase}/email/template/batch`, payload);\n }\n\n // ─── Payload builders ──────────────────────────────────────────────────────\n\n /**\n * Guards against requests that exceed Zoho CPaaS's per-request recipient\n * limit, counted across `to`, `cc`, and `bcc`.\n */\n private assertRecipientLimit(\n to: Recipient | Recipient[],\n cc?: Recipient | Recipient[],\n bcc?: Recipient | Recipient[],\n ): void {\n const total =\n normalizeRecipients(to).length +\n normalizeRecipients(cc).length +\n normalizeRecipients(bcc).length;\n\n if (total > CPAAS_MAX_RECIPIENTS) {\n throw new TransportError(\n `CpaasHttpSender: Too many recipients (${total}). Zoho CPaaS accepts at most ${CPAAS_MAX_RECIPIENTS} recipients per request across to, cc, and bcc.`,\n { transportName: this.name },\n );\n }\n }\n\n private buildSendPayload(options: SendMailOptions): CpaasSendPayload {\n this.assertRecipientLimit(options.to, options.cc, options.bcc);\n\n const toRecipients = normalizeRecipients(options.to);\n if (toRecipients.length === 0) {\n throw new TransportError(\"CpaasHttpSender: At least one recipient (to) must be specified.\", {\n transportName: this.name,\n });\n }\n\n const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;\n\n const payload: CpaasSendPayload = {\n from: {\n address: resolvedFrom.address,\n name: resolvedFrom.name,\n },\n to: toRecipients.map((r) => ({\n email_address: { address: r.address, name: r.name || \"\" },\n })),\n subject: options.subject,\n htmlbody: options.html,\n textbody: options.text,\n };\n\n this.applyCommon(payload, options);\n return payload;\n }\n\n private buildTemplatePayload(options: KnownTemplateSendOptions): CpaasTemplateSendPayload {\n this.assertRecipientLimit(options.to, options.cc, options.bcc);\n\n const toRecipients = normalizeRecipients(options.to);\n if (toRecipients.length === 0) {\n throw new TransportError(\"CpaasHttpSender: At least one recipient (to) must be specified.\", {\n transportName: this.name,\n });\n }\n\n const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;\n\n const payload: CpaasTemplateSendPayload = {\n ...this.templateRef(options.templateId),\n from: {\n address: resolvedFrom.address,\n name: resolvedFrom.name,\n },\n to: toRecipients.map((r) => ({\n email_address: { address: r.address, name: r.name || \"\" },\n })),\n };\n\n if (options.mergeInfo) {\n payload.merge_info = options.mergeInfo;\n }\n\n this.applyCommon(payload, options);\n return payload;\n }\n\n private buildBatchTemplatePayload(\n options: BulkKnownTemplateSendOptions,\n ): CpaasBatchTemplateSendPayload {\n this.assertRecipientLimit(options.to, options.cc, options.bcc);\n\n if (!options.to || options.to.length === 0) {\n throw new TransportError(\"CpaasHttpSender: At least one recipient (to) must be specified.\", {\n transportName: this.name,\n });\n }\n\n const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;\n\n const payload: CpaasBatchTemplateSendPayload = {\n ...this.templateRef(options.templateId),\n from: {\n address: resolvedFrom.address,\n name: resolvedFrom.name,\n },\n to: options.to.map((r) => ({\n email_address: { address: r.address, name: r.name || \"\" },\n ...(r.mergeInfo ? { merge_info: r.mergeInfo } : {}),\n })),\n };\n\n this.applyCommon(payload, options);\n return payload;\n }\n\n private templateRef(templateId: string): { template_key?: string; template_alias?: string } {\n return this.preferTemplateAlias ? { template_alias: templateId } : { template_key: templateId };\n }\n\n private applyCommon(payload: CommonPayloadFields, options: CommonOptions): void {\n if (options.cc) {\n const ccList = normalizeRecipients(options.cc);\n if (ccList.length > 0) {\n payload.cc = ccList.map((r) => ({\n email_address: { address: r.address, name: r.name || \"\" },\n }));\n }\n }\n\n if (options.bcc) {\n const bccList = normalizeRecipients(options.bcc);\n if (bccList.length > 0) {\n payload.bcc = bccList.map((r) => ({\n email_address: { address: r.address, name: r.name || \"\" },\n }));\n }\n }\n\n if (options.replyTo) {\n const replyToList = normalizeRecipients(options.replyTo);\n if (replyToList.length > 0) {\n payload.reply_to = replyToList.map((r) => ({\n address: r.address,\n name: r.name,\n }));\n }\n }\n\n if (options.attachments && options.attachments.length > 0) {\n payload.attachments = options.attachments.map((att) => this.toAttachmentPayload(att));\n }\n\n if (options.trackClicks !== undefined) payload.track_clicks = options.trackClicks;\n if (options.trackOpens !== undefined) payload.track_opens = options.trackOpens;\n if (options.bounceAddress !== undefined) payload.bounce_address = options.bounceAddress;\n if (options.clientReference !== undefined) payload.client_reference = options.clientReference;\n }\n\n private toAttachmentPayload(att: EmailAttachment): CpaasAttachmentPayload {\n let contentBase64: string;\n if (typeof att.content === \"string\") {\n contentBase64 = att.content;\n } else if (typeof Buffer !== \"undefined\") {\n contentBase64 = Buffer.from(att.content).toString(\"base64\");\n } else {\n // Browser / Edge fallback for Uint8Array\n let binary = \"\";\n const bytes = new Uint8Array(att.content);\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n contentBase64 = btoa(binary);\n }\n\n return {\n name: att.filename,\n content: contentBase64,\n mime_type: att.mimeType || att.contentType || \"application/octet-stream\",\n cid: att.cid,\n };\n }\n\n // ─── HTTP ──────────────────────────────────────────────────────────────────\n\n private async post(url: string, payload: unknown): Promise<SendResult> {\n let response: Response;\n try {\n response = await this.fetchImpl(url, {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n Authorization: this.authHeader,\n },\n body: JSON.stringify(payload),\n });\n } catch (err) {\n throw new TransportError(\n `CpaasHttpSender: Network request failed: ${err instanceof Error ? err.message : String(err)}`,\n {\n transportName: this.name,\n cause: err,\n },\n );\n }\n\n const responseText = await response.text();\n let responseData: any;\n try {\n responseData = responseText ? JSON.parse(responseText) : {};\n } catch {\n responseData = { raw: responseText };\n }\n\n if (!response.ok) {\n const errorMessage =\n responseData?.data?.message ||\n responseData?.error?.message ||\n responseData?.message ||\n `HTTP ${response.status}: ${responseText || response.statusText}`;\n\n throw new TransportError(`CpaasHttpSender: Failed to send email - ${errorMessage}`, {\n transportName: this.name,\n statusCode: response.status,\n responseBody: responseText,\n rawResponse: responseData,\n });\n }\n\n // Single-send responses carry `message_id`; batch/template responses carry\n // `code`/`message` entries.\n const messageId = responseData?.data?.[0]?.message_id;\n\n return {\n success: true,\n messageId,\n rawResponse: responseData,\n };\n }\n}\n","import type { SendMailOptions, SendResult } from \"../../core/types.js\";\nimport type {\n BulkKnownTemplateSendOptions,\n BulkSendOptions,\n KnownTemplateSendOptions,\n} from \"../../core/types.js\";\nimport type { IEmailSender } from \"../sender.interface.js\";\n\n/**\n * In-memory email sender for automated testing, local development, and CI environments.\n * Captures all dispatched emails and provides inspection methods.\n */\nexport class MemoryEmailSender implements IEmailSender {\n readonly name = \"memory\";\n\n private sentMails: SendMailOptions[] = [];\n private sentBulkMails: BulkSendOptions[] = [];\n private sentTemplateMails: KnownTemplateSendOptions[] = [];\n private sentBulkTemplateMails: BulkKnownTemplateSendOptions[] = [];\n private nextId = 1;\n\n private result(prefix?: string): SendResult {\n const id = this.nextId++;\n const messageId = prefix ? `mem-${prefix}-${id}-${Date.now()}` : `mem-${id}-${Date.now()}`;\n const rawResponse = { id: messageId };\n return {\n success: true,\n messageId,\n rawResponse,\n };\n }\n\n async send(options: SendMailOptions): Promise<SendResult> {\n this.sentMails.push({ ...options });\n return this.result();\n }\n\n async sendBulk(options: BulkSendOptions): Promise<SendResult> {\n this.sentBulkMails.push({ ...options });\n return this.result(\"bulk\");\n }\n\n async sendKnownTemplate(options: KnownTemplateSendOptions): Promise<SendResult> {\n this.sentTemplateMails.push({ ...options });\n return this.result(\"tpl\");\n }\n\n async sendBulkKnownTemplate(options: BulkKnownTemplateSendOptions): Promise<SendResult> {\n this.sentBulkTemplateMails.push({ ...options });\n return this.result(\"bulk-tpl\");\n }\n\n /**\n * Returns a copy of all single emails sent through this transport instance.\n */\n getSentMails(): SendMailOptions[] {\n return [...this.sentMails];\n }\n\n /**\n * Returns the most recent single email sent, or undefined if none have been sent.\n */\n getLastMail(): SendMailOptions | undefined {\n return this.sentMails.length > 0 ? { ...this.sentMails[this.sentMails.length - 1] } : undefined;\n }\n\n /**\n * Returns a copy of all bulk emails sent through this transport instance.\n */\n getSentBulkMails(): BulkSendOptions[] {\n return [...this.sentBulkMails];\n }\n\n /**\n * Returns a copy of all single template emails sent through this transport instance.\n */\n getSentTemplateMails(): KnownTemplateSendOptions[] {\n return [...this.sentTemplateMails];\n }\n\n /**\n * Returns a copy of all bulk template emails sent through this transport instance.\n */\n getSentBulkTemplateMails(): BulkKnownTemplateSendOptions[] {\n return [...this.sentBulkTemplateMails];\n }\n\n /**\n * Clears all recorded emails.\n */\n clear(): void {\n this.sentMails = [];\n this.sentBulkMails = [];\n this.sentTemplateMails = [];\n this.sentBulkTemplateMails = [];\n }\n\n /**\n * Returns the total count of single emails sent.\n */\n count(): number {\n return this.sentMails.length;\n }\n}\n","import {\n normalizeRecipients,\n type Recipient,\n type SendMailOptions,\n type SendResult,\n} from \"../../core/types.js\";\nimport type {\n BulkKnownTemplateSendOptions,\n BulkSendOptions,\n KnownTemplateSendOptions,\n} from \"../../core/types.js\";\nimport type { IEmailSender } from \"../sender.interface.js\";\n\nexport interface ConsoleEmailSenderOptions {\n /**\n * Log level or custom log printer. Defaults to `console.log`.\n */\n logger?: (message: string) => void;\n /**\n * Whether to include the full HTML body in console output.\n * Defaults to false (prints plain text or a summary).\n */\n includeHtml?: boolean;\n}\n\ninterface DispatchLog {\n kind: string;\n to: Recipient | Recipient[];\n from?: Recipient;\n subject?: string;\n templateId?: string;\n text?: string;\n html?: string;\n}\n\n/**\n * Console logger transport that prints dispatched emails to stdout/stderr.\n * Ideal for development and debugging.\n */\nexport class ConsoleEmailSender implements IEmailSender {\n readonly name = \"console\";\n\n private readonly log: (message: string) => void;\n private readonly includeHtml: boolean;\n private nextId = 1;\n\n constructor(options: ConsoleEmailSenderOptions = {}) {\n this.log = options.logger ?? console.log;\n this.includeHtml = options.includeHtml ?? false;\n }\n\n async send(options: SendMailOptions): Promise<SendResult> {\n return this.logDispatch({\n kind: \"EMAIL\",\n to: options.to,\n from: options.from,\n subject: options.subject,\n text: options.text,\n html: options.html,\n });\n }\n\n async sendBulk(options: BulkSendOptions): Promise<SendResult> {\n return this.logDispatch({\n kind: \"BULK EMAIL\",\n to: options.to,\n from: options.from,\n subject: options.subject,\n text: options.text,\n html: options.html,\n });\n }\n\n async sendKnownTemplate(options: KnownTemplateSendOptions): Promise<SendResult> {\n return this.logDispatch({\n kind: \"TEMPLATE EMAIL\",\n to: options.to,\n from: options.from,\n templateId: options.templateId,\n });\n }\n\n async sendBulkKnownTemplate(options: BulkKnownTemplateSendOptions): Promise<SendResult> {\n return this.logDispatch({\n kind: \"BULK TEMPLATE EMAIL\",\n to: options.to,\n from: options.from,\n templateId: options.templateId,\n });\n }\n\n private logDispatch(entry: DispatchLog): SendResult {\n const to = normalizeRecipients(entry.to)\n .map((r) => (r.name ? `${r.name} <${r.address}>` : r.address))\n .join(\", \");\n const from = entry.from ? JSON.stringify(entry.from) : \"(default)\";\n const messageId = `console-${this.nextId++}-${Date.now()}`;\n\n const separator = \"─\".repeat(60);\n const lines = [\n `\\n┌${separator}┐`,\n `│ [ts-mailkit:console] DISPATCHED ${entry.kind} (${messageId})`,\n `├${separator}┤`,\n `│ From: ${from}`,\n `│ To: ${to}`,\n ];\n\n if (entry.subject !== undefined) {\n lines.push(`│ Subject: ${entry.subject}`);\n }\n if (entry.templateId !== undefined) {\n lines.push(`│ Template: ${entry.templateId}`);\n }\n lines.push(`├${separator}┤`);\n\n if (entry.text) {\n lines.push(\"│ Text Body:\");\n for (const line of entry.text.split(\"\\n\")) {\n lines.push(`│ ${line}`);\n }\n }\n\n if (this.includeHtml && entry.html) {\n lines.push(\"│ HTML Body:\");\n for (const line of entry.html.split(\"\\n\")) {\n lines.push(`│ ${line}`);\n }\n }\n\n lines.push(`└${separator}┘\\n`);\n\n this.log(lines.join(\"\\n\"));\n\n const rawResponse = { logged: true };\n return {\n success: true,\n messageId,\n rawResponse,\n };\n }\n}\n","import type {\n EmailTemplateDto,\n EmailVerificationParams,\n MagicLinkParams,\n NewsletterParams,\n PasswordResetParams,\n SimpleMessageParams,\n TwoFactorParams,\n} from \"./dto/index.js\";\nimport { htmlToPlainText } from \"../renderers/utils/html-to-plaintext.js\";\n\nexport interface BrandingConfig {\n /**\n * Default brand, application, or company name.\n * @default 'Application'\n */\n brandName?: string;\n\n /**\n * Optional custom security / disclaimer advisory message.\n */\n securityMessage?: string;\n\n /**\n * Optional custom footer generator or string.\n */\n footer?: string | ((brandName: string) => string);\n\n /**\n * Optional default logo URL to display top-center in templated emails.\n */\n logoUrl?: string;\n}\n\nconst DEFAULT_SECURITY_MESSAGE =\n \"If you did not request this, please ignore this email or contact support if you have concerns.\";\n\nfunction defaultFooter(brandName: string): string {\n return `© ${new Date().getFullYear()} ${brandName}. All rights reserved.`;\n}\n\n/**\n * Factory for creating pre-populated, type-safe email template DTOs.\n */\nexport class EmailTemplateFactory {\n private readonly defaultBrandName: string;\n private readonly defaultSecurityMessage: string;\n private readonly footerResolver: (brandName: string) => string;\n private readonly defaultLogoUrl?: string;\n\n constructor(config: BrandingConfig = {}) {\n this.defaultBrandName = config.brandName || \"Application\";\n this.defaultSecurityMessage = config.securityMessage || DEFAULT_SECURITY_MESSAGE;\n this.defaultLogoUrl = config.logoUrl;\n if (typeof config.footer === \"function\") {\n this.footerResolver = config.footer;\n } else if (typeof config.footer === \"string\") {\n const fixedFooter = config.footer;\n this.footerResolver = () => fixedFooter;\n } else {\n this.footerResolver = defaultFooter;\n }\n }\n\n private resolveBrand(override?: string): string {\n return override || this.defaultBrandName;\n }\n\n private buildFooter(brandName: string): string {\n return this.footerResolver(brandName);\n }\n\n private arrangePlainTextWithExtras(base: string, extraMessage?: string, footer?: string): string {\n const parts = [base];\n if (extraMessage) {\n parts.push(htmlToPlainText(extraMessage));\n }\n if (footer) {\n parts.push(htmlToPlainText(footer));\n }\n return parts.join(\"\\n\\n\");\n }\n\n /**\n * Welcome / email-verification email sent after registration.\n */\n createEmailVerification(params: EmailVerificationParams): EmailTemplateDto {\n const { username, verificationUrl, otp, brandName, logoUrl, extraMessage, footer } = params;\n const brand = this.resolveBrand(brandName);\n const body = `Hi ${username},<br/>Thank you for signing up. Use the code below or click the button to verify your email address.`;\n const resolvedExtraMessage = extraMessage ?? this.defaultSecurityMessage;\n const resolvedFooter = footer ?? this.buildFooter(brand);\n const plainTextBody =\n htmlToPlainText(body) +\n `\\n\\nYour verification code is: ${otp}\\n\\nAlternatively, verify here:\\n${verificationUrl}`;\n return {\n subject: \"Verify Your Email Address\",\n header: `Welcome to ${brand}!`,\n body,\n logoUrl: logoUrl ?? this.defaultLogoUrl,\n otpCode: otp,\n actionUrl: verificationUrl,\n actionText: \"Verify Email\",\n extraMessage: resolvedExtraMessage,\n footer: resolvedFooter,\n userName: username,\n fromName: brand,\n noHtmlMessage: this.arrangePlainTextWithExtras(\n plainTextBody,\n resolvedExtraMessage,\n resolvedFooter,\n ),\n };\n }\n\n /**\n * Password-reset email with an optional OTP code and action link.\n */\n createPasswordReset(params: PasswordResetParams): EmailTemplateDto {\n const { username, resetUrl, otp = \"\", brandName, logoUrl, extraMessage, footer } = params;\n const brand = this.resolveBrand(brandName);\n const body = `Hi ${username},<br/>We received a request to reset your password. Use the OTP code below or click the button to proceed.`;\n const plainTextOtp = otp ? `\\n\\nYour OTP code is: ${otp}` : \"\";\n const resolvedExtraMessage = extraMessage ?? this.defaultSecurityMessage;\n const resolvedFooter = footer ?? this.buildFooter(brand);\n const plainTextBody =\n htmlToPlainText(body) +\n plainTextOtp +\n `\\n\\nAlternatively, you can use the following link to reset your password:\\n${resetUrl}`;\n return {\n subject: \"Password Reset Request\",\n header: \"Reset Your Password\",\n body,\n logoUrl: logoUrl ?? this.defaultLogoUrl,\n otpCode: otp || undefined,\n actionUrl: resetUrl,\n actionText: \"Reset Password\",\n extraMessage: resolvedExtraMessage,\n footer: resolvedFooter,\n userName: username,\n fromName: brand,\n noHtmlMessage: this.arrangePlainTextWithExtras(\n plainTextBody,\n resolvedExtraMessage,\n resolvedFooter,\n ),\n };\n }\n\n /**\n * Generic plain message email (no action button, no OTP).\n */\n createSimpleMessage(params: SimpleMessageParams): EmailTemplateDto {\n const { subject, header, body, brandName, logoUrl, extraMessage, footer } = params;\n const brand = this.resolveBrand(brandName);\n const resolvedFooter = footer ?? this.buildFooter(brand);\n return {\n subject,\n header,\n body,\n logoUrl: logoUrl ?? this.defaultLogoUrl,\n extraMessage,\n footer: resolvedFooter,\n fromName: brand,\n noHtmlMessage: this.arrangePlainTextWithExtras(\n htmlToPlainText(body),\n extraMessage,\n resolvedFooter,\n ),\n };\n }\n\n /**\n * Newsletter / announcement email with an optional hero image and CTA button.\n */\n createNewsLetter(params: NewsletterParams): EmailTemplateDto {\n const {\n subject,\n header,\n body,\n imageUrl,\n actionUrl,\n actionText,\n brandName,\n logoUrl,\n extraMessage,\n footer,\n } = params;\n const brand = this.resolveBrand(brandName);\n const resolvedFooter = footer ?? this.buildFooter(brand);\n let plainTextBody = htmlToPlainText(body);\n if (actionUrl) {\n plainTextBody += `\\n\\n${actionText || \"Learn more\"}: ${actionUrl}`;\n }\n return {\n subject,\n header,\n body,\n logoUrl: logoUrl ?? this.defaultLogoUrl,\n imageUrl,\n actionUrl,\n actionText,\n extraMessage,\n footer: resolvedFooter,\n fromName: brand,\n noHtmlMessage: this.arrangePlainTextWithExtras(plainTextBody, extraMessage, resolvedFooter),\n };\n }\n\n /**\n * Two-factor authentication email with OTP code and optional magic link.\n */\n create2FAEmail(params: TwoFactorParams): EmailTemplateDto {\n const { username, otp, magicLink = \"\", brandName, logoUrl, extraMessage, footer } = params;\n const brand = this.resolveBrand(brandName);\n const body = `Hi ${username},<br/>Use the code below or click the button to complete your login.`;\n const plainLink = magicLink ? `\\n\\nAlternatively, verify here:\\n${magicLink}` : \"\";\n const resolvedExtraMessage = extraMessage ?? this.defaultSecurityMessage;\n const resolvedFooter = footer ?? this.buildFooter(brand);\n const plainTextBody =\n htmlToPlainText(body) + `\\n\\nYour verification code is: ${otp}` + plainLink;\n return {\n subject: \"Your Login Verification Code\",\n header: \"Two-Factor Authentication\",\n body,\n logoUrl: logoUrl ?? this.defaultLogoUrl,\n otpCode: otp,\n actionUrl: magicLink || undefined,\n actionText: magicLink ? \"Verify Login\" : undefined,\n extraMessage: resolvedExtraMessage,\n footer: resolvedFooter,\n userName: username,\n fromName: brand,\n noHtmlMessage: this.arrangePlainTextWithExtras(\n plainTextBody,\n resolvedExtraMessage,\n resolvedFooter,\n ),\n };\n }\n\n /**\n * Magic link sign-in email (no OTP, button only).\n */\n createMagicLinkEmail(params: MagicLinkParams): EmailTemplateDto {\n const { username, magicLink, brandName, logoUrl, extraMessage, footer } = params;\n const brand = this.resolveBrand(brandName);\n const body = `Hi ${username},<br/>Click the button below to sign in. No password needed.`;\n const resolvedExtraMessage = extraMessage ?? this.defaultSecurityMessage;\n const resolvedFooter = footer ?? this.buildFooter(brand);\n const plainTextBody = htmlToPlainText(body) + `\\n\\nSign in here:\\n${magicLink}`;\n return {\n subject: \"Your Magic Link\",\n header: `Sign In to ${brand}`,\n body,\n logoUrl: logoUrl ?? this.defaultLogoUrl,\n actionUrl: magicLink,\n actionText: \"Sign In\",\n extraMessage: resolvedExtraMessage,\n footer: resolvedFooter,\n userName: username,\n fromName: brand,\n noHtmlMessage: this.arrangePlainTextWithExtras(\n plainTextBody,\n resolvedExtraMessage,\n resolvedFooter,\n ),\n };\n }\n\n // ─── Default Static Instance ──────────────────────────────────────────────\n private static _defaultInstance?: EmailTemplateFactory;\n\n private static get defaultInstance(): EmailTemplateFactory {\n if (!this._defaultInstance) {\n this._defaultInstance = new EmailTemplateFactory();\n }\n return this._defaultInstance;\n }\n\n static createEmailVerification(params: EmailVerificationParams): EmailTemplateDto {\n return this.defaultInstance.createEmailVerification(params);\n }\n\n static createPasswordReset(params: PasswordResetParams): EmailTemplateDto {\n return this.defaultInstance.createPasswordReset(params);\n }\n\n static createSimpleMessage(params: SimpleMessageParams): EmailTemplateDto {\n return this.defaultInstance.createSimpleMessage(params);\n }\n\n static createNewsLetter(params: NewsletterParams): EmailTemplateDto {\n return this.defaultInstance.createNewsLetter(params);\n }\n\n static create2FAEmail(params: TwoFactorParams): EmailTemplateDto {\n return this.defaultInstance.create2FAEmail(params);\n }\n\n static createMagicLinkEmail(params: MagicLinkParams): EmailTemplateDto {\n return this.defaultInstance.createMagicLinkEmail(params);\n }\n}\n","import type { EmailTemplateDto } from \"./dto/index.js\";\nimport { buildHtml, buildPlainText } from \"../renderers/default/default-renderer.js\";\nimport type { RendererOptions } from \"../renderers/renderer.interface.js\";\n\n/**\n * Styling options for building upload-ready templates.\n */\nexport interface TemplateBuildOptions extends RendererOptions {}\n\n/**\n * Builds an upload-ready HTML template from the canonical {@link EmailTemplateDto}.\n *\n * The output can be uploaded to a messaging platform (Zoho CPaaS, SendGrid, etc.)\n * as a provider-side template.\n */\nexport function buildHTMLTemplate(\n dto: EmailTemplateDto,\n options: TemplateBuildOptions = {},\n): string {\n return buildHtml(dto, options);\n}\n\n/**\n * Builds an upload-ready plain-text template from the canonical {@link EmailTemplateDto}.\n */\nexport function buildPlainTextTemplate(\n dto: EmailTemplateDto,\n _options?: TemplateBuildOptions,\n): string {\n return buildPlainText(dto);\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -258,11 +258,12 @@ var MailKit = class {
|
|
|
258
258
|
});
|
|
259
259
|
}
|
|
260
260
|
/**
|
|
261
|
-
* Sends
|
|
261
|
+
* Sends the same rendered template DTO to every recipient in a single batch.
|
|
262
262
|
*
|
|
263
263
|
* The transport delivers an individual copy to each recipient, so
|
|
264
|
-
* recipients are not exposed to one another.
|
|
265
|
-
*
|
|
264
|
+
* recipients are not exposed to one another. The content is identical for
|
|
265
|
+
* everyone; for per-recipient content, use {@link sendBulkKnownTemplate}
|
|
266
|
+
* with a provider-side template and `mergeInfo`.
|
|
266
267
|
*
|
|
267
268
|
* Example:
|
|
268
269
|
* ```ts
|
|
@@ -359,6 +360,11 @@ function createMailKit(options) {
|
|
|
359
360
|
//#endregion
|
|
360
361
|
//#region src/transports/cpaas/cpaas-sender.ts
|
|
361
362
|
/**
|
|
363
|
+
* Zoho CPaaS accepts at most 500 recipients per request, counted across
|
|
364
|
+
* `to`, `cc`, and `bcc`.
|
|
365
|
+
*/
|
|
366
|
+
const CPAAS_MAX_RECIPIENTS = 500;
|
|
367
|
+
/**
|
|
362
368
|
* Zoho CPaaS transactional email HTTP transport adapter.
|
|
363
369
|
*
|
|
364
370
|
* Uses the standard global `fetch` API for zero-dependency, universal edge
|
|
@@ -369,6 +375,9 @@ function createMailKit(options) {
|
|
|
369
375
|
* - `sendBulk` — batch raw email (`POST /v1.1/email/batch`)
|
|
370
376
|
* - `sendKnownTemplate` — single template email (`POST /v1.1/email/template`)
|
|
371
377
|
* - `sendBulkKnownTemplate` — batch template email (`POST /v1.1/email/template/batch`)
|
|
378
|
+
*
|
|
379
|
+
* Requests exceeding Zoho's 500-recipient limit (across `to`, `cc`, and `bcc`)
|
|
380
|
+
* are rejected before any network call is made.
|
|
372
381
|
*/
|
|
373
382
|
var CpaasHttpSender = class {
|
|
374
383
|
name = "cpaas";
|
|
@@ -421,7 +430,16 @@ var CpaasHttpSender = class {
|
|
|
421
430
|
const payload = this.buildBatchTemplatePayload(options);
|
|
422
431
|
return await this.post(`${this.apiBase}/email/template/batch`, payload);
|
|
423
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* Guards against requests that exceed Zoho CPaaS's per-request recipient
|
|
435
|
+
* limit, counted across `to`, `cc`, and `bcc`.
|
|
436
|
+
*/
|
|
437
|
+
assertRecipientLimit(to, cc, bcc) {
|
|
438
|
+
const total = normalizeRecipients(to).length + normalizeRecipients(cc).length + normalizeRecipients(bcc).length;
|
|
439
|
+
if (total > CPAAS_MAX_RECIPIENTS) throw new TransportError(`CpaasHttpSender: Too many recipients (${total}). Zoho CPaaS accepts at most ${CPAAS_MAX_RECIPIENTS} recipients per request across to, cc, and bcc.`, { transportName: this.name });
|
|
440
|
+
}
|
|
424
441
|
buildSendPayload(options) {
|
|
442
|
+
this.assertRecipientLimit(options.to, options.cc, options.bcc);
|
|
425
443
|
const toRecipients = normalizeRecipients(options.to);
|
|
426
444
|
if (toRecipients.length === 0) throw new TransportError("CpaasHttpSender: At least one recipient (to) must be specified.", { transportName: this.name });
|
|
427
445
|
const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;
|
|
@@ -442,6 +460,7 @@ var CpaasHttpSender = class {
|
|
|
442
460
|
return payload;
|
|
443
461
|
}
|
|
444
462
|
buildTemplatePayload(options) {
|
|
463
|
+
this.assertRecipientLimit(options.to, options.cc, options.bcc);
|
|
445
464
|
const toRecipients = normalizeRecipients(options.to);
|
|
446
465
|
if (toRecipients.length === 0) throw new TransportError("CpaasHttpSender: At least one recipient (to) must be specified.", { transportName: this.name });
|
|
447
466
|
const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;
|
|
@@ -461,6 +480,7 @@ var CpaasHttpSender = class {
|
|
|
461
480
|
return payload;
|
|
462
481
|
}
|
|
463
482
|
buildBatchTemplatePayload(options) {
|
|
483
|
+
this.assertRecipientLimit(options.to, options.cc, options.bcc);
|
|
464
484
|
if (!options.to || options.to.length === 0) throw new TransportError("CpaasHttpSender: At least one recipient (to) must be specified.", { transportName: this.name });
|
|
465
485
|
const resolvedFrom = options.from ? normalizeRecipient(options.from) : this.defaultFrom;
|
|
466
486
|
const payload = {
|
|
@@ -769,130 +789,152 @@ var EmailTemplateFactory = class EmailTemplateFactory {
|
|
|
769
789
|
buildFooter(brandName) {
|
|
770
790
|
return this.footerResolver(brandName);
|
|
771
791
|
}
|
|
792
|
+
arrangePlainTextWithExtras(base, extraMessage, footer) {
|
|
793
|
+
const parts = [base];
|
|
794
|
+
if (extraMessage) parts.push(htmlToPlainText(extraMessage));
|
|
795
|
+
if (footer) parts.push(htmlToPlainText(footer));
|
|
796
|
+
return parts.join("\n\n");
|
|
797
|
+
}
|
|
772
798
|
/**
|
|
773
799
|
* Welcome / email-verification email sent after registration.
|
|
774
800
|
*/
|
|
775
801
|
createEmailVerification(params) {
|
|
776
|
-
const { username, verificationUrl, otp, brandName } = params;
|
|
802
|
+
const { username, verificationUrl, otp, brandName, logoUrl, extraMessage, footer } = params;
|
|
777
803
|
const brand = this.resolveBrand(brandName);
|
|
778
804
|
const body = `Hi ${username},<br/>Thank you for signing up. Use the code below or click the button to verify your email address.`;
|
|
805
|
+
const resolvedExtraMessage = extraMessage ?? this.defaultSecurityMessage;
|
|
806
|
+
const resolvedFooter = footer ?? this.buildFooter(brand);
|
|
807
|
+
const plainTextBody = htmlToPlainText(body) + `\n\nYour verification code is: ${otp}\n\nAlternatively, verify here:\n${verificationUrl}`;
|
|
779
808
|
return {
|
|
780
809
|
subject: "Verify Your Email Address",
|
|
781
810
|
header: `Welcome to ${brand}!`,
|
|
782
811
|
body,
|
|
783
|
-
logoUrl: this.defaultLogoUrl,
|
|
812
|
+
logoUrl: logoUrl ?? this.defaultLogoUrl,
|
|
784
813
|
otpCode: otp,
|
|
785
814
|
actionUrl: verificationUrl,
|
|
786
815
|
actionText: "Verify Email",
|
|
787
|
-
extraMessage:
|
|
788
|
-
footer:
|
|
816
|
+
extraMessage: resolvedExtraMessage,
|
|
817
|
+
footer: resolvedFooter,
|
|
789
818
|
userName: username,
|
|
790
819
|
fromName: brand,
|
|
791
|
-
noHtmlMessage:
|
|
820
|
+
noHtmlMessage: this.arrangePlainTextWithExtras(plainTextBody, resolvedExtraMessage, resolvedFooter)
|
|
792
821
|
};
|
|
793
822
|
}
|
|
794
823
|
/**
|
|
795
824
|
* Password-reset email with an optional OTP code and action link.
|
|
796
825
|
*/
|
|
797
826
|
createPasswordReset(params) {
|
|
798
|
-
const { username, resetUrl, otp = "", brandName } = params;
|
|
827
|
+
const { username, resetUrl, otp = "", brandName, logoUrl, extraMessage, footer } = params;
|
|
799
828
|
const brand = this.resolveBrand(brandName);
|
|
800
829
|
const body = `Hi ${username},<br/>We received a request to reset your password. Use the OTP code below or click the button to proceed.`;
|
|
801
830
|
const plainTextOtp = otp ? `\n\nYour OTP code is: ${otp}` : "";
|
|
831
|
+
const resolvedExtraMessage = extraMessage ?? this.defaultSecurityMessage;
|
|
832
|
+
const resolvedFooter = footer ?? this.buildFooter(brand);
|
|
833
|
+
const plainTextBody = htmlToPlainText(body) + plainTextOtp + `\n\nAlternatively, you can use the following link to reset your password:\n${resetUrl}`;
|
|
802
834
|
return {
|
|
803
835
|
subject: "Password Reset Request",
|
|
804
836
|
header: "Reset Your Password",
|
|
805
837
|
body,
|
|
806
|
-
logoUrl: this.defaultLogoUrl,
|
|
838
|
+
logoUrl: logoUrl ?? this.defaultLogoUrl,
|
|
807
839
|
otpCode: otp || void 0,
|
|
808
840
|
actionUrl: resetUrl,
|
|
809
841
|
actionText: "Reset Password",
|
|
810
|
-
extraMessage:
|
|
811
|
-
footer:
|
|
842
|
+
extraMessage: resolvedExtraMessage,
|
|
843
|
+
footer: resolvedFooter,
|
|
812
844
|
userName: username,
|
|
813
845
|
fromName: brand,
|
|
814
|
-
noHtmlMessage:
|
|
846
|
+
noHtmlMessage: this.arrangePlainTextWithExtras(plainTextBody, resolvedExtraMessage, resolvedFooter)
|
|
815
847
|
};
|
|
816
848
|
}
|
|
817
849
|
/**
|
|
818
850
|
* Generic plain message email (no action button, no OTP).
|
|
819
851
|
*/
|
|
820
852
|
createSimpleMessage(params) {
|
|
821
|
-
const { subject, header, body, brandName } = params;
|
|
853
|
+
const { subject, header, body, brandName, logoUrl, extraMessage, footer } = params;
|
|
822
854
|
const brand = this.resolveBrand(brandName);
|
|
855
|
+
const resolvedFooter = footer ?? this.buildFooter(brand);
|
|
823
856
|
return {
|
|
824
857
|
subject,
|
|
825
858
|
header,
|
|
826
859
|
body,
|
|
827
|
-
logoUrl: this.defaultLogoUrl,
|
|
828
|
-
|
|
860
|
+
logoUrl: logoUrl ?? this.defaultLogoUrl,
|
|
861
|
+
extraMessage,
|
|
862
|
+
footer: resolvedFooter,
|
|
829
863
|
fromName: brand,
|
|
830
|
-
noHtmlMessage: htmlToPlainText(body)
|
|
864
|
+
noHtmlMessage: this.arrangePlainTextWithExtras(htmlToPlainText(body), extraMessage, resolvedFooter)
|
|
831
865
|
};
|
|
832
866
|
}
|
|
833
867
|
/**
|
|
834
868
|
* Newsletter / announcement email with an optional hero image and CTA button.
|
|
835
869
|
*/
|
|
836
870
|
createNewsLetter(params) {
|
|
837
|
-
const { subject, header, body, imageUrl, actionUrl, actionText, brandName, logoUrl } = params;
|
|
871
|
+
const { subject, header, body, imageUrl, actionUrl, actionText, brandName, logoUrl, extraMessage, footer } = params;
|
|
838
872
|
const brand = this.resolveBrand(brandName);
|
|
839
|
-
|
|
840
|
-
|
|
873
|
+
const resolvedFooter = footer ?? this.buildFooter(brand);
|
|
874
|
+
let plainTextBody = htmlToPlainText(body);
|
|
875
|
+
if (actionUrl) plainTextBody += `\n\n${actionText || "Learn more"}: ${actionUrl}`;
|
|
841
876
|
return {
|
|
842
877
|
subject,
|
|
843
878
|
header,
|
|
844
879
|
body,
|
|
845
|
-
logoUrl: logoUrl
|
|
880
|
+
logoUrl: logoUrl ?? this.defaultLogoUrl,
|
|
846
881
|
imageUrl,
|
|
847
882
|
actionUrl,
|
|
848
883
|
actionText,
|
|
849
|
-
|
|
884
|
+
extraMessage,
|
|
885
|
+
footer: resolvedFooter,
|
|
850
886
|
fromName: brand,
|
|
851
|
-
noHtmlMessage
|
|
887
|
+
noHtmlMessage: this.arrangePlainTextWithExtras(plainTextBody, extraMessage, resolvedFooter)
|
|
852
888
|
};
|
|
853
889
|
}
|
|
854
890
|
/**
|
|
855
891
|
* Two-factor authentication email with OTP code and optional magic link.
|
|
856
892
|
*/
|
|
857
893
|
create2FAEmail(params) {
|
|
858
|
-
const { username, otp, magicLink = "", brandName } = params;
|
|
894
|
+
const { username, otp, magicLink = "", brandName, logoUrl, extraMessage, footer } = params;
|
|
859
895
|
const brand = this.resolveBrand(brandName);
|
|
860
896
|
const body = `Hi ${username},<br/>Use the code below or click the button to complete your login.`;
|
|
861
897
|
const plainLink = magicLink ? `\n\nAlternatively, verify here:\n${magicLink}` : "";
|
|
898
|
+
const resolvedExtraMessage = extraMessage ?? this.defaultSecurityMessage;
|
|
899
|
+
const resolvedFooter = footer ?? this.buildFooter(brand);
|
|
900
|
+
const plainTextBody = htmlToPlainText(body) + `\n\nYour verification code is: ${otp}` + plainLink;
|
|
862
901
|
return {
|
|
863
902
|
subject: "Your Login Verification Code",
|
|
864
903
|
header: "Two-Factor Authentication",
|
|
865
904
|
body,
|
|
866
|
-
logoUrl: this.defaultLogoUrl,
|
|
905
|
+
logoUrl: logoUrl ?? this.defaultLogoUrl,
|
|
867
906
|
otpCode: otp,
|
|
868
907
|
actionUrl: magicLink || void 0,
|
|
869
908
|
actionText: magicLink ? "Verify Login" : void 0,
|
|
870
|
-
extraMessage:
|
|
871
|
-
footer:
|
|
909
|
+
extraMessage: resolvedExtraMessage,
|
|
910
|
+
footer: resolvedFooter,
|
|
872
911
|
userName: username,
|
|
873
912
|
fromName: brand,
|
|
874
|
-
noHtmlMessage:
|
|
913
|
+
noHtmlMessage: this.arrangePlainTextWithExtras(plainTextBody, resolvedExtraMessage, resolvedFooter)
|
|
875
914
|
};
|
|
876
915
|
}
|
|
877
916
|
/**
|
|
878
917
|
* Magic link sign-in email (no OTP, button only).
|
|
879
918
|
*/
|
|
880
919
|
createMagicLinkEmail(params) {
|
|
881
|
-
const { username, magicLink, brandName } = params;
|
|
920
|
+
const { username, magicLink, brandName, logoUrl, extraMessage, footer } = params;
|
|
882
921
|
const brand = this.resolveBrand(brandName);
|
|
883
922
|
const body = `Hi ${username},<br/>Click the button below to sign in. No password needed.`;
|
|
923
|
+
const resolvedExtraMessage = extraMessage ?? this.defaultSecurityMessage;
|
|
924
|
+
const resolvedFooter = footer ?? this.buildFooter(brand);
|
|
925
|
+
const plainTextBody = htmlToPlainText(body) + `\n\nSign in here:\n${magicLink}`;
|
|
884
926
|
return {
|
|
885
927
|
subject: "Your Magic Link",
|
|
886
928
|
header: `Sign In to ${brand}`,
|
|
887
929
|
body,
|
|
888
|
-
logoUrl: this.defaultLogoUrl,
|
|
930
|
+
logoUrl: logoUrl ?? this.defaultLogoUrl,
|
|
889
931
|
actionUrl: magicLink,
|
|
890
932
|
actionText: "Sign In",
|
|
891
|
-
extraMessage:
|
|
892
|
-
footer:
|
|
933
|
+
extraMessage: resolvedExtraMessage,
|
|
934
|
+
footer: resolvedFooter,
|
|
893
935
|
userName: username,
|
|
894
936
|
fromName: brand,
|
|
895
|
-
noHtmlMessage:
|
|
937
|
+
noHtmlMessage: this.arrangePlainTextWithExtras(plainTextBody, resolvedExtraMessage, resolvedFooter)
|
|
896
938
|
};
|
|
897
939
|
}
|
|
898
940
|
static _defaultInstance;
|