@visulima/email
Version:
A comprehensive email library with multi-provider support, crypto utilities, and template engines
33 lines (32 loc) • 7.55 kB
JavaScript
var V=Object.defineProperty;var M=(m,n)=>V(m,"name",{value:n,configurable:!0});import{createRequire as F}from"node:module";import T from"./EmailError-zm2ffVav.js";import Q from"./RequiredOptionError-CevW3u2K.js";import X from"./createLogger-DlElSVQP.js";import S from"./formatEmailAddress-CHeME3Vk.js";import J from"./headersToRecord-BKUTr40L.js";import{makeRequest as Y}from"./makeRequest-DwxHX0xo.js";import{s as P,a as Z}from"./sanitize-header-wWav-Scu.js";import ee from"./validateEmailOptions-BzlJECG5.js";import{defineProvider as te}from"./defineProvider-B9rSklAJ.js";const L=F(import.meta.url),$=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,G=M(m=>{if(typeof $<"u"&&$.versions&&$.versions.node){const[n,d]=$.versions.node.split(".").map(Number);if(n>22||n===22&&d>=3||n===20&&d>=16)return $.getBuiltinModule(m)}return L(m)},"__cjs_getBuiltinModule"),{randomUUID:q,createHash:k,createHmac:w}=G("node:crypto");var re=Object.defineProperty,f=M((m,n)=>re(m,"name",{value:n,configurable:!0}),"m");const se=globalThis.Buffer!==void 0,j=f(()=>{if(!se)throw new Error("Buffer is required for AWS SES provider");return globalThis.Buffer},"getBuffer"),y="aws-ses",C={apiVersion:"2010-12-01",maxAttempts:3,region:"us-east-1"},fe=te((m={})=>{const n={...C,...m},d=X("AWS-SES",m.logger),W=f((e,i,t,s,a)=>{const u=Object.keys(t).toSorted().map(c=>{const l=t[c];return l===void 0?"":`${encodeURIComponent(c)}=${encodeURIComponent(l)}`}).filter(Boolean).join("&"),r=`${Object.keys(s).toSorted().map(c=>`${c.toLowerCase()}:${s[c]}`).join(`
`)}
`,o=Object.keys(s).toSorted().map(c=>c.toLowerCase()).join(";"),g=k("sha256").update(a).digest("hex");return[e,i,u,r,o,g].join(`
`)},"createCanonicalRequest"),_=f((e,i,t)=>{const s=e.slice(0,8),a=k("sha256").update(t).digest("hex");return["AWS4-HMAC-SHA256",e,`${s}/${i}/ses/aws4_request`,a].join(`
`)},"createStringToSign"),K=f((e,i,t,s)=>{const a=i.slice(0,8),u=w("sha256",`AWS4${e}`).update(a).digest(),r=w("sha256",u).update(t).digest(),o=w("sha256",r).update("ses").digest(),g=w("sha256",o).update("aws4_request").digest();return w("sha256",g).update(s).digest("hex")},"calculateSignature"),O=f((e,i,t,s,a)=>{const u=i.slice(0,8),r=Object.keys(s).toSorted().map(o=>o.toLowerCase()).join(";");return[`AWS4-HMAC-SHA256 Credential=${e}/${u}/${t}/ses/aws4_request`,`SignedHeaders=${r}`,`Signature=${a}`].join(", ")},"createAuthHeader"),v=f(async(e,i)=>{if(!n.accessKeyId||!n.secretAccessKey)throw d.debug("Missing required credentials: accessKeyId or secretAccessKey"),new Q(y,["accessKeyId","secretAccessKey"]);try{const t=n.region||C.region,s=n.apiVersion||C.apiVersion,a=n.endpoint||`email.${t}.amazonaws.com`,u="/",r="POST";d.debug("Making request to AWS SES:",{action:e,host:a,region:t});const o=new URLSearchParams;o.append("Action",e),o.append("Version",s),Object.entries(i).forEach(([h,p])=>{p!=null&&o.append(h,String(p))});const g=o.toString();d.debug("Request body:",g);const c=new Date().toISOString().replaceAll(/[:-]|\.\d{3}/g,""),l={"Content-Length":j().byteLength(g).toString(),"Content-Type":"application/x-www-form-urlencoded",Host:a,"X-Amz-Date":c};n.sessionToken&&(l["X-Amz-Security-Token"]=n.sessionToken),d.debug("Request headers:",l);const A=W(r,u,{},l,g),B=_(c,t,A),z=K(n.secretAccessKey,c,t,B);l.Authorization=O(n.accessKeyId,c,t,l,z),d.debug("Making HTTPS request to:",`https://${a}${u}`);const D=`https://${a}${u}`,E=await Y(D,{headers:l,method:r},g);if(!E.success)throw E.error||new Error("AWS SES API request failed");const b=E.data?.body;if(!b)throw new Error("No response body from AWS SES");const H=E.data?.statusCode;d.debug("Response status:",H),d.debug("Response data:",b);const I=H;if(I&&I>=200&&I<300){const h={};if(e==="SendRawEmail"){const p=b.match(/<MessageId>(.*?)<\/MessageId>/);if(p){const[,N]=p;h.MessageId=N,d.debug("Extracted MessageId:",h.MessageId)}}else if(e==="GetSendQuota"){const p=b.match(/<Max24HourSend>(.*?)<\/Max24HourSend>/);p&&p[1]&&(h.Max24HourSend=Number.parseFloat(p[1]),d.debug("Extracted Max24HourSend:",h.Max24HourSend))}return h}const x=b.match(/<Message>(.*?)<\/Message>/),R=x?x[1]:"Unknown AWS SES error";throw d.debug("AWS SES Error:",R),new Error(`AWS SES API Error: ${R}`)}catch(t){const s=t instanceof Error?t.message:String(t);throw d.debug("makeAwsRequest exception:",s),t instanceof Error?t:new Error(s)}},"makeAwsRequest"),U=f(e=>{const i=`----=${q().replaceAll("-","")}`,t=new Date().toUTCString(),s=e.from.email.includes("@")?e.from.email.split("@")[1]:"localhost",a=`<${q().replaceAll("-","")}@${s}>`,u=j();let r="";if(r+=`From: ${S(e.from)}\r
`,r+=Array.isArray(e.to)?`To: ${e.to.map(o=>S(o)).join(", ")}\r
`:`To: ${S(e.to)}\r
`,e.cc&&(r+=Array.isArray(e.cc)?`Cc: ${e.cc.map(o=>S(o)).join(", ")}\r
`:`Cc: ${S(e.cc)}\r
`),e.bcc&&(r+=Array.isArray(e.bcc)?`Bcc: ${e.bcc.map(o=>S(o)).join(", ")}\r
`:`Bcc: ${S(e.bcc)}\r
`),r+=`Subject: ${P(e.subject)}\r
`,r+=`Date: ${t}\r
`,r+=`Message-ID: ${a}\r
`,r+=`MIME-Version: 1.0\r
`,e.headers){const o=J(e.headers);for(const[g,c]of Object.entries(o)){const l=Z(g),A=P(c);r+=`${l}: ${A}\r
`}}return r+=`Content-Type: multipart/alternative; boundary="${i}"\r
\r
`,e.text&&(r+=`--${i}\r
`,r+=`Content-Type: text/plain; charset=UTF-8\r
`,r+=`Content-Transfer-Encoding: base64\r
\r
`,r+=`${u.from(e.text,"utf8").toString("base64")}\r
\r
`),e.html&&(r+=`--${i}\r
`,r+=`Content-Type: text/html; charset=UTF-8\r
`,r+=`Content-Transfer-Encoding: base64\r
\r
`,r+=`${u.from(e.html,"utf8").toString("base64")}\r
\r
`),r+=`--${i}--\r
`,r},"generateMimeMessage");return{features:{attachments:!1,batchSending:!1,customHeaders:!0,html:!0,replyTo:!1,scheduling:!1,tagging:!1,templates:!1,tracking:!1},getInstance:f(()=>{},"getInstance"),initialize(){d.debug("Initializing AWS SES provider with options:",{accessKeyId:n.accessKeyId?`***${n.accessKeyId.slice(-4)}`:void 0,endpoint:n.endpoint,region:n.region,secretAccessKey:n.secretAccessKey?"***":void 0})},async isAvailable(){try{return!!(await v("GetSendQuota",{})).Max24HourSend}catch{return!1}},name:y,options:n,async sendEmail(e){const i=f(t=>{const s=[];return t.attachments&&t.attachments.length>0&&s.push("attachments"),t.priority&&s.push("priority"),t.tags&&t.tags.length>0&&s.push("tags (use messageTags instead)"),t.replyTo&&s.push("replyTo"),s},"checkUnsupportedFields");try{const t=ee(e);if(t.length>0)throw new T(y,`Invalid email options: ${t.join(", ")}`);const s=i(e);if(s.length>0)throw new T(y,`Unsupported fields provided: ${s.join(", ")}. These fields are not supported by AWS SES Raw Email API and would be silently ignored.`);const a={};e.configurationSetName&&(a.ConfigurationSetName=e.configurationSetName),e.sourceArn&&(a.SourceArn=e.sourceArn),e.returnPath&&(a.ReturnPath=e.returnPath),e.returnPathArn&&(a.ReturnPathArn=e.returnPathArn),e.messageTags&&Object.keys(e.messageTags).length>0&&Object.entries(e.messageTags).forEach(([c,l],A)=>{a[`Tags.member.${A+1}.Name`]=c,a[`Tags.member.${A+1}.Value`]=l});const u=U(e),r=j().from(u).toString("base64");a["RawMessage.Data"]=r;const o=await v("SendRawEmail",a),g=o.MessageId;return!g||g.trim()===""?{error:new T(y,"AWS SES API returned a response without a MessageId. The email may not have been sent successfully."),success:!1}:{data:{messageId:g,provider:y,response:o,sent:!0,timestamp:new Date},success:!0}}catch(t){return{error:new T(y,`Failed to send email: ${t instanceof Error?t.message:String(t)}`,{cause:t instanceof Error?t:new Error(String(t))}),success:!1}}},async validateCredentials(){return this.isAvailable()}}});export{fe as default};