@voltro/plugin-mail 0.81.0 → 0.82.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 +1520 -6
- package/README.md +1 -1
- package/THIRD-PARTY-NOTICES.md +2 -2
- package/dist/index.d.ts +152 -5
- package/dist/index.js +489 -283
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -11,36 +11,39 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
11
11
|
message: u.String,
|
|
12
12
|
transient: u.Boolean,
|
|
13
13
|
status: u.optional(u.Number)
|
|
14
|
-
}) {}, x = (
|
|
14
|
+
}) {}, x = class extends u.TaggedError()("MailWebhookError", {
|
|
15
|
+
provider: u.String,
|
|
16
|
+
message: u.String
|
|
17
|
+
}) {}, S = (e) => ({
|
|
15
18
|
name: e.name,
|
|
16
19
|
props: e.props,
|
|
17
20
|
subject: e.subject,
|
|
18
21
|
render: e.render,
|
|
19
22
|
...e.locale === void 0 ? {} : { locale: e.locale },
|
|
20
23
|
...e.preview === void 0 ? {} : { preview: e.preview }
|
|
21
|
-
}),
|
|
24
|
+
}), C = class extends t.Tag("@voltro/plugin-mail/MailService")() {}, w = (e) => e === void 0 ? [] : typeof e == "string" ? [e] : e, T = (e) => {
|
|
22
25
|
try {
|
|
23
26
|
let t = s.ArrayFormatter.formatErrorSync(e);
|
|
24
27
|
return t.length === 0 ? String(e) : t.map((e) => `${e.path.length > 0 ? e.path.join(".") : "(root)"}: ${e.message}`).join("; ");
|
|
25
28
|
} catch {
|
|
26
29
|
return String(e);
|
|
27
30
|
}
|
|
28
|
-
},
|
|
29
|
-
if (!
|
|
31
|
+
}, E = null, D = async () => {
|
|
32
|
+
if (!E) {
|
|
30
33
|
let e = await import("@react-email/render");
|
|
31
|
-
if (
|
|
34
|
+
if (E = e.render ?? e.default?.render ?? null, !E) throw Error("@react-email/render: `render` export not found (install @react-email/render + react + react-dom)");
|
|
32
35
|
}
|
|
33
|
-
return
|
|
34
|
-
},
|
|
36
|
+
return E;
|
|
37
|
+
}, O = (e, t) => r.gen(function* () {
|
|
35
38
|
let n = e, i = yield* u.decodeUnknown(n.props)(t).pipe(r.mapError((t) => new b({
|
|
36
39
|
provider: "template",
|
|
37
|
-
message: `invalid props for '${e.name}': ${
|
|
40
|
+
message: `invalid props for '${e.name}': ${T(t)}`.slice(0, 500),
|
|
38
41
|
transient: !1
|
|
39
42
|
}))), a = n.subject(i), o = n.render(i);
|
|
40
43
|
return {
|
|
41
44
|
subject: a,
|
|
42
45
|
html: yield* r.tryPromise({
|
|
43
|
-
try: async () => (await
|
|
46
|
+
try: async () => (await D())(o, { plainText: !1 }),
|
|
44
47
|
catch: (t) => new b({
|
|
45
48
|
provider: "template",
|
|
46
49
|
message: `render failed for '${e.name}': ${String(t)}`.slice(0, 500),
|
|
@@ -48,7 +51,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
48
51
|
})
|
|
49
52
|
}),
|
|
50
53
|
text: yield* r.tryPromise({
|
|
51
|
-
try: async () => (await
|
|
54
|
+
try: async () => (await D())(o, { plainText: !0 }),
|
|
52
55
|
catch: (t) => new b({
|
|
53
56
|
provider: "template",
|
|
54
57
|
message: `render(text) failed for '${e.name}': ${String(t)}`.slice(0, 500),
|
|
@@ -56,23 +59,23 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
56
59
|
})
|
|
57
60
|
})
|
|
58
61
|
};
|
|
59
|
-
}),
|
|
60
|
-
for (let t of e)
|
|
61
|
-
},
|
|
62
|
+
}), k = /* @__PURE__ */ new Map(), ee = (e) => e.locale ? `${e.name}:${e.locale}` : e.name, te = (e) => {
|
|
63
|
+
for (let t of e) k.set(ee(t), t);
|
|
64
|
+
}, A = (e, t) => (t ? k.get(`${e}:${t}`) : void 0) ?? k.get(e), ne = () => k.size, re = () => [...k.values()].map((e) => e.locale ? {
|
|
62
65
|
name: e.name,
|
|
63
66
|
locale: e.locale
|
|
64
|
-
} : { name: e.name }), ie = () =>
|
|
67
|
+
} : { name: e.name }), ie = () => k.clear(), ae = (e) => {
|
|
65
68
|
if (typeof e != "object" || !e) return !1;
|
|
66
69
|
let t = e;
|
|
67
70
|
return typeof t.name == "string" && typeof t.subject == "function" && typeof t.render == "function" && t.props !== void 0;
|
|
68
|
-
}, oe = m({ scope: "@voltro/plugin-mail" }),
|
|
69
|
-
let e = /* @__PURE__ */ new Set(), t = (e, t) => `${
|
|
71
|
+
}, oe = m({ scope: "@voltro/plugin-mail" }), j = (e) => e.trim().toLowerCase(), M = "*", N = (e) => e ?? M, P = () => {
|
|
72
|
+
let e = /* @__PURE__ */ new Set(), t = (e, t) => `${N(e)}\u0000${j(t)}`;
|
|
70
73
|
return {
|
|
71
74
|
isSuppressed: (n, i) => r.sync(() => e.has(t(n, i)) || e.has(t(null, i))),
|
|
72
75
|
suppress: (n, i) => r.sync(() => void e.add(t(n, i))),
|
|
73
76
|
unsuppress: (n, i) => r.sync(() => void e.delete(t(n, i)))
|
|
74
77
|
};
|
|
75
|
-
},
|
|
78
|
+
}, se = (e) => {
|
|
76
79
|
let t = e.DB_URL ?? e.DB_PRIMARY_URL;
|
|
77
80
|
if (t) {
|
|
78
81
|
let e = new URL(t);
|
|
@@ -91,7 +94,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
91
94
|
password: e.DB_PASSWORD ?? e.PG_PASSWORD ?? "app",
|
|
92
95
|
database: e.DB_DATABASE ?? e.PG_DATABASE ?? "app"
|
|
93
96
|
};
|
|
94
|
-
},
|
|
97
|
+
}, ce = (t = {}) => {
|
|
95
98
|
let n = t.table ?? "_voltro_mail_suppression", i = null, a = (n) => g.layerConfig({
|
|
96
99
|
host: e.succeed(n.host),
|
|
97
100
|
port: e.succeed(n.port),
|
|
@@ -108,7 +111,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
108
111
|
let r = yield* h.SqlClient;
|
|
109
112
|
return (yield* r`
|
|
110
113
|
SELECT 1 AS one FROM ${r(n)}
|
|
111
|
-
WHERE email = ${
|
|
114
|
+
WHERE email = ${j(t)} AND tenant IN (${N(e)}, ${M}) LIMIT 1`).length > 0;
|
|
112
115
|
});
|
|
113
116
|
return r.tryPromise(() => a.runPromise(o)).pipe(r.catchAll(() => r.succeed(!1)));
|
|
114
117
|
},
|
|
@@ -119,11 +122,11 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
119
122
|
let r = yield* h.SqlClient;
|
|
120
123
|
yield* r`
|
|
121
124
|
INSERT INTO ${r(n)} (tenant, email, reason, created_at)
|
|
122
|
-
VALUES (${
|
|
125
|
+
VALUES (${N(e)}, ${j(t)}, ${a}, ${Date.now()})
|
|
123
126
|
ON CONFLICT (tenant, email) DO UPDATE SET reason = ${a}, created_at = ${Date.now()}`;
|
|
124
127
|
});
|
|
125
128
|
return r.tryPromise(() => o.runPromise(s)).pipe(r.catchAll((e) => r.sync(() => oe.warn("suppress write failed", {
|
|
126
|
-
email:
|
|
129
|
+
email: j(t),
|
|
127
130
|
message: String(e)
|
|
128
131
|
}))));
|
|
129
132
|
},
|
|
@@ -132,12 +135,12 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
132
135
|
if (!a) return r.void;
|
|
133
136
|
let o = r.gen(function* () {
|
|
134
137
|
let r = yield* h.SqlClient;
|
|
135
|
-
yield* r`DELETE FROM ${r(n)} WHERE tenant = ${
|
|
138
|
+
yield* r`DELETE FROM ${r(n)} WHERE tenant = ${N(e)} AND email = ${j(t)}`;
|
|
136
139
|
});
|
|
137
140
|
return r.tryPromise(() => a.runPromise(o)).pipe(r.catchAll(() => r.void));
|
|
138
141
|
},
|
|
139
142
|
activate: async (e) => {
|
|
140
|
-
let t = o.make(a(
|
|
143
|
+
let t = o.make(a(se(e)));
|
|
141
144
|
await t.runPromise(r.gen(function* () {
|
|
142
145
|
let e = yield* h.SqlClient;
|
|
143
146
|
yield* e`
|
|
@@ -155,8 +158,8 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
155
158
|
i = null, e && await e.dispose();
|
|
156
159
|
}
|
|
157
160
|
};
|
|
158
|
-
},
|
|
159
|
-
let e = /* @__PURE__ */ new Map(), t = (e, t) => `${
|
|
161
|
+
}, le = m({ scope: "@voltro/plugin-mail" }), F = (e) => e ?? "*", I = () => {
|
|
162
|
+
let e = /* @__PURE__ */ new Map(), t = (e, t) => `${F(e)} ${t}`;
|
|
160
163
|
return {
|
|
161
164
|
reserve: (n, i) => r.sync(() => {
|
|
162
165
|
let r = t(n, i);
|
|
@@ -171,7 +174,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
171
174
|
}),
|
|
172
175
|
record: (n, i, a) => r.sync(() => void e.set(t(n, i), a))
|
|
173
176
|
};
|
|
174
|
-
},
|
|
177
|
+
}, ue = (e) => {
|
|
175
178
|
let t = e.DB_URL ?? e.DB_PRIMARY_URL;
|
|
176
179
|
if (t) {
|
|
177
180
|
let e = new URL(t);
|
|
@@ -190,7 +193,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
190
193
|
password: e.DB_PASSWORD ?? e.PG_PASSWORD ?? "app",
|
|
191
194
|
database: e.DB_DATABASE ?? e.PG_DATABASE ?? "app"
|
|
192
195
|
};
|
|
193
|
-
},
|
|
196
|
+
}, de = (t = {}) => {
|
|
194
197
|
let n = t.table ?? "_voltro_mail_idempotency", i = null, a = (n) => g.layerConfig({
|
|
195
198
|
host: e.succeed(n.host),
|
|
196
199
|
port: e.succeed(n.port),
|
|
@@ -207,12 +210,12 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
207
210
|
let r = yield* h.SqlClient;
|
|
208
211
|
if ((yield* r`
|
|
209
212
|
INSERT INTO ${r(n)} (tenant, key, created_at)
|
|
210
|
-
VALUES (${
|
|
213
|
+
VALUES (${F(e)}, ${t}, ${Date.now()})
|
|
211
214
|
ON CONFLICT (tenant, key) DO NOTHING
|
|
212
215
|
RETURNING key`).length > 0) return { won: !0 };
|
|
213
216
|
let i = (yield* r`
|
|
214
217
|
SELECT result_id, result_provider FROM ${r(n)}
|
|
215
|
-
WHERE tenant = ${
|
|
218
|
+
WHERE tenant = ${F(e)} AND key = ${t} LIMIT 1`)[0];
|
|
216
219
|
return i && i.result_id && i.result_provider ? {
|
|
217
220
|
won: !1,
|
|
218
221
|
prior: {
|
|
@@ -221,7 +224,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
221
224
|
}
|
|
222
225
|
} : { won: !1 };
|
|
223
226
|
});
|
|
224
|
-
return r.tryPromise(() => a.runPromise(o)).pipe(r.catchAll((e) => r.sync(() => (
|
|
227
|
+
return r.tryPromise(() => a.runPromise(o)).pipe(r.catchAll((e) => r.sync(() => (le.warn("idempotency reserve failed — skipping send to avoid a double", {
|
|
225
228
|
key: t,
|
|
226
229
|
message: String(e)
|
|
227
230
|
}), { won: !1 }))));
|
|
@@ -233,15 +236,15 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
233
236
|
let r = yield* h.SqlClient;
|
|
234
237
|
yield* r`
|
|
235
238
|
UPDATE ${r(n)} SET result_id = ${a.id}, result_provider = ${a.provider}
|
|
236
|
-
WHERE tenant = ${
|
|
239
|
+
WHERE tenant = ${F(e)} AND key = ${t}`;
|
|
237
240
|
});
|
|
238
|
-
return r.tryPromise(() => o.runPromise(s)).pipe(r.catchAll((e) => r.sync(() =>
|
|
241
|
+
return r.tryPromise(() => o.runPromise(s)).pipe(r.catchAll((e) => r.sync(() => le.warn("idempotency record failed", {
|
|
239
242
|
key: t,
|
|
240
243
|
message: String(e)
|
|
241
244
|
}))));
|
|
242
245
|
},
|
|
243
246
|
activate: async (e) => {
|
|
244
|
-
let t = o.make(a(
|
|
247
|
+
let t = o.make(a(ue(e)));
|
|
245
248
|
await t.runPromise(r.gen(function* () {
|
|
246
249
|
let e = yield* h.SqlClient;
|
|
247
250
|
yield* e`
|
|
@@ -260,7 +263,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
260
263
|
i = null, e && await e.dispose();
|
|
261
264
|
}
|
|
262
265
|
};
|
|
263
|
-
}, fe = m({ scope: "@voltro/plugin-mail" }),
|
|
266
|
+
}, fe = m({ scope: "@voltro/plugin-mail" }), L = () => {
|
|
264
267
|
let e = /* @__PURE__ */ new Map();
|
|
265
268
|
return {
|
|
266
269
|
hold: (t) => r.sync(() => void e.set(t.id, t)),
|
|
@@ -271,12 +274,12 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
271
274
|
}),
|
|
272
275
|
size: () => r.sync(() => e.size)
|
|
273
276
|
};
|
|
274
|
-
}, pe = 0,
|
|
277
|
+
}, pe = 0, R = (e) => e.idempotencyKey ?? `held-${pe += 1}`, z = (e) => ({
|
|
275
278
|
shouldHold: (t) => !e.providerSchedules && t !== void 0 && t.getTime() > Date.now(),
|
|
276
279
|
hold: (t) => {
|
|
277
280
|
let { scheduledAt: n, ...r } = t;
|
|
278
281
|
return e.store.hold({
|
|
279
|
-
id:
|
|
282
|
+
id: R(t),
|
|
280
283
|
dueAt: t.scheduledAt?.getTime() ?? Date.now(),
|
|
281
284
|
message: r
|
|
282
285
|
});
|
|
@@ -290,7 +293,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
290
293
|
return n.length;
|
|
291
294
|
}),
|
|
292
295
|
pending: () => e.store.size()
|
|
293
|
-
}), me = 0,
|
|
296
|
+
}), me = 0, B = (e) => `${e}-${me += 1}`, he = (e) => e === void 0 || e === 408 || e === 429 || e >= 500, V = (e, t) => {
|
|
294
297
|
let n = t.status;
|
|
295
298
|
return new b({
|
|
296
299
|
provider: e,
|
|
@@ -298,7 +301,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
298
301
|
transient: e === "smtp" ? n === void 0 || n >= 400 && n < 500 : he(n),
|
|
299
302
|
...n === void 0 ? {} : { status: n }
|
|
300
303
|
});
|
|
301
|
-
},
|
|
304
|
+
}, H = (e, t, n, i, a) => r.tryPromise({
|
|
302
305
|
try: async () => {
|
|
303
306
|
let e = await fetch(t, {
|
|
304
307
|
method: "POST",
|
|
@@ -319,20 +322,97 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
319
322
|
},
|
|
320
323
|
catch: (t) => {
|
|
321
324
|
let n = t;
|
|
322
|
-
return
|
|
325
|
+
return V(e, {
|
|
323
326
|
...typeof n.status == "number" ? { status: n.status } : {},
|
|
324
327
|
message: n.message ?? t?.message ?? String(t)
|
|
325
328
|
});
|
|
326
329
|
}
|
|
327
|
-
}),
|
|
330
|
+
}), U = 500, ge = (e, t) => {
|
|
331
|
+
let n = e.stream === "broadcast" ? t?.broadcast ?? "broadcast" : t?.transactional;
|
|
332
|
+
return {
|
|
333
|
+
...n === void 0 ? {} : { route: n },
|
|
334
|
+
from: e.from,
|
|
335
|
+
to: w(e.to),
|
|
336
|
+
...e.cc ? { cc: w(e.cc) } : {},
|
|
337
|
+
...e.bcc ? { bcc: w(e.bcc) } : {},
|
|
338
|
+
...e.replyTo ? { reply_to: [e.replyTo] } : {},
|
|
339
|
+
subject: e.subject,
|
|
340
|
+
...e.html === void 0 ? {} : { html: e.html },
|
|
341
|
+
...e.text === void 0 ? {} : { text: e.text },
|
|
342
|
+
...e.headers ? { headers: e.headers } : {},
|
|
343
|
+
...e.metadata ? { metadata: e.metadata } : {},
|
|
344
|
+
...e.tags && e.tags.length > 0 ? { tags: e.tags.map((e) => ({
|
|
345
|
+
name: e,
|
|
346
|
+
value: "1"
|
|
347
|
+
})) } : {},
|
|
348
|
+
...e.scheduledAt ? { scheduled_at: e.scheduledAt.toISOString() } : {},
|
|
349
|
+
...e.attachments ? { attachments: e.attachments.map((e) => ({
|
|
350
|
+
filename: e.filename,
|
|
351
|
+
content: e.content,
|
|
352
|
+
...e.contentType ? { content_type: e.contentType } : {}
|
|
353
|
+
})) } : {}
|
|
354
|
+
};
|
|
355
|
+
}, _e = (e, t, n, i = Math.floor(Date.now() / 1e3), a = 300) => r.tryPromise({
|
|
356
|
+
try: async () => {
|
|
357
|
+
let r = (e) => {
|
|
358
|
+
throw new x({
|
|
359
|
+
provider: "lettermint",
|
|
360
|
+
message: e
|
|
361
|
+
});
|
|
362
|
+
}, o = (t) => Object.entries(e).find(([e]) => e.toLowerCase() === t)?.[1];
|
|
363
|
+
n === "" && r("no webhook secret configured"), t === "" && r("empty body");
|
|
364
|
+
let s = o("x-lettermint-signature") ?? r("missing x-lettermint-signature"), c = o("x-lettermint-delivery") ?? r("missing x-lettermint-delivery"), l, u = [];
|
|
365
|
+
for (let e of s.split(",")) {
|
|
366
|
+
let [t, n] = e.trim().split("=", 2);
|
|
367
|
+
t === "t" && n !== void 0 && /^\d+$/.test(n) ? l = n : t === "v1" && n !== void 0 && /^[a-fA-F0-9]{64}$/.test(n) && u.push(n.toLowerCase());
|
|
368
|
+
}
|
|
369
|
+
(l === void 0 || u.length === 0) && r("malformed signature header"), c !== l && r("signature and delivery timestamps differ"), Math.abs(i - Number(l)) > a && r("signature timestamp outside the tolerance");
|
|
370
|
+
let { createHmac: d, timingSafeEqual: f } = await import("node:crypto"), p = d("sha256", n).update(`${l}.`).update(t).digest();
|
|
371
|
+
u.some((e) => f(Buffer.from(e, "hex"), p)) || r("signature does not match");
|
|
372
|
+
try {
|
|
373
|
+
return JSON.parse(t);
|
|
374
|
+
} catch {
|
|
375
|
+
return r("body is not JSON");
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
catch: (e) => e instanceof x ? e : new x({
|
|
379
|
+
provider: "lettermint",
|
|
380
|
+
message: e?.message ?? String(e)
|
|
381
|
+
})
|
|
382
|
+
}), ve = (e) => {
|
|
383
|
+
let t = (e.baseUrl ?? "https://api.lettermint.co/v1").replace(/\/+$/, ""), n = {
|
|
384
|
+
"x-lettermint-token": e.projectToken,
|
|
385
|
+
accept: "application/json"
|
|
386
|
+
};
|
|
387
|
+
return {
|
|
388
|
+
name: "lettermint",
|
|
389
|
+
supportsScheduling: !0,
|
|
390
|
+
managesUnsubscribe: !0,
|
|
391
|
+
send: (r) => H("lettermint", `${t}/send`, {
|
|
392
|
+
...n,
|
|
393
|
+
...r.idempotencyKey === void 0 ? {} : { "idempotency-key": r.idempotencyKey }
|
|
394
|
+
}, ge(r, e.routes), async (e) => ({
|
|
395
|
+
id: (await e.json()).message_id ?? "lettermint",
|
|
396
|
+
provider: "lettermint"
|
|
397
|
+
})),
|
|
398
|
+
sendBatch: (i) => r.forEach(Array.from({ length: Math.ceil(i.length / U) }, (e, t) => i.slice(t * U, (t + 1) * U)), (r) => H("lettermint", `${t}/send/batch`, n, r.map((t) => ge(t, e.routes)), async (e) => {
|
|
399
|
+
let t = await e.json();
|
|
400
|
+
return r.map((e, n) => ({
|
|
401
|
+
id: t[n]?.message_id ?? "lettermint",
|
|
402
|
+
provider: "lettermint"
|
|
403
|
+
}));
|
|
404
|
+
})).pipe(r.map((e) => e.flat())),
|
|
405
|
+
webhook: { verify: _e }
|
|
406
|
+
};
|
|
407
|
+
}, ye = (e) => ({
|
|
328
408
|
from: e.from,
|
|
329
|
-
to:
|
|
409
|
+
to: w(e.to),
|
|
330
410
|
subject: e.subject,
|
|
331
411
|
html: e.html,
|
|
332
412
|
text: e.text,
|
|
333
413
|
reply_to: e.replyTo,
|
|
334
|
-
cc: e.cc ?
|
|
335
|
-
bcc: e.bcc ?
|
|
414
|
+
cc: e.cc ? w(e.cc) : void 0,
|
|
415
|
+
bcc: e.bcc ? w(e.bcc) : void 0,
|
|
336
416
|
headers: e.headers,
|
|
337
417
|
tags: e.tags?.map((e) => ({
|
|
338
418
|
name: e,
|
|
@@ -344,34 +424,40 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
344
424
|
content: e.content,
|
|
345
425
|
...e.contentType ? { content_type: e.contentType } : {}
|
|
346
426
|
}))
|
|
347
|
-
}),
|
|
427
|
+
}), be = (e) => ({
|
|
348
428
|
name: "resend",
|
|
349
429
|
supportsScheduling: !0,
|
|
350
|
-
send: (t) =>
|
|
430
|
+
send: (t) => H("resend", "https://api.resend.com/emails", { authorization: `Bearer ${e.apiKey}` }, ye(t), async (e) => ({
|
|
351
431
|
id: (await e.json()).id ?? "resend",
|
|
352
432
|
provider: "resend"
|
|
353
433
|
})),
|
|
354
|
-
sendBatch: (t) =>
|
|
434
|
+
sendBatch: (t) => H("resend", "https://api.resend.com/emails/batch", { authorization: `Bearer ${e.apiKey}` }, t.map(ye), async (e) => {
|
|
355
435
|
let n = (await e.json()).data ?? [];
|
|
356
436
|
return t.map((e, t) => ({
|
|
357
437
|
id: n[t]?.id ?? "resend",
|
|
358
438
|
provider: "resend"
|
|
359
439
|
}));
|
|
360
440
|
})
|
|
361
|
-
}),
|
|
441
|
+
}), xe = (e) => ({
|
|
362
442
|
name: "postmark",
|
|
363
|
-
send: (t) =>
|
|
443
|
+
send: (t) => H("postmark", "https://api.postmarkapp.com/email", {
|
|
364
444
|
"x-postmark-server-token": e.serverToken,
|
|
365
445
|
accept: "application/json"
|
|
366
446
|
}, {
|
|
367
447
|
From: t.from,
|
|
368
|
-
To:
|
|
448
|
+
To: w(t.to).join(","),
|
|
369
449
|
Subject: t.subject,
|
|
370
450
|
HtmlBody: t.html,
|
|
371
451
|
TextBody: t.text,
|
|
372
452
|
ReplyTo: t.replyTo,
|
|
373
|
-
Cc: t.cc ?
|
|
374
|
-
Bcc: t.bcc ?
|
|
453
|
+
Cc: t.cc ? w(t.cc).join(",") : void 0,
|
|
454
|
+
Bcc: t.bcc ? w(t.bcc).join(",") : void 0,
|
|
455
|
+
MessageStream: t.stream === "broadcast" ? e.streams?.broadcast ?? "broadcast" : e.streams?.transactional ?? "outbound",
|
|
456
|
+
Headers: t.headers ? Object.entries(t.headers).map(([e, t]) => ({
|
|
457
|
+
Name: e,
|
|
458
|
+
Value: t
|
|
459
|
+
})) : void 0,
|
|
460
|
+
Metadata: t.metadata,
|
|
375
461
|
Attachments: t.attachments?.map((e) => ({
|
|
376
462
|
Name: e.filename,
|
|
377
463
|
Content: e.content,
|
|
@@ -381,44 +467,44 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
381
467
|
id: (await e.json()).MessageID ?? "postmark",
|
|
382
468
|
provider: "postmark"
|
|
383
469
|
}))
|
|
384
|
-
}),
|
|
470
|
+
}), Se = (e) => [...e.text ? [{
|
|
385
471
|
type: "text/plain",
|
|
386
472
|
value: e.text
|
|
387
473
|
}] : [], ...e.html ? [{
|
|
388
474
|
type: "text/html",
|
|
389
475
|
value: e.html
|
|
390
|
-
}] : []],
|
|
391
|
-
to:
|
|
392
|
-
cc: e.cc ?
|
|
393
|
-
bcc: e.bcc ?
|
|
476
|
+
}] : []], Ce = (e) => ({
|
|
477
|
+
to: w(e.to).map((e) => ({ email: e })),
|
|
478
|
+
cc: e.cc ? w(e.cc).map((e) => ({ email: e })) : void 0,
|
|
479
|
+
bcc: e.bcc ? w(e.bcc).map((e) => ({ email: e })) : void 0,
|
|
394
480
|
subject: e.subject,
|
|
395
481
|
...e.scheduledAt ? { send_at: Math.floor(e.scheduledAt.getTime() / 1e3) } : {}
|
|
396
|
-
}),
|
|
482
|
+
}), we = (e) => e.attachments?.map((e) => ({
|
|
397
483
|
filename: e.filename,
|
|
398
484
|
content: e.content,
|
|
399
485
|
type: e.contentType ?? "application/octet-stream",
|
|
400
486
|
disposition: "attachment"
|
|
401
|
-
})),
|
|
487
|
+
})), Te = (e) => {
|
|
402
488
|
let t = (e) => ({
|
|
403
|
-
personalizations: [
|
|
489
|
+
personalizations: [Ce(e)],
|
|
404
490
|
from: { email: e.from },
|
|
405
491
|
reply_to: e.replyTo ? { email: e.replyTo } : void 0,
|
|
406
492
|
subject: e.subject,
|
|
407
|
-
content:
|
|
408
|
-
attachments:
|
|
493
|
+
content: Se(e),
|
|
494
|
+
attachments: we(e)
|
|
409
495
|
});
|
|
410
496
|
return {
|
|
411
497
|
name: "sendgrid",
|
|
412
498
|
supportsScheduling: !0,
|
|
413
|
-
send: (n) =>
|
|
499
|
+
send: (n) => H("sendgrid", "https://api.sendgrid.com/v3/mail/send", { authorization: `Bearer ${e.apiKey}` }, t(n), async (e) => ({
|
|
414
500
|
id: e.headers.get("x-message-id") ?? "sendgrid",
|
|
415
501
|
provider: "sendgrid"
|
|
416
502
|
})),
|
|
417
|
-
sendBatch: (t) =>
|
|
418
|
-
personalizations: t.map(
|
|
503
|
+
sendBatch: (t) => H("sendgrid", "https://api.sendgrid.com/v3/mail/send", { authorization: `Bearer ${e.apiKey}` }, {
|
|
504
|
+
personalizations: t.map(Ce),
|
|
419
505
|
from: { email: t[0]?.from },
|
|
420
506
|
subject: t[0]?.subject,
|
|
421
|
-
content:
|
|
507
|
+
content: Se(t[0] ?? {})
|
|
422
508
|
}, async (e) => {
|
|
423
509
|
let n = e.headers.get("x-message-id") ?? "sendgrid";
|
|
424
510
|
return t.map((e, t) => ({
|
|
@@ -427,35 +513,35 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
427
513
|
}));
|
|
428
514
|
})
|
|
429
515
|
};
|
|
430
|
-
},
|
|
516
|
+
}, Ee = (e, t) => {
|
|
431
517
|
let n = t, r = n.response?.status;
|
|
432
|
-
return
|
|
518
|
+
return V(e, {
|
|
433
519
|
...typeof r == "number" ? { status: r } : {},
|
|
434
520
|
message: n.message ?? String(t)
|
|
435
521
|
});
|
|
436
|
-
},
|
|
522
|
+
}, De = (e, t, n) => r.gen(function* () {
|
|
437
523
|
let i = yield* (yield* v.HttpClient).execute(t);
|
|
438
524
|
if (i.status < 200 || i.status >= 300) {
|
|
439
525
|
let t = yield* i.text.pipe(r.catchAll(() => r.succeed("")));
|
|
440
|
-
return yield* r.fail(
|
|
526
|
+
return yield* r.fail(V(e, {
|
|
441
527
|
status: i.status,
|
|
442
528
|
message: t || `HTTP ${i.status}`
|
|
443
529
|
}));
|
|
444
530
|
}
|
|
445
531
|
return yield* n(i);
|
|
446
|
-
}).pipe(r.catchAll((t) => t instanceof b ? r.fail(t) : r.fail(
|
|
532
|
+
}).pipe(r.catchAll((t) => t instanceof b ? r.fail(t) : r.fail(Ee(e, t))), r.provide(_.layer)), Oe = (e) => ({ sign: async (t, n, r) => {
|
|
447
533
|
let { createHash: i, createHmac: a } = await import("node:crypto"), o = (e) => i("sha256").update(e).digest("hex"), s = (e, t) => a("sha256", e).update(t).digest(), c = (/* @__PURE__ */ new Date()).toISOString().replace(/[:-]|\.\d{3}/g, ""), l = c.slice(0, 8), u = `host:${new URL(t).host}\nx-amz-date:${c}\n` + (e.sessionToken ? `x-amz-security-token:${e.sessionToken}\n` : ""), d = e.sessionToken ? "host;x-amz-date;x-amz-security-token" : "host;x-amz-date", f = `POST\n${n}\n\n${u}\n${d}\n${o(r)}`, p = `${l}/${e.region}/ses/aws4_request`, m = `AWS4-HMAC-SHA256\n${c}\n${p}\n${o(f)}`, h = a("sha256", s(s(s(s(`AWS4${e.secretAccessKey}`, l), e.region), "ses"), "aws4_request")).update(m).digest("hex");
|
|
448
534
|
return {
|
|
449
535
|
authorization: `AWS4-HMAC-SHA256 Credential=${e.accessKeyId}/${p}, SignedHeaders=${d}, Signature=${h}`,
|
|
450
536
|
"x-amz-date": c,
|
|
451
537
|
...e.sessionToken ? { "x-amz-security-token": e.sessionToken } : {}
|
|
452
538
|
};
|
|
453
|
-
} }),
|
|
539
|
+
} }), ke = (e) => JSON.stringify({
|
|
454
540
|
FromEmailAddress: e.from,
|
|
455
541
|
Destination: {
|
|
456
|
-
ToAddresses:
|
|
457
|
-
...e.cc ? { CcAddresses:
|
|
458
|
-
...e.bcc ? { BccAddresses:
|
|
542
|
+
ToAddresses: w(e.to),
|
|
543
|
+
...e.cc ? { CcAddresses: w(e.cc) } : {},
|
|
544
|
+
...e.bcc ? { BccAddresses: w(e.bcc) } : {}
|
|
459
545
|
},
|
|
460
546
|
...e.replyTo ? { ReplyToAddresses: [e.replyTo] } : {},
|
|
461
547
|
Content: { Simple: {
|
|
@@ -474,16 +560,16 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
474
560
|
} } : {}
|
|
475
561
|
}
|
|
476
562
|
} }
|
|
477
|
-
}),
|
|
478
|
-
let t =
|
|
563
|
+
}), Ae = (e) => {
|
|
564
|
+
let t = Oe(e), n = `https://email.${e.region}.amazonaws.com`, i = "/v2/email/outbound-emails";
|
|
479
565
|
return {
|
|
480
566
|
name: "ses",
|
|
481
567
|
send: (e) => r.gen(function* () {
|
|
482
|
-
let a =
|
|
568
|
+
let a = ke(e), o = yield* r.tryPromise({
|
|
483
569
|
try: () => t.sign(n, i, a),
|
|
484
|
-
catch: (e) =>
|
|
570
|
+
catch: (e) => V("ses", { message: e?.message ?? String(e) })
|
|
485
571
|
});
|
|
486
|
-
return yield*
|
|
572
|
+
return yield* De("ses", y.post(`${n}${i}`).pipe(y.setHeaders({
|
|
487
573
|
"content-type": "application/json",
|
|
488
574
|
...o
|
|
489
575
|
}), y.bodyUnsafeJson(JSON.parse(a))), (e) => e.json.pipe(r.map((e) => ({
|
|
@@ -492,29 +578,29 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
492
578
|
}))));
|
|
493
579
|
})
|
|
494
580
|
};
|
|
495
|
-
},
|
|
581
|
+
}, je = (e) => {
|
|
496
582
|
let t = [["from", e.from], ["subject", e.subject]];
|
|
497
|
-
for (let n of
|
|
498
|
-
if (e.cc) for (let n of
|
|
499
|
-
if (e.bcc) for (let n of
|
|
583
|
+
for (let n of w(e.to)) t.push(["to", n]);
|
|
584
|
+
if (e.cc) for (let n of w(e.cc)) t.push(["cc", n]);
|
|
585
|
+
if (e.bcc) for (let n of w(e.bcc)) t.push(["bcc", n]);
|
|
500
586
|
if (e.replyTo && t.push(["h:Reply-To", e.replyTo]), e.html && t.push(["html", e.html]), e.text && t.push(["text", e.text]), e.headers) for (let [n, r] of Object.entries(e.headers)) t.push([`h:${n}`, r]);
|
|
501
587
|
if (e.tags) for (let n of e.tags) t.push(["o:tag", n]);
|
|
502
588
|
return e.scheduledAt && t.push(["o:deliverytime", e.scheduledAt.toUTCString()]), t;
|
|
503
|
-
},
|
|
589
|
+
}, Me = (e) => {
|
|
504
590
|
let t = `${e.baseUrl ?? "https://api.mailgun.net"}/v3/${e.domain}/messages`;
|
|
505
591
|
return {
|
|
506
592
|
name: "mailgun",
|
|
507
593
|
supportsScheduling: !0,
|
|
508
594
|
send: (n) => {
|
|
509
595
|
let i = new URLSearchParams();
|
|
510
|
-
for (let [e, t] of
|
|
511
|
-
return
|
|
596
|
+
for (let [e, t] of je(n)) i.append(e, t);
|
|
597
|
+
return De("mailgun", y.post(t).pipe(y.setHeader("authorization", `Basic ${Buffer.from(`api:${e.apiKey}`).toString("base64")}`), y.bodyText(i.toString(), "application/x-www-form-urlencoded")), (e) => e.json.pipe(r.map((e) => ({
|
|
512
598
|
id: e.id ?? "mailgun",
|
|
513
599
|
provider: "mailgun"
|
|
514
600
|
}))));
|
|
515
601
|
}
|
|
516
602
|
};
|
|
517
|
-
},
|
|
603
|
+
}, W = (e) => {
|
|
518
604
|
let t = null;
|
|
519
605
|
return {
|
|
520
606
|
name: "smtp",
|
|
@@ -533,9 +619,9 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
533
619
|
return {
|
|
534
620
|
id: (await t.sendMail({
|
|
535
621
|
from: n.from,
|
|
536
|
-
to:
|
|
537
|
-
cc: n.cc ?
|
|
538
|
-
bcc: n.bcc ?
|
|
622
|
+
to: w(n.to),
|
|
623
|
+
cc: n.cc ? w(n.cc) : void 0,
|
|
624
|
+
bcc: n.bcc ? w(n.bcc) : void 0,
|
|
539
625
|
replyTo: n.replyTo,
|
|
540
626
|
subject: n.subject,
|
|
541
627
|
html: n.html,
|
|
@@ -553,52 +639,53 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
553
639
|
},
|
|
554
640
|
catch: (e) => {
|
|
555
641
|
let t = e;
|
|
556
|
-
return
|
|
642
|
+
return V("smtp", {
|
|
557
643
|
...typeof t.responseCode == "number" ? { status: t.responseCode } : {},
|
|
558
644
|
message: t.message ?? String(e)
|
|
559
645
|
});
|
|
560
646
|
}
|
|
561
647
|
})
|
|
562
648
|
};
|
|
563
|
-
},
|
|
649
|
+
}, Ne = (e) => ({
|
|
564
650
|
name: "console",
|
|
565
651
|
send: (t) => r.sync(() => {
|
|
566
|
-
let n = `✉ to=${
|
|
652
|
+
let n = `✉ to=${w(t.to).join(",")} subject=${JSON.stringify(t.subject)}`;
|
|
567
653
|
return e ? e(n, { from: t.from }) : process.stdout.write(`[mail:console] ${n}\n`), {
|
|
568
|
-
id:
|
|
654
|
+
id: B("console"),
|
|
569
655
|
provider: "console"
|
|
570
656
|
};
|
|
571
657
|
})
|
|
572
|
-
}),
|
|
573
|
-
|
|
574
|
-
},
|
|
658
|
+
}), G = [], Pe = () => [...G], Fe = () => {
|
|
659
|
+
G.length = 0;
|
|
660
|
+
}, Ie = () => ({
|
|
575
661
|
name: "memory",
|
|
576
|
-
send: (e) => r.sync(() => (
|
|
577
|
-
id:
|
|
662
|
+
send: (e) => r.sync(() => (G.push(e), {
|
|
663
|
+
id: B("mem"),
|
|
578
664
|
provider: "memory"
|
|
579
665
|
}))
|
|
580
|
-
}),
|
|
666
|
+
}), K = m({ scope: "@voltro/plugin-mail" }), Le = (e) => {
|
|
581
667
|
if (e.provider !== void 0 && typeof e.provider != "string") return e.provider;
|
|
582
668
|
let t = e.env ?? {}, n = e.provider ?? t.MAIL_PROVIDER ?? "console", r = (e, t) => {
|
|
583
669
|
if (!t) throw Error(`@voltro/plugin-mail: provider '${n}' needs apiKey or ${e}`);
|
|
584
670
|
return t;
|
|
585
671
|
};
|
|
586
672
|
switch (n) {
|
|
587
|
-
case "
|
|
588
|
-
case "
|
|
589
|
-
case "
|
|
590
|
-
case "
|
|
673
|
+
case "lettermint": return ve({ projectToken: r("LETTERMINT_PROJECT_TOKEN", e.apiKey ?? t.LETTERMINT_PROJECT_TOKEN) });
|
|
674
|
+
case "resend": return be({ apiKey: r("RESEND_API_KEY", e.apiKey ?? t.RESEND_API_KEY) });
|
|
675
|
+
case "postmark": return xe({ serverToken: r("POSTMARK_SERVER_TOKEN", e.apiKey ?? t.POSTMARK_SERVER_TOKEN) });
|
|
676
|
+
case "sendgrid": return Te({ apiKey: r("SENDGRID_API_KEY", e.apiKey ?? t.SENDGRID_API_KEY) });
|
|
677
|
+
case "ses": return Ae({
|
|
591
678
|
region: r("AWS_REGION", t.AWS_REGION),
|
|
592
679
|
accessKeyId: r("AWS_ACCESS_KEY_ID", t.AWS_ACCESS_KEY_ID),
|
|
593
680
|
secretAccessKey: r("AWS_SECRET_ACCESS_KEY", t.AWS_SECRET_ACCESS_KEY),
|
|
594
681
|
...t.AWS_SESSION_TOKEN ? { sessionToken: t.AWS_SESSION_TOKEN } : {}
|
|
595
682
|
});
|
|
596
|
-
case "mailgun": return
|
|
683
|
+
case "mailgun": return Me({
|
|
597
684
|
apiKey: r("MAILGUN_API_KEY", e.apiKey ?? t.MAILGUN_API_KEY),
|
|
598
685
|
domain: r("MAILGUN_DOMAIN", t.MAILGUN_DOMAIN),
|
|
599
686
|
...t.MAILGUN_BASE_URL ? { baseUrl: t.MAILGUN_BASE_URL } : {}
|
|
600
687
|
});
|
|
601
|
-
case "smtp": return t.SMTP_URL ?
|
|
688
|
+
case "smtp": return t.SMTP_URL ? W({ url: t.SMTP_URL }) : W({
|
|
602
689
|
host: r("SMTP_URL/SMTP_HOST", t.SMTP_HOST),
|
|
603
690
|
...t.SMTP_PORT ? { port: Number(t.SMTP_PORT) } : {},
|
|
604
691
|
secure: t.SMTP_SECURE === "true",
|
|
@@ -607,32 +694,45 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
607
694
|
pass: t.SMTP_PASS
|
|
608
695
|
} } : {}
|
|
609
696
|
});
|
|
610
|
-
case "memory": return
|
|
611
|
-
default: return
|
|
697
|
+
case "memory": return Ie();
|
|
698
|
+
default: return Ne((e, t) => K.info(e, t));
|
|
612
699
|
}
|
|
613
|
-
},
|
|
614
|
-
let t = e.attempts ?? 3, n = e.suppression ??
|
|
700
|
+
}, q = (e) => {
|
|
701
|
+
let t = e.attempts ?? 3, n = e.suppression ?? P(), i = e.allowlist, a = e.production !== !0 && i !== void 0 && i.length > 0, o = (e) => i.some((t) => t.toLowerCase() === e.toLowerCase()), s = l.recurs(t).pipe(l.whileInput((e) => e.transient)), c = /* @__PURE__ */ new Map();
|
|
615
702
|
for (let t of e.templates ?? []) c.set(t.locale ? `${t.name}:${t.locale}` : t.name, t);
|
|
616
|
-
let u = (e, t) => (t ? c.get(`${e}:${t}`) : void 0) ?? c.get(e) ??
|
|
703
|
+
let u = (e, t) => (t ? c.get(`${e}:${t}`) : void 0) ?? c.get(e) ?? A(e, t), d = /* @__PURE__ */ new Map(), f = (e, t) => `${e.name}|${e.locale ?? ""}|${JSON.stringify(t ?? null)}`, p = (e, t) => {
|
|
617
704
|
if (d.set(e, t), d.size > 500) {
|
|
618
705
|
let e = d.keys().next().value;
|
|
619
706
|
e !== void 0 && d.delete(e);
|
|
620
707
|
}
|
|
621
|
-
}, m = e.idempotency ??
|
|
622
|
-
store: e.schedule ??
|
|
708
|
+
}, m = e.idempotency ?? I(), h = z({
|
|
709
|
+
store: e.schedule ?? L(),
|
|
623
710
|
providerSchedules: e.provider.supportsScheduling === !0,
|
|
624
|
-
flush: (e) =>
|
|
625
|
-
}), g = (t) =>
|
|
711
|
+
flush: (e) => y(e)
|
|
712
|
+
}), g = (t) => {
|
|
713
|
+
if (t.stream !== "broadcast" || e.provider.managesUnsubscribe === !0) return r.succeed(void 0);
|
|
714
|
+
let n = t.unsubscribe;
|
|
715
|
+
if (n === void 0 || !n.oneClickUrl.startsWith("https://")) return r.fail(new b({
|
|
716
|
+
provider: e.provider.name,
|
|
717
|
+
message: `a broadcast needs \`unsubscribe: { oneClickUrl }\` (an https URL) — '${e.provider.name}' does not unsubscribe recipients itself`,
|
|
718
|
+
transient: !1
|
|
719
|
+
}));
|
|
720
|
+
let i = [`<${n.oneClickUrl}>`, ...n.mailto === void 0 ? [] : [`<mailto:${n.mailto.replace(/^mailto:/, "")}>`]];
|
|
721
|
+
return r.succeed({
|
|
722
|
+
"List-Unsubscribe": i.join(", "),
|
|
723
|
+
"List-Unsubscribe-Post": "List-Unsubscribe=One-Click"
|
|
724
|
+
});
|
|
725
|
+
}, _ = (t) => r.gen(function* () {
|
|
626
726
|
let i = t.from ?? e.from;
|
|
627
727
|
if (!i) return yield* r.fail(new b({
|
|
628
728
|
provider: e.provider.name,
|
|
629
729
|
message: "no `from` — set mailPlugin({ from }) or pass message.from",
|
|
630
730
|
transient: !1
|
|
631
731
|
}));
|
|
632
|
-
let s =
|
|
732
|
+
let s = w(t.to);
|
|
633
733
|
if (a) {
|
|
634
734
|
let n = s.filter(o);
|
|
635
|
-
if (n.length === 0) return
|
|
735
|
+
if (n.length === 0) return K.warn("mail skipped — no recipient in allowlist (non-production)", {
|
|
636
736
|
to: s.join(","),
|
|
637
737
|
subject: t.subject
|
|
638
738
|
}), e.onSent?.({
|
|
@@ -654,7 +754,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
654
754
|
}
|
|
655
755
|
let c = t.tenantId ?? null, l = [];
|
|
656
756
|
for (let e of s) (yield* n.isSuppressed(c, e)) || l.push(e);
|
|
657
|
-
if (l.length === 0) return
|
|
757
|
+
if (l.length === 0) return K.warn("mail skipped — all recipients suppressed", {
|
|
658
758
|
to: s.join(","),
|
|
659
759
|
subject: t.subject
|
|
660
760
|
}), e.onSent?.({
|
|
@@ -673,20 +773,24 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
673
773
|
}
|
|
674
774
|
};
|
|
675
775
|
s = l;
|
|
676
|
-
let u = t.replyTo ?? e.replyTo
|
|
776
|
+
let u = t.replyTo ?? e.replyTo, d = yield* g(t), f = d === void 0 ? t.headers : {
|
|
777
|
+
...t.headers,
|
|
778
|
+
...d
|
|
779
|
+
};
|
|
677
780
|
return {
|
|
678
781
|
kind: "send",
|
|
679
782
|
message: {
|
|
680
783
|
...t,
|
|
681
784
|
from: i,
|
|
682
785
|
to: s,
|
|
683
|
-
...u === void 0 ? {} : { replyTo: u }
|
|
786
|
+
...u === void 0 ? {} : { replyTo: u },
|
|
787
|
+
...f === void 0 ? {} : { headers: f }
|
|
684
788
|
}
|
|
685
789
|
};
|
|
686
|
-
}),
|
|
687
|
-
let n =
|
|
790
|
+
}), v = (t) => {
|
|
791
|
+
let n = w(t.to);
|
|
688
792
|
return e.provider.send(t).pipe(r.retry(s), r.tap((i) => r.sync(() => {
|
|
689
|
-
|
|
793
|
+
K.info("mail sent", {
|
|
690
794
|
provider: i.provider,
|
|
691
795
|
id: i.id,
|
|
692
796
|
to: n.join(",")
|
|
@@ -700,7 +804,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
700
804
|
ts: Date.now()
|
|
701
805
|
});
|
|
702
806
|
})), r.tapError((i) => r.sync(() => {
|
|
703
|
-
|
|
807
|
+
K.warn("mail failed", {
|
|
704
808
|
provider: i.provider,
|
|
705
809
|
status: i.status,
|
|
706
810
|
message: i.message
|
|
@@ -715,14 +819,14 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
715
819
|
ts: Date.now()
|
|
716
820
|
});
|
|
717
821
|
})));
|
|
718
|
-
},
|
|
719
|
-
let n = yield*
|
|
720
|
-
return n.kind === "skip" ? n.result : h.shouldHold(t.scheduledAt) ? (yield* h.hold(n.message),
|
|
822
|
+
}, y = (t) => r.gen(function* () {
|
|
823
|
+
let n = yield* _(t);
|
|
824
|
+
return n.kind === "skip" ? n.result : h.shouldHold(t.scheduledAt) ? (yield* h.hold(n.message), K.info("mail scheduled (held for later flush)", {
|
|
721
825
|
subject: n.message.subject,
|
|
722
826
|
dueAt: t.scheduledAt?.toISOString()
|
|
723
827
|
}), e.onSent?.({
|
|
724
828
|
id: "scheduled",
|
|
725
|
-
to:
|
|
829
|
+
to: w(n.message.to),
|
|
726
830
|
from: n.message.from,
|
|
727
831
|
subject: n.message.subject,
|
|
728
832
|
provider: "scheduled",
|
|
@@ -731,16 +835,16 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
731
835
|
}), {
|
|
732
836
|
id: "scheduled",
|
|
733
837
|
provider: "scheduled"
|
|
734
|
-
}) : yield*
|
|
735
|
-
}),
|
|
838
|
+
}) : yield* v(n.message);
|
|
839
|
+
}), x = (e, t, n) => t === void 0 ? n : r.gen(function* () {
|
|
736
840
|
let r = yield* m.reserve(e, t);
|
|
737
|
-
if (!r.won) return
|
|
841
|
+
if (!r.won) return K.info("mail idempotent replay — skipped", { key: t }), r.prior ?? {
|
|
738
842
|
id: "idempotent-replay",
|
|
739
843
|
provider: "idempotent"
|
|
740
844
|
};
|
|
741
845
|
let i = yield* n;
|
|
742
846
|
return yield* m.record(e, t, i), i;
|
|
743
|
-
}),
|
|
847
|
+
}), S = (e) => r.suspend(() => {
|
|
744
848
|
let t = u(e.template, e.locale);
|
|
745
849
|
if (!t) return r.fail(new b({
|
|
746
850
|
provider: "template",
|
|
@@ -748,7 +852,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
748
852
|
transient: !1
|
|
749
853
|
}));
|
|
750
854
|
let n = f(t, e.props), i = d.get(n);
|
|
751
|
-
return (i ? r.succeed(i) :
|
|
855
|
+
return (i ? r.succeed(i) : O(t, e.props).pipe(r.tap((e) => r.sync(() => p(n, e))))).pipe(r.map((t) => ({
|
|
752
856
|
to: e.to,
|
|
753
857
|
subject: t.subject,
|
|
754
858
|
html: t.html,
|
|
@@ -762,33 +866,36 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
762
866
|
...e.attachments === void 0 ? {} : { attachments: e.attachments },
|
|
763
867
|
...e.tenantId === void 0 ? {} : { tenantId: e.tenantId },
|
|
764
868
|
...e.scheduledAt === void 0 ? {} : { scheduledAt: e.scheduledAt },
|
|
765
|
-
...e.idempotencyKey === void 0 ? {} : { idempotencyKey: e.idempotencyKey }
|
|
869
|
+
...e.idempotencyKey === void 0 ? {} : { idempotencyKey: e.idempotencyKey },
|
|
870
|
+
...e.stream === void 0 ? {} : { stream: e.stream },
|
|
871
|
+
...e.unsubscribe === void 0 ? {} : { unsubscribe: e.unsubscribe },
|
|
872
|
+
...e.metadata === void 0 ? {} : { metadata: e.metadata }
|
|
766
873
|
})));
|
|
767
|
-
}),
|
|
874
|
+
}), C = (e) => "template" in e ? S(e) : r.succeed(e);
|
|
768
875
|
return {
|
|
769
876
|
service: {
|
|
770
877
|
send: (e) => r.gen(function* () {
|
|
771
|
-
let t = yield*
|
|
772
|
-
return yield*
|
|
878
|
+
let t = yield* C(e);
|
|
879
|
+
return yield* x(t.tenantId ?? null, t.idempotencyKey, y(t));
|
|
773
880
|
}),
|
|
774
881
|
sendBatch: (t) => r.gen(function* () {
|
|
775
|
-
let n = yield* r.forEach(t,
|
|
882
|
+
let n = yield* r.forEach(t, C), i = n.some((e) => e.idempotencyKey !== void 0 || h.shouldHold(e.scheduledAt));
|
|
776
883
|
if (e.provider.sendBatch && !i) {
|
|
777
884
|
let t = [], i = [];
|
|
778
885
|
for (let e of n) {
|
|
779
|
-
let n = yield*
|
|
886
|
+
let n = yield* _(e);
|
|
780
887
|
n.kind === "skip" ? i.push(n.result) : (i.push(t.length), t.push(n.message));
|
|
781
888
|
}
|
|
782
889
|
let a = t.length > 0 ? yield* e.provider.sendBatch(t).pipe(r.retry(s), r.tap((n) => r.sync(() => {
|
|
783
890
|
t.forEach((t, r) => {
|
|
784
891
|
let i = n[r];
|
|
785
|
-
i && (
|
|
892
|
+
i && (K.info("mail sent (batch)", {
|
|
786
893
|
provider: i.provider,
|
|
787
894
|
id: i.id,
|
|
788
|
-
to:
|
|
895
|
+
to: w(t.to).join(",")
|
|
789
896
|
}), e.onSent?.({
|
|
790
897
|
id: i.id,
|
|
791
|
-
to:
|
|
898
|
+
to: w(t.to),
|
|
792
899
|
from: t.from,
|
|
793
900
|
subject: t.subject,
|
|
794
901
|
provider: i.provider,
|
|
@@ -796,7 +903,7 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
796
903
|
ts: Date.now()
|
|
797
904
|
}));
|
|
798
905
|
});
|
|
799
|
-
})), r.tapError((e) => r.sync(() =>
|
|
906
|
+
})), r.tapError((e) => r.sync(() => K.warn("mail batch failed", {
|
|
800
907
|
provider: e.provider,
|
|
801
908
|
status: e.status,
|
|
802
909
|
message: e.message
|
|
@@ -806,15 +913,60 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
806
913
|
provider: e.provider.name
|
|
807
914
|
} : t);
|
|
808
915
|
}
|
|
809
|
-
return yield* r.forEach(n, (e) =>
|
|
916
|
+
return yield* r.forEach(n, (e) => x(e.tenantId ?? null, e.idempotencyKey, y(e)));
|
|
810
917
|
}),
|
|
811
918
|
suppress: n.suppress,
|
|
812
919
|
unsuppress: n.unsuppress,
|
|
813
|
-
isSuppressed: n.isSuppressed
|
|
920
|
+
isSuppressed: n.isSuppressed,
|
|
921
|
+
provider: {
|
|
922
|
+
name: e.provider.name,
|
|
923
|
+
managesUnsubscribe: e.provider.managesUnsubscribe === !0
|
|
924
|
+
}
|
|
814
925
|
},
|
|
815
926
|
scheduler: h
|
|
816
927
|
};
|
|
817
|
-
},
|
|
928
|
+
}, Re = (e) => q(e).service, ze = (e) => a.succeed(C, Re(e)), J = (e, t, n, r, i, a = {}) => {
|
|
929
|
+
if (!t) return null;
|
|
930
|
+
let o = a.metadata !== null && typeof a.metadata == "object" ? Object.fromEntries(Object.entries(a.metadata).filter(([, e]) => typeof e == "string")) : void 0;
|
|
931
|
+
return {
|
|
932
|
+
type: e,
|
|
933
|
+
email: t,
|
|
934
|
+
provider: n,
|
|
935
|
+
raw: i,
|
|
936
|
+
...r === void 0 ? {} : { messageId: r },
|
|
937
|
+
...a.eventId === void 0 ? {} : { eventId: a.eventId },
|
|
938
|
+
...o === void 0 ? {} : { metadata: o }
|
|
939
|
+
};
|
|
940
|
+
}, Be = (e) => e === "message.hard_bounced" ? "bounced" : e === "message.spam_complaint" ? "complained" : e === "message.unsubscribed" ? "unsubscribed" : e === "message.delivered" ? "delivered" : e === "message.opened" ? "opened" : e === "message.clicked" ? "clicked" : "unknown", Ve = (e) => e === "hard_bounce" ? "bounced" : e === "spam_complaint" ? "complained" : e === "unsubscribe" ? "unsubscribed" : "unknown", He = (e) => e === "email.bounced" ? "bounced" : e === "email.complained" ? "complained" : e === "email.delivered" ? "delivered" : e === "email.opened" ? "opened" : e === "email.clicked" ? "clicked" : "unknown", Ue = (e) => e === "Bounce" ? "bounced" : e === "SpamComplaint" ? "complained" : e === "Delivery" ? "delivered" : e === "Open" ? "opened" : e === "Click" ? "clicked" : "unknown", We = (e) => e === "bounce" || e === "dropped" ? "bounced" : e === "spamreport" ? "complained" : e === "delivered" ? "delivered" : e === "open" ? "opened" : e === "click" ? "clicked" : "unknown", Y = (e, t) => {
|
|
941
|
+
let n = [], r = (e) => {
|
|
942
|
+
e && n.push(e);
|
|
943
|
+
};
|
|
944
|
+
if (e === "sendgrid" && Array.isArray(t)) {
|
|
945
|
+
for (let e of t) r(J(We(String(e.event)), e.email, "sendgrid", e.sg_message_id, e));
|
|
946
|
+
return n;
|
|
947
|
+
}
|
|
948
|
+
let i = t ?? {};
|
|
949
|
+
if (e === "lettermint") {
|
|
950
|
+
let e = String(i.event), a = i.data ?? {}, o = typeof i.id == "string" ? i.id : void 0;
|
|
951
|
+
return e === "suppression.added" ? (a.type === "email" && r(J(Ve(String(a.reason)), a.value, "lettermint", void 0, t, { eventId: o })), n) : e === "suppression.removed" ? (a.type === "email" && r(J("unsuppressed", a.value, "lettermint", void 0, t, { eventId: o })), n) : (e.startsWith("message.") && r(J(Be(e), a.recipient, "lettermint", a.message_id, t, {
|
|
952
|
+
eventId: o,
|
|
953
|
+
metadata: a.metadata
|
|
954
|
+
})), n);
|
|
955
|
+
}
|
|
956
|
+
if (e === "resend") {
|
|
957
|
+
let e = i.data ?? {}, a = Array.isArray(e.to) ? e.to[0] : e.to ?? e.email;
|
|
958
|
+
return r(J(He(String(i.type)), a, "resend", e.email_id, t)), n;
|
|
959
|
+
}
|
|
960
|
+
if (e === "postmark") {
|
|
961
|
+
let e = i.Email ?? i.Recipient;
|
|
962
|
+
return r(J(Ue(String(i.RecordType)), e, "postmark", i.MessageID, t, { metadata: i.Metadata })), n;
|
|
963
|
+
}
|
|
964
|
+
return n;
|
|
965
|
+
}, Ge = (e, t, n, i = null) => r.gen(function* () {
|
|
966
|
+
let r = Y(e, t);
|
|
967
|
+
for (let e of r) e.type === "bounced" || e.type === "complained" ? yield* n.suppress(i, e.email, e.type) : e.type === "unsuppressed" && (yield* n.unsuppress(i, e.email));
|
|
968
|
+
return r;
|
|
969
|
+
}), Ke = (e = 200) => {
|
|
818
970
|
let t = [];
|
|
819
971
|
return {
|
|
820
972
|
record: (n) => {
|
|
@@ -825,7 +977,37 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
825
977
|
t.length = 0;
|
|
826
978
|
}
|
|
827
979
|
};
|
|
828
|
-
},
|
|
980
|
+
}, qe = {
|
|
981
|
+
lettermint: "api.lettermint.co",
|
|
982
|
+
resend: "api.resend.com",
|
|
983
|
+
postmark: "api.postmarkapp.com",
|
|
984
|
+
sendgrid: "api.sendgrid.com"
|
|
985
|
+
}, Je = (e) => {
|
|
986
|
+
try {
|
|
987
|
+
return new URL(e).host || void 0;
|
|
988
|
+
} catch {
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
}, Ye = (e) => {
|
|
992
|
+
let t = e.env ?? {}, n = (e) => [{
|
|
993
|
+
destination: e,
|
|
994
|
+
purpose: "outgoing email: recipients, subject and body",
|
|
995
|
+
data: ["personal", "content"],
|
|
996
|
+
disable: "MAIL_PROVIDER=console (or memory) logs mail instead of sending it — none leaves"
|
|
997
|
+
}];
|
|
998
|
+
if (e.provider !== void 0 && typeof e.provider != "string") {
|
|
999
|
+
let t = e.provider.name;
|
|
1000
|
+
return t === "console" || t === "memory" ? [] : n(`runtime:the '${t}' mail provider`);
|
|
1001
|
+
}
|
|
1002
|
+
let r = e.provider ?? t.MAIL_PROVIDER ?? "console", i = qe[r];
|
|
1003
|
+
if (i !== void 0) return n(i);
|
|
1004
|
+
switch (r) {
|
|
1005
|
+
case "ses": return n(t.AWS_REGION ? `email.${t.AWS_REGION}.amazonaws.com` : "runtime:email.<AWS_REGION>.amazonaws.com");
|
|
1006
|
+
case "mailgun": return n((t.MAILGUN_BASE_URL ? Je(t.MAILGUN_BASE_URL) : void 0) ?? "api.mailgun.net");
|
|
1007
|
+
case "smtp": return n((t.SMTP_URL ? Je(t.SMTP_URL) : void 0) ?? (t.SMTP_HOST ? `${t.SMTP_HOST}${t.SMTP_PORT ? `:${t.SMTP_PORT}` : ""}` : void 0) ?? "runtime:SMTP_URL or SMTP_HOST");
|
|
1008
|
+
default: return [];
|
|
1009
|
+
}
|
|
1010
|
+
}, X = (e, t) => {
|
|
829
1011
|
if (t > 6) return null;
|
|
830
1012
|
switch (e._tag) {
|
|
831
1013
|
case "StringKeyword": return "";
|
|
@@ -835,50 +1017,34 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
835
1017
|
case "Enums": return e.enums.length > 0 ? e.enums[0][1] : null;
|
|
836
1018
|
case "TypeLiteral": {
|
|
837
1019
|
let n = {};
|
|
838
|
-
for (let r of e.propertySignatures) typeof r.name == "string" && (n[r.name] =
|
|
1020
|
+
for (let r of e.propertySignatures) typeof r.name == "string" && (n[r.name] = X(r.type, t + 1));
|
|
839
1021
|
return n;
|
|
840
1022
|
}
|
|
841
1023
|
case "TupleType": return [];
|
|
842
|
-
case "Union": return e.types.length > 0 ?
|
|
843
|
-
case "Refinement": return
|
|
844
|
-
case "Transformation": return
|
|
845
|
-
case "Suspend": return
|
|
1024
|
+
case "Union": return e.types.length > 0 ? X(e.types[0], t + 1) : null;
|
|
1025
|
+
case "Refinement": return X(e.from, t + 1);
|
|
1026
|
+
case "Transformation": return X(e.from, t + 1);
|
|
1027
|
+
case "Suspend": return X(e.f(), t + 1);
|
|
846
1028
|
default: return null;
|
|
847
1029
|
}
|
|
848
|
-
},
|
|
849
|
-
type: e,
|
|
850
|
-
email: t,
|
|
851
|
-
provider: n,
|
|
852
|
-
raw: i,
|
|
853
|
-
...r === void 0 ? {} : { messageId: r }
|
|
854
|
-
} : null, Ne = (e) => e === "email.bounced" ? "bounced" : e === "email.complained" ? "complained" : e === "email.delivered" ? "delivered" : e === "email.opened" ? "opened" : e === "email.clicked" ? "clicked" : "unknown", Pe = (e) => e === "Bounce" ? "bounced" : e === "SpamComplaint" ? "complained" : e === "Delivery" ? "delivered" : e === "Open" ? "opened" : e === "Click" ? "clicked" : "unknown", Fe = (e) => e === "bounce" || e === "dropped" ? "bounced" : e === "spamreport" ? "complained" : e === "delivered" ? "delivered" : e === "open" ? "opened" : e === "click" ? "clicked" : "unknown", Ie = (e, t) => {
|
|
855
|
-
let n = [], r = (e) => {
|
|
856
|
-
e && n.push(e);
|
|
857
|
-
};
|
|
858
|
-
if (e === "sendgrid" && Array.isArray(t)) {
|
|
859
|
-
for (let e of t) r($(Fe(String(e.event)), e.email, "sendgrid", e.sg_message_id, e));
|
|
860
|
-
return n;
|
|
861
|
-
}
|
|
862
|
-
let i = t ?? {};
|
|
863
|
-
if (e === "resend") {
|
|
864
|
-
let e = i.data ?? {}, a = Array.isArray(e.to) ? e.to[0] : e.to ?? e.email;
|
|
865
|
-
return r($(Ne(String(i.type)), a, "resend", e.email_id, t)), n;
|
|
866
|
-
}
|
|
867
|
-
if (e === "postmark") {
|
|
868
|
-
let e = i.Email ?? i.Recipient;
|
|
869
|
-
return r($(Pe(String(i.RecordType)), e, "postmark", i.MessageID, t)), n;
|
|
870
|
-
}
|
|
871
|
-
return n;
|
|
872
|
-
}, Le = (e, t, n, i = null) => r.gen(function* () {
|
|
873
|
-
let r = Ie(e, t);
|
|
874
|
-
for (let e of r) (e.type === "bounced" || e.type === "complained") && (yield* n.suppress(i, e.email, e.type));
|
|
875
|
-
return r;
|
|
876
|
-
}), Re = p([
|
|
1030
|
+
}, Xe = (e) => X(e.ast, 0), Z = p([
|
|
877
1031
|
{
|
|
878
1032
|
name: "MAIL_PROVIDER",
|
|
879
1033
|
required: !1,
|
|
880
1034
|
secret: !1,
|
|
881
|
-
description: "Provider to build from env: resend | postmark | sendgrid | ses | mailgun | smtp | console | memory. Default: console."
|
|
1035
|
+
description: "Provider to build from env: lettermint | resend | postmark | sendgrid | ses | mailgun | smtp | console | memory. Default: console."
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
name: "LETTERMINT_PROJECT_TOKEN",
|
|
1039
|
+
required: !1,
|
|
1040
|
+
secret: !0,
|
|
1041
|
+
description: "Lettermint project API token (lm_…) — needed only when the provider resolves to lettermint (or pass options.apiKey)."
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
name: "MAIL_WEBHOOK_SECRET",
|
|
1045
|
+
required: !1,
|
|
1046
|
+
secret: !0,
|
|
1047
|
+
description: "Signing secret of the provider delivery-event webhook mounted by mailPlugin({ webhook })."
|
|
882
1048
|
},
|
|
883
1049
|
{
|
|
884
1050
|
name: "RESEND_API_KEY",
|
|
@@ -1054,12 +1220,44 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
1054
1220
|
secret: !1,
|
|
1055
1221
|
description: "Fallback Postgres database name for the postgres suppression store."
|
|
1056
1222
|
}
|
|
1057
|
-
]),
|
|
1058
|
-
|
|
1223
|
+
]), Q = "@voltro/plugin-mail", $ = (e, t) => ({
|
|
1224
|
+
status: e,
|
|
1225
|
+
contentType: "application/json",
|
|
1226
|
+
body: JSON.stringify(t)
|
|
1227
|
+
}), Ze = (e, t, n, i) => {
|
|
1228
|
+
let a = e.webhook;
|
|
1229
|
+
if (a === void 0) throw Error(`@voltro/plugin-mail: provider '${e.name}' has no webhook verifier — mailPlugin({ webhook }) needs one (lettermint)`);
|
|
1230
|
+
let o = n.secret ?? Z.read("MAIL_WEBHOOK_SECRET");
|
|
1231
|
+
if (!o && i) throw Error("@voltro/plugin-mail: mailPlugin({ webhook }) needs a signing secret in production — set MAIL_WEBHOOK_SECRET");
|
|
1232
|
+
return {
|
|
1233
|
+
method: "POST",
|
|
1234
|
+
path: n.path ?? "/webhooks/mail",
|
|
1235
|
+
handle: async (i) => {
|
|
1236
|
+
if (!o) return $(503, { error: "mail webhook secret not configured" });
|
|
1237
|
+
let s = await r.runPromise(r.either(a.verify(i.headers, new TextDecoder().decode(i.rawBody), o)));
|
|
1238
|
+
if (s._tag === "Left") return $(401, { error: s.left.message });
|
|
1239
|
+
let c = Y(e.name, s.right);
|
|
1240
|
+
try {
|
|
1241
|
+
for (let e of c) {
|
|
1242
|
+
e.type === "bounced" || e.type === "complained" ? await r.runPromise(t.suppress(null, e.email, e.type)) : e.type === "unsuppressed" && await r.runPromise(t.unsuppress(null, e.email));
|
|
1243
|
+
let a = n.onEvent?.(e, { store: i.store });
|
|
1244
|
+
r.isEffect(a) ? await r.runPromise(a) : await a;
|
|
1245
|
+
}
|
|
1246
|
+
} catch (e) {
|
|
1247
|
+
return {
|
|
1248
|
+
...$(500, { error: "mail event handling failed" }),
|
|
1249
|
+
cause: e
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
return $(200, { received: c.length });
|
|
1253
|
+
}
|
|
1254
|
+
};
|
|
1255
|
+
}, Qe = (e = {}) => {
|
|
1256
|
+
let t = process.env, o = Le({
|
|
1059
1257
|
...e.provider === void 0 ? {} : { provider: e.provider },
|
|
1060
1258
|
...e.apiKey === void 0 ? {} : { apiKey: e.apiKey },
|
|
1061
1259
|
env: t
|
|
1062
|
-
}), s =
|
|
1260
|
+
}), s = Z.read("MAIL_ALLOWLIST"), c = e.allowlist ?? (s ? s.split(",").map((e) => e.trim()).filter(Boolean) : void 0), u = t.NODE_ENV === "production", p = e.suppression === void 0 || e.suppression === "memory" ? P() : e.suppression === "postgres" ? ce() : e.suppression, m = e.idempotency === void 0 || e.idempotency === "memory" ? I() : e.idempotency === "postgres" ? de() : e.idempotency, h = e.schedule ?? L(), g = Ke(), _ = q({
|
|
1063
1261
|
provider: o,
|
|
1064
1262
|
suppression: p,
|
|
1065
1263
|
idempotency: m,
|
|
@@ -1071,112 +1269,120 @@ var b = class extends u.TaggedError()("MailError", {
|
|
|
1071
1269
|
...e.attempts === void 0 ? {} : { attempts: e.attempts },
|
|
1072
1270
|
...e.templates === void 0 ? {} : { templates: e.templates },
|
|
1073
1271
|
production: u
|
|
1074
|
-
}), v = a.succeed(
|
|
1075
|
-
for (let t of e.templates ?? [])
|
|
1076
|
-
let
|
|
1272
|
+
}), v = a.succeed(C, _.service), y = o.supportsScheduling !== !0, b = e.scheduleTickMs ?? 1e3, x, S = /* @__PURE__ */ new Map();
|
|
1273
|
+
for (let t of e.templates ?? []) S.set(t.locale ? `${t.name}:${t.locale}` : t.name, t);
|
|
1274
|
+
let w = (e, t) => (t ? S.get(`${e}:${t}`) : void 0) ?? S.get(e) ?? A(e, t), T = (e) => ({
|
|
1077
1275
|
name: e.name,
|
|
1078
1276
|
...e.locale ? { locale: e.locale } : {},
|
|
1079
|
-
props: e.preview ??
|
|
1080
|
-
}),
|
|
1277
|
+
props: e.preview ?? Xe(e.props)
|
|
1278
|
+
}), E = () => {
|
|
1081
1279
|
let t = /* @__PURE__ */ new Map();
|
|
1082
|
-
for (let n of e.templates ?? []) t.set(n.locale ? `${n.name}:${n.locale}` : n.name,
|
|
1083
|
-
for (let e of
|
|
1084
|
-
let n =
|
|
1085
|
-
n && t.set(e.locale ? `${e.name}:${e.locale}` : e.name,
|
|
1280
|
+
for (let n of e.templates ?? []) t.set(n.locale ? `${n.name}:${n.locale}` : n.name, T(n));
|
|
1281
|
+
for (let e of re()) {
|
|
1282
|
+
let n = A(e.name, e.locale);
|
|
1283
|
+
n && t.set(e.locale ? `${e.name}:${e.locale}` : e.name, T(n));
|
|
1086
1284
|
}
|
|
1087
1285
|
return [...t.values()];
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
description: "Discovered + registered email templates",
|
|
1120
|
-
handler: () => r.succeed({
|
|
1121
|
-
kind: "json",
|
|
1122
|
-
data: { templates: T() }
|
|
1123
|
-
})
|
|
1124
|
-
},
|
|
1125
|
-
{
|
|
1126
|
-
method: "POST",
|
|
1127
|
-
path: "/preview",
|
|
1128
|
-
description: "Render a template to html + text without sending — body { template, props, locale? }",
|
|
1129
|
-
handler: (e) => r.gen(function* () {
|
|
1130
|
-
let t = {};
|
|
1131
|
-
try {
|
|
1132
|
-
t = JSON.parse(e.body || "{}");
|
|
1133
|
-
} catch {
|
|
1134
|
-
return {
|
|
1135
|
-
kind: "json",
|
|
1136
|
-
status: 400,
|
|
1137
|
-
data: { error: "invalid JSON body" }
|
|
1138
|
-
};
|
|
1139
|
-
}
|
|
1140
|
-
if (!t.template) return {
|
|
1141
|
-
kind: "json",
|
|
1142
|
-
status: 400,
|
|
1143
|
-
data: { error: "missing \"template\"" }
|
|
1144
|
-
};
|
|
1145
|
-
let n = ee(t.template, t.locale);
|
|
1146
|
-
return n ? yield* E(n, t.props).pipe(r.map((e) => ({
|
|
1147
|
-
kind: "json",
|
|
1148
|
-
data: e
|
|
1149
|
-
})), r.catchAll((e) => r.succeed({
|
|
1286
|
+
}, D = e.webhook === void 0 ? [] : [Ze(o, p, e.webhook, u)], k = [
|
|
1287
|
+
{
|
|
1288
|
+
method: "GET",
|
|
1289
|
+
path: "/outbox",
|
|
1290
|
+
scope: "process",
|
|
1291
|
+
description: "Recent sent mail (dev capture buffer), newest first",
|
|
1292
|
+
handler: () => r.succeed({
|
|
1293
|
+
kind: "json",
|
|
1294
|
+
data: { sends: g.list() }
|
|
1295
|
+
})
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
method: "GET",
|
|
1299
|
+
path: "/templates",
|
|
1300
|
+
scope: "declaration",
|
|
1301
|
+
description: "Discovered + registered email templates",
|
|
1302
|
+
handler: () => r.succeed({
|
|
1303
|
+
kind: "json",
|
|
1304
|
+
data: { templates: E() }
|
|
1305
|
+
})
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
method: "POST",
|
|
1309
|
+
path: "/preview",
|
|
1310
|
+
description: "Render a template to html + text without sending — body { template, props, locale? }",
|
|
1311
|
+
handler: (e) => r.gen(function* () {
|
|
1312
|
+
let t = {};
|
|
1313
|
+
try {
|
|
1314
|
+
t = JSON.parse(e.body || "{}");
|
|
1315
|
+
} catch {
|
|
1316
|
+
return {
|
|
1150
1317
|
kind: "json",
|
|
1151
1318
|
status: 400,
|
|
1152
|
-
data: { error:
|
|
1153
|
-
}))) : {
|
|
1154
|
-
kind: "json",
|
|
1155
|
-
status: 404,
|
|
1156
|
-
data: { error: `unknown template '${t.template}'` }
|
|
1319
|
+
data: { error: "invalid JSON body" }
|
|
1157
1320
|
};
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1321
|
+
}
|
|
1322
|
+
if (!t.template) return {
|
|
1323
|
+
kind: "json",
|
|
1324
|
+
status: 400,
|
|
1325
|
+
data: { error: "missing \"template\"" }
|
|
1326
|
+
};
|
|
1327
|
+
let n = w(t.template, t.locale);
|
|
1328
|
+
return n ? yield* O(n, t.props).pipe(r.map((e) => ({
|
|
1329
|
+
kind: "json",
|
|
1330
|
+
data: e
|
|
1331
|
+
})), r.catchAll((e) => r.succeed({
|
|
1332
|
+
kind: "json",
|
|
1333
|
+
status: 400,
|
|
1334
|
+
data: { error: e.message }
|
|
1335
|
+
}))) : {
|
|
1336
|
+
kind: "json",
|
|
1337
|
+
status: 404,
|
|
1338
|
+
data: { error: `unknown template '${t.template}'` }
|
|
1339
|
+
};
|
|
1340
|
+
})
|
|
1341
|
+
}
|
|
1342
|
+
];
|
|
1343
|
+
return {
|
|
1344
|
+
...d({
|
|
1345
|
+
name: f({
|
|
1346
|
+
base: Q,
|
|
1347
|
+
alias: e.alias,
|
|
1348
|
+
instance: e.name
|
|
1349
|
+
}),
|
|
1350
|
+
baseName: Q,
|
|
1351
|
+
description: "Transactional and broadcast email — MailService over Lettermint / Resend / Postmark / SendGrid / SES / Mailgun / SMTP (+ console/memory), with default-from, transient retry, suppression and a dev allowlist.",
|
|
1352
|
+
permissions: [
|
|
1353
|
+
"network:outbound:*",
|
|
1354
|
+
"inspect:read",
|
|
1355
|
+
"inspect:write"
|
|
1356
|
+
],
|
|
1357
|
+
declaredEnv: Z.declared,
|
|
1358
|
+
egress: Ye({
|
|
1359
|
+
...e.provider === void 0 ? {} : { provider: e.provider },
|
|
1360
|
+
env: t
|
|
1361
|
+
}),
|
|
1362
|
+
services: v,
|
|
1363
|
+
inspectEndpoints: k,
|
|
1364
|
+
...D.length > 0 ? { httpRoutes: D } : {},
|
|
1365
|
+
onActivate: (t) => r.gen(function* () {
|
|
1366
|
+
if (p.activate && (yield* r.promise(() => p.activate(t.env))), m.activate && (yield* r.promise(() => m.activate(t.env))), y) {
|
|
1367
|
+
let e = _.scheduler.tick().pipe(r.repeat(l.spaced(n.millis(Math.max(50, b)))));
|
|
1368
|
+
x = r.runFork(e);
|
|
1369
|
+
}
|
|
1370
|
+
t.logger.info("mail active", {
|
|
1371
|
+
provider: o.name,
|
|
1372
|
+
from: e.from ?? null,
|
|
1373
|
+
allowlist: c?.length ?? 0,
|
|
1374
|
+
suppression: typeof e.suppression == "string" ? e.suppression : e.suppression ? "custom" : "memory",
|
|
1375
|
+
idempotency: typeof e.idempotency == "string" ? e.idempotency : e.idempotency ? "custom" : "memory",
|
|
1376
|
+
scheduling: o.supportsScheduling === !0 ? "native" : "held",
|
|
1377
|
+
production: u
|
|
1378
|
+
});
|
|
1379
|
+
}),
|
|
1380
|
+
onDeactivate: () => r.gen(function* () {
|
|
1381
|
+
x !== void 0 && (yield* i.interrupt(x), x = void 0), p.deactivate && (yield* r.promise(() => p.deactivate())), m.deactivate && (yield* r.promise(() => m.deactivate()));
|
|
1382
|
+
})
|
|
1175
1383
|
}),
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
})
|
|
1179
|
-
});
|
|
1384
|
+
service: _.service
|
|
1385
|
+
};
|
|
1180
1386
|
};
|
|
1181
1387
|
//#endregion
|
|
1182
|
-
export { b as MailError,
|
|
1388
|
+
export { b as MailError, C as MailService, x as MailWebhookError, Re as buildMailService, q as buildMailServiceWithScheduler, ie as clearGlobalTemplates, Fe as clearMailBuffer, Ne as consoleProvider, Ke as createOutbox, S as defineEmail, ne as globalTemplateCount, Ge as handleMailEvents, R as heldId, ae as isEmailTemplate, ve as lettermintProvider, re as listGlobalTemplates, A as lookupGlobalTemplate, Ye as mailEgressOf, Qe as mailPlugin, ze as mailServiceLayer, Me as mailgunProvider, z as makeScheduler, I as memoryIdempotencyStore, Ie as memoryProvider, L as memoryScheduleStore, P as memorySuppressionStore, Y as parseMailEvent, de as postgresIdempotencyStore, ce as postgresSuppressionStore, xe as postmarkProvider, Pe as readMailBuffer, te as registerEmailTemplates, O as renderTemplate, be as resendProvider, Le as resolveProvider, Te as sendgridProvider, Ae as sesProvider, W as smtpProvider, _e as verifyLettermintSignature };
|