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