@visulima/email 4.0.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +74 -1
  3. package/dist/crypto/index.d.ts +2 -2
  4. package/dist/deliverability/dmarc-report.js +1 -1
  5. package/dist/deliverability/index.d.ts +1 -1
  6. package/dist/inbound/index.d.ts +1 -1
  7. package/dist/index.d.ts +11 -5
  8. package/dist/index.js +1 -1
  9. package/dist/middleware/index.d.ts +3 -3
  10. package/dist/middleware/index.js +1 -1
  11. package/dist/packem_chunks/index.js +5 -5
  12. package/dist/packem_shared/{DraftMailMessage-B0gwf8n_.js → DraftMailMessage-zkKTOxQ8.js} +1 -1
  13. package/dist/packem_shared/EmailError-SXEFQEyA.js +1 -0
  14. package/dist/packem_shared/Mail-DtpXUSIl.js +1 -0
  15. package/dist/packem_shared/MailMessage-Aw0vVAh-.js +1 -0
  16. package/dist/packem_shared/{RequiredOptionError-ctDEEB1s.js → RequiredOptionError-C2wWqmiE.js} +1 -1
  17. package/dist/packem_shared/{ahaSendProvider-D95-pwFh.js → ahaSendProvider-Ci_a_O8d.js} +1 -1
  18. package/dist/packem_shared/{attachment-processor-BJFDS343.js → attachment-processor-AB1m-n9T.js} +1 -1
  19. package/dist/packem_shared/{autoSendProvider-yxUx3sEj.js → autoSendProvider-DG3pNJML.js} +1 -1
  20. package/dist/packem_shared/{awsSesProvider-DnZo9X_g.js → awsSesProvider-C4b5kuL4.js} +1 -1
  21. package/dist/packem_shared/{azureProvider-D6Z4c1yT.js → azureProvider-CEnz3uy_.js} +1 -1
  22. package/dist/packem_shared/{brevoProvider-D1yTSs0l.js → brevoProvider-BsbInXjw.js} +1 -1
  23. package/dist/packem_shared/{build-mime-message-BCFKPqaW.js → build-mime-message-D45WdgfP.js} +1 -1
  24. package/dist/packem_shared/{circuitBreakerMiddleware-DcBN7K6H.js → circuitBreakerMiddleware-Cm0m75qi.js} +1 -1
  25. package/dist/packem_shared/{cloudflareEmailProvider-DZbs35NL.js → cloudflareEmailProvider-CnpH5R5p.js} +1 -1
  26. package/dist/packem_shared/{createTestEmail-gr512snl.js → createTestEmail-B2Fz-Ifb.js} +1 -1
  27. package/dist/packem_shared/createWorker-ChrQvLUF.js +1 -0
  28. package/dist/packem_shared/extractLinks-BwE1SjNF.js +1 -0
  29. package/dist/packem_shared/{failoverProvider-IED1M0M5.js → failoverProvider-DsBqMOfK.js} +1 -1
  30. package/dist/packem_shared/fileProvider-BnDeaSqA.js +1 -0
  31. package/dist/packem_shared/{httpProvider-CpdWJoRq.js → httpProvider-BuA00407.js} +1 -1
  32. package/dist/packem_shared/{infobipProvider-DTqFu5pM.js → infobipProvider-DVUg9jKC.js} +1 -1
  33. package/dist/packem_shared/{isValidSigningCertUrl-CK1t2a7x.js → isValidSigningCertUrl-gnYr3oyD.js} +1 -1
  34. package/dist/packem_shared/{loopsProvider-PiE2HxoW.js → loopsProvider-BJUNQ2rR.js} +1 -1
  35. package/dist/packem_shared/{mail.d-BdyyyQO5.d.ts → mail.d-2u-30lWz.d.ts} +12 -4
  36. package/dist/packem_shared/{mailCrabProvider-BSapw_mS.js → mailCrabProvider-CFKRofS5.js} +1 -1
  37. package/dist/packem_shared/{mailPaceProvider-BugIutTw.js → mailPaceProvider-DBQd6NYt.js} +1 -1
  38. package/dist/packem_shared/{mailchannelsProvider-Dn24mI8D.js → mailchannelsProvider-CPVW3wlH.js} +1 -1
  39. package/dist/packem_shared/{mailerSendProvider-B6X4Lhsf.js → mailerSendProvider-B2H6BtoV.js} +1 -1
  40. package/dist/packem_shared/{mailgunProvider-4sMtcQcW.js → mailgunProvider-JRVH70PB.js} +1 -1
  41. package/dist/packem_shared/{mailjetProvider-DvrHwipw.js → mailjetProvider-BzBovF5M.js} +1 -1
  42. package/dist/packem_shared/{mailomatProvider-CrkNQGfL.js → mailomatProvider-x6IHKuWU.js} +1 -1
  43. package/dist/packem_shared/{mailtrapProvider-DTgvCUrs.js → mailtrapProvider-CJTJoDcV.js} +1 -1
  44. package/dist/packem_shared/make-request-mszP240P.js +1 -0
  45. package/dist/packem_shared/{mandrillProvider-BaK0GEj7.js → mandrillProvider-BcztgKkr.js} +1 -1
  46. package/dist/packem_shared/{mockProvider-CTxxbT0d.js → mockProvider-DVtl5Jjy.js} +1 -1
  47. package/dist/packem_shared/{netcoreProvider-BZJ0noQ2.js → netcoreProvider-YWEpa-wi.js} +1 -1
  48. package/dist/packem_shared/{nodemailerProvider-DqJh8ghN.js → nodemailerProvider-WNNtLE2s.js} +1 -1
  49. package/dist/packem_shared/{opentelemetryProvider-s0gvh05u.js → opentelemetryProvider-ddbILMeS.js} +1 -1
  50. package/dist/packem_shared/{outlook365Provider-DQUSFy2d.js → outlook365Provider-D5eETMZq.js} +1 -1
  51. package/dist/packem_shared/{plunkProvider-WMi1yPya.js → plunkProvider-TrBBoX7_.js} +1 -1
  52. package/dist/packem_shared/{postalProvider-DpBFGpoX.js → postalProvider-CU53hX0R.js} +1 -1
  53. package/dist/packem_shared/{postmarkProvider-ClUaHPvw.js → postmarkProvider-DEYQp8Gv.js} +1 -1
  54. package/dist/packem_shared/{provider-base-BEz00m9R.js → provider-base-G22b4a87.js} +1 -1
  55. package/dist/packem_shared/{provider.d-Cx5YQJdP.d.ts → provider.d-BLLbEuQ8.d.ts} +1 -1
  56. package/dist/packem_shared/resendProvider-BaIZCvph.js +1 -0
  57. package/dist/packem_shared/{retry-aghj7y8m.js → retry-ByPJFAtq.js} +1 -1
  58. package/dist/packem_shared/{roundRobinProvider-Dr1DdT2L.js → roundRobinProvider-CfuNY7We.js} +1 -1
  59. package/dist/packem_shared/{scalewayProvider-BhbDUEah.js → scalewayProvider-x6S2ZSPd.js} +1 -1
  60. package/dist/packem_shared/{sendGridProvider-DBZHhxd6.js → sendGridProvider-8Po3P-ia.js} +1 -1
  61. package/dist/packem_shared/{smtpProvider-CjcOEiAh.js → smtpProvider-BPamDNuv.js} +1 -1
  62. package/dist/packem_shared/{sparkpostProvider-B19lU9D5.js → sparkpostProvider-BVu1KeNz.js} +1 -1
  63. package/dist/packem_shared/{sweegoProvider-H8KWGDff.js → sweegoProvider-DHISjS9s.js} +1 -1
  64. package/dist/packem_shared/{types.d-Cb5fls_X.d.ts → types.d-A1Eea-p_.d.ts} +1 -1
  65. package/dist/packem_shared/{types.d-kHO4kX9q.d.ts → types.d-B5iH_mgb.d.ts} +1 -1
  66. package/dist/packem_shared/{types.d-GKqPNk0I.d.ts → types.d-Chhzb-48.d.ts} +1 -1
  67. package/dist/packem_shared/{types.d-B9295M4_.d.ts → types.d-CyiC9uRC.d.ts} +10 -0
  68. package/dist/packem_shared/{types.d-C5geVKes.d.ts → types.d-V_yuAOrx.d.ts} +1 -1
  69. package/dist/packem_shared/{verifySendGridWebhook-csuRasH3.js → verifySendGridWebhook-0_3qKQA1.js} +1 -1
  70. package/dist/packem_shared/{withSuppression-B0eZ2xNQ.js → withSuppression-COCHL9Zx.js} +1 -1
  71. package/dist/packem_shared/{zeptomailProvider-L7nkdn0L.js → zeptomailProvider-IhCIl_2a.js} +1 -1
  72. package/dist/providers/ahasend/index.d.ts +3 -3
  73. package/dist/providers/ahasend/index.js +1 -1
  74. package/dist/providers/autosend/index.d.ts +3 -3
  75. package/dist/providers/autosend/index.js +1 -1
  76. package/dist/providers/aws-ses/index.d.ts +3 -3
  77. package/dist/providers/aws-ses/index.js +1 -1
  78. package/dist/providers/azure/index.d.ts +3 -3
  79. package/dist/providers/azure/index.js +1 -1
  80. package/dist/providers/brevo/index.d.ts +3 -3
  81. package/dist/providers/brevo/index.js +1 -1
  82. package/dist/providers/cloudflare-email/index.d.ts +4 -4
  83. package/dist/providers/cloudflare-email/index.js +1 -1
  84. package/dist/providers/failover/index.d.ts +3 -3
  85. package/dist/providers/failover/index.js +1 -1
  86. package/dist/providers/file/index.d.ts +23 -0
  87. package/dist/providers/file/index.js +1 -0
  88. package/dist/providers/http/index.d.ts +3 -3
  89. package/dist/providers/http/index.js +1 -1
  90. package/dist/providers/infobip/index.d.ts +3 -3
  91. package/dist/providers/infobip/index.js +1 -1
  92. package/dist/providers/loops/index.d.ts +3 -3
  93. package/dist/providers/loops/index.js +1 -1
  94. package/dist/providers/mailchannels/index.d.ts +3 -3
  95. package/dist/providers/mailchannels/index.js +1 -1
  96. package/dist/providers/mailcrab/index.d.ts +3 -3
  97. package/dist/providers/mailcrab/index.js +1 -1
  98. package/dist/providers/mailersend/index.d.ts +3 -3
  99. package/dist/providers/mailersend/index.js +1 -1
  100. package/dist/providers/mailgun/index.d.ts +3 -3
  101. package/dist/providers/mailgun/index.js +1 -1
  102. package/dist/providers/mailjet/index.d.ts +3 -3
  103. package/dist/providers/mailjet/index.js +1 -1
  104. package/dist/providers/mailomat/index.d.ts +3 -3
  105. package/dist/providers/mailomat/index.js +1 -1
  106. package/dist/providers/mailpace/index.d.ts +3 -3
  107. package/dist/providers/mailpace/index.js +1 -1
  108. package/dist/providers/mailtrap/index.d.ts +3 -3
  109. package/dist/providers/mailtrap/index.js +1 -1
  110. package/dist/providers/mandrill/index.d.ts +3 -3
  111. package/dist/providers/mandrill/index.js +1 -1
  112. package/dist/providers/mock/index.d.ts +5 -5
  113. package/dist/providers/mock/index.js +1 -1
  114. package/dist/providers/netcore/index.d.ts +3 -3
  115. package/dist/providers/netcore/index.js +1 -1
  116. package/dist/providers/nodemailer/index.d.ts +3 -3
  117. package/dist/providers/nodemailer/index.js +1 -1
  118. package/dist/providers/opentelemetry/index.d.ts +2 -2
  119. package/dist/providers/opentelemetry/index.js +1 -1
  120. package/dist/providers/outlook365/index.d.ts +3 -3
  121. package/dist/providers/outlook365/index.js +1 -1
  122. package/dist/providers/plunk/index.d.ts +3 -3
  123. package/dist/providers/plunk/index.js +1 -1
  124. package/dist/providers/postal/index.d.ts +3 -3
  125. package/dist/providers/postal/index.js +1 -1
  126. package/dist/providers/postmark/index.d.ts +3 -3
  127. package/dist/providers/postmark/index.js +1 -1
  128. package/dist/providers/resend/index.d.ts +3 -3
  129. package/dist/providers/resend/index.js +1 -1
  130. package/dist/providers/roundrobin/index.d.ts +3 -3
  131. package/dist/providers/roundrobin/index.js +1 -1
  132. package/dist/providers/scaleway/index.d.ts +3 -3
  133. package/dist/providers/scaleway/index.js +1 -1
  134. package/dist/providers/sendgrid/index.d.ts +3 -3
  135. package/dist/providers/sendgrid/index.js +1 -1
  136. package/dist/providers/smtp/index.d.ts +4 -4
  137. package/dist/providers/smtp/index.js +1 -1
  138. package/dist/providers/sparkpost/index.d.ts +3 -3
  139. package/dist/providers/sparkpost/index.js +1 -1
  140. package/dist/providers/sweego/index.d.ts +3 -3
  141. package/dist/providers/sweego/index.js +1 -1
  142. package/dist/providers/zeptomail/index.d.ts +3 -3
  143. package/dist/providers/zeptomail/index.js +1 -1
  144. package/dist/queue/bullmq-queue.d.ts +2 -2
  145. package/dist/queue/index.d.ts +4 -3
  146. package/dist/queue/index.js +1 -1
  147. package/dist/queue/pg-boss-queue.d.ts +2 -2
  148. package/dist/queue/sqs-queue.d.ts +2 -2
  149. package/dist/queue/unstorage-queue.d.ts +2 -2
  150. package/dist/render/css-inline.js +1 -1
  151. package/dist/template-engines/handlebars.js +1 -1
  152. package/dist/template-engines/html-to-text.js +1 -1
  153. package/dist/template-engines/i18n.js +1 -1
  154. package/dist/template-engines/jsx-email.js +1 -1
  155. package/dist/template-engines/liquid.js +1 -1
  156. package/dist/template-engines/locale-files.d.ts +66 -0
  157. package/dist/template-engines/locale-files.js +1 -0
  158. package/dist/template-engines/mjml.js +1 -1
  159. package/dist/template-engines/react-email.js +1 -1
  160. package/dist/template-engines/vue-email.js +1 -1
  161. package/dist/test/index.d.ts +24 -7
  162. package/dist/test/index.js +1 -1
  163. package/dist/utils/format-email-address.d.ts +1 -1
  164. package/dist/utils/format-email-address.js +1 -1
  165. package/dist/utils/parse-address.d.ts +1 -1
  166. package/dist/utils/parse-eml.d.ts +1 -1
  167. package/dist/utils/result.d.ts +1 -1
  168. package/dist/utils/result.js +1 -1
  169. package/dist/utils/validation/check-feature-support.d.ts +1 -1
  170. package/dist/utils/validation/check-feature-support.js +1 -1
  171. package/dist/webhooks/index.js +1 -1
  172. package/package.json +9 -1
  173. package/dist/packem_shared/EmailError-BIaHK1Uw.js +0 -1
  174. package/dist/packem_shared/Mail-DmYNQ0KK.js +0 -1
  175. package/dist/packem_shared/MailMessage-DmmaNHHc.js +0 -1
  176. package/dist/packem_shared/createWorker-ChuTgETP.js +0 -1
  177. package/dist/packem_shared/make-request-Cyz5_yu8.js +0 -1
  178. package/dist/packem_shared/resendProvider-DtaB7sk0.js +0 -1
