@visulima/email 1.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/README.md +48 -5
- package/dist/index.js +1 -1
- package/dist/middleware/index.d.ts +13 -5
- package/dist/middleware/index.js +1 -1
- package/dist/packem_shared/{Mail-C1zp-fqt.js → Mail-CYAwMRnX.js} +1 -1
- package/dist/packem_shared/autoSendProvider-Bz3g6aZi.js +1 -0
- package/dist/packem_shared/{awsSesProvider-BGZJUIep.js → awsSesProvider-962hIcbo.js} +1 -1
- package/dist/packem_shared/{build-mime-message-B6927SQy.js → build-mime-message-BW5FQ2Bt.js} +1 -1
- package/dist/packem_shared/{cloudflareEmailProvider-Bv-ty-74.js → cloudflareEmailProvider-B8mOEl3N.js} +1 -1
- package/dist/packem_shared/create-token-cache-D56KQGVQ.js +1 -0
- package/dist/packem_shared/{createTestEmail-D3XPGiHY.js → createTestEmail-f12yLlWn.js} +1 -1
- package/dist/packem_shared/{mailCrabProvider-CPtBT6Th.js → mailCrabProvider-DRE5snM9.js} +1 -1
- package/dist/packem_shared/{mailPaceProvider-CJEvOFBh.js → mailPaceProvider-DZent57J.js} +1 -1
- package/dist/packem_shared/{mandrillProvider-DWPFh_Sj.js → mandrillProvider-DSsAknmA.js} +1 -1
- package/dist/packem_shared/oauth2Middleware-DQh-hIkv.js +1 -0
- package/dist/packem_shared/outlook365Provider-CkjXkY-7.js +1 -0
- package/dist/packem_shared/{plunkProvider-yTlrJVEp.js → plunkProvider-D3T2T-2V.js} +1 -1
- package/dist/packem_shared/{postmarkProvider-CDx85Yge.js → postmarkProvider-vspVSSOp.js} +1 -1
- package/dist/packem_shared/{sanitize-header-DJhgZl3y.js → sanitize-header-DtQwAnzg.js} +1 -1
- package/dist/packem_shared/{smtpProvider-Bm0yP97W.js → smtpProvider-5MI3-EPh.js} +1 -1
- package/dist/packem_shared/{zeptomailProvider-DKBPSHPj.js → zeptomailProvider-DUesgbC-.js} +1 -1
- package/dist/providers/autosend/index.d.ts +56 -0
- package/dist/providers/autosend/index.js +1 -0
- package/dist/providers/aws-ses/index.js +1 -1
- package/dist/providers/cloudflare-email/index.js +1 -1
- package/dist/providers/mailcrab/index.js +1 -1
- package/dist/providers/mailpace/index.js +1 -1
- package/dist/providers/mandrill/index.js +1 -1
- package/dist/providers/outlook365/index.d.ts +83 -5
- package/dist/providers/outlook365/index.js +1 -1
- package/dist/providers/plunk/index.js +1 -1
- package/dist/providers/postmark/index.js +1 -1
- package/dist/providers/smtp/index.js +1 -1
- package/dist/providers/zeptomail/index.js +1 -1
- package/dist/render/cid.d.ts +17 -0
- package/dist/render/css-inline.d.ts +15 -0
- package/dist/render/css-inline.js +1 -0
- package/dist/render/dark-mode.d.ts +20 -0
- package/dist/render/preheader.d.ts +24 -0
- package/dist/test/index.js +1 -1
- package/package.json +35 -19
- package/dist/packem_shared/inlineCss-BQ7d2Cyz.js +0 -1
- package/dist/packem_shared/oauth2Middleware-CodqXkyj.js +0 -1
- package/dist/packem_shared/outlook365Provider-63ggDpqV.js +0 -1
- package/dist/packem_shared/postProcessHtml-DqKi2Ucg.js +0 -1
- package/dist/render/index.d.ts +0 -107
- package/dist/render/index.js +0 -1
- /package/dist/{packem_shared/extractCidReferences-BuoTREMp.js → render/cid.js} +0 -0
- /package/dist/{packem_shared/addDarkModeSupport-DZYxULjl.js → render/dark-mode.js} +0 -0
- /package/dist/{packem_shared/injectPreheader-CF9WHZ7J.js → render/preheader.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
+
## @visulima/email [2.1.0](https://github.com/visulima/visulima/compare/%40visulima%2Femail%402.0.0...%40visulima%2Femail%402.1.0) (2026-07-17)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Dependencies
|
|
5
|
+
|
|
6
|
+
* **@visulima/fs:** upgraded to 5.0.3
|
|
7
|
+
|
|
8
|
+
## @visulima/email [2.0.0](https://github.com/visulima/visulima/compare/%40visulima%2Femail%401.0.1...%40visulima%2Femail%402.0.0) (2026-07-15)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### ⚠ BREAKING CHANGES
|
|
12
|
+
|
|
13
|
+
* **email:** the `@visulima/email/render` barrel and the `postProcessHtml` helper (with `PostProcessOptions`) have been removed, and the CSS inliner now resolves the optional `juice` peer at import time.
|
|
14
|
+
|
|
15
|
+
**Why:** importing `@visulima/email` eagerly evaluated `createRequire(import.meta.url)` from the render `css-inline` module, which the bundler folds into the shared chunk that the package root pulls. In bundled non-Node runtimes (Cloudflare Workers/workerd, Vite production builds) `import.meta.url` is `undefined`, so the worker crashed at boot — before serving any request — even for consumers that never inline CSS ([#723](https://github.com/visulima/visulima/issues/723)).
|
|
16
|
+
|
|
17
|
+
**Migration:**
|
|
18
|
+
|
|
19
|
+
1. Import each render helper from its own subpath instead of the removed `@visulima/email/render` barrel:
|
|
20
|
+
|
|
21
|
+
```diff
|
|
22
|
+
- import { injectPreheader, addDarkModeSupport, rewriteCidLinks, extractCidReferences } from "@visulima/email/render";
|
|
23
|
+
+ import { injectPreheader } from "@visulima/email/render/preheader";
|
|
24
|
+
+ import { addDarkModeSupport } from "@visulima/email/render/dark-mode";
|
|
25
|
+
+ import { rewriteCidLinks, extractCidReferences } from "@visulima/email/render/cid";
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
2. Replace `postProcessHtml(...)` by composing the helpers yourself (order: inline CSS → preheader → dark-mode → CID rewrite):
|
|
29
|
+
|
|
30
|
+
```diff
|
|
31
|
+
- import { postProcessHtml } from "@visulima/email/render";
|
|
32
|
+
-
|
|
33
|
+
- const out = postProcessHtml(html, { inlineCss: true, preheader: "Hi", darkMode: true });
|
|
34
|
+
+ import inlineCss from "@visulima/email/render/css-inline";
|
|
35
|
+
+ import { injectPreheader } from "@visulima/email/render/preheader";
|
|
36
|
+
+ import { addDarkModeSupport } from "@visulima/email/render/dark-mode";
|
|
37
|
+
+
|
|
38
|
+
+ const out = addDarkModeSupport(injectPreheader(inlineCss(html), "Hi"));
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Read the nesting inside-out to follow the pipeline order: `inlineCss` runs first, then `injectPreheader`, then `addDarkModeSupport`. CID rewriting is intentionally omitted here because the original `postProcessHtml` call set no `cidResolver`; if you used one, apply it as the outermost call — `rewriteCidLinks(out, resolver)`.
|
|
42
|
+
|
|
43
|
+
3. `inlineCss` now lives at `@visulima/email/render/css-inline` (default export) and imports `juice` eagerly. Install the optional peer (`pnpm add juice`) before importing it — a missing peer now surfaces as a module-resolution error at import time rather than an `EmailError` on first call. Import it only where you actually inline CSS so `juice` (a Node-only library) stays out of edge/worker bundles.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Bug Fixes
|
|
47
|
+
|
|
48
|
+
* **email:** make render exports worker-safe ([#726](https://github.com/visulima/visulima/pull/726)) ([2d09b49](https://github.com/visulima/visulima/commit/2d09b4985e78c71c208b12b3507659b9ab5594cf))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
### Dependencies
|
|
52
|
+
|
|
53
|
+
* **@visulima/fs:** upgraded to 5.0.2
|
|
54
|
+
|
|
1
55
|
## @visulima/email [1.0.1](https://github.com/visulima/visulima/compare/%40visulima%2Femail%401.0.0...%40visulima%2Femail%401.0.1) (2026-07-15)
|
|
2
56
|
|
|
3
57
|
|
package/README.md
CHANGED
|
@@ -341,6 +341,33 @@ const roundRobin = roundRobinProvider({
|
|
|
341
341
|
|
|
342
342
|
**Note:** The round-robin provider starts at a random provider and then rotates through providers for each subsequent email. If a provider is unavailable, it will automatically try the next provider in the rotation.
|
|
343
343
|
|
|
344
|
+
### AutoSend Provider
|
|
345
|
+
|
|
346
|
+
AutoSend is a transactional and marketing email service with volume-based pricing. Universal runtime (Fetch API).
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
import { createMail } from "@visulima/email";
|
|
350
|
+
import { autoSendProvider } from "@visulima/email/providers/autosend";
|
|
351
|
+
|
|
352
|
+
const mail = createMail(
|
|
353
|
+
autoSendProvider({
|
|
354
|
+
apiKey: process.env.AUTOSEND_API_KEY,
|
|
355
|
+
}),
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
await mail.send({
|
|
359
|
+
from: { email: "hello@mail.yourdomain.com", name: "Your Company" },
|
|
360
|
+
to: { email: "customer@example.com", name: "Jane Doe" },
|
|
361
|
+
subject: "Welcome!",
|
|
362
|
+
html: "<h1>Thanks for signing up.</h1>",
|
|
363
|
+
// AutoSend-specific: templateId, dynamicData, trackingOpen, trackingClick,
|
|
364
|
+
// unsubscribeGroupId, bypassSuppressions
|
|
365
|
+
});
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
AutoSend's send endpoint takes a **single** `to` recipient (max 50 across to/cc/bcc). Passing several
|
|
369
|
+
is rejected rather than silently truncated.
|
|
370
|
+
|
|
344
371
|
### Mailgun Provider
|
|
345
372
|
|
|
346
373
|
Mailgun is a developer-friendly email API service built for transactional emails and automated messaging.
|
|
@@ -955,16 +982,32 @@ await mail.send({
|
|
|
955
982
|
|
|
956
983
|
### Outlook365 Provider
|
|
957
984
|
|
|
958
|
-
Sends through Microsoft Graph `sendMail`. Universal runtime (Fetch API).
|
|
959
|
-
|
|
985
|
+
Sends through Microsoft Graph `sendMail`. Universal runtime (Fetch API). No auth SDK is bundled — authenticate with
|
|
986
|
+
the built-in app-only (client credentials) or delegated (refresh token) flow, or bring your own OAuth2 token
|
|
987
|
+
(`Mail.Send` scope) via `accessToken` / `getAccessToken`.
|
|
960
988
|
|
|
961
989
|
```typescript
|
|
962
|
-
import { createMail
|
|
990
|
+
import { createMail } from "@visulima/email";
|
|
991
|
+
import { outlook365Provider } from "@visulima/email/providers/outlook365";
|
|
963
992
|
|
|
993
|
+
// App-only (client credentials) — userId is required, app-only tokens have no "me" mailbox.
|
|
964
994
|
const mail = createMail(
|
|
965
995
|
outlook365Provider({
|
|
966
|
-
|
|
967
|
-
|
|
996
|
+
tenantId: process.env.AZURE_TENANT_ID,
|
|
997
|
+
clientId: process.env.AZURE_CLIENT_ID,
|
|
998
|
+
clientSecret: process.env.AZURE_CLIENT_SECRET,
|
|
999
|
+
userId: "sender@contoso.com",
|
|
1000
|
+
}),
|
|
1001
|
+
);
|
|
1002
|
+
|
|
1003
|
+
// Delegated (refresh token) — sends as the consenting user.
|
|
1004
|
+
const delegated = createMail(
|
|
1005
|
+
outlook365Provider({
|
|
1006
|
+
tenantId: process.env.AZURE_TENANT_ID,
|
|
1007
|
+
clientId: process.env.AZURE_CLIENT_ID,
|
|
1008
|
+
refreshToken: await loadRefreshToken(),
|
|
1009
|
+
// Azure AD may rotate the refresh token; this is awaited before the token is used.
|
|
1010
|
+
onRefreshToken: async (token) => saveRefreshToken(token),
|
|
968
1011
|
}),
|
|
969
1012
|
);
|
|
970
1013
|
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{detectMimeType as a,generateContentId as t,readFileAsBuffer as o}from"./packem_shared/detectMimeType-DFH3kp6K.js";import{default as p}from"./packem_shared/DraftMailMessage-CGTVuy2z.js";import{default as m}from"./packem_shared/EmailError-B-sEf3bp.js";import{default as l}from"./packem_shared/RequiredOptionError-DuNNY_a5.js";import{Mail as u,createMail as x}from"./packem_shared/Mail-
|
|
1
|
+
import{detectMimeType as a,generateContentId as t,readFileAsBuffer as o}from"./packem_shared/detectMimeType-DFH3kp6K.js";import{default as p}from"./packem_shared/DraftMailMessage-CGTVuy2z.js";import{default as m}from"./packem_shared/EmailError-B-sEf3bp.js";import{default as l}from"./packem_shared/RequiredOptionError-DuNNY_a5.js";import{Mail as u,createMail as x}from"./packem_shared/Mail-CYAwMRnX.js";import{default as M}from"./packem_shared/MailMessage-DQRgs5Dz.js";import{defineProvider as E}from"./packem_shared/defineProvider-C9q-7iwt.js";import{default as g}from"./utils/parse-eml.js";import{isErr as y,isOk as w,mapOk as A,tryAsync as F,unwrap as h,unwrapOr as q}from"./utils/result.js";import{default as B}from"./utils/validation/check-feature-support.js";export{p as DraftMailMessage,m as EmailError,u as Mail,M as MailMessage,l as RequiredOptionError,B as checkFeatureSupport,x as createMail,E as defineProvider,a as detectMimeType,t as generateContentId,y as isErr,w as isOk,A as mapOk,g as parseEml,o as readFileAsBuffer,F as tryAsync,h as unwrap,q as unwrapOr};
|
|
@@ -150,9 +150,9 @@ interface MetricsMiddlewareOptions {
|
|
|
150
150
|
*/
|
|
151
151
|
declare const withMetrics: (recorder: MetricsRecorder, options?: MetricsMiddlewareOptions) => Middleware;
|
|
152
152
|
/**
|
|
153
|
-
*
|
|
153
|
+
* A token that may carry an absolute expiry.
|
|
154
154
|
*/
|
|
155
|
-
interface
|
|
155
|
+
interface CacheableToken {
|
|
156
156
|
/**
|
|
157
157
|
* The bearer access token.
|
|
158
158
|
*/
|
|
@@ -163,6 +163,13 @@ interface OAuth2Token {
|
|
|
163
163
|
expiresAt?: number;
|
|
164
164
|
}
|
|
165
165
|
/**
|
|
166
|
+
* Options for {@link createTokenCache}.
|
|
167
|
+
*/
|
|
168
|
+
/**
|
|
169
|
+
* An OAuth2 access token plus its expiry.
|
|
170
|
+
*/
|
|
171
|
+
type OAuth2Token = CacheableToken;
|
|
172
|
+
/**
|
|
166
173
|
* Options for the {@link oauth2Middleware}.
|
|
167
174
|
*/
|
|
168
175
|
interface OAuth2MiddlewareOptions {
|
|
@@ -250,16 +257,17 @@ declare const RATE_LIMIT_PRESETS: {
|
|
|
250
257
|
declare const rateLimitMiddleware: (options: RateLimitMiddlewareOptions) => Middleware;
|
|
251
258
|
/**
|
|
252
259
|
* Wraps a transform that rewrites the message before it is sent — e.g. running the HTML through the
|
|
253
|
-
*
|
|
260
|
+
* render helpers (preheader, dark-mode, CID rewrite) and the opt-in CSS inliner.
|
|
254
261
|
* @param transform Maps the resolved {@link EmailOptions} to its rendered form.
|
|
255
262
|
* @returns A middleware that applies the transform ahead of the provider call.
|
|
256
263
|
* @example
|
|
257
264
|
* ```ts
|
|
258
|
-
* import {
|
|
265
|
+
* import { injectPreheader } from "@visulima/email/render/preheader";
|
|
266
|
+
* import { addDarkModeSupport } from "@visulima/email/render/dark-mode";
|
|
259
267
|
*
|
|
260
268
|
* mail.use(withRender(async (email) => ({
|
|
261
269
|
* ...email,
|
|
262
|
-
* html: email.html ?
|
|
270
|
+
* html: email.html ? addDarkModeSupport(injectPreheader(email.html, "Hi!")) : email.html,
|
|
263
271
|
* })));
|
|
264
272
|
* ```
|
|
265
273
|
*/
|
package/dist/middleware/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{circuitBreakerMiddleware as o}from"../packem_shared/circuitBreakerMiddleware-D2SA_07f.js";import{dedupeMiddleware as i}from"../packem_shared/dedupeMiddleware-B6ajWOTi.js";import{withEvents as p}from"../packem_shared/withEvents-CCVblSqj.js";import{loggingMiddleware as f}from"../packem_shared/loggingMiddleware-CZIiVMVp.js";import{withMetrics as w}from"../packem_shared/withMetrics-DWywX_6M.js";import{oauth2Middleware as l}from"../packem_shared/oauth2Middleware-
|
|
1
|
+
import{circuitBreakerMiddleware as o}from"../packem_shared/circuitBreakerMiddleware-D2SA_07f.js";import{dedupeMiddleware as i}from"../packem_shared/dedupeMiddleware-B6ajWOTi.js";import{withEvents as p}from"../packem_shared/withEvents-CCVblSqj.js";import{loggingMiddleware as f}from"../packem_shared/loggingMiddleware-CZIiVMVp.js";import{withMetrics as w}from"../packem_shared/withMetrics-DWywX_6M.js";import{oauth2Middleware as l}from"../packem_shared/oauth2Middleware-DQh-hIkv.js";import{RATE_LIMIT_PRESETS as h,rateLimitMiddleware as s}from"../packem_shared/RATE_LIMIT_PRESETS-BPADLgcA.js";import{default as c}from"../packem_shared/withRender-VP9wE5Yr.js";import{retryMiddleware as E}from"../packem_shared/retryMiddleware-DbViDX4r.js";import{withSuppression as g}from"../packem_shared/withSuppression-DBaZDMAr.js";import{withTelemetry as S}from"../packem_shared/withTelemetry-DL2qo0Hp.js";import{composeMiddleware as I}from"../packem_shared/composeMiddleware-BHwLLEzK.js";export{h as RATE_LIMIT_PRESETS,o as circuitBreakerMiddleware,I as composeMiddleware,i as dedupeMiddleware,f as loggingMiddleware,l as oauth2Middleware,s as rateLimitMiddleware,E as retryMiddleware,p as withEvents,w as withMetrics,c as withRender,g as withSuppression,S as withTelemetry};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import h from"./DraftMailMessage-CGTVuy2z.js";import v from"./EmailError-B-sEf3bp.js";import f from"./MailMessage-DQRgs5Dz.js";import{composeMiddleware as C}from"./composeMiddleware-BHwLLEzK.js";import{F as w}from"./build-mime-message-
|
|
1
|
+
import h from"./DraftMailMessage-CGTVuy2z.js";import v from"./EmailError-B-sEf3bp.js";import f from"./MailMessage-DQRgs5Dz.js";import{composeMiddleware as C}from"./composeMiddleware-BHwLLEzK.js";import{F as w}from"./build-mime-message-BW5FQ2Bt.js";import{c as M}from"./create-logger-BQTS3f-I.js";import{s as l}from"./headers-to-record-De1Eg951.js";import T from"../utils/validation/check-feature-support.js";const S=(i,e,r)=>{e.success&&e.data?i&&i.info("Email sent successfully",{messageId:e.data.messageId,provider:e.data.provider}):i&&i.error("Email send failed",{error:e.error,provider:r})},j=async(i,e,r)=>{const s={...i,to:e.to};return e.cc&&(s.cc=e.cc),e.bcc&&(s.bcc=e.bcc),e.replyTo&&(s.replyTo=e.replyTo),e.headers&&(s.headers={...l(i.headers??{}),...l(e.headers)}),s.subject=e.subject??i.subject,r&&e.data&&(s.subject=await r(s.subject,e.data),i.html!==void 0&&(s.html=await r(i.html,e.data)),i.text!==void 0&&(s.text=await r(i.text,e.data))),s};class u{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=l(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 f||e instanceof h?await this.buildDraftFromMessage(e):await this.buildDraftFromOptions(e);r=this.applyGlobalConfig(r);const s=r.headers?l(r.headers):{};r.headers={...s,"X-Unsent":"1"};const t=await w(r,{includeBcc:!0});return this.logger&&this.logger.debug("Draft created successfully in EML format",{from:r.from.email,size:t.length,subject:r.subject,to:Array.isArray(r.to)?r.to.length:1}),t}async send(e){if(e instanceof h)throw new TypeError("Cannot send draft messages. Convert to MailMessage first or remove X-Unsent header.");let r;if(e instanceof f)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 s=this.assertFeatureSupport(r);if(s)return{error:s,success:!1};const t=await this.dispatch(r);return S(this.logger,t,this.provider.name??"unknown"),t}sendMany(e,r){const s=Math.max(1,Math.floor(r?.concurrency??1));return s===1?this.sendManySerial(e,r?.signal):this.sendManyConcurrent(e,s,r?.signal)}sendBatch(e,r,s){const t=async function*(){const o=e instanceof f?await e.build():e;for(const a of r)yield await j(o,a,s?.render)};return this.sendMany(t(),{concurrency:s?.concurrency,signal:s?.signal})}async sendOneToReceipt(e){const r=this.provider.name;try{const s=await this.send(e);return s.success&&s.data?{messageId:s.data.messageId,provider:s.data.provider??r,response:s.data.response,successful:!0,timestamp:s.data.timestamp}:{errorMessages:u.extractErrorMessages(s.error),provider:r,successful:!1}}catch(s){return{errorMessages:u.extractErrorMessages(s),provider:r,successful:!1}}}async*sendManySerial(e,r){const s=this.provider.name;let t=0,o=0,a=0;this.logger&&this.logger.debug("Starting batch email send",{provider:s});for await(const n of e){if(n instanceof h)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:a,processed:t,successful:o}),yield{errorMessages:["Send operation was aborted"],provider:s,successful:!1};return}t+=1;const g=await this.sendOneToReceipt(n);g.successful?o+=1:a+=1,yield g}this.logger&&this.logger.debug("Batch email send completed",{failureCount:a,processedCount:t,provider:s,successCount:o})}async*sendManyConcurrent(e,r,s){const t=this.provider.name;this.logger&&this.logger.debug("Starting concurrent batch email send",{concurrency:r,provider:t});const o=Symbol.asyncIterator in e?e[Symbol.asyncIterator]():e[Symbol.iterator](),a=new Map;let n=0,g=!1,p=!1;const m=async()=>{for(;!g&&!p&&a.size<r;){const d=await o.next();if(d.done){g=!0;break}const c=d.value;if(c instanceof h)throw new TypeError("Cannot send draft messages. Convert to MailMessage first or remove X-Unsent header.");const b=n;n+=1,a.set(b,this.sendOneToReceipt(c).then(y=>({id:b,receipt:y})))}};for(await m();a.size>0;){if(s?.aborted){p=!0,yield{errorMessages:["Send operation was aborted"],provider:t,successful:!1};return}const{id:d,receipt:c}=await Promise.race(a.values());a.delete(d),yield c,await m()}this.logger&&this.logger.debug("Concurrent batch email send completed",{processedCount:n,provider:t})}async dispatch(e){return this.middlewares.length===0?await this.resolveProvider(e).sendEmail(e):(this.composedSend??=C(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){if(this.featureCheck==="off")return;const{supported:r,violations:s}=T(e,this.provider.features);if(r)return;const t=this.provider.name??"unknown",o=s.map(n=>n.field),a=s.map(n=>n.message);if(this.featureCheck==="warn"){this.logger&&this.logger.warn("Message uses capabilities the provider does not support",{fields:o,provider:t});return}return this.logger&&this.logger.error("Message rejected by capability guard",{fields:o,provider:t}),new v(t,`Provider does not support the following message fields: ${o.join(", ")}`,{code:"UNSUPPORTED_FEATURES",hint:a})}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 s=this.globalConfig?.replyTo;if(s&&!e.getReplyTo()&&e.replyTo(s),this.globalConfig?.headers){const t=l(this.globalConfig.headers);Object.entries(t).forEach(([o,a])=>{e.header(o,a)})}}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 s=l(this.globalConfig.headers),t=r.headers?l(r.headers):{};r.headers={...s,...t},this.logger&&this.logger.debug("Merged global headers",{globalCount:Object.keys(s).length,messageCount:Object.keys(t).length})}return r}}const D=(i,e)=>new u(i,e);export{u as Mail,D as createMail};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import c from"./EmailError-B-sEf3bp.js";import T from"./RequiredOptionError-DuNNY_a5.js";import{s as x}from"./headers-to-record-De1Eg951.js";import{m as z}from"./make-request-BlK_B72c.js";import{u as K}from"./retry-3pnxTuNs.js";import{s as P,a as G}from"./sanitize-header-DtQwAnzg.js";import{f as C}from"./validate-email-options-mCM9dVug.js";import{defineProvider as E}from"./defineProvider-C9q-7iwt.js";import{a as y,f as w}from"./address-formatter-BWR2Dddx.js";import{h as q,P as D,c as B}from"./provider-base-C7PuopYK.js";import{c as F}from"./attachment-processor-CMp7qqMI.js";const n="autosend",H="https://api.autosend.com/v1",N=3e4,R=3,v=50,$=20,A=40*1024*1024,k=20,I=1e3,j=998,Z=/^[A-Z0-9-]{1,76}$/i,J="^[A-Za-z0-9-]{1,76}$",L="Either text or html content is required",g=t=>[t].flat(),V=async t=>{const s=await F(t,n);return{content:s.content,contentType:s.contentType,fileName:s.filename}},U=(t,s)=>{const i=g(t.to);if(i.length!==1)return`AutoSend's send endpoint takes exactly one 'to' recipient, but ${String(i.length)} were given — use 'cc'/'bcc', or send one message per recipient`;const o=i.length+(t.cc?g(t.cc).length:0)+(t.bcc?g(t.bcc).length:0);if(o>v)return`AutoSend allows at most ${String(v)} recipients across to/cc/bcc, but ${String(o)} were given`;if(t.attachments&&t.attachments.length>$)return`AutoSend allows at most ${String($)} attachments, but ${String(t.attachments.length)} were given`;const e=t.attachments?.find(r=>r.cid);if(e)return`AutoSend does not support inline (cid) attachments, but '${e.filename}' sets one — host the image and reference it by URL instead`;if(t.subject.length>j)return`AutoSend allows a subject of at most ${String(j)} characters, but ${String(t.subject.length)} were given`;const a=Object.keys(s);if(a.length>k)return`AutoSend allows at most ${String(k)} custom headers, but ${String(a.length)} were given`;const d=a.find(r=>!Z.test(r));if(d!==void 0)return`AutoSend header names must match ${J}, but '${d}' does not`;const u=a.find(r=>s[r].length>I);if(u!==void 0)return`AutoSend allows header values of at most ${String(I)} characters, but '${u}' is longer`},X=t=>{if(typeof t!="object"||t===null)return{};const s=t;if(s.success===!0)return{emailId:typeof s.data?.emailId=="string"?s.data.emailId:void 0};const i=[s.message,s.error].find(o=>typeof o=="string");return{reason:typeof i=="string"?i:void 0}},ie=E((t={})=>{if(!t.apiKey)throw new T(n,"apiKey");const s={apiKey:t.apiKey,debug:t.debug??!1,endpoint:t.endpoint??H,logger:t.logger,retries:t.retries??R,timeout:t.timeout??N},i=new D,o=B(n,t.logger);return{features:{attachments:!0,batchSending:!1,customHeaders:!0,html:!0,replyTo:!0,scheduling:!1,tagging:!1,templates:!0,tracking:!0},async initialize(){i.setInitialized()},async isAvailable(){return s.apiKey.length>0},name:n,options:s,async sendEmail(e){try{const a=C(e).filter(l=>!(e.templateId!==void 0&&l===L));if(a.length>0)return{error:new c(n,`Invalid email options: ${a.join(", ")}`),success:!1};const d=e.headers?x(e.headers):{},u=U(e,d);if(u)return{error:new c(n,u),success:!1};const r={from:y(e.from),subject:e.subject,to:y(g(e.to)[0])};if(e.templateId?r.templateId=e.templateId:(e.html&&(r.html=e.html),e.text&&(r.text=e.text)),e.cc&&(r.cc=w(e.cc)),e.bcc&&(r.bcc=w(e.bcc)),e.replyTo&&(r.replyTo=y(e.replyTo)),e.dynamicData&&(r.dynamicData=e.dynamicData),e.unsubscribeGroupId&&(r.unsubscribeGroupId=e.unsubscribeGroupId),e.trackingClick!==void 0&&(r.trackingClick=e.trackingClick),e.trackingOpen!==void 0&&(r.trackingOpen=e.trackingOpen),e.bypassSuppressions!==void 0&&(r.bypassSuppressions=e.bypassSuppressions),Object.keys(d).length>0&&(r.headers=Object.fromEntries(Object.entries(d).map(([l,p])=>[P(l),G(p)]))),e.attachments&&e.attachments.length>0){const l=await Promise.all(e.attachments.map(async b=>V(b))),p=l.reduce((b,O)=>b+O.content.length,0);if(p>A)return{error:new c(n,`AutoSend allows at most ${String(A)} bytes of attachments after Base64 encoding, but this message carries ${String(p)}`),success:!1};r.attachments=l}const f=await K(async()=>z(`${s.endpoint}/mails/send`,{headers:{Authorization:`Bearer ${s.apiKey}`,"Content-Type":"application/json"},method:"POST",timeout:s.timeout},JSON.stringify(r)),s.retries),S=f.data?.body,{emailId:h,reason:m}=X(S);return f.success?m?{error:new c(n,`Failed to send email: ${m}`),success:!1}:h?(o.debug(`Email queued successfully: ${h}`),{data:{messageId:h,provider:n,response:S,sent:!0,timestamp:new Date},success:!0}):{error:new c(n,"AutoSend accepted the message but returned no emailId"),success:!1}:{error:m?new c(n,`Failed to send email: ${m}`):f.error??new c(n,"Failed to send email"),success:!1}}catch(a){return{error:q(n,"send email",a,o),success:!1}}},async validateCredentials(){return this.isAvailable()}}});export{ie as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createRequire as D}from"node:module";import E from"./EmailError-B-sEf3bp.js";import L from"./RequiredOptionError-DuNNY_a5.js";import{c as F}from"./create-logger-BQTS3f-I.js";import f from"../utils/format-email-address.js";import{s as G}from"./headers-to-record-De1Eg951.js";import{m as Q}from"./make-request-BlK_B72c.js";import{
|
|
1
|
+
import{createRequire as D}from"node:module";import E from"./EmailError-B-sEf3bp.js";import L from"./RequiredOptionError-DuNNY_a5.js";import{c as F}from"./create-logger-BQTS3f-I.js";import f from"../utils/format-email-address.js";import{s as G}from"./headers-to-record-De1Eg951.js";import{m as Q}from"./make-request-BlK_B72c.js";import{a as q,s as X}from"./sanitize-header-DtQwAnzg.js";import{f as Y}from"./validate-email-options-mCM9dVug.js";import{defineProvider as Z}from"./defineProvider-C9q-7iwt.js";const N=D(import.meta.url),A=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,V=y=>{if(typeof A<"u"&&A.versions&&A.versions.node){const[a,d]=A.versions.node.split(".").map(Number);if(a>22||a===22&&d>=3||a===20&&d>=16)return A.getBuiltinModule(y)}return N(y)},{randomUUID:v,createHash:R,createHmac:$}=V("node:crypto"),J=globalThis.Buffer!==void 0,I=()=>{if(!J)throw new Error("Buffer is required for AWS SES provider");return globalThis.Buffer},h="aws-ses",j={apiVersion:"2010-12-01",maxAttempts:3,region:"us-east-1"},ue=Z((y={})=>{const a={...j,...y},d=F("AWS-SES",y.logger),P=(e,i,t,s,o)=>{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(`
|
|
2
2
|
`)}
|
|
3
3
|
`,n=Object.keys(s).toSorted().map(c=>c.toLowerCase()).join(";"),g=R("sha256").update(o).digest("hex");return[e,i,u,r,n,g].join(`
|
|
4
4
|
`)},W=(e,i,t)=>{const s=e.slice(0,8),o=R("sha256").update(t).digest("hex");return["AWS4-HMAC-SHA256",e,`${s}/${i}/ses/aws4_request`,o].join(`
|
package/dist/packem_shared/{build-mime-message-B6927SQy.js → build-mime-message-BW5FQ2Bt.js}
RENAMED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import v from"./EmailError-B-sEf3bp.js";import l from"../utils/format-email-address.js";import{r as F}from"./generate-boundary-DQbzDiOI.js";import{s as M}from"./headers-to-record-De1Eg951.js";import{f as g}from"./quoted-printable-nSGDA1tA.js";import{
|
|
1
|
+
import v from"./EmailError-B-sEf3bp.js";import l from"../utils/format-email-address.js";import{r as F}from"./generate-boundary-DQbzDiOI.js";import{s as M}from"./headers-to-record-De1Eg951.js";import{f as g}from"./quoted-printable-nSGDA1tA.js";import{s as T,a as f}from"./sanitize-header-DtQwAnzg.js";const d=globalThis.Buffer!==void 0,$=t=>{if(typeof t=="string"){if(d)return Buffer.from(t,"utf8").toString("base64");const e=new TextEncoder().encode(t);let r="";const n=8192;for(let s=0;s<e.length;s+=n){const i=e.subarray(s,Math.min(s+n,e.length));r+=String.fromCharCode(...i)}return btoa(r)}if(d&&t instanceof Buffer)return t.toString("base64");const a=t instanceof Uint8Array?t:new Uint8Array(t);if(d)return Buffer.from(a).toString("base64");let h="";const o=8192;for(let e=0;e<a.length;e+=o){const r=a.subarray(e,Math.min(e+o,a.length));h+=String.fromCharCode(...r)}return btoa(h)},j=globalThis.Buffer!==void 0,A=/[^\u0000-\u007F]/,S=t=>!A.test(t),U=t=>{if(S(t))return t;const a=45,h=new TextDecoder,o=j?Buffer.from(t,"utf8"):new TextEncoder().encode(t),e=[];let r=0;for(;r<o.length;){let n=Math.min(r+a,o.length);if(n<o.length)for(;n>r&&(o[n]&192)===128;)n-=1;const s=o.subarray(r,n),i=h.decode(s);e.push(`=?UTF-8?B?${$(i)}?=`),r=n}return e.join(" ")},b=t=>Array.isArray(t)?t.map(a=>l(a)).join(", "):l(t),y=/[^ -]/,C=t=>U(f(t)),D=globalThis.Buffer!==void 0,x=t=>t.replaceAll(/.{1,76}/g,`$&\r
|
|
2
2
|
`).trimEnd(),P=async(t,a={})=>{const{includeBcc:h=!1}=a,o=F(),e=[`From: ${l(t.from)}`,`To: ${b(t.to)}`];if(t.cc&&e.push(`Cc: ${b(t.cc)}`),h&&t.bcc&&e.push(`Bcc: ${b(t.bcc)}`),t.replyTo&&e.push(`Reply-To: ${l(t.replyTo)}`),e.push(`Subject: ${C(t.subject)}`,"MIME-Version: 1.0"),t.headers){const r=M(t.headers);Object.entries(r).forEach(([n,s])=>{const i=T(n),p=f(s);e.push(`${i}: ${p}`)})}if(e.push(`Content-Type: multipart/mixed; boundary="${o}"`,""),t.text&&(y.test(t.text)?e.push(`--${o}`,"Content-Type: text/plain; charset=UTF-8","Content-Transfer-Encoding: quoted-printable","",g(t.text),""):e.push(`--${o}`,"Content-Type: text/plain; charset=UTF-8","Content-Transfer-Encoding: 7bit","",t.text,"")),t.html&&(y.test(t.html)?e.push(`--${o}`,"Content-Type: text/html; charset=UTF-8","Content-Transfer-Encoding: quoted-printable","",g(t.html),""):e.push(`--${o}`,"Content-Type: text/html; charset=UTF-8","Content-Transfer-Encoding: 7bit","",t.html,"")),t.attachments&&t.attachments.length>0){const r=await Promise.all(t.attachments.map(async n=>{let s;if(n.raw!==void 0)s=n.raw;else{if(n.content===void 0)throw new v("attachment",`Attachment '${n.filename}' must have content, raw, or be resolved from path/href before building MIME message`);s=n.content instanceof Promise?await n.content:n.content}return{...n,resolvedContent:s}}));for(const n of r){e.push(`--${o}`);const s=f(n.contentType??"application/octet-stream"),i=C(n.filename);e.push(`Content-Type: ${s}; name="${i}"`);const p=f(n.contentDisposition??"attachment");e.push(`Content-Disposition: ${p}; filename="${i}"`),n.cid&&e.push(`Content-ID: <${f(n.cid)}>`),n.headers&&Object.entries(n.headers).forEach(([m,w])=>{const B=T(m),E=f(w);e.push(`${B}: ${E}`)});const u=f(n.encoding??"base64");e.push(`Content-Transfer-Encoding: ${u}`,"");const c=n.resolvedContent;if(u==="base64")e.push(x($(c)));else if(u==="7bit"||u==="8bit")if(typeof c=="string")e.push(c);else if(D&&c instanceof Buffer)e.push(c.toString("utf8"));else{const m=new TextDecoder;e.push(m.decode(c))}else e.push(x($(c)));e.push("")}}return e.push(`--${o}--`),e.join(`\r
|
|
3
3
|
`)};export{P as F};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import a from"./EmailError-B-sEf3bp.js";import m from"./RequiredOptionError-DuNNY_a5.js";import{F as d}from"./build-mime-message-
|
|
1
|
+
import a from"./EmailError-B-sEf3bp.js";import m from"./RequiredOptionError-DuNNY_a5.js";import{F as d}from"./build-mime-message-BW5FQ2Bt.js";import{e as f}from"./generate-message-id-gXAuMz4g.js";import{s as u}from"./headers-to-record-De1Eg951.js";import{f as p}from"./validate-email-options-mCM9dVug.js";import{defineProvider as g}from"./defineProvider-C9q-7iwt.js";import{h,c as v}from"./provider-base-C7PuopYK.js";const r="cloudflare-email",C=g((t={})=>{if(typeof t.send!="function")throw new m(r,"send");const c=v(r,t.logger);return{features:{attachments:!0,batchSending:!1,customHeaders:!0,html:!0,replyTo:!0,scheduling:!1,tagging:!1,templates:!1,tracking:!1},async initialize(){await Promise.resolve()},async isAvailable(){return typeof t.send=="function"},name:r,async sendEmail(e){try{const s=p(e);if(s.length>0)return{error:new a(r,`Invalid email options: ${s.join(", ")}`),success:!1};const i=Array.isArray(e.to)?e.to:[e.to];if(i.length!==1||e.cc!==void 0||e.bcc!==void 0)return{error:new a(r,"Cloudflare Email supports exactly one `to` recipient and does not support cc/bcc"),success:!1};const[o]=i;if(!o)return{error:new a(r,"A single `to` recipient is required"),success:!1};const n=f(),l=await d({...e,headers:{...e.headers?u(e.headers):{},"Message-ID":n}});return await t.send(e.from.email,o.email,l),{data:{messageId:n,provider:r,sent:!0,timestamp:new Date},success:!0}}catch(s){return{error:h(r,"send email",s,c),success:!1}}},async validateCredentials(){return this.isAvailable()}}});export{C as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const w=(a,s={})=>{const{now:i=Date.now,skewMs:o=6e4}=s,c=Math.max(0,o);let t,n;const r=e=>e.expiresAt===void 0||e.expiresAt-c>i();return async()=>t&&r(t)?t:(n??=a().then(e=>(t=e,e)).finally(()=>{n=void 0}),await n)};export{w as d};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createMail as w}from"./Mail-
|
|
1
|
+
import{createMail as w}from"./Mail-CYAwMRnX.js";import u from"./mockProvider-Behywbl1.js";const d=(t,o,e,n)=>{const r=Date.now()+e;return new Promise((s,c)=>{const a=()=>{const i=t.find(m=>o(m));if(i){s(i);return}if(Date.now()>=r){c(new Error("waitFor timed out before a matching message was captured"));return}setTimeout(a,n)};a()})},l=()=>{const t=u(),o=w(t),e=t.getInstance?.();if(!e)throw new Error("mockProvider() did not expose getInstance(); cannot create a test harness");return{mail:o,reset:()=>{e.length=0},sent:()=>e,waitFor:(n,r={})=>d(e,n,r.timeout??1e3,r.interval??10)}};export{l as createTestEmail};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineProvider as l}from"./defineProvider-C9q-7iwt.js";import o from"./smtpProvider-
|
|
1
|
+
import{defineProvider as l}from"./defineProvider-C9q-7iwt.js";import o from"./smtpProvider-5MI3-EPh.js";const s="mailcrab",u="localhost",n=1025,g=l((t={})=>{const e={debug:t.debug??!1,host:t.host??u,logger:t.logger,port:t.port??n,retries:t.retries,secure:t.secure??!1,timeout:t.timeout},a=o({debug:e.debug,host:e.host,logger:e.logger,port:e.port,retries:e.retries,secure:e.secure,timeout:e.timeout});return{features:{attachments:!0,batchSending:!1,customHeaders:!0,html:!0,replyTo:!0,scheduling:!1,tagging:!1,templates:!1,tracking:!1},async initialize(){await a.initialize()},async isAvailable(){return a.isAvailable()},name:s,options:e,async sendEmail(i){const r=await a.sendEmail(i);return r.success&&r.data?{data:{...r.data,provider:s},success:!0}:r},async validateCredentials(){return a.validateCredentials?.()??!1}}});export{g as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import c from"./EmailError-B-sEf3bp.js";import P from"./RequiredOptionError-DuNNY_a5.js";import{e as I}from"./generate-message-id-gXAuMz4g.js";import{s as T}from"./headers-to-record-De1Eg951.js";import{m as d}from"./make-request-BlK_B72c.js";import{u as g}from"./retry-3pnxTuNs.js";import{
|
|
1
|
+
import c from"./EmailError-B-sEf3bp.js";import P from"./RequiredOptionError-DuNNY_a5.js";import{e as I}from"./generate-message-id-gXAuMz4g.js";import{s as T}from"./headers-to-record-De1Eg951.js";import{m as d}from"./make-request-BlK_B72c.js";import{u as g}from"./retry-3pnxTuNs.js";import{s as A,a as k}from"./sanitize-header-DtQwAnzg.js";import{f as z}from"./validate-email-options-mCM9dVug.js";import{defineProvider as j}from"./defineProvider-C9q-7iwt.js";import{P as E,h as f,c as C}from"./provider-base-C7PuopYK.js";import{j as u,g as h}from"./address-formatter-BWR2Dddx.js";const r="mailpace",M="https://app.mailpace.com/api/v1",S=3e4,q=3,R=j((o={})=>{if(!o.apiToken)throw new P(r,"apiToken");const s={apiToken:o.apiToken,debug:o.debug??!1,endpoint:o.endpoint??M,retries:o.retries??q,timeout:o.timeout??S,...o.logger&&{logger:o.logger}},l=new E,i=C(r,o.logger);return{features:{attachments:!0,batchSending:!1,customHeaders:!0,html:!0,replyTo:!0,scheduling:!1,tagging:!0,templates:!0,tracking:!1},async getEmail(e){try{if(!e)return{error:new c(r,"Email ID is required to retrieve email details"),success:!1};await l.ensureInitialized(()=>this.initialize(),r);const a={Authorization:`Bearer ${s.apiToken}`,"Content-Type":"application/json"};i.debug("Retrieving email details",{id:e});const t=await g(async()=>d(`${s.endpoint}/emails/${e}`,{headers:a,method:"GET",timeout:s.timeout}),s.retries);return t.success?(i.debug("Email details retrieved successfully"),{data:t.data?.body,success:!0}):(i.debug("API request failed when retrieving email",t.error),{error:new c(r,`Failed to retrieve email: ${t.error instanceof Error?t.error.message:"Unknown error"}`,{cause:t.error}),success:!1})}catch(a){return{error:f(r,"retrieve email",a,i),success:!1}}},async initialize(){await l.ensureInitialized(async()=>{if(!await this.isAvailable())throw new c(r,"MailPace API not available or invalid API token");i.debug("Provider initialized successfully")},r)},async isAvailable(){try{const e={Authorization:`Bearer ${s.apiToken}`,"Content-Type":"application/json"};i.debug("Checking MailPace API availability");const a=await d(`${s.endpoint}/account`,{headers:e,method:"GET",timeout:s.timeout});return i.debug("MailPace API availability check response:",{error:a.error instanceof Error?a.error.message:void 0,statusCode:a.data?.statusCode,success:a.success}),!!(a.success&&a.data&&typeof a.data=="object"&&"statusCode"in a.data&&typeof a.data.statusCode=="number"&&a.data.statusCode>=200&&a.data.statusCode<300)}catch(e){return i.debug("Error checking availability:",e),!1}},name:r,options:s,async sendEmail(e){try{const a=z(e);if(a.length>0)return{error:new c(r,`Invalid email options: ${a.join(", ")}`),success:!1};await l.ensureInitialized(()=>this.initialize(),r);const t={from:h(e.from),subject:e.subject,to:u(e.to)};if(e.html&&(t.htmlbody=e.html),e.text&&(t.textbody=e.text),e.cc&&(t.cc=u(e.cc)),e.bcc&&(t.bcc=u(e.bcc)),e.replyTo&&(t.replyto=h(e.replyTo)),e.templateId&&(t.template_id=e.templateId,e.templateVariables&&(t.template_variables=e.templateVariables)),e.tags&&e.tags.length>0&&(t.tags=e.tags),e.listUnsubscribe&&(t.list_unsubscribe=e.listUnsubscribe),e.headers){const y=T(e.headers),p={};Object.entries(y).forEach(([v,w])=>{p[A(v)]=k(w)}),t.headers=p}e.attachments&&e.attachments.length>0&&(t.attachments=e.attachments),i.debug("Sending email via MailPace API",{subject:t.subject,to:t.to});const b={Authorization:`Bearer ${s.apiToken}`,"Content-Type":"application/json"},n=await g(async()=>d(`${s.endpoint}/send`,{headers:b,method:"POST",timeout:s.timeout},JSON.stringify(t)),s.retries);if(!n.success)return i.debug("API request failed when sending email",n.error),{error:n.error??new c(r,"Failed to send email"),success:!1};const m=n.data?.body?.id??I();return i.debug("Email sent successfully",{messageId:m}),{data:{messageId:m,provider:r,response:n.data,sent:!0,timestamp:new Date},success:!0}}catch(a){return{error:f(r,"send email",a,i),success:!1}}},async validateCredentials(){return this.isAvailable()}}});export{R as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import d from"./EmailError-B-sEf3bp.js";import P from"./RequiredOptionError-DuNNY_a5.js";import{e as C}from"./generate-message-id-gXAuMz4g.js";import{s as j}from"./headers-to-record-De1Eg951.js";import{m as g}from"./make-request-BlK_B72c.js";import{u as y}from"./retry-3pnxTuNs.js";import{
|
|
1
|
+
import d from"./EmailError-B-sEf3bp.js";import P from"./RequiredOptionError-DuNNY_a5.js";import{e as C}from"./generate-message-id-gXAuMz4g.js";import{s as j}from"./headers-to-record-De1Eg951.js";import{m as g}from"./make-request-BlK_B72c.js";import{u as y}from"./retry-3pnxTuNs.js";import{a as h,s as I}from"./sanitize-header-DtQwAnzg.js";import{f as T}from"./validate-email-options-mCM9dVug.js";import{defineProvider as _}from"./defineProvider-C9q-7iwt.js";import{P as k,h as b,c as z}from"./provider-base-C7PuopYK.js";import{k as p}from"./address-formatter-BWR2Dddx.js";import{p as E}from"./attachment-processor-CMp7qqMI.js";const a="mandrill",S="https://mandrillapp.com/api/1.0",K=3e4,M=3,B=_((l={})=>{if(!l.apiKey)throw new P(a,"apiKey");const s={apiKey:l.apiKey,debug:l.debug??!1,endpoint:l.endpoint??S,retries:l.retries??M,timeout:l.timeout??K,...l.logger&&{logger:l.logger}},u=new k,i=z(a,l.logger);return{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 d(a,"Email ID is required to retrieve email details"),success:!1};await u.ensureInitialized(()=>this.initialize(),a),i.debug("Retrieving email details",{id:e});const n={id:e,key:s.apiKey},t={"Content-Type":"application/json"},o=await y(async()=>g(`${s.endpoint}/messages/info.json`,{headers:t,method:"POST",timeout:s.timeout},JSON.stringify(n)),s.retries);return o.success?(i.debug("Email details retrieved successfully"),{data:o.data?.body,success:!0}):(i.debug("API request failed when retrieving email",o.error),{error:new d(a,`Failed to retrieve email: ${o.error instanceof Error?o.error.message:"Unknown error"}`,{cause:o.error}),success:!1})}catch(n){return{error:b(a,"retrieve email",n,i),success:!1}}},async initialize(){await u.ensureInitialized(async()=>{if(!await this.isAvailable())throw new d(a,"Mandrill API not available or invalid API key");i.debug("Provider initialized successfully")},a)},async isAvailable(){try{i.debug("Checking Mandrill API availability");const e={key:s.apiKey},n={"Content-Type":"application/json"},t=await g(`${s.endpoint}/users/info.json`,{headers:n,method:"POST",timeout:s.timeout},JSON.stringify(e));return i.debug("Mandrill 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 i.debug("Error checking availability:",e),!1}},name:a,options:s,async sendEmail(e){try{const n=T(e);if(n.length>0)return{error:new d(a,`Invalid email options: ${n.join(", ")}`),success:!1};await u.ensureInitialized(()=>this.initialize(),a);const t={from_email:e.from.email,html:e.html??"",subject:e.subject,text:e.text??"",...e.from.name&&{from_name:e.from.name},to:p(e.to,"to")};if(e.cc){const r=p(e.cc,"cc");for(const c of r)c.type="cc";t.to=[...t.to,...r]}if(e.bcc){const r=p(e.bcc,"bcc");for(const c of r)c.type="bcc";t.to=[...t.to,...r]}if(e.replyTo){const r=e.replyTo.name?`${h(e.replyTo.name)} <${e.replyTo.email}>`:e.replyTo.email;t.headers={"Reply-To":r}}if(e.headers){const r=j(e.headers),c={};Object.entries(r).forEach(([w,A])=>{c[I(w)]=h(A)}),t.headers={...t.headers,...c}}e.tags&&e.tags.length>0&&(t.tags=e.tags),e.metadata&&(t.metadata=e.metadata),(e.googleAnalyticsDomains||e.googleAnalyticsCampaign)&&(t.google_analytics_domains=e.googleAnalyticsDomains,t.google_analytics_campaign=e.googleAnalyticsCampaign),e.subaccount&&(t.subaccount=e.subaccount),e.attachments&&e.attachments.length>0&&(t.attachments=await Promise.all(e.attachments.map(async r=>({content:await E(r,a),name:r.filename,type:r.contentType??"application/octet-stream"}))));const o={key:s.apiKey,message:t};e.templateName&&(o.template_name=e.templateName,e.templateContent&&(o.template_content=e.templateContent),e.templateVariables&&(o.template_vars=e.templateVariables)),e.globalMergeVars&&(t.global_merge_vars=e.globalMergeVars),e.mergeVars&&(t.merge_vars=e.mergeVars),e.sendAt&&(o.send_at=e.sendAt),i.debug("Sending email via Mandrill API",{subject:t.subject,to:t.to});const v={"Content-Type":"application/json"},m=await y(async()=>g(`${s.endpoint}/messages/send.json`,{headers:v,method:"POST",timeout:s.timeout},JSON.stringify(o)),s.retries);if(!m.success)return i.debug("API request failed when sending email",m.error),{error:m.error??new d(a,"Failed to send email"),success:!1};const f=m.data?.body?.[0]?.id??C();return i.debug("Email sent successfully",{messageId:f}),{data:{messageId:f,provider:a,response:m.data,sent:!0,timestamp:new Date},success:!0}}catch(n){return{error:b(a,"send email",n,i),success:!1}}},async validateCredentials(){return this.isAvailable()}}});export{B as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as k}from"./create-token-cache-D56KQGVQ.js";import{s as m}from"./headers-to-record-De1Eg951.js";const p=r=>{const{fetchToken:o,headerName:a="Authorization",now:n=Date.now,onToken:s,scheme:t="Bearer",skewMs:c=6e4}=r,h=k(async()=>{const e=await o();return s?.(e),e},{now:n,skewMs:c});return async(e,i)=>{const w=await h(),d=e.headers?m(e.headers):{};return i({...e,headers:{...d,[a]:`${t} ${w.accessToken}`}})}};export{p as oauth2Middleware};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import m from"./EmailError-B-sEf3bp.js";import{e as _}from"./generate-message-id-gXAuMz4g.js";import{m as S}from"./make-request-BlK_B72c.js";import{u as v}from"./retry-3pnxTuNs.js";import{f as A}from"./validate-email-options-mCM9dVug.js";import{defineProvider as C}from"./defineProvider-C9q-7iwt.js";import p from"./RequiredOptionError-DuNNY_a5.js";import{d as x}from"./create-token-cache-D56KQGVQ.js";import{c as M}from"./attachment-processor-CMp7qqMI.js";import{h as P,P as R,c as j}from"./provider-base-C7PuopYK.js";const b="https://login.microsoftonline.com",E=6e4,O=3e4,L=3,q=300,z="https://graph.microsoft.com/.default",D=["https://graph.microsoft.com/Mail.Send","offline_access"],F={accessToken:["clientId","clientSecret","refreshToken","tenantId"],clientCredentials:["refreshToken"],getAccessToken:["accessToken","clientId","clientSecret","refreshToken","tenantId"],refreshToken:[]},N=e=>{let t=e.length;for(;t>0&&e[t-1]==="/";)t-=1;return e.slice(0,t)},U=(e,t)=>{if(e.getAccessToken)return"getAccessToken";if(e.accessToken)return"accessToken";if(e.refreshToken)return"refreshToken";if(e.clientSecret)return"clientCredentials";throw new p(t,["accessToken","getAccessToken","clientSecret","refreshToken"])},$=(e,t,c)=>{const n=e.authMode??U(e,t),h=F[n].filter(a=>e[a]!==void 0);if(h.length>0&&c.warn(`The '${n}' auth mode ignores these configured options: ${h.join(", ")}. Set 'authMode' to pin the intended flow.`),n==="getAccessToken"){const{getAccessToken:a}=e;if(!a)throw new p(t,"getAccessToken");return{getToken:async()=>a(),mode:n}}if(n==="accessToken"){const{accessToken:a}=e;if(!a)throw new p(t,"accessToken");return{getToken:()=>Promise.resolve(a),mode:n}}const{clientId:f,clientSecret:r,refreshToken:i,tenantId:d}=e,o=[];if(d||o.push("tenantId"),f||o.push("clientId"),n==="clientCredentials"&&!r&&o.push("clientSecret"),n==="refreshToken"&&!i&&o.push("refreshToken"),!d||!f)throw new p(t,o);if(n==="clientCredentials"){if(!r)throw new p(t,o);return{clientId:f,clientSecret:r,mode:n,tenantId:d}}if(!i)throw new p(t,o);return{clientId:f,clientSecret:r,mode:n,refreshToken:i,tenantId:d}},B=(e,t,c)=>{if(typeof e!="object"||e===null)throw new m(t,`Token endpoint returned a non-JSON response (status ${String(c)})`);const n=e;if(typeof n.error=="string"){const h=typeof n.error_description=="string"?n.error_description:"no description provided";throw new m(t,`Token request failed: ${n.error} — ${h}`,{code:n.error,hint:"Check the tenantId, clientId and client secret / refresh token in the Azure app registration."})}if(typeof n.access_token!="string")throw new m(t,`Token endpoint response did not contain an access_token (status ${String(c)})`);return{accessToken:n.access_token,expiresIn:typeof n.expires_in=="number"?n.expires_in:q,refreshToken:typeof n.refresh_token=="string"?n.refresh_token:void 0}},H=e=>e===0||e===408||e===429||e>=500,J=(e,t,c,n)=>{if(e.mode==="accessToken"||e.mode==="getAccessToken")return e.getToken;const h=`${N(t.authority??b)}/${e.tenantId}/oauth2/v2.0/token`,f=t.timeout??O,r=t.retries??L,i=t.now??Date.now;let d=e.mode==="refreshToken"?e.refreshToken:void 0;const o=()=>{const s=new URLSearchParams({client_id:e.clientId,scope:(t.scopes??(e.mode==="clientCredentials"?[z]:D)).join(" ")});return e.clientSecret&&s.set("client_secret",e.clientSecret),e.mode==="clientCredentials"?s.set("grant_type","client_credentials"):(s.set("grant_type","refresh_token"),s.set("refresh_token",d)),s.toString()},a=s=>s.data?.statusCode??0,k=async()=>{const s=await S(h,{headers:{"Content-Type":"application/x-www-form-urlencoded"},method:"POST",timeout:f},o());return!s.success&&H(a(s))?s:{data:s.data,success:!0}},T=async s=>{if(d=s,!!t.onRefreshToken)try{await t.onRefreshToken(s)}catch(u){n.error(`Failed to persist the rotated refresh token — the stored token is stale and sending will fail after a restart: ${u instanceof Error?u.message:String(u)}`)}},g=x(async()=>{const s=await v(k,r),u=s.data?.body;if(!s.success&&(typeof u!="object"||u===null))throw s.error instanceof Error?s.error:new m(c,"Token request failed");const w=B(u,c,a(s));return w.refreshToken&&w.refreshToken!==d&&await T(w.refreshToken),{accessToken:w.accessToken,expiresAt:i()+w.expiresIn*1e3}},{now:i,skewMs:t.tokenRefreshSkewMs??E});return async()=>(await g()).accessToken},l="outlook365",G="https://graph.microsoft.com/v1.0",K=3e4,Q=3,y=e=>[e].flat(),I=e=>e.map(t=>({emailAddress:{address:t.email,...t.name?{name:t.name}:{}}})),V=e=>{if(e instanceof m)return e;const t=e instanceof Error?e.message:String(e);return new m(l,`Failed to obtain access token: ${t}`,{cause:e})},ae=C((e={})=>{const t=j(l,e.logger),c=$(e,l,t);if(c.mode==="clientCredentials"&&(e.userId===void 0||e.userId==="me"))throw new m(l,"The app-only (client credentials) flow requires an explicit 'userId'",{hint:"Set 'userId' to the sender's mailbox id or UPN — app-only tokens have no 'me' mailbox."});const n={authMode:c.mode,authority:e.authority??b,clientId:e.clientId,debug:e.debug??!1,endpoint:e.endpoint??G,logger:e.logger,now:e.now,retries:e.retries??Q,saveToSentItems:e.saveToSentItems??!0,scopes:e.scopes,tenantId:e.tenantId,timeout:e.timeout??K,tokenRefreshSkewMs:e.tokenRefreshSkewMs,userId:e.userId??"me"},h=new R,f=J(c,e,l,t);return{features:{attachments:!0,batchSending:!1,customHeaders:!0,html:!0,replyTo:!0,scheduling:!1,tagging:!1,templates:!1,tracking:!1},async initialize(){h.setInitialized()},async isAvailable(){return!0},name:l,options:n,async sendEmail(r){try{const i=A(r);if(i.length>0)return{error:new m(l,`Invalid email options: ${i.join(", ")}`),success:!1};let d;try{d=await f()}catch(T){return{error:V(T),success:!1}}const o={body:{content:r.html??r.text??"",contentType:r.html?"HTML":"Text"},subject:r.subject,toRecipients:I(y(r.to))};r.cc&&(o.ccRecipients=I(y(r.cc))),r.bcc&&(o.bccRecipients=I(y(r.bcc))),r.replyTo&&(o.replyTo=I(y(r.replyTo))),r.importance&&(o.importance=r.importance),r.attachments&&r.attachments.length>0&&(o.attachments=await Promise.all(r.attachments.map(async T=>{const g=await M(T,l);return{"@odata.type":"#microsoft.graph.fileAttachment",contentBytes:g.content,contentType:g.contentType,name:g.filename}})));const a=n.userId==="me"?"me":`users/${n.userId}`,k=await v(async()=>S(`${n.endpoint}/${a}/sendMail`,{headers:{Authorization:`Bearer ${d}`,"Content-Type":"application/json"},method:"POST",timeout:n.timeout},JSON.stringify({message:o,saveToSentItems:n.saveToSentItems})),n.retries);return k.success?{data:{messageId:_(),provider:l,response:k.data,sent:!0,timestamp:new Date},success:!0}:{error:k.error??new m(l,"Failed to send email"),success:!1}}catch(i){return{error:P(l,"send email",i,t),success:!1}}},async validateCredentials(){try{return await f(),!0}catch(r){return t.warn(`Credential validation failed: ${r instanceof Error?r.message:String(r)}`),!1}}}});export{ae as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import m from"./EmailError-B-sEf3bp.js";import P from"./RequiredOptionError-DuNNY_a5.js";import{e as y}from"./generate-message-id-gXAuMz4g.js";import{s as A}from"./headers-to-record-De1Eg951.js";import{m as b}from"./make-request-BlK_B72c.js";import{u as g}from"./retry-3pnxTuNs.js";import{
|
|
1
|
+
import m from"./EmailError-B-sEf3bp.js";import P from"./RequiredOptionError-DuNNY_a5.js";import{e as y}from"./generate-message-id-gXAuMz4g.js";import{s as A}from"./headers-to-record-De1Eg951.js";import{m as b}from"./make-request-BlK_B72c.js";import{u as g}from"./retry-3pnxTuNs.js";import{s as k,a as z}from"./sanitize-header-DtQwAnzg.js";import{f as j}from"./validate-email-options-mCM9dVug.js";import{defineProvider as E}from"./defineProvider-C9q-7iwt.js";import{P as T,h,c as K}from"./provider-base-C7PuopYK.js";import{p as x}from"./attachment-processor-CMp7qqMI.js";const r="plunk",_="https://api.useplunk.com/v1",S=3e4,q=3,U=E((n={})=>{if(!n.apiKey)throw new P(r,"apiKey");const a={apiKey:n.apiKey,debug:n.debug??!1,endpoint:n.endpoint??_,logger:n.logger,retries:n.retries??q,timeout:n.timeout??S},p=new T,i=K(r,n.logger);return{features:{attachments:!0,batchSending:!1,customHeaders:!0,html:!0,replyTo:!0,scheduling:!1,tagging:!1,templates:!0,tracking:!0},async getEmail(e){try{if(!e)return{error:new m(r,"Email ID is required to retrieve email details"),success:!1};await p.ensureInitialized(()=>this.initialize(),r);const l={Authorization:`Bearer ${a.apiKey}`,"Content-Type":"application/json"};i.debug("Retrieving email details",{id:e});const s=await g(async()=>b(`${a.endpoint}/emails/${e}`,{headers:l,method:"GET",timeout:a.timeout}),a.retries);return s.success?(i.debug("Email details retrieved successfully"),{data:s.data?.body,success:!0}):(i.debug("API request failed when retrieving email",s.error),{error:new m(r,`Failed to retrieve email: ${s.error instanceof Error?s.error.message:"Unknown error"}`,{cause:s.error}),success:!1})}catch(l){return{error:h(r,"retrieve email",l,i),success:!1}}},async initialize(){await p.ensureInitialized(async()=>{if(!await this.isAvailable())throw new m(r,"Plunk API not available or invalid API key");i.debug("Provider initialized successfully")},r)},async isAvailable(){try{return a.apiKey&&a.apiKey.length>0?(i.debug("API key exists, assuming Plunk is available"),!0):!1}catch(e){return i.debug("Error checking availability:",e),!1}},name:r,options:a,async sendEmail(e){try{const l=j(e);if(l.length>0)return{error:new m(r,`Invalid email options: ${l.join(", ")}`),success:!1};await p.ensureInitialized(()=>this.initialize(),r);const s=o=>Array.isArray(o)?o.map(d=>d.email):[o.email],t={to:e.subscriber??(Array.isArray(e.to)?e.to[0]?.email:e.to.email)};if(e.from&&(t.from=e.from.email,e.from.name&&(t.from_name=e.from.name)),e.subject&&(t.subject=e.subject),e.html?t.body=e.html:e.text&&(t.body=e.text),e.html&&e.text&&(t.text=e.text),e.cc&&(t.cc=s(e.cc)),e.bcc&&(t.bcc=s(e.bcc)),e.replyTo&&(t.reply_to=e.replyTo.email,e.replyTo.name&&(t.reply_to_name=e.replyTo.name)),e.templateId&&(t.template=e.templateId),e.data&&(t.data=e.data),e.subscriberId&&(t.subscriber_id=e.subscriberId),e.headers){const o=A(e.headers),d={};Object.entries(o).forEach(([w,I])=>{d[k(w)]=z(I)}),t.headers=d}e.attachments&&e.attachments.length>0&&(t.attachments=await Promise.all(e.attachments.map(async o=>({content:await x(o,r),filename:o.filename,type:o.contentType??"application/octet-stream"})))),i.debug("Sending email via Plunk API",{subject:t.subject,to:t.to});const v={Authorization:`Bearer ${a.apiKey}`,"Content-Type":"application/json"},c=await g(async()=>b(`${a.endpoint}/send`,{headers:v,method:"POST",timeout:a.timeout},JSON.stringify(t)),a.retries);if(!c.success)return i.debug("API request failed when sending email",c.error),{error:c.error??new m(r,"Failed to send email"),success:!1};const u=c.data,f=u?.body&&typeof u.body=="object"?u.body.id??u.body.messageId??y():y();return i.debug("Email sent successfully",{messageId:f}),{data:{messageId:f,provider:r,response:c.data,sent:!0,timestamp:new Date},success:!0}}catch(l){return{error:h(r,"send email",l,i),success:!1}}},async validateCredentials(){return this.isAvailable()}}});export{U as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import d from"./EmailError-B-sEf3bp.js";import T from"./RequiredOptionError-DuNNY_a5.js";import{e as P}from"./generate-message-id-gXAuMz4g.js";import{s as w}from"./headers-to-record-De1Eg951.js";import{m as u}from"./make-request-BlK_B72c.js";import{u as f}from"./retry-3pnxTuNs.js";import{
|
|
1
|
+
import d from"./EmailError-B-sEf3bp.js";import T from"./RequiredOptionError-DuNNY_a5.js";import{e as P}from"./generate-message-id-gXAuMz4g.js";import{s as w}from"./headers-to-record-De1Eg951.js";import{m as u}from"./make-request-BlK_B72c.js";import{u as f}from"./retry-3pnxTuNs.js";import{a as I,s as A}from"./sanitize-header-DtQwAnzg.js";import{f as C}from"./validate-email-options-mCM9dVug.js";import{defineProvider as S}from"./defineProvider-C9q-7iwt.js";import{P as j,h,c as E}from"./provider-base-C7PuopYK.js";import{e as p,g as v}from"./address-formatter-BWR2Dddx.js";import{b as z}from"./attachment-processor-CMp7qqMI.js";const r="postmark",M="https://api.postmarkapp.com",O=3e4,H=3,U=S((o={})=>{if(!o.serverToken)throw new T(r,"serverToken");const s={debug:o.debug??!1,endpoint:o.endpoint??M,retries:o.retries??H,serverToken:o.serverToken,timeout:o.timeout??O,...o.logger&&{logger:o.logger}},m=new j,i=E(r,o.logger);return{endpoint:s.endpoint,features:{attachments:!0,batchSending:!0,customHeaders:!0,html:!0,replyTo:!0,scheduling:!1,tagging:!0,templates:!0,tracking:!0},async getEmail(e){try{if(!e)return{error:new d(r,"Email ID is required to retrieve email details"),success:!1};await m.ensureInitialized(()=>this.initialize(),r);const a={"Content-Type":"application/json","X-Postmark-Server-Token":s.serverToken};i.debug("Retrieving email details",{id:e});const t=await f(async()=>u(`${s.endpoint}/messages/outbound/${e}/details`,{headers:a,method:"GET",timeout:s.timeout}),s.retries);return t.success?(i.debug("Email details retrieved successfully"),{data:t.data?.body,success:!0}):(i.debug("API request failed when retrieving email",t.error),{error:new d(r,`Failed to retrieve email: ${t.error instanceof Error?t.error.message:"Unknown error"}`,{cause:t.error}),success:!1})}catch(a){return{error:h(r,"retrieve email",a,i),success:!1}}},async initialize(){await m.ensureInitialized(async()=>{if(!await this.isAvailable())throw new d(r,"Postmark API not available or invalid server token");i.debug("Provider initialized successfully")},r)},async isAvailable(){try{const e={"Content-Type":"application/json","X-Postmark-Server-Token":s.serverToken};i.debug("Checking Postmark API availability");const a=await u(`${s.endpoint}/server`,{headers:e,method:"GET",timeout:s.timeout});return i.debug("Postmark API availability check response:",{error:a.error instanceof Error?a.error.message:void 0,statusCode:a.data?.statusCode,success:a.success}),!!(a.success&&a.data&&typeof a.data=="object"&&"statusCode"in a.data&&typeof a.data.statusCode=="number"&&a.data.statusCode>=200&&a.data.statusCode<300)}catch(e){return i.debug("Error checking availability:",e),!1}},name:r,options:s,async sendEmail(e){try{const a=C(e);if(a.length>0)return{error:new d(r,`Invalid email options: ${a.join(", ")}`),success:!1};await m.ensureInitialized(()=>this.initialize(),r);const t={From:v(e.from),Subject:e.subject,To:p(e.to).join(",")};if(e.html&&(t.HtmlBody=e.html),e.text&&(t.TextBody=e.text),e.cc&&(t.Cc=p(e.cc).join(",")),e.bcc&&(t.Bcc=p(e.bcc).join(",")),e.replyTo&&(t.ReplyTo=v(e.replyTo)),e.templateId?(t.TemplateId=e.templateId,e.templateModel&&(t.TemplateModel=e.templateModel)):e.templateAlias&&(t.TemplateAlias=e.templateAlias,e.templateModel&&(t.TemplateModel=e.templateModel)),e.trackOpens!==void 0&&(t.TrackOpens=e.trackOpens),e.trackLinks!==void 0&&(t.TrackLinks=e.trackLinks),e.inlineCss!==void 0&&(t.InlineCss=e.inlineCss),e.messageStream&&(t.MessageStream=e.messageStream),e.metadata&&(t.Metadata=e.metadata),e.tags&&e.tags.length>0){const[l]=e.tags;t.Tag=l}if(e.headers){const l=w(e.headers),c=[];for(const[b,y]of Object.entries(l))c.push({Name:A(b),Value:I(String(y))});c.length>0&&(t.Headers=c)}e.attachments&&e.attachments.length>0&&(t.Attachments=await Promise.all(e.attachments.map(async l=>z(l,r)))),i.debug("Sending email via Postmark API",{subject:t.Subject,to:t.To});const k={"Content-Type":"application/json","X-Postmark-Server-Token":s.serverToken},n=await f(async()=>u(`${s.endpoint}/email`,{headers:k,method:"POST",timeout:s.timeout},JSON.stringify(t)),s.retries);if(!n.success)return i.debug("API request failed when sending email",n.error),{error:n.error??new d(r,"Failed to send email"),success:!1};const g=n.data?.body?.MessageID??P();return i.debug("Email sent successfully",{messageId:g}),{data:{messageId:g,provider:r,response:n.data,sent:!0,timestamp:new Date},success:!0}}catch(a){return{error:h(r,"send email",a,i),success:!1}}},async validateCredentials(){return this.isAvailable()}}});export{U as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const r=/[\r\n]/,s=e=>{const a=e.search(r);return a===-1?e:e.slice(0,a)},n=e=>e.replaceAll(/[\r\n]/g,"");export{
|
|
1
|
+
const r=/[\r\n]/,s=e=>{const a=e.search(r);return a===-1?e:e.slice(0,a)},n=e=>e.replaceAll(/[\r\n]/g,"");export{s as a,n as s};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createRequire as x}from"node:module";import f from"./EmailError-B-sEf3bp.js";import W from"./RequiredOptionError-DuNNY_a5.js";import{F as Q}from"./build-mime-message-
|
|
1
|
+
import{createRequire as x}from"node:module";import f from"./EmailError-B-sEf3bp.js";import W from"./RequiredOptionError-DuNNY_a5.js";import{F as Q}from"./build-mime-message-BW5FQ2Bt.js";import{e as Z}from"./generate-message-id-gXAuMz4g.js";import{f as J}from"./validate-email-options-mCM9dVug.js";import{defineProvider as V}from"./defineProvider-C9q-7iwt.js";const G=x(import.meta.url),O=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,k=h=>{if(typeof O<"u"&&O.versions&&O.versions.node){const[t,A]=O.versions.node.split(".").map(Number);if(t>22||t===22&&A>=3||t===20&&A>=16)return O.getBuiltinModule(h)}return G(h)},{Buffer:I}=k("node:buffer"),{createHmac:B,createHash:X,createSign:Y}=k("node:crypto"),{Socket:q,createConnection:K}=k("node:net"),{connect:E}=k("node:tls"),ee=(h,t)=>new Promise(A=>{const w=new q,y=()=>{w.destroy(),A(!1)};w.setTimeout(1e3),w.on("error",y),w.on("timeout",y),w.connect(t,h,()=>{w.end(),A(!0)})}),l="smtp",te=25,re=465,se=1e4,oe=!1,ae=5,P=3e4,ie=/^\./,pe=V((h={})=>{if(!h.host)throw new W(l,"host");const t={authMethod:h.authMethod??"LOGIN",debug:h.debug??!1,dkim:h.dkim,host:h.host,maxConnections:h.maxConnections??ae,oauth2:h.oauth2,password:h.password,pool:h.pool??!1,port:h.port!==void 0?h.port:h.secure?re:te,rejectUnauthorized:h.rejectUnauthorized??!0,retries:h.retries??0,secure:h.secure??oe,timeout:h.timeout??se,user:h.user,...h.logger&&{logger:h.logger}};let A=!1;const w=[],y=[],S=e=>e.replaceAll(/[\r\n\t\v\f]/g," ").trim(),v=e=>{const u=e.split(`\r
|
|
2
2
|
`),o={};for(const c of u)if(c.startsWith("250-")||c.startsWith("250 ")){const i=c.slice(4).trim().split(" "),n=i[0];n&&(o[n]=i.slice(1))}return o},p=async(e,u,o)=>new Promise((c,i)=>{const n=Array.isArray(o)?o:[o];let s="",a="";const d=setTimeout(()=>{g(),i(new f(l,`Command timeout after ${t.timeout}ms: ${u?.slice(0,50)}...`))},t.timeout),g=()=>{e.removeListener("data",b),e.removeListener("error",r),d&&clearTimeout(d)},b=m=>{s+=m.toString();const j=s.split(`\r
|
|
3
3
|
`).filter(Boolean);if(j.length>0){const $=j.at(-1);if($){const U=$.match(/^(\d{3})[\s-]/);if(U){const[,M]=U;a=M,$[3]===" "&&(g(),n.includes(a)?c(s):i(new f(l,`Expected ${n.join(" or ")}, got ${a}: ${s.trim()}`)))}}}},r=m=>{g(),i(new f(l,`Socket error: ${m.message}`,{cause:m}))};e.on("data",b),e.on("error",r),u&&e.write(`${u}\r
|
|
4
4
|
`)}),C=async()=>{if(t.pool&&w.length>0){const e=w.pop();if(e&&!e.destroyed)return e}return t.pool&&w.length+1>=t.maxConnections?new Promise((e,u)=>{const o={reject:u,resolve:e};o.timeout=setTimeout(()=>{const c=y.indexOf(o);c!==-1&&y.splice(c,1),u(new f(l,`Connection queue timeout after ${P}ms`))},P),y.push(o)}):new Promise((e,u)=>{let o,c=!1,i;const n=()=>{o&&clearTimeout(o)};o=setTimeout(()=>{c||(c=!0,i&&!i.destroyed&&i.destroy(),n(),u(new f(l,`Connection timeout to ${t.host}:${t.port} after ${t.timeout}ms`)))},t.timeout);try{i=t.secure?E({host:t.host,port:t.port,rejectUnauthorized:t.rejectUnauthorized}):K(t.port,t.host),i.on("error",s=>{c||(c=!0,n(),u(new f(l,`Connection error: ${s.message}`,{cause:s})))}),i.once("data",s=>{if(!c&&i){c=!0,n();const a=s.toString();a.slice(0,3)==="220"?e(i):(i.destroy(),u(new f(l,`Unexpected server greeting: ${a.trim()}`)))}})}catch(s){c||(c=!0,n(),u(new f(l,`Failed to create connection: ${s.message}`,{cause:s})))}})},R=async e=>new Promise((u,o)=>{let c,i=!1,n;const s=()=>{c&&clearTimeout(c)};c=setTimeout(()=>{i||(i=!0,n&&!n.destroyed&&n.destroy(),s(),o(new f(l,`TLS connection timeout after ${t.timeout}ms`)))},t.timeout);try{const a={host:t.host,rejectUnauthorized:t.rejectUnauthorized,socket:e};n=E(a),n.on("error",d=>{i||(i=!0,s(),o(new f(l,`TLS connection error: ${d.message}`,{cause:d})))}),n.once("secure",()=>{!i&&n&&(i=!0,s(),u(n))})}catch(a){i||(i=!0,s(),o(new f(l,`Failed to upgrade to TLS: ${a.message}`,{cause:a})))}}),z=e=>{if(e.destroyed||!t.pool){try{e.destroy()}catch{}return}if(y.length>0){const u=y.shift();if(u){clearTimeout(u.timeout),u.resolve(e);return}}w.push(e)},L=async(e,u=!1)=>new Promise(o=>{try{if(u){e.write(`RSET\r
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import l from"./EmailError-B-sEf3bp.js";import y from"./RequiredOptionError-DuNNY_a5.js";import{e as b}from"./generate-message-id-gXAuMz4g.js";import{s as k}from"./headers-to-record-De1Eg951.js";import{m as v}from"./make-request-BlK_B72c.js";import{u as w}from"./retry-3pnxTuNs.js";import{
|
|
1
|
+
import l from"./EmailError-B-sEf3bp.js";import y from"./RequiredOptionError-DuNNY_a5.js";import{e as b}from"./generate-message-id-gXAuMz4g.js";import{s as k}from"./headers-to-record-De1Eg951.js";import{m as v}from"./make-request-BlK_B72c.js";import{u as w}from"./retry-3pnxTuNs.js";import{s as A,a as z}from"./sanitize-header-DtQwAnzg.js";import{f as _}from"./validate-email-options-mCM9dVug.js";import{defineProvider as j}from"./defineProvider-C9q-7iwt.js";import{P,h as I,c as T}from"./provider-base-C7PuopYK.js";import{n as h}from"./address-formatter-BWR2Dddx.js";const i="zeptomail",O="https://api.zeptomail.com/v1.1",Z=3e4,E=3,L=j((s={})=>{if(!s.token)throw new y(i,"token");if(!s.token.startsWith("Zoho-enczapikey "))throw new l(i,'Token should be in the format "Zoho-enczapikey <your_api_key>"');const o={debug:s.debug??!1,endpoint:s.endpoint??O,logger:s.logger,retries:s.retries??E,timeout:s.timeout??Z,token:s.token},u=new P,n=T(i,s.logger);return{features:{attachments:!0,batchSending:!1,customHeaders:!0,html:!0,replyTo:!0,scheduling:!1,tagging:!1,templates:!1,tracking:!0},async initialize(){await u.ensureInitialized(async()=>{if(!await this.isAvailable())throw new l(i,"Zeptomail API not available or invalid token");n.debug("Provider initialized successfully")},i)},async isAvailable(){try{return o.token&&o.token.startsWith("Zoho-enczapikey ")?(n.debug("Token format is valid, assuming Zeptomail is available"),!0):!1}catch(e){return n.debug("Error checking availability:",e),!1}},name:i,options:o,async sendEmail(e){try{const m=_(e);if(m.length>0)return{error:new l(i,`Invalid email options: ${m.join(", ")}`),success:!1};await u.ensureInitialized(()=>this.initialize(),i);const a={from:h(e.from).address,subject:e.subject};if(Array.isArray(e.to)?a.to=e.to.length===1?e.to[0].email:e.to.map(t=>t.email):a.to=e.to.email,e.text&&(a.textbody=e.text),e.html&&(a.htmlbody=e.html),e.cc&&(Array.isArray(e.cc)?a.cc=e.cc.length===1?e.cc[0].email:e.cc.map(t=>t.email):a.cc=e.cc.email),e.bcc&&(Array.isArray(e.bcc)?a.bcc=e.bcc.length===1?e.bcc[0].email:e.bcc.map(t=>t.email):a.bcc=e.bcc.email),e.replyTo&&(a.reply_to=[h(e.replyTo).address]),e.trackClicks!==void 0&&(a.track_clicks=e.trackClicks),e.trackOpens!==void 0&&(a.track_opens=e.trackOpens),e.clientReference&&(a.client_reference=e.clientReference),e.mimeHeaders&&Object.keys(e.mimeHeaders).length>0){const t={};for(const[r,d]of Object.entries(e.mimeHeaders))t[r]=d;a.mime_headers=t}if(e.headers){const t=k(e.headers);Object.keys(t).length>0&&(a.mime_headers??={},Object.entries(t).forEach(([r,d])=>{a.mime_headers&&(a.mime_headers[A(r)]=z(d))}))}e.attachments&&e.attachments.length>0&&(a.attachments=e.attachments.map(t=>{const r={name:t.filename};return t.content?(r.content=typeof t.content=="string"?t.content:t.content.toString("base64"),t.contentType&&(r.mime_type=t.contentType)):t.path&&(r.file_cache_key=t.path),r})),n.debug("Sending email via Zeptomail API",{subject:a.subject,to:a.to});const g={Accept:"application/json",Authorization:o.token,"Content-Type":"application/json"},c=await w(async()=>v(`${o.endpoint}/email`,{headers:g,method:"POST",timeout:o.timeout},JSON.stringify(a)),o.retries);if(!c.success){n.debug("API request failed",c.error);let t=c.error instanceof Error?c.error.message:"Unknown error";const r=c.data;return r?.body?.message?t+=` Details: ${r.body.message}`:r?.body?.error?.message&&(t+=` Details: ${r.body.error.message}`),{error:new l(i,`Failed to send email: ${t}`,{cause:c.error}),success:!1}}const p=c.data?.body,f=p?.request_id??b();return n.debug("Email sent successfully",{messageId:f}),{data:{messageId:f,provider:i,response:p,sent:!0,timestamp:new Date},success:!0}}catch(m){return{error:I(i,"send email",m,n),success:!1}}},async validateCredentials(){return this.isAvailable()}}});export{L as default};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { P as ProviderFactory } from "../../packem_shared/provider.d-J-y6iogK.js";
|
|
2
|
+
export type { a as Provider } from "../../packem_shared/provider.d-J-y6iogK.js";
|
|
3
|
+
import { B as BaseConfig, E as EmailOptions } from "../../packem_shared/types.d-ejzh-xBP.js";
|
|
4
|
+
import 'node:buffer';
|
|
5
|
+
/**
|
|
6
|
+
* AutoSend configuration.
|
|
7
|
+
*/
|
|
8
|
+
interface AutoSendConfig extends BaseConfig {
|
|
9
|
+
/**
|
|
10
|
+
* AutoSend API key (sent as a Bearer token).
|
|
11
|
+
*/
|
|
12
|
+
apiKey: string;
|
|
13
|
+
/**
|
|
14
|
+
* API endpoint override (default `https://api.autosend.com/v1`).
|
|
15
|
+
*/
|
|
16
|
+
endpoint?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* AutoSend-specific email options.
|
|
20
|
+
*
|
|
21
|
+
* See the [send reference](https://docs.autosend.com/api-reference/mails/send). AutoSend's
|
|
22
|
+
* `/mails/send` takes exactly one `to` recipient — pass a single address, and use `cc`/`bcc`
|
|
23
|
+
* for additional ones.
|
|
24
|
+
*/
|
|
25
|
+
interface AutoSendEmailOptions extends EmailOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Sends even if the recipient is on the suppression list. Use sparingly: suppressions exist
|
|
28
|
+
* to protect the sending domain's reputation.
|
|
29
|
+
*/
|
|
30
|
+
bypassSuppressions?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Values substituted into Handlebars placeholders (`{{ name }}`) in the template or `html`.
|
|
33
|
+
*/
|
|
34
|
+
dynamicData?: Record<string, unknown>;
|
|
35
|
+
/**
|
|
36
|
+
* An AutoSend template id, used instead of `html` / `text`.
|
|
37
|
+
*/
|
|
38
|
+
templateId?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Enables click tracking for this message.
|
|
41
|
+
*/
|
|
42
|
+
trackingClick?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Enables open tracking for this message.
|
|
45
|
+
*/
|
|
46
|
+
trackingOpen?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* The unsubscribe group this message belongs to.
|
|
49
|
+
*/
|
|
50
|
+
unsubscribeGroupId?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* AutoSend provider — sends through the [AutoSend send endpoint](https://docs.autosend.com/api-reference/mails/send).
|
|
54
|
+
*/
|
|
55
|
+
declare const autoSendProvider: ProviderFactory<AutoSendConfig, unknown, AutoSendEmailOptions>;
|
|
56
|
+
export { type AutoSendConfig, type AutoSendEmailOptions, type ProviderFactory, autoSendProvider };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{default as r}from"../../packem_shared/autoSendProvider-Bz3g6aZi.js";export{r as autoSendProvider};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as a}from"../../packem_shared/awsSesProvider-
|
|
1
|
+
import{default as a}from"../../packem_shared/awsSesProvider-962hIcbo.js";export{a as awsSesProvider};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as e}from"../../packem_shared/cloudflareEmailProvider-
|
|
1
|
+
import{default as e}from"../../packem_shared/cloudflareEmailProvider-B8mOEl3N.js";export{e as cloudflareEmailProvider};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as e}from"../../packem_shared/mailCrabProvider-
|
|
1
|
+
import{default as e}from"../../packem_shared/mailCrabProvider-DRE5snM9.js";export{e as mailCrabProvider};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as r}from"../../packem_shared/mailPaceProvider-
|
|
1
|
+
import{default as r}from"../../packem_shared/mailPaceProvider-DZent57J.js";export{r as mailPaceProvider};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as d}from"../../packem_shared/mandrillProvider-
|
|
1
|
+
import{default as d}from"../../packem_shared/mandrillProvider-DSsAknmA.js";export{d as mandrillProvider};
|
|
@@ -3,11 +3,25 @@ export type { a as Provider } from "../../packem_shared/provider.d-J-y6iogK.js";
|
|
|
3
3
|
import { M as MaybePromise, B as BaseConfig, E as EmailOptions } from "../../packem_shared/types.d-ejzh-xBP.js";
|
|
4
4
|
import 'node:buffer';
|
|
5
5
|
/**
|
|
6
|
+
* Which OAuth2 flow a config resolves to.
|
|
7
|
+
*/
|
|
8
|
+
type Outlook365AuthMode = "accessToken" | "clientCredentials" | "getAccessToken" | "refreshToken";
|
|
9
|
+
/**
|
|
6
10
|
* Outlook365 / Microsoft Graph configuration.
|
|
7
11
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
12
|
+
* Four authentication modes are supported, in precedence order:
|
|
13
|
+
*
|
|
14
|
+
* 1. `getAccessToken` — bring your own async token source (e.g. `@azure/msal-node`).
|
|
15
|
+
* 2. `accessToken` — a static token, for scripts and tests.
|
|
16
|
+
* 3. `refreshToken` — delegated flow; the provider exchanges the refresh token for access tokens and caches them. Requires `tenantId` + `clientId`.
|
|
17
|
+
* 4. `clientCredentials` (selected by supplying `clientSecret`) — app-only flow. Requires `tenantId` + `clientId`, and `userId` must name a real mailbox, since app-only tokens have no `me`.
|
|
18
|
+
*
|
|
19
|
+
* A confidential-client delegated config and an app-only config with a leftover `refreshToken`
|
|
20
|
+
* are indistinguishable — both carry `tenantId`, `clientId`, `clientSecret` and `refreshToken`.
|
|
21
|
+
* Set {@link Outlook365Config.authMode} to pin the flow when both are present.
|
|
22
|
+
*
|
|
23
|
+
* The built-in flows talk to the Azure AD token endpoint over `fetch` — no auth SDK is
|
|
24
|
+
* bundled, so the provider stays dependency-light and runtime-agnostic.
|
|
11
25
|
*/
|
|
12
26
|
interface Outlook365Config extends BaseConfig {
|
|
13
27
|
/**
|
|
@@ -16,6 +30,28 @@ interface Outlook365Config extends BaseConfig {
|
|
|
16
30
|
*/
|
|
17
31
|
accessToken?: string;
|
|
18
32
|
/**
|
|
33
|
+
* Pins the authentication flow instead of inferring it from which credentials are present.
|
|
34
|
+
* Use it when a config could match more than one flow — most notably `clientCredentials`
|
|
35
|
+
* vs `refreshToken` when both a client secret and a refresh token are supplied.
|
|
36
|
+
*/
|
|
37
|
+
authMode?: Outlook365AuthMode;
|
|
38
|
+
/**
|
|
39
|
+
* Azure AD login authority (default `https://login.microsoftonline.com`). Override for
|
|
40
|
+
* sovereign clouds, e.g. `https://login.microsoftonline.us`.
|
|
41
|
+
*/
|
|
42
|
+
authority?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Application (client) id of the Azure AD app registration. Required by the
|
|
45
|
+
* `refreshToken` and `clientSecret` flows.
|
|
46
|
+
*/
|
|
47
|
+
clientId?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Client secret of the Azure AD app registration. Supplying this without a
|
|
50
|
+
* {@link Outlook365Config.refreshToken} selects the app-only (client credentials) flow.
|
|
51
|
+
* It is also sent with the delegated flow when the app is a confidential client.
|
|
52
|
+
*/
|
|
53
|
+
clientSecret?: string;
|
|
54
|
+
/**
|
|
19
55
|
* API endpoint override (default `https://graph.microsoft.com/v1.0`).
|
|
20
56
|
*/
|
|
21
57
|
endpoint?: string;
|
|
@@ -24,11 +60,50 @@ interface Outlook365Config extends BaseConfig {
|
|
|
24
60
|
*/
|
|
25
61
|
getAccessToken?: () => MaybePromise<string>;
|
|
26
62
|
/**
|
|
63
|
+
* Time source in milliseconds — injectable for tests. Defaults to `Date.now`.
|
|
64
|
+
*/
|
|
65
|
+
now?: () => number;
|
|
66
|
+
/**
|
|
67
|
+
* Called whenever Azure AD rotates the refresh token, so it can be persisted. Awaited
|
|
68
|
+
* before the access token is handed out, so a rejected promise is reported rather than
|
|
69
|
+
* surfacing as an unhandled rejection.
|
|
70
|
+
*
|
|
71
|
+
* The rotated token is adopted in-process regardless of whether persisting succeeds — the
|
|
72
|
+
* previous token is already dead at Azure AD, so the new one is the only usable value. A
|
|
73
|
+
* persist failure is logged rather than failing the send, but the stored token is then
|
|
74
|
+
* stale and sending will break on restart.
|
|
75
|
+
* @param refreshToken The new refresh token.
|
|
76
|
+
*/
|
|
77
|
+
onRefreshToken?: (refreshToken: string) => MaybePromise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* A long-lived OAuth2 refresh token, selecting the delegated flow. Obtain it via the
|
|
80
|
+
* authorization-code flow with the `offline_access` scope.
|
|
81
|
+
*/
|
|
82
|
+
refreshToken?: string;
|
|
83
|
+
/**
|
|
27
84
|
* Whether to keep a copy in the Sent Items folder (default true).
|
|
28
85
|
*/
|
|
29
86
|
saveToSentItems?: boolean;
|
|
30
87
|
/**
|
|
88
|
+
* Scope override for the built-in flows. Defaults to
|
|
89
|
+
* `["https://graph.microsoft.com/.default"]` for the app-only flow and
|
|
90
|
+
* `["https://graph.microsoft.com/Mail.Send", "offline_access"]` for the delegated flow.
|
|
91
|
+
*/
|
|
92
|
+
scopes?: string[];
|
|
93
|
+
/**
|
|
94
|
+
* Azure AD tenant id (or `common` / `organizations` for the delegated flow). Required by
|
|
95
|
+
* the `refreshToken` and `clientSecret` flows.
|
|
96
|
+
*/
|
|
97
|
+
tenantId?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Renew the access token this many milliseconds before it expires (default 60000).
|
|
100
|
+
* Negative values are clamped to 0. A value larger than the token lifetime disables
|
|
101
|
+
* caching, costing one token request per send.
|
|
102
|
+
*/
|
|
103
|
+
tokenRefreshSkewMs?: number;
|
|
104
|
+
/**
|
|
31
105
|
* The mailbox to send as: a user id/UPN. Defaults to `me` (the token's own mailbox).
|
|
106
|
+
* The app-only flow has no `me`, so it requires an explicit value.
|
|
32
107
|
*/
|
|
33
108
|
userId?: string;
|
|
34
109
|
}
|
|
@@ -43,6 +118,9 @@ interface Outlook365EmailOptions extends EmailOptions {
|
|
|
43
118
|
}
|
|
44
119
|
/**
|
|
45
120
|
* Outlook365 provider — sends email through the Microsoft Graph `sendMail` endpoint.
|
|
121
|
+
*
|
|
122
|
+
* Authenticates with a caller-supplied token (`accessToken` / `getAccessToken`), the delegated
|
|
123
|
+
* refresh-token flow, or the app-only client-credentials flow. See {@link Outlook365Config}.
|
|
46
124
|
*/
|
|
47
|
-
declare const outlook365Provider: ProviderFactory<Outlook365Config>;
|
|
48
|
-
export { type Outlook365Config, type Outlook365EmailOptions, type ProviderFactory, outlook365Provider };
|
|
125
|
+
declare const outlook365Provider: ProviderFactory<Outlook365Config, unknown, Outlook365EmailOptions>;
|
|
126
|
+
export { type Outlook365AuthMode, type Outlook365Config, type Outlook365EmailOptions, type ProviderFactory, outlook365Provider };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as e}from"../../packem_shared/outlook365Provider-
|
|
1
|
+
import{default as e}from"../../packem_shared/outlook365Provider-CkjXkY-7.js";export{e as outlook365Provider};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as o}from"../../packem_shared/plunkProvider-
|
|
1
|
+
import{default as o}from"../../packem_shared/plunkProvider-D3T2T-2V.js";export{o as plunkProvider};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as a}from"../../packem_shared/postmarkProvider-
|
|
1
|
+
import{default as a}from"../../packem_shared/postmarkProvider-vspVSSOp.js";export{a as postmarkProvider};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as o}from"../../packem_shared/smtpProvider-
|
|
1
|
+
import{default as o}from"../../packem_shared/smtpProvider-5MI3-EPh.js";export{o as smtpProvider};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as r}from"../../packem_shared/zeptomailProvider-
|
|
1
|
+
import{default as r}from"../../packem_shared/zeptomailProvider-DUesgbC-.js";export{r as zeptomailProvider};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rewrites `cid:` references in `src`/`href` attributes using a resolver.
|
|
3
|
+
*
|
|
4
|
+
* Useful for turning inline (Content-ID) image references into hosted URLs for previews/webmail, or
|
|
5
|
+
* for normalizing them. Returning `undefined` from the resolver leaves that reference unchanged.
|
|
6
|
+
* @param html The HTML email.
|
|
7
|
+
* @param resolver Maps a Content-ID to a replacement URL (or `undefined` to keep it).
|
|
8
|
+
* @returns The rewritten HTML.
|
|
9
|
+
*/
|
|
10
|
+
declare const rewriteCidLinks: (html: string, resolver: (cid: string) => string | undefined) => string;
|
|
11
|
+
/**
|
|
12
|
+
* Collects the Content-IDs referenced via `cid:` in `src`/`href` attributes.
|
|
13
|
+
* @param html The HTML email.
|
|
14
|
+
* @returns The referenced Content-IDs (de-duplicated, in first-seen order).
|
|
15
|
+
*/
|
|
16
|
+
declare const extractCidReferences: (html: string) => string[];
|
|
17
|
+
export { extractCidReferences, rewriteCidLinks };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inlines `<style>`/linked CSS into element `style` attributes for maximum email-client
|
|
3
|
+
* compatibility, via [juice](https://github.com/Automattic/juice).
|
|
4
|
+
*
|
|
5
|
+
* `juice` is an optional peer dependency, and this helper is published as its own subpath
|
|
6
|
+
* (`@visulima/email/render/css-inline`). Importing this module resolves `juice` eagerly, so install
|
|
7
|
+
* it (`pnpm add juice`) before importing — consumers who never inline CSS simply never import this
|
|
8
|
+
* entry, which keeps `juice` out of edge/worker bundles.
|
|
9
|
+
* @param html The HTML email.
|
|
10
|
+
* @param options Options forwarded to `juice` (e.g. `preserveImportant`, `removeStyleTags`).
|
|
11
|
+
* @returns The HTML with CSS inlined.
|
|
12
|
+
* @throws {EmailError} When inlining fails.
|
|
13
|
+
*/
|
|
14
|
+
declare const inlineCss: (html: string, options?: Record<string, unknown>) => string;
|
|
15
|
+
export { inlineCss as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t from"juice";import n from"../packem_shared/EmailError-B-sEf3bp.js";const i=(r,o)=>{const a=t.default??t;try{return a(r,o)}catch(e){throw new n("render",`Failed to inline CSS: ${e.message}`,{cause:e})}};export{i as default};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for {@link addDarkModeSupport}.
|
|
3
|
+
*/
|
|
4
|
+
interface DarkModeOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Extra CSS to wrap in a `@media (prefers-color-scheme: dark)` block (the dark-mode overrides).
|
|
7
|
+
*/
|
|
8
|
+
styles?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Adds dark-mode hooks to an HTML email: the `color-scheme` / `supported-color-schemes` meta tags and,
|
|
12
|
+
* when provided, a `@media (prefers-color-scheme: dark)` style block.
|
|
13
|
+
*
|
|
14
|
+
* Injected into `<head>` (created after `<html>` if absent, otherwise prepended).
|
|
15
|
+
* @param html The HTML email.
|
|
16
|
+
* @param options Dark-mode options. See {@link DarkModeOptions}.
|
|
17
|
+
* @returns The HTML with dark-mode support added.
|
|
18
|
+
*/
|
|
19
|
+
declare const addDarkModeSupport: (html: string, options?: DarkModeOptions) => string;
|
|
20
|
+
export { DarkModeOptions, addDarkModeSupport };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for {@link injectPreheader}.
|
|
3
|
+
*/
|
|
4
|
+
interface PreheaderOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Append invisible padding characters so the preheader text isn't followed by leaked body content
|
|
7
|
+
* in the inbox preview.
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
spacer?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Injects a hidden preheader (inbox preview text) at the start of the HTML body.
|
|
14
|
+
*
|
|
15
|
+
* The preheader is wrapped in a `display:none` element with `mso-hide:all`, followed by zero-width
|
|
16
|
+
* padding so the preview shows only your text. Inserted right after `<body>` (or prepended if no
|
|
17
|
+
* body tag is present).
|
|
18
|
+
* @param html The HTML email.
|
|
19
|
+
* @param preheader The preview text.
|
|
20
|
+
* @param options Injection options. See {@link PreheaderOptions}.
|
|
21
|
+
* @returns The HTML with the preheader injected.
|
|
22
|
+
*/
|
|
23
|
+
declare const injectPreheader: (html: string, preheader: string, options?: PreheaderOptions) => string;
|
|
24
|
+
export { PreheaderOptions, injectPreheader };
|
package/dist/test/index.js
CHANGED
|
@@ -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-DZDkrZ_3.js";import{toEmailSnapshot as c}from"../packem_shared/toEmailSnapshot-DeRqM5SJ.js";import{createTestEmail as s}from"../packem_shared/createTestEmail-
|
|
1
|
+
import{emailMatchers as a,registerEmailMatchers as o,toHaveSentMatching as r,toHaveSentTo as i,toHaveSentWithAttachment as m,toHaveSentWithSubject as h}from"../packem_shared/emailMatchers-DZDkrZ_3.js";import{toEmailSnapshot as c}from"../packem_shared/toEmailSnapshot-DeRqM5SJ.js";import{createTestEmail as s}from"../packem_shared/createTestEmail-f12yLlWn.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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@visulima/email",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A comprehensive email library with multi-provider support, crypto utilities, and template engines",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ahasend",
|
|
@@ -63,11 +63,6 @@
|
|
|
63
63
|
],
|
|
64
64
|
"homepage": "https://visulima.com/packages/email",
|
|
65
65
|
"bugs": "https://github.com/visulima/visulima/issues",
|
|
66
|
-
"license": "MIT",
|
|
67
|
-
"author": {
|
|
68
|
-
"name": "Daniel Bannert",
|
|
69
|
-
"email": "d.bannert@anolilab.de"
|
|
70
|
-
},
|
|
71
66
|
"repository": {
|
|
72
67
|
"type": "git",
|
|
73
68
|
"url": "git+https://github.com/visulima/visulima.git",
|
|
@@ -83,13 +78,13 @@
|
|
|
83
78
|
"url": "https://anolilab.com/support"
|
|
84
79
|
}
|
|
85
80
|
],
|
|
86
|
-
"
|
|
87
|
-
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
"type": "module",
|
|
81
|
+
"license": "MIT",
|
|
82
|
+
"author": {
|
|
83
|
+
"name": "Daniel Bannert",
|
|
84
|
+
"email": "d.bannert@anolilab.de"
|
|
85
|
+
},
|
|
92
86
|
"sideEffects": false,
|
|
87
|
+
"type": "module",
|
|
93
88
|
"exports": {
|
|
94
89
|
".": {
|
|
95
90
|
"types": "./dist/index.d.ts",
|
|
@@ -127,6 +122,10 @@
|
|
|
127
122
|
"types": "./dist/template-engines/i18n.d.ts",
|
|
128
123
|
"default": "./dist/template-engines/i18n.js"
|
|
129
124
|
},
|
|
125
|
+
"./providers/autosend": {
|
|
126
|
+
"types": "./dist/providers/autosend/index.d.ts",
|
|
127
|
+
"default": "./dist/providers/autosend/index.js"
|
|
128
|
+
},
|
|
130
129
|
"./providers/aws-ses": {
|
|
131
130
|
"types": "./dist/providers/aws-ses/index.d.ts",
|
|
132
131
|
"default": "./dist/providers/aws-ses/index.js"
|
|
@@ -179,9 +178,21 @@
|
|
|
179
178
|
"types": "./dist/crypto/index.d.ts",
|
|
180
179
|
"default": "./dist/crypto/index.js"
|
|
181
180
|
},
|
|
182
|
-
"./render": {
|
|
183
|
-
"types": "./dist/render/
|
|
184
|
-
"default": "./dist/render/
|
|
181
|
+
"./render/cid": {
|
|
182
|
+
"types": "./dist/render/cid.d.ts",
|
|
183
|
+
"default": "./dist/render/cid.js"
|
|
184
|
+
},
|
|
185
|
+
"./render/css-inline": {
|
|
186
|
+
"types": "./dist/render/css-inline.d.ts",
|
|
187
|
+
"default": "./dist/render/css-inline.js"
|
|
188
|
+
},
|
|
189
|
+
"./render/dark-mode": {
|
|
190
|
+
"types": "./dist/render/dark-mode.d.ts",
|
|
191
|
+
"default": "./dist/render/dark-mode.js"
|
|
192
|
+
},
|
|
193
|
+
"./render/preheader": {
|
|
194
|
+
"types": "./dist/render/preheader.d.ts",
|
|
195
|
+
"default": "./dist/render/preheader.js"
|
|
185
196
|
},
|
|
186
197
|
"./webhooks": {
|
|
187
198
|
"types": "./dist/webhooks/index.d.ts",
|
|
@@ -353,10 +364,11 @@
|
|
|
353
364
|
},
|
|
354
365
|
"./package.json": "./package.json"
|
|
355
366
|
},
|
|
356
|
-
"
|
|
357
|
-
"
|
|
358
|
-
"
|
|
359
|
-
|
|
367
|
+
"files": [
|
|
368
|
+
"dist",
|
|
369
|
+
"README.md",
|
|
370
|
+
"CHANGELOG.md"
|
|
371
|
+
],
|
|
360
372
|
"dependencies": {
|
|
361
373
|
"html-to-text": "^10.0.0",
|
|
362
374
|
"ical-generator": "11.0.0",
|
|
@@ -421,5 +433,9 @@
|
|
|
421
433
|
},
|
|
422
434
|
"engines": {
|
|
423
435
|
"node": "^22.14.0 || >=24.10.0"
|
|
436
|
+
},
|
|
437
|
+
"publishConfig": {
|
|
438
|
+
"access": "public",
|
|
439
|
+
"provenance": true
|
|
424
440
|
}
|
|
425
441
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createRequire as i}from"node:module";import r from"./EmailError-B-sEf3bp.js";const a=i(import.meta.url),n=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,c=o=>{if(typeof n<"u"&&n.versions&&n.versions.node){const[s,t]=n.versions.node.split(".").map(Number);if(s>22||s===22&&t>=3||s===20&&t>=16)return n.getBuiltinModule(o)}return a(o)},{createRequire:u}=c("node:module"),d=u(import.meta.url),p=(o,s)=>{let t;try{const e=d("juice");t=e.default??e}catch(e){throw e instanceof Error&&(e.code==="MODULE_NOT_FOUND"||e.message.includes("Cannot find module")||e.message.includes("Cannot find package"))?new r("render","juice is not installed. Please install it: pnpm add juice",{cause:e}):new r("render",`Failed to load juice: ${e.message}`,{cause:e})}try{return t(o,s)}catch(e){throw new r("render",`Failed to inline CSS: ${e.message}`,{cause:e})}};export{p as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as u}from"./headers-to-record-De1Eg951.js";const p=o=>{const{fetchToken:r,headerName:s="Authorization",now:t=Date.now,onToken:n,scheme:c="Bearer",skewMs:i=6e4}=o;let e;const h=async()=>(e&&(e.expiresAt===void 0||e.expiresAt-i>t())&&e||(e=await r(),n?.(e)),e);return async(a,d)=>{const w=await h(),k=a.headers?u(a.headers):{};return d({...a,headers:{...k,[s]:`${c} ${w.accessToken}`}})}};export{p as oauth2Middleware};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import u from"./EmailError-B-sEf3bp.js";import h from"./RequiredOptionError-DuNNY_a5.js";import{e as y}from"./generate-message-id-gXAuMz4g.js";import{m as k}from"./make-request-BlK_B72c.js";import{u as v}from"./retry-3pnxTuNs.js";import{f as A}from"./validate-email-options-mCM9dVug.js";import{defineProvider as b}from"./defineProvider-C9q-7iwt.js";import{c as w}from"./attachment-processor-CMp7qqMI.js";import{h as I,P as S,c as P}from"./provider-base-C7PuopYK.js";const a="outlook365",j="https://graph.microsoft.com/v1.0",R=3e4,x=3,o=t=>[t].flat(),c=t=>t.map(s=>({emailAddress:{address:s.email,...s.name?{name:s.name}:{}}})),q=b((t={})=>{if(!t.accessToken&&!t.getAccessToken)throw new h(a,["accessToken","getAccessToken"]);const s={accessToken:t.accessToken,debug:t.debug??!1,endpoint:t.endpoint??j,getAccessToken:t.getAccessToken,logger:t.logger,retries:t.retries??x,saveToSentItems:t.saveToSentItems??!0,timeout:t.timeout??R,userId:t.userId??"me"},p=new S,f=P(a,t.logger),g=async()=>s.getAccessToken?s.getAccessToken():s.accessToken;return{features:{attachments:!0,batchSending:!1,customHeaders:!0,html:!0,replyTo:!0,scheduling:!1,tagging:!1,templates:!1,tracking:!1},async initialize(){p.setInitialized()},async isAvailable(){return!!(s.accessToken??s.getAccessToken)},name:a,options:s,async sendEmail(e){try{const n=A(e);if(n.length>0)return{error:new u(a,`Invalid email options: ${n.join(", ")}`),success:!1};let d;try{d=await g()}catch(m){return{error:new u(a,"Failed to obtain access token",{cause:m}),success:!1}}const r={body:{content:e.html??e.text??"",contentType:e.html?"HTML":"Text"},subject:e.subject,toRecipients:c(o(e.to))};e.cc&&(r.ccRecipients=c(o(e.cc))),e.bcc&&(r.bccRecipients=c(o(e.bcc))),e.replyTo&&(r.replyTo=c(o(e.replyTo))),e.importance&&(r.importance=e.importance),e.attachments&&e.attachments.length>0&&(r.attachments=await Promise.all(e.attachments.map(async m=>{const l=await w(m,a);return{"@odata.type":"#microsoft.graph.fileAttachment",contentBytes:l.content,contentType:l.contentType,name:l.filename}})));const T=s.userId==="me"?"me":`users/${s.userId}`,i=await v(async()=>k(`${s.endpoint}/${T}/sendMail`,{headers:{Authorization:`Bearer ${d}`,"Content-Type":"application/json"},method:"POST",timeout:s.timeout},JSON.stringify({message:r,saveToSentItems:s.saveToSentItems})),s.retries);return i.success?{data:{messageId:y(),provider:a,response:i.data,sent:!0,timestamp:new Date},success:!0}:{error:i.error??new u(a,"Failed to send email"),success:!1}}catch(n){return{error:I(a,"send email",n,f),success:!1}}},async validateCredentials(){return this.isAvailable()}}});export{q as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{rewriteCidLinks as i}from"./extractCidReferences-BuoTREMp.js";import d from"./inlineCss-BQ7d2Cyz.js";import{addDarkModeSupport as t}from"./addDarkModeSupport-DZYxULjl.js";import{injectPreheader as s}from"./injectPreheader-CF9WHZ7J.js";const c=(e,r={})=>{let o=e;return r.inlineCss&&(o=d(o,typeof r.inlineCss=="object"?r.inlineCss:void 0)),r.preheader!==void 0&&(o=s(o,r.preheader,r.preheaderOptions)),r.darkMode&&(o=t(o,typeof r.darkMode=="object"?r.darkMode:void 0)),r.cidResolver&&(o=i(o,r.cidResolver)),o};export{c as postProcessHtml};
|
package/dist/render/index.d.ts
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rewrites `cid:` references in `src`/`href` attributes using a resolver.
|
|
3
|
-
*
|
|
4
|
-
* Useful for turning inline (Content-ID) image references into hosted URLs for previews/webmail, or
|
|
5
|
-
* for normalizing them. Returning `undefined` from the resolver leaves that reference unchanged.
|
|
6
|
-
* @param html The HTML email.
|
|
7
|
-
* @param resolver Maps a Content-ID to a replacement URL (or `undefined` to keep it).
|
|
8
|
-
* @returns The rewritten HTML.
|
|
9
|
-
*/
|
|
10
|
-
declare const rewriteCidLinks: (html: string, resolver: (cid: string) => string | undefined) => string;
|
|
11
|
-
/**
|
|
12
|
-
* Collects the Content-IDs referenced via `cid:` in `src`/`href` attributes.
|
|
13
|
-
* @param html The HTML email.
|
|
14
|
-
* @returns The referenced Content-IDs (de-duplicated, in first-seen order).
|
|
15
|
-
*/
|
|
16
|
-
declare const extractCidReferences: (html: string) => string[];
|
|
17
|
-
/**
|
|
18
|
-
* Inlines `<style>`/linked CSS into element `style` attributes for maximum email-client
|
|
19
|
-
* compatibility, via [juice](https://github.com/Automattic/juice).
|
|
20
|
-
*
|
|
21
|
-
* `juice` is an optional peer dependency — install it (`pnpm add juice`) to use this. It is loaded
|
|
22
|
-
* lazily (via `createRequire`) so importing the render entry point doesn't fail when `juice` is absent.
|
|
23
|
-
* @param html The HTML email.
|
|
24
|
-
* @param options Options forwarded to `juice` (e.g. `preserveImportant`, `removeStyleTags`).
|
|
25
|
-
* @returns The HTML with CSS inlined.
|
|
26
|
-
* @throws {EmailError} When `juice` is not installed or inlining fails.
|
|
27
|
-
*/
|
|
28
|
-
declare const inlineCss: (html: string, options?: Record<string, unknown>) => string;
|
|
29
|
-
/**
|
|
30
|
-
* Options for {@link addDarkModeSupport}.
|
|
31
|
-
*/
|
|
32
|
-
interface DarkModeOptions {
|
|
33
|
-
/**
|
|
34
|
-
* Extra CSS to wrap in a `@media (prefers-color-scheme: dark)` block (the dark-mode overrides).
|
|
35
|
-
*/
|
|
36
|
-
styles?: string;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Adds dark-mode hooks to an HTML email: the `color-scheme` / `supported-color-schemes` meta tags and,
|
|
40
|
-
* when provided, a `@media (prefers-color-scheme: dark)` style block.
|
|
41
|
-
*
|
|
42
|
-
* Injected into `<head>` (created after `<html>` if absent, otherwise prepended).
|
|
43
|
-
* @param html The HTML email.
|
|
44
|
-
* @param options Dark-mode options. See {@link DarkModeOptions}.
|
|
45
|
-
* @returns The HTML with dark-mode support added.
|
|
46
|
-
*/
|
|
47
|
-
declare const addDarkModeSupport: (html: string, options?: DarkModeOptions) => string;
|
|
48
|
-
/**
|
|
49
|
-
* Options for {@link injectPreheader}.
|
|
50
|
-
*/
|
|
51
|
-
interface PreheaderOptions {
|
|
52
|
-
/**
|
|
53
|
-
* Append invisible padding characters so the preheader text isn't followed by leaked body content
|
|
54
|
-
* in the inbox preview.
|
|
55
|
-
* @default true
|
|
56
|
-
*/
|
|
57
|
-
spacer?: boolean;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Injects a hidden preheader (inbox preview text) at the start of the HTML body.
|
|
61
|
-
*
|
|
62
|
-
* The preheader is wrapped in a `display:none` element with `mso-hide:all`, followed by zero-width
|
|
63
|
-
* padding so the preview shows only your text. Inserted right after `<body>` (or prepended if no
|
|
64
|
-
* body tag is present).
|
|
65
|
-
* @param html The HTML email.
|
|
66
|
-
* @param preheader The preview text.
|
|
67
|
-
* @param options Injection options. See {@link PreheaderOptions}.
|
|
68
|
-
* @returns The HTML with the preheader injected.
|
|
69
|
-
*/
|
|
70
|
-
declare const injectPreheader: (html: string, preheader: string, options?: PreheaderOptions) => string;
|
|
71
|
-
/**
|
|
72
|
-
* Options for {@link postProcessHtml}. Each step runs only when its option is set; the order is
|
|
73
|
-
* CSS-inline → preheader → dark-mode → CID rewrite.
|
|
74
|
-
*/
|
|
75
|
-
interface PostProcessOptions {
|
|
76
|
-
/**
|
|
77
|
-
* Rewrite `cid:` references using this resolver (see {@link rewriteCidLinks}).
|
|
78
|
-
*/
|
|
79
|
-
cidResolver?: (cid: string) => string | undefined;
|
|
80
|
-
/**
|
|
81
|
-
* Add dark-mode hooks. `true` adds the meta tags only; an object also injects dark CSS.
|
|
82
|
-
*/
|
|
83
|
-
darkMode?: DarkModeOptions | boolean;
|
|
84
|
-
/**
|
|
85
|
-
* Inline CSS via `juice`. `true` uses defaults; an object forwards options to `juice`.
|
|
86
|
-
*/
|
|
87
|
-
inlineCss?: Record<string, unknown> | boolean;
|
|
88
|
-
/**
|
|
89
|
-
* Inject hidden preview text at the top of the body.
|
|
90
|
-
*/
|
|
91
|
-
preheader?: string;
|
|
92
|
-
/**
|
|
93
|
-
* Options for the preheader injection.
|
|
94
|
-
*/
|
|
95
|
-
preheaderOptions?: PreheaderOptions;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Runs an HTML email through the selected post-processing steps.
|
|
99
|
-
*
|
|
100
|
-
* A convenience pipeline over {@link inlineCss}, {@link injectPreheader}, {@link addDarkModeSupport} and
|
|
101
|
-
* {@link rewriteCidLinks}. Only the steps whose options are provided run.
|
|
102
|
-
* @param html The HTML email.
|
|
103
|
-
* @param options Which steps to apply. See {@link PostProcessOptions}.
|
|
104
|
-
* @returns The processed HTML.
|
|
105
|
-
*/
|
|
106
|
-
declare const postProcessHtml: (html: string, options?: PostProcessOptions) => string;
|
|
107
|
-
export { type DarkModeOptions, type PostProcessOptions, type PreheaderOptions, addDarkModeSupport, extractCidReferences, injectPreheader, inlineCss, postProcessHtml, rewriteCidLinks };
|
package/dist/render/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{extractCidReferences as o,rewriteCidLinks as t}from"../packem_shared/extractCidReferences-BuoTREMp.js";import{default as s}from"../packem_shared/inlineCss-BQ7d2Cyz.js";import{addDarkModeSupport as f}from"../packem_shared/addDarkModeSupport-DZYxULjl.js";import{postProcessHtml as a}from"../packem_shared/postProcessHtml-DqKi2Ucg.js";import{injectPreheader as x}from"../packem_shared/injectPreheader-CF9WHZ7J.js";export{f as addDarkModeSupport,o as extractCidReferences,x as injectPreheader,s as inlineCss,a as postProcessHtml,t as rewriteCidLinks};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|