@visulima/email
Version:
A comprehensive email library with multi-provider support, crypto utilities, and template engines
2 lines (1 loc) • 9.72 kB
JavaScript
var x=Object.defineProperty;var h=(o,e)=>x(o,"name",{value:e,configurable:!0});import{createRequire as v}from"node:module";import{readFileAsBuffer as b,detectMimeType as u,generateContentId as A}from"./detectMimeType-S8WRsNtY.js";import w from"../template-engines/html-to-text.js";import T from"./createLogger-DlElSVQP.js";import j from"./headersToRecord-BKUTr40L.js";const E=v(import.meta.url),l=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,M=h(o=>{if(typeof l<"u"&&l.versions&&l.versions.node){const[e,t]=l.versions.node.split(".").map(Number);if(e>22||e===22&&t>=3||e===20&&t>=16)return l.getBuiltinModule(o)}return E(o)},"__cjs_getBuiltinModule"),{basename:y}=M("node:path");var C=Object.defineProperty,d=h((o,e)=>C(o,"name",{value:e,configurable:!0}),"g");const p=d(o=>Array.isArray(o)?o.map(e=>typeof e=="string"?{email:e}:e):typeof o=="string"?[{email:o}]:[o],"normalizeAddresses"),f=d((o,e,t)=>{e.success&&e.data?o&&o.info("Email sent successfully",{messageId:e.data.messageId,provider:e.data.provider}):o&&o.error("Email send failed",{error:e.error,provider:t})},"logSendResult");class I{static{h(this,"MailMessage")}static{d(this,"MailMessage")}fromAddress;toAddresses=[];ccAddresses=[];bccAddresses=[];subjectText="";textContent;autoTextEnabled=!0;htmlContent;headers={};attachments=[];replyToAddress;priorityValue;tagsValue=[];provider;signer;encrypter;logger;from(e){return this.fromAddress=typeof e=="string"?{email:e}:e,this}to(e){return this.toAddresses.push(...p(e)),this}cc(e){return this.ccAddresses.push(...p(e)),this}bcc(e){return this.bccAddresses.push(...p(e)),this}subject(e){return this.subjectText=e,this}text(e){return this.textContent=e,this}html(e){return this.htmlContent=e,this}header(e,t){return this.headers[e]=t,this}setHeaders(e){const t=j(e);return Object.assign(this.headers,t),this}async attachFromPath(e,t){try{const s=await b(e),i=t?.filename||y(e)||"attachment",r=t?.contentType||u(i);this.attachments.push({cid:t?.cid,content:s,contentDisposition:t?.contentDisposition||"attachment",contentType:r,encoding:t?.encoding,filename:i,headers:t?.headers}),this.logger&&this.logger.debug(`Attachment added from path: ${e}`,{contentType:r,filename:i})}catch(s){throw this.logger&&this.logger.error(`Failed to attach file from path: ${e}`,s),s}return this}attachData(e,t){const s=t.contentType||u(t.filename);return this.attachments.push({cid:t.cid,content:e,contentDisposition:t.contentDisposition||"attachment",contentType:s,encoding:t.encoding,filename:t.filename,headers:t.headers}),this.logger&&this.logger.debug("Attachment added from data",{contentType:s,filename:t.filename}),this}async embedFromPath(e,t){try{const s=await b(e),i=t?.filename||y(e)||"inline",r=t?.contentType||u(i),n=A(i);return this.attachments.push({cid:n,content:s,contentDisposition:"inline",contentType:r,filename:i}),this.logger&&this.logger.debug(`Inline attachment embedded from path: ${e}`,{cid:n,contentType:r,filename:i}),n}catch(s){throw this.logger&&this.logger.error(`Failed to embed file from path: ${e}`,s),s}}embedData(e,t,s){const i=s?.contentType||u(t),r=A(t);return this.attachments.push({cid:r,content:e,contentDisposition:"inline",contentType:i,filename:t}),this.logger&&this.logger.debug("Inline attachment embedded from data",{cid:r,contentType:i,filename:t}),r}replyTo(e){return this.replyToAddress=typeof e=="string"?{email:e}:e,this}priority(e){return this.priorityValue=e,this}tags(e){return this.tagsValue=Array.isArray(e)?e:[e],this}mailer(e){return this.provider=e,this}sign(e){return this.signer=e,this.logger&&this.logger.debug("Email signer configured"),this}encrypt(e){return this.encrypter=e,this.logger&&this.logger.debug("Email encrypter configured"),this}setLogger(e){return this.logger=T("MailMessage",e),this}async view(e,t,s,i){try{const r=i?.autoText!==!1;this.autoTextEnabled=r,this.logger&&this.logger.debug("Rendering template",{autoText:r});const n=await e(t,s,i);this.html(n),r&&n&&this.tryAutoGenerateText(n),this.logger&&this.logger.debug("Template rendered successfully")}catch(r){throw this.logger&&this.logger.error("Failed to render template",r),new Error(`Failed to render template: ${r.message}`)}return this}async viewText(e,t,s,i){try{this.logger&&this.logger.debug("Rendering text template");const r=await e(t,s,i);if(typeof r=="string")this.text(r),this.logger&&this.logger.debug("Text template rendered successfully");else throw new TypeError("Text renderer must return a string")}catch(r){throw this.logger&&this.logger.error("Failed to render text template",r),new Error(`Failed to render text template: ${r.message}`)}return this}async build(){if(this.logger&&this.logger.debug("Building email message"),this.fromAddress||this.throwAndLogError("From address is required","Build failed: from address is required"),this.toAddresses.length===0&&this.throwAndLogError("At least one recipient is required","Build failed: at least one recipient is required"),this.subjectText||this.throwAndLogError("Subject is required","Build failed: subject is required"),this.htmlContent&&!this.textContent&&this.autoTextEnabled)try{this.textContent=w(this.htmlContent),this.logger&&this.logger.debug("Auto-generated text content from HTML")}catch{this.logger&&this.logger.debug("Failed to convert HTML to text; proceeding without text content.")}!this.textContent&&!this.htmlContent&&this.throwAndLogError("Either text or html content is required","Build failed: either text or html content is required");let e={from:this.fromAddress,html:this.htmlContent,subject:this.subjectText,text:this.textContent,to:this.toAddresses.length===1?this.toAddresses[0]:this.toAddresses};return this.ccAddresses.length>0&&(e.cc=this.ccAddresses.length===1?this.ccAddresses[0]:this.ccAddresses),this.bccAddresses.length>0&&(e.bcc=this.bccAddresses.length===1?this.bccAddresses[0]:this.bccAddresses),Object.keys(this.headers).length>0&&(e.headers=this.headers),this.attachments.length>0&&(e.attachments=this.attachments,this.logger&&this.logger.debug(`Email includes ${this.attachments.length} attachment(s)`)),this.replyToAddress&&(e.replyTo=this.replyToAddress),this.priorityValue&&(e.priority=this.priorityValue),this.tagsValue.length>0&&(e.tags=this.tagsValue),this.signer&&(this.logger&&this.logger.debug("Signing email message"),e=await this.signer.sign(e),this.logger&&this.logger.debug("Email message signed successfully")),this.encrypter&&(this.logger&&this.logger.debug("Encrypting email message"),e=await this.encrypter.encrypt(e),this.logger&&this.logger.debug("Email message encrypted successfully")),this.logger&&this.logger.debug("Email message built successfully",{bcc:this.bccAddresses.length,cc:this.ccAddresses.length,hasHtml:!!this.htmlContent,hasText:!!this.textContent,to:this.toAddresses.length}),e}async send(){this.provider||this.throwAndLogError("No provider configured. Use mailer() method to set a provider.","Send failed: no provider configured"),this.logger&&this.logger.debug("Sending email",{subject:this.subjectText,to:this.toAddresses.length});const e=await this.build(),t=await this.provider.sendEmail(e);return f(this.logger,t,this.provider.name||"unknown"),t}tryAutoGenerateText(e){try{const t=w(e);t&&!this.textContent&&this.text(t),this.logger&&this.logger.debug("Auto-generated text content from HTML template")}catch(t){this.logger&&this.logger.warn("Failed to auto-generate text from HTML template",t)}}throwAndLogError(e,t){const s=new Error(e);throw this.logger&&this.logger.error(t||e),s}}class m{static{h(this,"Mail")}static{d(this,"Mail")}static extractErrorMessages(e){return e instanceof Error?[e.message]:[String(e||"Unknown error")]}provider;logger;loggerInstance;constructor(e){this.provider=e}setLogger(e){return this.loggerInstance=e,this.logger=T("Mail",e),this}message(){this.logger&&this.logger.debug("Creating new mail message");const e=new I;return e.mailer(this.provider),this.loggerInstance&&e.setLogger(this.loggerInstance),e}async send(e){this.logger&&this.logger.debug("Sending mailable instance");const t=await e.build(),s=await this.provider.sendEmail(t);return f(this.logger,s,this.provider.name||"unknown"),s}async sendEmail(e){this.logger&&this.logger.debug("Sending email with options",{subject:e.subject,to:Array.isArray(e.to)?e.to.length:1});const t=await this.provider.sendEmail(e);return f(this.logger,t,this.provider.name||"unknown"),t}async*sendMany(e,t){const s=this.provider.name;let i=0,r=0,n=0;this.logger&&this.logger.debug("Starting batch email send",{provider:s});for await(const c of e){if(t?.signal?.aborted){this.logger&&this.logger.warn("Batch send operation was aborted",{failed:n,processed:i,successful:r}),yield{errorMessages:["Send operation was aborted"],provider:s,successful:!1};return}i+=1;try{const g="build"in c&&typeof c.build=="function"?await c.build():c;this.logger&&this.logger.debug(`Sending email ${i}`,{subject:g.subject,to:Array.isArray(g.to)?g.to.length:1});const a=await this.provider.sendEmail(g);a.success&&a.data?(r+=1,this.logger&&this.logger.debug(`Email ${i} sent successfully`,{messageId:a.data.messageId}),yield{messageId:a.data.messageId,provider:a.data.provider||s,response:a.data.response,successful:!0,timestamp:a.data.timestamp}):(n+=1,this.logger&&this.logger.error(`Email ${i} send failed`,{error:a.error}),yield{errorMessages:m.extractErrorMessages(a.error),provider:s,successful:!1})}catch(g){n+=1,this.logger&&this.logger.error(`Email ${i} send failed with exception`,g),yield{errorMessages:m.extractErrorMessages(g),provider:s,successful:!1}}}this.logger&&this.logger.info("Batch email send completed",{failed:n,successful:r,total:i})}}const _=d(o=>new m(o),"createMail");export{m as Mail,I as MailMessage,_ as createMail};