@@ -0,0 +1,66 @@
1
+ import { LocaleTemplates } from "./i18n.js";
2
+ import { T as TemplateRenderer } from "../packem_shared/types.d-I-KeZ5w2.js";
3
+ /**
4
+ * Loads a named template in every locale that has a file on disk.
5
+ *
6
+ * For a name such as `welcome`, the directory is searched for `welcome.{locale}.html`, and each
7
+ * match is keyed by its locale. The un-suffixed `welcome.html` is registered under `defaultLocale`.
8
+ * The result plugs straight into {@link renderI18n}, which then resolves `de-AT` to `de` and falls
9
+ * back to the default locale.
10
+ *
11
+ * Only files are read, and only names built from the directory listing. The requested name must not
12
+ * contain a path separator or `..`, and a locale is never turned into a path, so a locale taken from
13
+ * user input (such as `Accept-Language`) only selects a key in the returned map.
14
+ * @param directory The directory that holds the template files.
15
+ * @param name The template name, without locale or extension.
16
+ * @param options The default locale and file extension. See {@link LocaleFileOptions}.
17
+ * @returns The file contents keyed by locale. See {@link LocaleTemplates}.
18
+ * @throws {EmailError} When the name is unsafe, the directory cannot be read, no file matches, the
19
+ * un-suffixed file has no `defaultLocale`, the default locale has both files, or two locales differ
20
+ * only by case.
21
+ * @example
22
+ * ```ts
23
+ * const templates = await loadLocaleTemplates("./emails", "welcome", { defaultLocale: "en" });
24
+ * await renderI18n(templates, "de-AT", renderHandlebars, { name: "Ada" }, { defaultLocale: "en" });
25
+ * ```
26
+ */
27
+ declare const loadLocaleTemplates: (directory: string, name: string, options?: LocaleFileOptions) => Promise<LocaleTemplates>;
28
+ /**
29
+ * Creates a renderer that loads a named template from disk for a locale and renders it with an
30
+ * engine, falling back to the default locale. Binding `defaultLocale` once here means the fallback
31
+ * cannot be left out of a call.
32
+ * @param directory The directory that holds the template files.
33
+ * @param renderer The text template engine, such as `renderHandlebars` or `renderMjml`.
34
+ * @param options The default locale and file extension. See {@link LocaleFileOptions}.
35
+ * @returns A {@link LocaleFileRenderer}.
36
+ * @example
37
+ * ```ts
38
+ * import { renderHandlebars } from "@visulima/email/template/handlebars";
39
+ * import { createLocaleFileRenderer } from "@visulima/email/template/locale-files";
40
+ *
41
+ * const render = createLocaleFileRenderer("./emails", renderHandlebars, { defaultLocale: "en" });
42
+ * await render("welcome", "fr", { name: "Ada" }); // reads welcome.html when there is no welcome.fr.html
43
+ * ```
44
+ */
45
+ declare const createLocaleFileRenderer: (directory: string, renderer: TemplateRenderer, options?: LocaleFileOptions) => LocaleFileRenderer;
46
+ /**
47
+ * Options for {@link loadLocaleTemplates} and {@link createLocaleFileRenderer}.
48
+ */
49
+ interface LocaleFileOptions {
50
+ /**
51
+ * Locale the un-suffixed file (`name` plus `extension`) is registered under. It is also the
52
+ * fallback for any locale without its own file. Required when that file exists.
53
+ */
54
+ defaultLocale?: string;
55
+ /**
56
+ * File extension, including the leading dot.
57
+ * @default ".html"
58
+ */
59
+ extension?: string;
60
+ }
61
+ /**
62
+ * Renders a named template from disk for a locale. Each call reads the directory again, so cache
63
+ * the result yourself if the templates do not change at runtime.
64
+ */
65
+ type LocaleFileRenderer = (name: string, locale: string, data?: Record<string, unknown>, rendererOptions?: Record<string, unknown>) => Promise<string>;
66
+ export { LocaleFileOptions, LocaleFileRenderer, createLocaleFileRenderer, loadLocaleTemplates };
@@ -0,0 +1 @@
1
+ import{createRequire as _}from"node:module";import l from"../packem_shared/EmailError-SXEFQEyA.js";import{renderI18n as y}from"./i18n.js";let L;const F=t=>(L??=_(import.meta.url))(t),d=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,w=t=>{if(typeof d<"u"&&d.versions&&d.versions.node){const[e,s]=d.versions.node.split(".").map(Number);if(e>22||e===22&&s>=3||e===20&&s>=16)return d.getBuiltinModule(t)}return F(t)},{readFile:h,readdir:g}=w("node:fs/promises"),{join:m}=w("node:path"),c="template-files",T=".html",b=/^[\da-z-]+$/i,v=["/","\\","\0",".."],p=t=>v.some(e=>t.includes(e)),E=(t,e)=>{if(t===""||p(t))throw new l(c,`Invalid template name "${t}"`);if(!e.startsWith(".")||e.length<2||p(e))throw new l(c,`Invalid template extension "${e}"`)},j=async t=>{try{return await g(t,{withFileTypes:!0})}catch(e){throw new l(c,`Cannot read template directory "${t}"`,{cause:e})}},N=(t,e,s)=>{const o=`${e}${s}`,n=`${e}.`,r=new Map;let i=!1;for(const a of t)if(a.isFile()){if(a.name===o)i=!0;else if(a.name.startsWith(n)&&a.name.endsWith(s)){const f=a.name.slice(n.length,a.name.length-s.length);b.test(f)&&r.set(f,a.name)}}return{hasBaseFile:i,localeFiles:r}},C=t=>{const e=new Map;for(const s of t){const o=e.get(s.toLowerCase());if(o!==void 0)throw new l(c,`Locales "${o}" and "${s}" differ only by case`);e.set(s.toLowerCase(),s)}},R=async(t,e)=>{const s=await Promise.all([...e].map(async([o,n])=>[o,await h(m(t,n),"utf8")]));return Object.fromEntries(s)},I=async(t,e,s={})=>{const o=s.extension??T,{defaultLocale:n}=s,r=`${e}${o}`;E(e,o);const{hasBaseFile:i,localeFiles:a}=N(await j(t),e,o),f=[...a.keys()];if(i&&n===void 0)throw new l(c,`"${r}" exists, so pass defaultLocale to register it`);if(!i&&f.length===0)throw new l(c,`No "${e}" template files with extension "${o}" in "${t}"`);if(C(f),i&&n!==void 0&&f.some($=>$.toLowerCase()===n.toLowerCase()))throw new l(c,`"${r}" and "${e}.${n}${o}" both exist; keep one`);const u=await R(t,a);return i&&n!==void 0&&(u[n]=await h(m(t,r),"utf8")),u},D=(t,e,s={})=>async(o,n,r,i)=>{const a=await I(t,o,s);return y(a,n,e,r,{defaultLocale:s.defaultLocale,rendererOptions:i})};export{D as createLocaleFileRenderer,I as loadLocaleTemplates};
@@ -1 +1 @@
1
- import n from"mjml";import t from"../packem_shared/EmailError-BIaHK1Uw.js";const f=async(a,o,r)=>{try{if(typeof a!="string")throw new TypeError("MJML template must be a string");const e=await n(a,{beautify:r?.beautify??!1,fonts:r?.fonts,keepComments:r?.keepComments??!0,minify:r?.minify??!1,validationLevel:r?.validationLevel??"soft"});if(e.errors&&e.errors.length>0){const s=e.errors.map(m=>m.message).join("; ");throw new t("mjml",`MJML validation errors: ${s}`)}return e.html}catch(e){throw e instanceof Error&&e.message.includes("Cannot find module")?new t("mjml","MJML is not installed. Please install it: pnpm add mjml",{cause:e}):e instanceof t?e:new t("mjml",`Failed to render MJML: ${e.message}`,{cause:e})}};export{f as default};
1
+ import n from"mjml";import t from"../packem_shared/EmailError-SXEFQEyA.js";const f=async(a,o,r)=>{try{if(typeof a!="string")throw new TypeError("MJML template must be a string");const e=await n(a,{beautify:r?.beautify??!1,fonts:r?.fonts,keepComments:r?.keepComments??!0,minify:r?.minify??!1,validationLevel:r?.validationLevel??"soft"});if(e.errors&&e.errors.length>0){const s=e.errors.map(m=>m.message).join("; ");throw new t("mjml",`MJML validation errors: ${s}`)}return e.html}catch(e){throw e instanceof Error&&e.message.includes("Cannot find module")?new t("mjml","MJML is not installed. Please install it: pnpm add mjml",{cause:e}):e instanceof t?e:new t("mjml",`Failed to render MJML: ${e.message}`,{cause:e})}};export{f as default};
@@ -1 +1 @@
1
- import{render as a}from"@react-email/render";import n from"../packem_shared/EmailError-BIaHK1Uw.js";const m=async(i,o,r)=>{try{const e=i,t=r?.pretty;return await(r?.plainText===!0?a(e,{plainText:!0,pretty:t}):a(e,{pretty:t}))}catch(e){throw e instanceof Error&&e.message.includes("Cannot find module")?new n("react-email","@react-email/render is not installed. Please install it: pnpm add @react-email/render",{cause:e}):new n("react-email",`Failed to render React Email component: ${e.message}`,{cause:e})}};export{m as default};
1
+ import{render as a}from"@react-email/render";import n from"../packem_shared/EmailError-SXEFQEyA.js";const m=async(i,o,r)=>{try{const e=i,t=r?.pretty;return await(r?.plainText===!0?a(e,{plainText:!0,pretty:t}):a(e,{pretty:t}))}catch(e){throw e instanceof Error&&e.message.includes("Cannot find module")?new n("react-email","@react-email/render is not installed. Please install it: pnpm add @react-email/render",{cause:e}):new n("react-email",`Failed to render React Email component: ${e.message}`,{cause:e})}};export{m as default};
@@ -1 +1 @@
1
- import{render as i}from"@vue-email/render";import t from"../packem_shared/EmailError-BIaHK1Uw.js";const o=async(a,n,r)=>{try{return await i(a,n??{},{htmlToTextOptions:r?.htmlToTextOptions,plainText:r?.plainText,pretty:r?.pretty})}catch(e){throw e instanceof Error&&e.message.includes("Cannot find module")?new t("vue-email","@vue-email/render is not installed. Please install it: pnpm add @vue-email/render",{cause:e}):new t("vue-email",`Failed to render Vue Email component: ${e.message}`,{cause:e})}};export{o as default};
1
+ import{render as i}from"@vue-email/render";import t from"../packem_shared/EmailError-SXEFQEyA.js";const o=async(a,n,r)=>{try{return await i(a,n??{},{htmlToTextOptions:r?.htmlToTextOptions,plainText:r?.plainText,pretty:r?.pretty})}catch(e){throw e instanceof Error&&e.message.includes("Cannot find module")?new t("vue-email","@vue-email/render is not installed. Please install it: pnpm add @vue-email/render",{cause:e}):new t("vue-email",`Failed to render Vue Email component: ${e.message}`,{cause:e})}};export{o as default};
@@ -1,12 +1,29 @@
1
- import { a as MockEmailEntry } from "../packem_shared/types.d-C5geVKes.js";
2
- import { E as EmailOptions } from "../packem_shared/types.d-B9295M4_.js";
3
- import { M as Mail } from "../packem_shared/mail.d-BdyyyQO5.js";
1
+ import { E as EmailOptions } from "../packem_shared/types.d-CyiC9uRC.js";
2
+ import { a as MockEmailEntry } from "../packem_shared/types.d-V_yuAOrx.js";
3
+ import { M as Mail } from "../packem_shared/mail.d-2u-30lWz.js";
4
4
  import 'node:buffer';
5
5
  import 'ical-generator';
6
6
  import "../packem_shared/types.d-I-KeZ5w2.js";
7
- import "../packem_shared/types.d-Cb5fls_X.js";
8
- import "../packem_shared/types.d-GKqPNk0I.js";
9
- import "../packem_shared/provider.d-Cx5YQJdP.js";
7
+ import "../packem_shared/types.d-A1Eea-p_.js";
8
+ import "../packem_shared/types.d-Chhzb-48.js";
9
+ import "../packem_shared/provider.d-BLLbEuQ8.js";
10
+ /**
11
+ * Returns the http(s) URLs in a message without duplicates, in this order: `href` targets of
12
+ * anchor and area elements in the HTML, bare URLs in the visible HTML text, then bare URLs in the
13
+ * plain text.
14
+ *
15
+ * Images, `link` and `base` elements, `script` and `style` contents, comments, and CSS `url()`
16
+ * values are not links, so they are not returned. Entities are decoded in HTML only. Only absolute
17
+ * http and https URLs are returned.
18
+ * @param source A plain-text string, or an object with the message's `html` and/or `text`. Pass
19
+ * markup as `{ html }`; a bare string is always treated as plain text.
20
+ * @returns The matching URLs.
21
+ * @example
22
+ * ```ts
23
+ * const [verifyUrl] = extractLinks(email.sent()[0]!.options);
24
+ * ```
25
+ */
26
+ declare const extractLinks: (source: string | Pick<EmailOptions, "html" | "text">) => string[];
10
27
  /**
11
28
  * A test harness around the in-memory mock provider: a ready-to-use {@link Mail} instance plus
12
29
  * helpers to inspect, wait on, and reset the captured outbox.
@@ -151,4 +168,4 @@ interface EmailSnapshot {
151
168
  * ```
152
169
  */
153
170
  declare const toEmailSnapshot: (source: EmailOptions | MockEmailEntry) => EmailSnapshot;
154
- export { type EmailSnapshot, type MatcherResult, type MatcherTarget, type TestEmail, createTestEmail, emailMatchers, registerEmailMatchers, toEmailSnapshot, toHaveSentMatching, toHaveSentTo, toHaveSentWithAttachment, toHaveSentWithSubject };
171
+ export { type EmailSnapshot, type MatcherResult, type MatcherTarget, type TestEmail, createTestEmail, emailMatchers, extractLinks, registerEmailMatchers, toEmailSnapshot, toHaveSentMatching, toHaveSentTo, toHaveSentWithAttachment, toHaveSentWithSubject };
@@ -1 +1 @@
1
- import{emailMatchers as a,registerEmailMatchers as o,toHaveSentMatching as r,toHaveSentTo as i,toHaveSentWithAttachment as m,toHaveSentWithSubject as h}from"../packem_shared/emailMatchers-DpwItmkN.js";import{toEmailSnapshot as c}from"../packem_shared/toEmailSnapshot-BOhTn44E.js";import{createTestEmail as s}from"../packem_shared/createTestEmail-gr512snl.js";export{s as createTestEmail,a as emailMatchers,o as registerEmailMatchers,c as toEmailSnapshot,r as toHaveSentMatching,i as toHaveSentTo,m as toHaveSentWithAttachment,h as toHaveSentWithSubject};
1
+ import{default as a}from"../packem_shared/extractLinks-BwE1SjNF.js";import{emailMatchers as r,registerEmailMatchers as i,toHaveSentMatching as m,toHaveSentTo as n,toHaveSentWithAttachment as c,toHaveSentWithSubject as h}from"../packem_shared/emailMatchers-DpwItmkN.js";import{toEmailSnapshot as S}from"../packem_shared/toEmailSnapshot-BOhTn44E.js";import{createTestEmail as l}from"../packem_shared/createTestEmail-B2Fz-Ifb.js";export{l as createTestEmail,r as emailMatchers,a as extractLinks,i as registerEmailMatchers,S as toEmailSnapshot,m as toHaveSentMatching,n as toHaveSentTo,c as toHaveSentWithAttachment,h as toHaveSentWithSubject};
@@ -1,4 +1,4 @@
1
- import { c as EmailAddress } from "../packem_shared/types.d-B9295M4_.js";
1
+ import { c as EmailAddress } from "../packem_shared/types.d-CyiC9uRC.js";
2
2
  import 'node:buffer';
3
3
  /**
4
4
  * Formats an email address as "Name &lt;email@example.com>" or just "email@example.com" if no name is provided.
@@ -1 +1 @@
1
- import{s as a}from"../packem_shared/format-address-Bb7xk8G_.js";import e from"../packem_shared/EmailError-BIaHK1Uw.js";import t from"./validation/validate-email.js";const n=i=>{if(!t(i.email))throw new e("email",`Invalid email address: ${i.email}`);if(i.name){const m=a(i.name);if(m)return`"${m}" <${i.email}>`}return i.email};export{n as default};
1
+ import{s as a}from"../packem_shared/format-address-Bb7xk8G_.js";import e from"../packem_shared/EmailError-SXEFQEyA.js";import t from"./validation/validate-email.js";const n=i=>{if(!t(i.email))throw new e("email",`Invalid email address: ${i.email}`);if(i.name){const m=a(i.name);if(m)return`"${m}" <${i.email}>`}return i.email};export{n as default};
@@ -1,4 +1,4 @@
1
- import { c as EmailAddress } from "../packem_shared/types.d-B9295M4_.js";
1
+ import { c as EmailAddress } from "../packem_shared/types.d-CyiC9uRC.js";
2
2
  import 'node:buffer';
3
3
  /**
4
4
  * Parses a string representation of an email address into an EmailAddress object.
@@ -1,4 +1,4 @@
1
- import { E as EmailOptions } from "../packem_shared/types.d-B9295M4_.js";
1
+ import { E as EmailOptions } from "../packem_shared/types.d-CyiC9uRC.js";
2
2
  import 'node:buffer';
3
3
  /**
4
4
  * Parses a raw EML / RFC 5322 MIME message back into {@link EmailOptions}.
@@ -1,4 +1,4 @@
1
- import { R as Result } from "../packem_shared/types.d-B9295M4_.js";
1
+ import { R as Result } from "../packem_shared/types.d-CyiC9uRC.js";
2
2
  import 'node:buffer';
3
3
  /**
4
4
  * A successful {@link Result} with its data narrowed to be present.
@@ -1 +1 @@
1
- import c from"../packem_shared/EmailError-BIaHK1Uw.js";const a=r=>r.success,t=r=>!r.success,n=r=>{if(r.success)return r.data;throw r.error instanceof Error?r.error:new c("result","Called unwrap() on a failed result",{cause:r.error})},o=(r,s)=>r.success?r.data:s,u=(r,s)=>r.success?{data:s(r.data),success:!0}:{error:r.error,success:!1},i=async r=>{try{return{data:await r(),success:!0}}catch(s){return{error:s,success:!1}}};export{t as isErr,a as isOk,u as mapOk,i as tryAsync,n as unwrap,o as unwrapOr};
1
+ import c from"../packem_shared/EmailError-SXEFQEyA.js";const a=r=>r.success,t=r=>!r.success,n=r=>{if(r.success)return r.data;throw r.error instanceof Error?r.error:new c("result","Called unwrap() on a failed result",{cause:r.error})},o=(r,s)=>r.success?r.data:s,u=(r,s)=>r.success?{data:s(r.data),success:!0}:{error:r.error,success:!1},i=async r=>{try{return{data:await r(),success:!0}}catch(s){return{error:s,success:!1}}};export{t as isErr,a as isOk,u as mapOk,i as tryAsync,n as unwrap,o as unwrapOr};
@@ -1,4 +1,4 @@
1
- import { F as FeatureFlags, E as EmailOptions } from "../../packem_shared/types.d-B9295M4_.js";
1
+ import { F as FeatureFlags, E as EmailOptions } from "../../packem_shared/types.d-CyiC9uRC.js";
2
2
  import 'node:buffer';
3
3
  /**
4
4
  * A single capability that a message uses but the target provider cannot represent.
@@ -1 +1 @@
1
- import{h as o}from"../../packem_shared/headers-to-record-ZOZ7Dubo.js";const d=e=>Array.isArray(e)&&e.length>0,l=(e,r)=>r.find(t=>e[t]!==void 0),c=[{detect:e=>d(e.attachments)?"attachments":void 0,feature:"attachments",label:"attachments"},{detect:e=>typeof e.html=="string"&&e.html.length>0?"html":void 0,feature:"html",label:"HTML content"},{detect:e=>e.replyTo?"replyTo":void 0,feature:"replyTo",label:"a reply-to address"},{detect:e=>e.headers!==void 0&&Object.keys(o(e.headers)).length>0?"headers":void 0,feature:"customHeaders",label:"custom headers"},{detect:e=>d(e.tags)?"tags":void 0,feature:"tagging",label:"tags"},{detect:e=>l(e,["scheduledAt","sendAt"]),feature:"scheduling",label:"scheduled delivery"},{detect:e=>l(e,["templateId","template"]),feature:"templates",label:"provider templates"}];function n(e,r){if(!r)return{supported:!0,violations:[]};const t=[];for(const a of c){if(r[a.feature]!==!1)continue;const s=a.detect(e);s!==void 0&&t.push({feature:a.feature,field:s,message:`Message uses ${a.label} (field "${s}") but the provider does not support the "${a.feature}" capability`})}return{supported:t.length===0,violations:t}}export{n as default};
1
+ import{h as l}from"../../packem_shared/headers-to-record-ZOZ7Dubo.js";const s=e=>Array.isArray(e)&&e.length>0,o=(e,d)=>d.find(t=>e[t]!==void 0),c=[{detect:e=>s(e.attachments)?"attachments":void 0,feature:"attachments",label:"attachments"},{detect:e=>typeof e.html=="string"&&e.html.length>0?"html":void 0,feature:"html",label:"HTML content"},{detect:e=>e.replyTo?"replyTo":void 0,feature:"replyTo",label:"a reply-to address"},{detect:e=>e.headers!==void 0&&Object.keys(l(e.headers)).length>0?"headers":void 0,feature:"customHeaders",label:"custom headers"},{detect:e=>s(e.tags)?"tags":void 0,feature:"tagging",label:"tags"},{detect:e=>e.idempotencyKey===void 0?void 0:"idempotencyKey",feature:"idempotency",label:"an idempotency key"},{detect:e=>o(e,["scheduledAt","sendAt"]),feature:"scheduling",label:"scheduled delivery"},{detect:e=>o(e,["templateId","template"]),feature:"templates",label:"provider templates"}];function n(e,d){if(!d)return{supported:!0,violations:[]};const t=[];for(const a of c){if(d[a.feature]!==!1)continue;const r=a.detect(e);r!==void 0&&t.push({feature:a.feature,field:r,message:`Message uses ${a.label} (field "${r}") but the provider does not support the "${a.feature}" capability`})}return{supported:t.length===0,violations:t}}export{n as default};
@@ -1 +1 @@
1
- import{verifyMailgunWebhook as r}from"../packem_shared/verifyMailgunWebhook-DS4o0kZL.js";import{verifyPostmarkWebhook as f}from"../packem_shared/verifyPostmarkWebhook-pLwWmeOl.js";import{verifySendGridWebhook as n}from"../packem_shared/verifySendGridWebhook-csuRasH3.js";import{isValidSigningCertUrl as d,verifySnsMessage as k}from"../packem_shared/isValidSigningCertUrl-CK1t2a7x.js";import{verifyResendWebhook as s,verifyStandardWebhook as v}from"../packem_shared/verifyResendWebhook-D3GuwWjv.js";export{d as isValidSigningCertUrl,r as verifyMailgunWebhook,f as verifyPostmarkWebhook,s as verifyResendWebhook,n as verifySendGridWebhook,k as verifySnsMessage,v as verifyStandardWebhook};
1
+ import{verifyMailgunWebhook as r}from"../packem_shared/verifyMailgunWebhook-DS4o0kZL.js";import{verifyPostmarkWebhook as f}from"../packem_shared/verifyPostmarkWebhook-pLwWmeOl.js";import{verifySendGridWebhook as n}from"../packem_shared/verifySendGridWebhook-0_3qKQA1.js";import{isValidSigningCertUrl as d,verifySnsMessage as k}from"../packem_shared/isValidSigningCertUrl-gnYr3oyD.js";import{verifyResendWebhook as s,verifyStandardWebhook as v}from"../packem_shared/verifyResendWebhook-D3GuwWjv.js";export{d as isValidSigningCertUrl,r as verifyMailgunWebhook,f as verifyPostmarkWebhook,s as verifyResendWebhook,n as verifySendGridWebhook,k as verifySnsMessage,v as verifyStandardWebhook};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visulima/email",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "description": "A comprehensive email library with multi-provider support, crypto utilities, and template engines",
5
5
  "keywords": [
6
6
  "ahasend",
@@ -122,6 +122,10 @@
122
122
  "types": "./dist/template-engines/i18n.d.ts",
123
123
  "default": "./dist/template-engines/i18n.js"
124
124
  },
125
+ "./template/locale-files": {
126
+ "types": "./dist/template-engines/locale-files.d.ts",
127
+ "default": "./dist/template-engines/locale-files.js"
128
+ },
125
129
  "./providers/autosend": {
126
130
  "types": "./dist/providers/autosend/index.d.ts",
127
131
  "default": "./dist/providers/autosend/index.js"
@@ -158,6 +162,10 @@
158
162
  "types": "./dist/providers/mailcrab/index.d.ts",
159
163
  "default": "./dist/providers/mailcrab/index.js"
160
164
  },
165
+ "./providers/file": {
166
+ "types": "./dist/providers/file/index.d.ts",
167
+ "default": "./dist/providers/file/index.js"
168
+ },
161
169
  "./providers/nodemailer": {
162
170
  "types": "./dist/providers/nodemailer/index.d.ts",
163
171
  "default": "./dist/providers/nodemailer/index.js"
@@ -1 +0,0 @@
1
- class l extends Error{loc;title;hint;type="VisulimaError";constructor({cause:t,hint:e,location:s,message:a,name:c,stack:r,title:h}){super(a,{cause:t}),this.title=h,this.name=c,this.stack=r??this.stack,this.loc=s,this.hint=e}setLocation(t){this.loc=t}setName(t){this.name=t}setMessage(t){this.message=t}setHint(t){this.hint=t}}class o extends l{component;code;constructor(t,e,s){super({cause:s?.cause,hint:s?.hint,message:`[@visulima/email] [${t}] ${e}`,name:"EmailError",title:`Email ${t} Error`}),this.component=t,this.code=s?.code}}export{o as default};
@@ -1 +0,0 @@
1
- import f from"./DraftMailMessage-B0gwf8n_.js";import v from"./EmailError-BIaHK1Uw.js";import u from"./MailMessage-DmmaNHHc.js";import{composeMiddleware as w}from"./composeMiddleware-B3RCDFsy.js";import{b as C}from"./build-mime-message-BCFKPqaW.js";import{c as M}from"./create-logger-CazqtXcL.js";import{h as d}from"./headers-to-record-ZOZ7Dubo.js";import T from"../utils/validation/check-feature-support.js";const S=(a,e,r)=>{e.success&&e.data?a&&a.info("Email sent successfully",{messageId:e.data.messageId,provider:e.data.provider}):a&&a.error("Email send failed",{error:e.error,provider:r})},E=async(a,e,r)=>{const t={...a,to:e.to};return e.cc&&(t.cc=e.cc),e.bcc&&(t.bcc=e.bcc),e.replyTo&&(t.replyTo=e.replyTo),e.headers&&(t.headers={...d(a.headers??{}),...d(e.headers)}),t.subject=e.subject??a.subject,r&&e.data&&(t.subject=await r(t.subject,e.data),a.html!==void 0&&(t.html=await r(a.html,e.data)),a.text!==void 0&&(t.text=await r(a.text,e.data))),t};class h{static extractErrorMessages(e){return e instanceof Error?[e.message]:[String(e??"Unknown error")]}provider;logger;loggerInstance;globalConfig;featureCheck;middlewares=[];composedSend;mountedProviders=new Map;constructor(e,r){this.provider=e,this.featureCheck=r?.featureCheck??"error"}setLogger(e){return this.loggerInstance=e,this.logger=M("Mail",e),this}use(e){return this.middlewares.push(e),this.composedSend=void 0,this}mount(e,r){return this.mountedProviders.set(e,r),this}setFrom(e){return this.globalConfig??={},this.globalConfig.from=e,this.logger&&this.logger.debug("Default from address updated",{from:e.email}),this}setReplyTo(e){return this.globalConfig??={},this.globalConfig.replyTo=e,this.logger&&this.logger.debug("Default reply-to address updated",{replyTo:e.email}),this}setHeaders(e){if(this.globalConfig??={},this.globalConfig.headers=e,this.logger){const r=d(e);this.logger.debug("Default headers updated",{count:Object.keys(r).length,headers:Object.keys(r)})}return this}async draft(e){let r=e instanceof u||e instanceof f?await this.buildDraftFromMessage(e):await this.buildDraftFromOptions(e);r=this.applyGlobalConfig(r);const t=r.headers?d(r.headers):{};r.headers={...t,"X-Unsent":"1"};const s=await C(r,{includeBcc:!0});return this.logger&&this.logger.debug("Draft created successfully in EML format",{from:r.from.email,size:s.length,subject:r.subject,to:Array.isArray(r.to)?r.to.length:1}),s}async send(e){if(e instanceof f)throw new TypeError("Cannot send draft messages. Convert to MailMessage first or remove X-Unsent header.");let r;if(e instanceof u)this.logger&&this.logger.debug("Sending MailMessage instance"),this.loggerInstance&&e.setLogger(this.loggerInstance),r=await e.build();else{const o=e;this.logger&&this.logger.debug("Sending email with options",{subject:o.subject,to:Array.isArray(o.to)?o.to.length:1}),r=o}r=this.applyGlobalConfig(r);const t=this.assertFeatureSupport(r,this.resolveProvider(r));if(t)return{error:t,success:!1};const s=await this.dispatch(r);return S(this.logger,s,this.provider.name??"unknown"),s}sendMany(e,r){const t=Math.max(1,Math.floor(r?.concurrency??1));return t===1?this.sendManySerial(e,r?.signal):this.sendManyConcurrent(e,t,r?.signal)}sendBatch(e,r,t){const s=async function*(){const o=e instanceof u?await e.build():e;for(const i of r)yield await E(o,i,t?.render)};return this.sendMany(s(),{concurrency:t?.concurrency,signal:t?.signal})}async sendOneToReceipt(e){const r=this.provider.name;try{const t=await this.send(e);return t.success&&t.data?{messageId:t.data.messageId,provider:t.data.provider??r,response:t.data.response,successful:!0,timestamp:t.data.timestamp}:{errorMessages:h.extractErrorMessages(t.error),provider:r,successful:!1}}catch(t){return{errorMessages:h.extractErrorMessages(t),provider:r,successful:!1}}}async*sendManySerial(e,r){const t=this.provider.name;let s=0,o=0,i=0;this.logger&&this.logger.debug("Starting batch email send",{provider:t});for await(const l of e){if(l instanceof f)throw new TypeError("Cannot send draft messages. Convert to MailMessage first or remove X-Unsent header.");if(r?.aborted){this.logger&&this.logger.warn("Batch send operation was aborted",{failed:i,processed:s,successful:o}),yield{errorMessages:["Send operation was aborted"],provider:t,successful:!1};return}s+=1;const n=await this.sendOneToReceipt(l);n.successful?o+=1:i+=1,yield n}this.logger&&this.logger.debug("Batch email send completed",{failureCount:i,processedCount:s,provider:t,successCount:o})}async*sendManyConcurrent(e,r,t){const s=this.provider.name;this.logger&&this.logger.debug("Starting concurrent batch email send",{concurrency:r,provider:s});const o=Symbol.asyncIterator in e?e[Symbol.asyncIterator]():e[Symbol.iterator](),i=new Map;let l=0,n=!1,p=!1;const m=async()=>{for(;!n&&!p&&i.size<r;){const g=await o.next();if(g.done){n=!0;break}const c=g.value;if(c instanceof f)throw new TypeError("Cannot send draft messages. Convert to MailMessage first or remove X-Unsent header.");const b=l;l+=1,i.set(b,this.sendOneToReceipt(c).then(y=>({id:b,receipt:y})))}};for(await m();i.size>0;){if(t?.aborted){p=!0,yield{errorMessages:["Send operation was aborted"],provider:s,successful:!1};return}const{id:g,receipt:c}=await Promise.race(i.values());i.delete(g),yield c,await m()}this.logger&&this.logger.debug("Concurrent batch email send completed",{processedCount:l,provider:s})}async dispatch(e){return this.middlewares.length===0?await this.resolveProvider(e).sendEmail(e):(this.composedSend??=w(this.middlewares,r=>Promise.resolve(this.resolveProvider(r).sendEmail(r))),await this.composedSend(e))}resolveProvider(e){return e.stream!==void 0?this.mountedProviders.get(e.stream)??this.provider:this.provider}assertFeatureSupport(e,r){if(this.featureCheck==="off")return;const{supported:t,violations:s}=T(e,r.features);if(t)return;const o=r.name??"unknown",i=s.map(n=>n.field),l=s.map(n=>n.message);if(this.featureCheck==="warn"){this.logger&&this.logger.warn("Message uses capabilities the provider does not support",{fields:i,provider:o});return}return this.logger&&this.logger.error("Message rejected by capability guard",{fields:i,provider:o}),new v(o,`Provider does not support the following message fields: ${i.join(", ")}`,{code:"UNSUPPORTED_FEATURES",hint:l})}async buildDraftFromMessage(e){return this.logger&&this.logger.debug("Creating draft from MailMessage instance"),this.loggerInstance&&e.setLogger(this.loggerInstance),this.applyGlobalConfigToMessage(e),await e.build()}async buildDraftFromOptions(e){return this.logger&&this.logger.debug("Creating draft from email options",{subject:e.subject,to:Array.isArray(e.to)?e.to.length:1}),e}applyGlobalConfigToMessage(e){const r=this.globalConfig?.from;r&&!e.getFrom()&&e.from(r);const t=this.globalConfig?.replyTo;if(t&&!e.getReplyTo()&&e.replyTo(t),this.globalConfig?.headers){const s=d(this.globalConfig.headers);Object.entries(s).forEach(([o,i])=>{e.header(o,i)})}}applyGlobalConfig(e){if(!this.globalConfig)return e;const r={...e};if(!r.from&&this.globalConfig.from&&(r.from=this.globalConfig.from,this.logger&&this.logger.debug("Applied global from address",{from:this.globalConfig.from.email})),!r.replyTo&&this.globalConfig.replyTo&&(r.replyTo=this.globalConfig.replyTo,this.logger&&this.logger.debug("Applied global reply-to address",{replyTo:this.globalConfig.replyTo.email})),this.globalConfig.headers){const t=d(this.globalConfig.headers),s=r.headers?d(r.headers):{};r.headers={...t,...s},this.logger&&this.logger.debug("Merged global headers",{globalCount:Object.keys(t).length,messageCount:Object.keys(s).length})}return r}}const O=(a,e)=>new h(a,e);export{h as Mail,O as createMail};
@@ -1 +0,0 @@
1
- import{createRequire as p}from"node:module";import{readFileAsBuffer as c,detectMimeType as d,generateContentId as u}from"./detectMimeType-BRz3Qjyz.js";import{c as T}from"./create-logger-CazqtXcL.js";import{h as l}from"./headers-to-record-ZOZ7Dubo.js";let y;const A=n=>(y??=p(import.meta.url))(n),o=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,f=n=>{if(typeof o<"u"&&o.versions&&o.versions.node){const[t,e]=o.versions.node.split(".").map(Number);if(t>22||t===22&&e>=3||t===20&&e>=16)return o.getBuiltinModule(n)}return A(n)},{basename:g}=f("node:path"),{fileURLToPath:b}=f("node:url"),m=async()=>(await import("../template-engines/html-to-text.js")).default,x=async()=>(await import("ical-generator")).default,a=n=>Array.isArray(n)?n.map(t=>typeof t=="string"?{email:t}:t):typeof n=="string"?[{email:n}]:[n];class D{fromAddress;toAddresses=[];ccAddresses=[];bccAddresses=[];subjectText="";textContent;textCharset="utf8";autoTextEnabled=!0;htmlContent;htmlCharset="utf8";dateValue;returnPathAddress;senderAddress;headers={};attachments=[];replyToAddress;priorityValue;tagsValue=[];signer;encrypter;logger;icalEventData;icalEventBuilder;from(t){return this.fromAddress=typeof t=="string"?{email:t}:t,this}to(t){return this.toAddresses.length=0,this.toAddresses.push(...a(t)),this}cc(t){return this.ccAddresses.length=0,this.ccAddresses.push(...a(t)),this}bcc(t){return this.bccAddresses.length=0,this.bccAddresses.push(...a(t)),this}subject(t){return this.subjectText=t,this}text(t,e="utf8"){return this.textContent=t,this.textCharset=e,this}html(t,e="utf8"){return this.htmlContent=t,this.htmlCharset=e,this}header(t,e){return this.headers[t]=e,this}setHeaders(t){const e=l(t);return Object.assign(this.headers,e),this}async attachFromPath(t,e){try{const r=await c(t),i=e?.filename??(g(t)||"attachment"),s=e?.contentType??d(i);this.attachments.push({cid:e?.cid,content:r,contentDisposition:e?.contentDisposition??"attachment",contentType:s,encoding:e?.encoding,filename:i,headers:e?.headers}),this.logger&&this.logger.debug(`Attachment added from path: ${t}`,{contentType:s,filename:i})}catch(r){throw this.logger&&this.logger.error(`Failed to attach file from path: ${t}`,r),r}return this}attachData(t,e){const r=e.contentType??d(e.filename);return this.attachments.push({cid:e.cid,content:t,contentDisposition:e.contentDisposition??"attachment",contentType:r,encoding:e.encoding,filename:e.filename,headers:e.headers}),this.logger&&this.logger.debug("Attachment added from data",{contentType:r,filename:e.filename}),this}async embedFromPath(t,e){try{const r=await c(t),i=e?.filename??(g(t)||"inline"),s=e?.contentType??d(i),h=u(i);return this.attachments.push({cid:h,content:r,contentDisposition:"inline",contentType:s,filename:i}),this.logger&&this.logger.debug(`Inline attachment embedded from path: ${t}`,{cid:h,contentType:s,filename:i}),h}catch(r){throw this.logger&&this.logger.error(`Failed to embed file from path: ${t}`,r),r}}embedData(t,e,r){const i=r?.contentType??d(e),s=u(e);return this.attachments.push({cid:s,content:t,contentDisposition:"inline",contentType:i,filename:e}),this.logger&&this.logger.debug("Inline attachment embedded from data",{cid:s,contentType:i,filename:e}),s}replyTo(t){return this.replyToAddress=typeof t=="string"?{email:t}:t,this}date(t){return this.dateValue=typeof t=="string"?new Date(t):t,this.logger&&this.logger.debug("Date header set",{date:this.dateValue.toISOString()}),this}returnPath(t){return this.returnPathAddress=typeof t=="string"?{email:t}:t,this.logger&&this.logger.debug("Return-path address set",{returnPath:this.returnPathAddress.email}),this}sender(t){return this.senderAddress=typeof t=="string"?{email:t}:t,this.logger&&this.logger.debug("Sender address set",{sender:this.senderAddress.email}),this}addTo(t){return this.toAddresses.push(...a(t)),this}addCc(t){return this.ccAddresses.push(...a(t)),this}addBcc(t){return this.bccAddresses.push(...a(t)),this}addReplyTo(t){const e=Array.isArray(t)?t:[t];if(e.length>0){const r=e[0];this.replyToAddress=typeof r=="string"?{email:r}:r}return this}addFrom(t){const e=Array.isArray(t)?t:[t];if(e.length>0){const r=e[0];this.fromAddress=typeof r=="string"?{email:r}:r}return this}priority(t){return this.priorityValue=t,this}tags(t){return this.tagsValue=Array.isArray(t)?t:[t],this}sign(t){return this.signer=t,this.logger&&this.logger.debug("Email signer configured"),this}encrypt(t){return this.encrypter=t,this.logger&&this.logger.debug("Email encrypter configured"),this}setLogger(t){return this.logger=T("MailMessage",t),this}icalEvent(t,e){return typeof t=="function"?(this.icalEventBuilder={build:t,options:e},this.icalEventData=void 0):(this.icalEventBuilder=void 0,this.icalEventData={content:t,...e}),this.logger&&this.logger.debug("Calendar event attached",{method:e?.method}),this}icalEventFromFile(t,e){const r=typeof t=="string"?t:b(t);return this.icalEventData={path:r,...e},this.logger&&this.logger.debug(`Calendar event attached from file: ${r}`,{method:e?.method}),this}icalEventFromUrl(t,e){return this.icalEventData={href:t,...e},this.logger&&this.logger.debug(`Calendar event attached from URL: ${t}`,{method:e?.method}),this}async view(t,e,r,i){try{const s=i?.autoText!==!1;this.autoTextEnabled=s,this.logger&&this.logger.debug("Rendering template",{autoText:s});const h=await t(e,r,i);this.html(h),s&&h&&await this.tryAutoGenerateText(h),this.logger&&this.logger.debug("Template rendered successfully")}catch(s){throw this.logger&&this.logger.error("Failed to render template",s),new Error(`Failed to render template: ${s.message}`)}return this}async viewText(t,e,r,i){try{this.logger&&this.logger.debug("Rendering text template");const s=await t(e,r,i);if(typeof s=="string")this.text(s),this.logger&&this.logger.debug("Text template rendered successfully");else throw new TypeError("Text renderer must return a string")}catch(s){throw this.logger&&this.logger.error("Failed to render text template",s),new Error(`Failed to render text template: ${s.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{const e=await m();this.textContent=e(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 t={from:this.fromAddress,html:this.htmlContent,subject:this.subjectText,text:this.textContent,to:this.toAddresses.length===1?this.toAddresses[0]:this.toAddresses};if(this.ccAddresses.length>0&&(t.cc=this.ccAddresses.length===1?this.ccAddresses[0]:this.ccAddresses),this.bccAddresses.length>0&&(t.bcc=this.bccAddresses.length===1?this.bccAddresses[0]:this.bccAddresses),Object.keys(this.headers).length>0&&(t.headers=this.headers),this.attachments.length>0&&(t.attachments=this.attachments,this.logger&&this.logger.debug(`Email includes ${this.attachments.length} attachment(s)`)),this.replyToAddress&&(t.replyTo=this.replyToAddress),this.priorityValue&&(t.priority=this.priorityValue),this.tagsValue.length>0&&(t.tags=this.tagsValue),this.icalEventBuilder){const r=(await x())();this.icalEventBuilder.build(r),this.icalEventData={content:r.toString(),...this.icalEventBuilder.options}}return this.icalEventData&&(t.icalEvent=this.icalEventData,this.logger&&this.logger.debug("Email includes calendar event")),this.dateValue&&(t.headers={...l(t.headers??{}),Date:this.dateValue.toUTCString()}),this.returnPathAddress&&(t.headers={...l(t.headers??{}),"Return-Path":this.returnPathAddress.email}),this.senderAddress&&(t.headers={...l(t.headers??{}),Sender:this.senderAddress.email}),this.signer&&(this.logger&&this.logger.debug("Signing email message"),t=await this.signer.sign(t),this.logger&&this.logger.debug("Email message signed successfully")),this.encrypter&&(this.logger&&this.logger.debug("Encrypting email message"),t=await this.encrypter.encrypt(t),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}),t}getSubject(){return this.subjectText}getFrom(){return this.fromAddress}getTo(){return[...this.toAddresses]}getCc(){return[...this.ccAddresses]}getBcc(){return[...this.bccAddresses]}getReplyTo(){return this.replyToAddress}getSender(){return this.senderAddress}getReturnPath(){return this.returnPathAddress}getDate(){return this.dateValue}getPriority(){return this.priorityValue}getTextBody(){return this.textContent}getTextCharset(){return this.textCharset}getHtmlBody(){return this.htmlContent}getHtmlCharset(){return this.htmlCharset}getAttachments(){return[...this.attachments]}async tryAutoGenerateText(t){try{const r=(await m())(t);r&&!this.textContent&&this.text(r),this.logger&&this.logger.debug("Auto-generated text content from HTML template")}catch(e){this.logger&&this.logger.warn("Failed to auto-generate text from HTML template",e)}}throwAndLogError(t,e){const r=new Error(t);throw this.logger&&this.logger.error(e??t),r}}export{D as default};
@@ -1 +0,0 @@
1
- const k=n=>1e3*2**(n-1),g=n=>{const{backoff:d=k,concurrency:v=1,maxAttempts:y=3,onDeadLetter:h,onError:u,onReserveError:m,pollInterval:p=1e3,queue:t,send:E}=n;let c=!1,i=0,s;const o=async(r,e)=>{try{await e()}catch(a){u?.(r,{error:a instanceof Error?a:new Error(String(a)),success:!1})}},l=async r=>{let e;try{e=await E(r.message)}catch(f){e={error:f instanceof Error?f:new Error(String(f)),success:!1}}if(e.success){await o(r,()=>t.ack(r.id));return}u?.(r,e);const a=r.attempts+1;if(a>=y){await o(r,()=>t.ack(r.id)),h?.(r,e);return}await o(r,()=>t.retry(r.id,d(a)))},w=async()=>{if(c){try{for(;i<v;){const r=await t.reserve();if(!r)break;i+=1,l(r).finally(()=>{i-=1}).catch(()=>{})}}catch(r){m?.(r instanceof Error?r:new Error(String(r)))}c&&(s=setTimeout(()=>{w().catch(()=>{})},p))}};return{drain:async()=>{let r=await t.reserve();for(;r;)await l(r),r=await t.reserve()},start:()=>{c||(c=!0,w().catch(()=>{}))},stop:()=>{c=!1,s&&(clearTimeout(s),s=void 0)}}};export{g as createWorker};
@@ -1 +0,0 @@
1
- import f from"./EmailError-BIaHK1Uw.js";const w=globalThis.Buffer!==void 0,T="ETIMEDOUT",g=async(i,s={},t)=>{const d=typeof i=="string"?new URL(i):i;try{const r=new Headers;s.headers&&Object.entries(s.headers).forEach(([e,u])=>{r.set(e,u)});const o={headers:r,method:s.method??(t?"POST":"GET")};t&&(typeof t=="string"||t instanceof Uint8Array?o.body=t:w&&t instanceof globalThis.Buffer?o.body=new Uint8Array(t):o.body=new Uint8Array(t));let a,n;s.timeout&&(a=new AbortController,n=setTimeout(()=>{a?.abort()},s.timeout),o.signal=a.signal);try{const e=await fetch(d.toString(),o);n&&clearTimeout(n);const m=(e.headers.get("content-type")??"").includes("application/json");let c;if(m)try{c=await e.json()}catch{c=await e.text()}else c=await e.text();const l=e.status>=200&&e.status<300,h={};return e.headers.forEach((y,E)=>{h[E]=y}),{data:{body:c,headers:h,statusCode:e.status},error:l?void 0:new f("http",`Request failed with status ${e.status}`,{code:e.status.toString()}),success:l}}catch(e){if(n&&clearTimeout(n),e instanceof Error&&e.name==="AbortError")return{error:new f("http",`Request timed out after ${s.timeout}ms`,{code:T}),success:!1};throw e}}catch(r){return{error:new f("http",`Request failed: ${r instanceof Error?r.message:String(r)}`,{cause:r instanceof Error?r:new Error(String(r))}),success:!1}}};export{T as R,g as m};
@@ -1 +0,0 @@
1
- import c from"./EmailError-BIaHK1Uw.js";import E from"./RequiredOptionError-ctDEEB1s.js";import{g as I}from"./generate-message-id-C5AM6DFE.js";import{h as w}from"./headers-to-record-ZOZ7Dubo.js";import{m as f}from"./make-request-Cyz5_yu8.js";import{r as g}from"./retry-aghj7y8m.js";import{v as T}from"./validate-email-options-DxdAVLlu.js";import{defineProvider as P}from"./defineProvider-C9q-7iwt.js";import{c as $,P as C,h as p}from"./provider-base-BEz00m9R.js";import{e as j,g as b,d as v}from"./address-formatter-w36S9ZYj.js";import{p as R}from"./attachment-processor-BJFDS343.js";const o="resend",S="https://api.resend.com",k=3e4,z=3,D=s=>{const r=[],d=/^[\w-]+$/;return d.test(s.name)||r.push(`Tag name '${s.name}' must only contain ASCII letters, numbers, underscores, or dashes`),s.name.length>256&&r.push(`Tag name '${s.name}' exceeds maximum length of 256 characters`),d.test(s.value)||r.push(`Tag value '${s.value}' for tag '${s.name}' must only contain ASCII letters, numbers, underscores, or dashes`),r},V=P((s={})=>{if(!s.apiKey)throw new E(o,"apiKey");const r={apiKey:s.apiKey,debug:s.debug??!1,endpoint:s.endpoint??S,logger:s.logger,retries:s.retries??z,timeout:s.timeout??k},d=new C,n=$(o,s.logger);return{endpoint:r.endpoint,features:{attachments:!0,batchSending:!0,customHeaders:!0,html:!0,replyTo:!0,scheduling:!0,tagging:!0,templates:!0,tracking:!0},async getEmail(e){try{if(!e)return{error:new c(o,"Email ID is required to retrieve email details"),success:!1};await d.ensureInitialized(()=>this.initialize(),o);const t={Authorization:`Bearer ${r.apiKey}`,"Content-Type":"application/json"};n.debug("Retrieving email details",{id:e});const a=await g(async()=>f(`${r.endpoint}/emails/${e}`,{headers:t,method:"GET",timeout:r.timeout}),r.retries);return a.success?(n.debug("Email details retrieved successfully"),{data:a.data?.body,success:!0}):(n.debug("API request failed when retrieving email",a.error),{error:new c(o,`Failed to retrieve email: ${a.error instanceof Error?a.error.message:"Unknown error"}`,{cause:a.error}),success:!1})}catch(t){return{error:p(o,"retrieve email",t,n),success:!1}}},async initialize(){await d.ensureInitialized(async()=>{if(!await this.isAvailable())throw new c(o,"Resend API not available or invalid API key");n.debug("Provider initialized successfully")},o)},async isAvailable(){try{if(r.apiKey&&r.apiKey.startsWith("re_"))return n.debug("API key format is valid, assuming Resend is available"),!0;const e={Authorization:`Bearer ${r.apiKey}`,"Content-Type":"application/json"};n.debug("Checking Resend API availability");const t=await f(`${r.endpoint}/domains`,{headers:e,method:"GET",timeout:r.timeout});return t.data&&typeof t.data=="object"&&"body"in t.data&&t.data.body&&typeof t.data.body=="object"&&"name"in t.data.body&&t.data.body.name==="restricted_api_key"?(n.debug("API key is valid but restricted to only sending emails"),!0):(n.debug("Resend API availability check response:",{error:t.error instanceof Error?t.error.message:void 0,statusCode:t.data?.statusCode,success:t.success}),!!(t.success&&t.data&&typeof t.data=="object"&&"statusCode"in t.data&&typeof t.data.statusCode=="number"&&t.data.statusCode>=200&&t.data.statusCode<300))}catch(e){return n.debug("Error checking availability:",e),!1}},name:o,options:r,async sendEmail(e){try{const t=T(e);if(t.length>0)return{error:new c(o,`Invalid email options: ${t.join(", ")}`),success:!1};await d.ensureInitialized(()=>this.initialize(),o);const a={from:b(e.from),headers:e.headers?w(e.headers):{},html:e.html,subject:e.subject,text:e.text,to:j(e.to)};if(e.cc&&(a.cc=v(e.cc)),e.bcc&&(a.bcc=v(e.bcc)),e.replyTo&&(a.reply_to=b(e.replyTo)),e.templateId&&(a.template=e.templateId,e.templateData&&(a.data=e.templateData)),e.scheduledAt&&(a.scheduled_at=typeof e.scheduledAt=="string"?e.scheduledAt:e.scheduledAt.toISOString()),e.tags&&e.tags.length>0){const i=[];if(e.tags.forEach(l=>{const y=D(l);y.length>0&&i.push(...y)}),i.length>0)return{error:new c(o,`Invalid email tags: ${i.join(", ")}`),success:!1};a.tags=e.tags.map(l=>({name:l.name,value:l.value}))}e.attachments&&e.attachments.length>0&&(a.attachments=await Promise.all(e.attachments.map(async i=>({content:await R(i,o),content_type:i.contentType,filename:i.filename,...i.path&&{path:i.path}})))),n.debug("Sending email via Resend API",{subject:a.subject,to:a.to});const A={Authorization:`Bearer ${r.apiKey}`,"Content-Type":"application/json"},u=await g(async()=>f(`${r.endpoint}/emails`,{headers:A,method:"POST",timeout:r.timeout},JSON.stringify(a)),r.retries);if(!u.success)return n.debug("API request failed when sending email",u.error),{error:u.error??new c(o,"Failed to send email"),success:!1};const m=u.data,h=m?.body&&typeof m.body=="object"&&m.body.id?m.body.id:I();return n.debug("Email sent successfully",{messageId:h}),{data:{messageId:h,provider:o,response:u.data,sent:!0,timestamp:new Date},success:!0}}catch(t){return{error:p(o,"send email",t,n),success:!1}}},async validateCredentials(){return this.isAvailable()}}});export{V as default};