@voltro/plugin-mail 0.82.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/dist/index.js CHANGED
@@ -1,313 +1,229 @@
1
- import { Config as e, Context as t, Duration as n, Effect as r, Fiber as i, Layer as a, ManagedRuntime as o, ParseResult as s, Redacted as c, Schedule as l, Schema as u } from "effect";
2
- import { definePlugin as d, pluginInstanceName as f } from "@voltro/protocol";
3
- import { pluginEnv as p } from "@voltro/env";
4
- import { createLogger as m } from "@voltro/logger";
5
- import { SqlClient as h } from "@effect/sql";
6
- import { PgClient as g } from "@effect/sql-pg";
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 b = class extends u.TaggedError()("MailError", {
10
- provider: u.String,
11
- message: u.String,
12
- transient: u.Boolean,
13
- status: u.optional(u.Number)
14
- }) {}, x = class extends u.TaggedError()("MailWebhookError", {
15
- provider: u.String,
16
- message: u.String
17
- }) {}, S = (e) => ({
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) => ({
18
17
  name: e.name,
19
18
  props: e.props,
20
19
  subject: e.subject,
21
20
  render: e.render,
22
21
  ...e.locale === void 0 ? {} : { locale: e.locale },
23
22
  ...e.preview === void 0 ? {} : { preview: e.preview }
24
- }), C = class extends t.Tag("@voltro/plugin-mail/MailService")() {}, w = (e) => e === void 0 ? [] : typeof e == "string" ? [e] : e, T = (e) => {
23
+ }), ne = class extends e.Tag("@voltro/plugin-mail/MailService")() {}, E = (e) => e === void 0 ? [] : typeof e == "string" ? [e] : e, D = (e) => {
25
24
  try {
26
- let t = s.ArrayFormatter.formatErrorSync(e);
25
+ let t = a.ArrayFormatter.formatErrorSync(e);
27
26
  return t.length === 0 ? String(e) : t.map((e) => `${e.path.length > 0 ? e.path.join(".") : "(root)"}: ${e.message}`).join("; ");
28
27
  } catch {
29
28
  return String(e);
30
29
  }
31
- }, E = null, D = async () => {
32
- if (!E) {
30
+ }, O = null, re = async () => {
31
+ if (!O) {
33
32
  let e = await import("@react-email/render");
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)");
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)");
35
34
  }
36
- return E;
37
- }, O = (e, t) => r.gen(function* () {
38
- let n = e, i = yield* u.decodeUnknown(n.props)(t).pipe(r.mapError((t) => new b({
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({
39
38
  provider: "template",
40
- message: `invalid props for '${e.name}': ${T(t)}`.slice(0, 500),
39
+ message: `invalid props for '${e.name}': ${D(t)}`.slice(0, 500),
41
40
  transient: !1
42
- }))), a = n.subject(i), o = n.render(i);
41
+ }))), a = r.subject(i), o = r.render(i);
43
42
  return {
44
43
  subject: a,
45
- html: yield* r.tryPromise({
46
- try: async () => (await D())(o, { plainText: !1 }),
47
- catch: (t) => new b({
44
+ html: yield* n.tryPromise({
45
+ try: async () => (await re())(o, { plainText: !1 }),
46
+ catch: (t) => new C({
48
47
  provider: "template",
49
48
  message: `render failed for '${e.name}': ${String(t)}`.slice(0, 500),
50
49
  transient: !1
51
50
  })
52
51
  }),
53
- text: yield* r.tryPromise({
54
- try: async () => (await D())(o, { plainText: !0 }),
55
- catch: (t) => new b({
52
+ text: yield* n.tryPromise({
53
+ try: async () => (await re())(o, { plainText: !0 }),
54
+ catch: (t) => new C({
56
55
  provider: "template",
57
56
  message: `render(text) failed for '${e.name}': ${String(t)}`.slice(0, 500),
58
57
  transient: !1
59
58
  })
60
59
  })
61
60
  };
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 ? {
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 ? {
65
64
  name: e.name,
66
65
  locale: e.locale
67
- } : { name: e.name }), ie = () => k.clear(), ae = (e) => {
66
+ } : { name: e.name }), ce = () => A.clear(), le = (e) => {
68
67
  if (typeof e != "object" || !e) return !1;
69
68
  let t = e;
70
69
  return typeof t.name == "string" && typeof t.subject == "function" && typeof t.render == "function" && t.props !== void 0;
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)}`;
73
- return {
74
- isSuppressed: (n, i) => r.sync(() => e.has(t(n, i)) || e.has(t(null, i))),
75
- suppress: (n, i) => r.sync(() => void e.add(t(n, i))),
76
- unsuppress: (n, i) => r.sync(() => void e.delete(t(n, i)))
77
- };
78
- }, se = (e) => {
79
- let t = e.DB_URL ?? e.DB_PRIMARY_URL;
80
- if (t) {
81
- let e = new URL(t);
82
- return {
83
- host: e.hostname || "localhost",
84
- port: e.port ? Number(e.port) : 5432,
85
- username: decodeURIComponent(e.username || "app"),
86
- password: decodeURIComponent(e.password || "app"),
87
- database: e.pathname.replace(/^\//, "") || "app"
88
- };
89
- }
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)}`;
90
92
  return {
91
- host: e.DB_HOST ?? e.PG_HOST ?? "localhost",
92
- port: Number(e.DB_PORT ?? e.PG_PORT ?? 5432),
93
- username: e.DB_USER ?? e.PG_USER ?? "app",
94
- password: e.DB_PASSWORD ?? e.PG_PASSWORD ?? "app",
95
- 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)))
96
96
  };
97
- }, ce = (t = {}) => {
98
- let n = t.table ?? "_voltro_mail_suppression", i = null, a = (n) => g.layerConfig({
99
- host: e.succeed(n.host),
100
- port: e.succeed(n.port),
101
- username: e.succeed(n.username),
102
- password: e.succeed(c.make(n.password)),
103
- database: e.succeed(n.database),
104
- ...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)
105
101
  });
106
102
  return {
107
- isSuppressed: (e, t) => {
108
- let a = i;
109
- if (!a) return r.succeed(!1);
110
- let o = r.gen(function* () {
111
- let r = yield* h.SqlClient;
112
- return (yield* r`
113
- SELECT 1 AS one FROM ${r(n)}
114
- WHERE email = ${j(t)} AND tenant IN (${N(e)}, ${M}) LIMIT 1`).length > 0;
115
- });
116
- return r.tryPromise(() => a.runPromise(o)).pipe(r.catchAll(() => r.succeed(!1)));
117
- },
118
- suppress: (e, t, a) => {
119
- let o = i;
120
- if (!o) return r.void;
121
- let s = r.gen(function* () {
122
- let r = yield* h.SqlClient;
123
- yield* r`
124
- INSERT INTO ${r(n)} (tenant, email, reason, created_at)
125
- VALUES (${N(e)}, ${j(t)}, ${a}, ${Date.now()})
126
- ON CONFLICT (tenant, email) DO UPDATE SET reason = ${a}, created_at = ${Date.now()}`;
127
- });
128
- return r.tryPromise(() => o.runPromise(s)).pipe(r.catchAll((e) => r.sync(() => oe.warn("suppress write failed", {
129
- email: j(t),
130
- message: String(e)
131
- }))));
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);
132
106
  },
133
- unsuppress: (e, t) => {
134
- let a = i;
135
- if (!a) return r.void;
136
- let o = r.gen(function* () {
137
- let r = yield* h.SqlClient;
138
- yield* r`DELETE FROM ${r(n)} WHERE tenant = ${N(e)} AND email = ${j(t)}`;
139
- });
140
- return r.tryPromise(() => a.runPromise(o)).pipe(r.catchAll(() => r.void));
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;
141
117
  },
142
- activate: async (e) => {
143
- let t = o.make(a(se(e)));
144
- await t.runPromise(r.gen(function* () {
145
- let e = yield* h.SqlClient;
146
- yield* e`
147
- CREATE TABLE IF NOT EXISTS ${e(n)} (
148
- tenant TEXT NOT NULL,
149
- email TEXT NOT NULL,
150
- reason TEXT,
151
- created_at BIGINT NOT NULL,
152
- PRIMARY KEY (tenant, email)
153
- )`;
154
- })), 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));
155
123
  },
156
- deactivate: async () => {
157
- let e = i;
158
- i = null, e && await e.dispose();
124
+ bind: async (t) => {
125
+ e = t;
159
126
  }
160
127
  };
161
- }, le = m({ scope: "@voltro/plugin-mail" }), F = (e) => e ?? "*", I = () => {
162
- let e = /* @__PURE__ */ new Map(), t = (e, t) => `${F(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}`;
163
130
  return {
164
- reserve: (n, i) => r.sync(() => {
165
- let r = t(n, i);
166
- if (e.has(r)) {
167
- let t = e.get(r);
131
+ reserve: (r, i) => n.sync(() => {
132
+ let n = t(r, i);
133
+ if (e.has(n)) {
134
+ let t = e.get(n);
168
135
  return t ? {
169
136
  won: !1,
170
137
  prior: t
171
138
  } : { won: !1 };
172
139
  }
173
- return e.set(r, null), { won: !0 };
140
+ return e.set(n, null), { won: !0 };
174
141
  }),
175
- record: (n, i, a) => r.sync(() => void e.set(t(n, i), a))
142
+ record: (r, i, a) => n.sync(() => void e.set(t(r, i), a))
176
143
  };
177
- }, ue = (e) => {
178
- let t = e.DB_URL ?? e.DB_PRIMARY_URL;
179
- if (t) {
180
- let e = new URL(t);
181
- return {
182
- host: e.hostname || "localhost",
183
- port: e.port ? Number(e.port) : 5432,
184
- username: decodeURIComponent(e.username || "app"),
185
- password: decodeURIComponent(e.password || "app"),
186
- database: e.pathname.replace(/^\//, "") || "app"
187
- };
188
- }
144
+ }, ge = () => {
145
+ let e = null;
189
146
  return {
190
- host: e.DB_HOST ?? e.PG_HOST ?? "localhost",
191
- port: Number(e.DB_PORT ?? e.PG_PORT ?? 5432),
192
- username: e.DB_USER ?? e.PG_USER ?? "app",
193
- password: e.DB_PASSWORD ?? e.PG_PASSWORD ?? "app",
194
- database: e.DB_DATABASE ?? e.PG_DATABASE ?? "app"
195
- };
196
- }, de = (t = {}) => {
197
- let n = t.table ?? "_voltro_mail_idempotency", i = null, a = (n) => g.layerConfig({
198
- host: e.succeed(n.host),
199
- port: e.succeed(n.port),
200
- username: e.succeed(n.username),
201
- password: e.succeed(c.make(n.password)),
202
- database: e.succeed(n.database),
203
- ...t.maxConnections === void 0 ? {} : { maxConnections: e.succeed(t.maxConnections) }
204
- });
205
- return {
206
- reserve: (e, t) => {
207
- let a = i;
208
- if (!a) return r.succeed({ won: !0 });
209
- let o = r.gen(function* () {
210
- let r = yield* h.SqlClient;
211
- if ((yield* r`
212
- INSERT INTO ${r(n)} (tenant, key, created_at)
213
- VALUES (${F(e)}, ${t}, ${Date.now()})
214
- ON CONFLICT (tenant, key) DO NOTHING
215
- RETURNING key`).length > 0) return { won: !0 };
216
- let i = (yield* r`
217
- SELECT result_id, result_provider FROM ${r(n)}
218
- WHERE tenant = ${F(e)} AND key = ${t} LIMIT 1`)[0];
219
- 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" ? {
220
157
  won: !1,
221
158
  prior: {
222
- id: i.result_id,
223
- provider: i.result_provider
159
+ id: n,
160
+ provider: r
224
161
  }
225
162
  } : { won: !1 };
226
- });
227
- return r.tryPromise(() => a.runPromise(o)).pipe(r.catchAll((e) => r.sync(() => (le.warn("idempotency reserve failed — skipping send to avoid a double", {
228
- key: t,
229
- message: String(e)
230
- }), { 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 });
231
167
  },
232
- record: (e, t, a) => {
233
- let o = i;
234
- if (!o) return r.void;
235
- let s = r.gen(function* () {
236
- let r = yield* h.SqlClient;
237
- yield* r`
238
- UPDATE ${r(n)} SET result_id = ${a.id}, result_provider = ${a.provider}
239
- WHERE tenant = ${F(e)} AND key = ${t}`;
240
- });
241
- return r.tryPromise(() => o.runPromise(s)).pipe(r.catchAll((e) => r.sync(() => le.warn("idempotency record failed", {
242
- key: t,
243
- message: String(e)
244
- }))));
245
- },
246
- activate: async (e) => {
247
- let t = o.make(a(ue(e)));
248
- await t.runPromise(r.gen(function* () {
249
- let e = yield* h.SqlClient;
250
- yield* e`
251
- CREATE TABLE IF NOT EXISTS ${e(n)} (
252
- tenant TEXT NOT NULL,
253
- key TEXT NOT NULL,
254
- result_id TEXT,
255
- result_provider TEXT,
256
- created_at BIGINT NOT NULL,
257
- PRIMARY KEY (tenant, key)
258
- )`;
259
- })), 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;
260
177
  },
261
- deactivate: async () => {
262
- let e = i;
263
- i = null, e && await e.dispose();
178
+ bind: async (t) => {
179
+ e = t;
264
180
  }
265
181
  };
266
- }, fe = m({ scope: "@voltro/plugin-mail" }), L = () => {
182
+ }, _e = d({ scope: "@voltro/plugin-mail" }), V = () => {
267
183
  let e = /* @__PURE__ */ new Map();
268
184
  return {
269
- hold: (t) => r.sync(() => void e.set(t.id, t)),
270
- claimDue: (t) => r.sync(() => {
185
+ hold: (t) => n.sync(() => void e.set(t.id, t)),
186
+ claimDue: (t) => n.sync(() => {
271
187
  let n = [];
272
188
  for (let [r, i] of e) i.dueAt <= t && (n.push(i), e.delete(r));
273
189
  return n;
274
190
  }),
275
- size: () => r.sync(() => e.size)
191
+ size: () => n.sync(() => e.size)
276
192
  };
277
- }, pe = 0, R = (e) => e.idempotencyKey ?? `held-${pe += 1}`, z = (e) => ({
193
+ }, ve = 0, ye = (e) => e.idempotencyKey ?? `held-${ve += 1}`, be = (e) => ({
278
194
  shouldHold: (t) => !e.providerSchedules && t !== void 0 && t.getTime() > Date.now(),
279
195
  hold: (t) => {
280
196
  let { scheduledAt: n, ...r } = t;
281
197
  return e.store.hold({
282
- id: R(t),
198
+ id: ye(t),
283
199
  dueAt: t.scheduledAt?.getTime() ?? Date.now(),
284
200
  message: r
285
201
  });
286
202
  },
287
- tick: (t = Date.now()) => r.gen(function* () {
288
- let n = yield* e.store.claimDue(t);
289
- for (let t of n) yield* e.flush(t.message).pipe(r.catchAll((e) => r.sync(() => fe.warn("scheduled flush failed", {
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", {
290
206
  id: t.id,
291
207
  message: String(e)
292
208
  }))));
293
- return n.length;
209
+ return r.length;
294
210
  }),
295
211
  pending: () => e.store.size()
296
- }), me = 0, B = (e) => `${e}-${me += 1}`, he = (e) => e === void 0 || e === 408 || e === 429 || e >= 500, V = (e, t) => {
212
+ }), xe = 0, Se = (e) => `${e}-${xe += 1}`, Ce = (e) => e === void 0 || e === 408 || e === 429 || e >= 500, H = (e, t) => {
297
213
  let n = t.status;
298
- return new b({
214
+ return new C({
299
215
  provider: e,
300
216
  message: (t.message ?? "send failed").slice(0, 500),
301
- transient: e === "smtp" ? n === void 0 || n >= 400 && n < 500 : he(n),
217
+ transient: e === "smtp" ? n === void 0 || n >= 400 && n < 500 : Ce(n),
302
218
  ...n === void 0 ? {} : { status: n }
303
219
  });
304
- }, H = (e, t, n, i, a) => r.tryPromise({
220
+ }, U = (e, t, r, i, a) => n.tryPromise({
305
221
  try: async () => {
306
222
  let e = await fetch(t, {
307
223
  method: "POST",
308
224
  headers: {
309
225
  "content-type": "application/json",
310
- ...n
226
+ ...r
311
227
  },
312
228
  body: JSON.stringify(i)
313
229
  });
@@ -322,19 +238,19 @@ var b = class extends u.TaggedError()("MailError", {
322
238
  },
323
239
  catch: (t) => {
324
240
  let n = t;
325
- return V(e, {
241
+ return H(e, {
326
242
  ...typeof n.status == "number" ? { status: n.status } : {},
327
243
  message: n.message ?? t?.message ?? String(t)
328
244
  });
329
245
  }
330
- }), U = 500, ge = (e, t) => {
246
+ }), W = 500, we = (e, t) => {
331
247
  let n = e.stream === "broadcast" ? t?.broadcast ?? "broadcast" : t?.transactional;
332
248
  return {
333
249
  ...n === void 0 ? {} : { route: n },
334
250
  from: e.from,
335
- to: w(e.to),
336
- ...e.cc ? { cc: w(e.cc) } : {},
337
- ...e.bcc ? { bcc: w(e.bcc) } : {},
251
+ to: E(e.to),
252
+ ...e.cc ? { cc: E(e.cc) } : {},
253
+ ...e.bcc ? { bcc: E(e.bcc) } : {},
338
254
  ...e.replyTo ? { reply_to: [e.replyTo] } : {},
339
255
  subject: e.subject,
340
256
  ...e.html === void 0 ? {} : { html: e.html },
@@ -352,35 +268,35 @@ var b = class extends u.TaggedError()("MailError", {
352
268
  ...e.contentType ? { content_type: e.contentType } : {}
353
269
  })) } : {}
354
270
  };
355
- }, _e = (e, t, n, i = Math.floor(Date.now() / 1e3), a = 300) => r.tryPromise({
271
+ }, Te = (e, t, r, i = Math.floor(Date.now() / 1e3), a = 300) => n.tryPromise({
356
272
  try: async () => {
357
- let r = (e) => {
358
- throw new x({
273
+ let n = (e) => {
274
+ throw new w({
359
275
  provider: "lettermint",
360
276
  message: e
361
277
  });
362
278
  }, 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 = [];
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 = [];
365
281
  for (let e of s.split(",")) {
366
282
  let [t, n] = e.trim().split("=", 2);
367
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());
368
284
  }
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");
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");
372
288
  try {
373
289
  return JSON.parse(t);
374
290
  } catch {
375
- return r("body is not JSON");
291
+ return n("body is not JSON");
376
292
  }
377
293
  },
378
- catch: (e) => e instanceof x ? e : new x({
294
+ catch: (e) => e instanceof w ? e : new w({
379
295
  provider: "lettermint",
380
296
  message: e?.message ?? String(e)
381
297
  })
382
- }), ve = (e) => {
383
- let t = (e.baseUrl ?? "https://api.lettermint.co/v1").replace(/\/+$/, ""), n = {
298
+ }), Ee = (e) => {
299
+ let t = (e.baseUrl ?? "https://api.lettermint.co/v1").replace(/\/+$/, ""), r = {
384
300
  "x-lettermint-token": e.projectToken,
385
301
  accept: "application/json"
386
302
  };
@@ -388,31 +304,31 @@ var b = class extends u.TaggedError()("MailError", {
388
304
  name: "lettermint",
389
305
  supportsScheduling: !0,
390
306
  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) => ({
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) => ({
395
311
  id: (await e.json()).message_id ?? "lettermint",
396
312
  provider: "lettermint"
397
313
  })),
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) => {
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) => {
399
315
  let t = await e.json();
400
- return r.map((e, n) => ({
316
+ return n.map((e, n) => ({
401
317
  id: t[n]?.message_id ?? "lettermint",
402
318
  provider: "lettermint"
403
319
  }));
404
- })).pipe(r.map((e) => e.flat())),
405
- webhook: { verify: _e }
320
+ })).pipe(n.map((e) => e.flat())),
321
+ webhook: { verify: Te }
406
322
  };
407
- }, ye = (e) => ({
323
+ }, De = (e) => ({
408
324
  from: e.from,
409
- to: w(e.to),
325
+ to: E(e.to),
410
326
  subject: e.subject,
411
327
  html: e.html,
412
328
  text: e.text,
413
329
  reply_to: e.replyTo,
414
- cc: e.cc ? w(e.cc) : void 0,
415
- bcc: e.bcc ? w(e.bcc) : void 0,
330
+ cc: e.cc ? E(e.cc) : void 0,
331
+ bcc: e.bcc ? E(e.bcc) : void 0,
416
332
  headers: e.headers,
417
333
  tags: e.tags?.map((e) => ({
418
334
  name: e,
@@ -424,34 +340,34 @@ var b = class extends u.TaggedError()("MailError", {
424
340
  content: e.content,
425
341
  ...e.contentType ? { content_type: e.contentType } : {}
426
342
  }))
427
- }), be = (e) => ({
343
+ }), Oe = (e) => ({
428
344
  name: "resend",
429
345
  supportsScheduling: !0,
430
- send: (t) => H("resend", "https://api.resend.com/emails", { authorization: `Bearer ${e.apiKey}` }, ye(t), async (e) => ({
346
+ send: (t) => U("resend", "https://api.resend.com/emails", { authorization: `Bearer ${e.apiKey}` }, De(t), async (e) => ({
431
347
  id: (await e.json()).id ?? "resend",
432
348
  provider: "resend"
433
349
  })),
434
- sendBatch: (t) => H("resend", "https://api.resend.com/emails/batch", { authorization: `Bearer ${e.apiKey}` }, t.map(ye), async (e) => {
350
+ sendBatch: (t) => U("resend", "https://api.resend.com/emails/batch", { authorization: `Bearer ${e.apiKey}` }, t.map(De), async (e) => {
435
351
  let n = (await e.json()).data ?? [];
436
352
  return t.map((e, t) => ({
437
353
  id: n[t]?.id ?? "resend",
438
354
  provider: "resend"
439
355
  }));
440
356
  })
441
- }), xe = (e) => ({
357
+ }), ke = (e) => ({
442
358
  name: "postmark",
443
- send: (t) => H("postmark", "https://api.postmarkapp.com/email", {
359
+ send: (t) => U("postmark", "https://api.postmarkapp.com/email", {
444
360
  "x-postmark-server-token": e.serverToken,
445
361
  accept: "application/json"
446
362
  }, {
447
363
  From: t.from,
448
- To: w(t.to).join(","),
364
+ To: E(t.to).join(","),
449
365
  Subject: t.subject,
450
366
  HtmlBody: t.html,
451
367
  TextBody: t.text,
452
368
  ReplyTo: t.replyTo,
453
- Cc: t.cc ? w(t.cc).join(",") : void 0,
454
- Bcc: t.bcc ? w(t.bcc).join(",") : void 0,
369
+ Cc: t.cc ? E(t.cc).join(",") : void 0,
370
+ Bcc: t.bcc ? E(t.bcc).join(",") : void 0,
455
371
  MessageStream: t.stream === "broadcast" ? e.streams?.broadcast ?? "broadcast" : e.streams?.transactional ?? "outbound",
456
372
  Headers: t.headers ? Object.entries(t.headers).map(([e, t]) => ({
457
373
  Name: e,
@@ -467,44 +383,44 @@ var b = class extends u.TaggedError()("MailError", {
467
383
  id: (await e.json()).MessageID ?? "postmark",
468
384
  provider: "postmark"
469
385
  }))
470
- }), Se = (e) => [...e.text ? [{
386
+ }), Ae = (e) => [...e.text ? [{
471
387
  type: "text/plain",
472
388
  value: e.text
473
389
  }] : [], ...e.html ? [{
474
390
  type: "text/html",
475
391
  value: e.html
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,
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,
480
396
  subject: e.subject,
481
397
  ...e.scheduledAt ? { send_at: Math.floor(e.scheduledAt.getTime() / 1e3) } : {}
482
- }), we = (e) => e.attachments?.map((e) => ({
398
+ }), Me = (e) => e.attachments?.map((e) => ({
483
399
  filename: e.filename,
484
400
  content: e.content,
485
401
  type: e.contentType ?? "application/octet-stream",
486
402
  disposition: "attachment"
487
- })), Te = (e) => {
403
+ })), Ne = (e) => {
488
404
  let t = (e) => ({
489
- personalizations: [Ce(e)],
405
+ personalizations: [je(e)],
490
406
  from: { email: e.from },
491
407
  reply_to: e.replyTo ? { email: e.replyTo } : void 0,
492
408
  subject: e.subject,
493
- content: Se(e),
494
- attachments: we(e)
409
+ content: Ae(e),
410
+ attachments: Me(e)
495
411
  });
496
412
  return {
497
413
  name: "sendgrid",
498
414
  supportsScheduling: !0,
499
- send: (n) => H("sendgrid", "https://api.sendgrid.com/v3/mail/send", { authorization: `Bearer ${e.apiKey}` }, t(n), async (e) => ({
415
+ send: (n) => U("sendgrid", "https://api.sendgrid.com/v3/mail/send", { authorization: `Bearer ${e.apiKey}` }, t(n), async (e) => ({
500
416
  id: e.headers.get("x-message-id") ?? "sendgrid",
501
417
  provider: "sendgrid"
502
418
  })),
503
- sendBatch: (t) => H("sendgrid", "https://api.sendgrid.com/v3/mail/send", { authorization: `Bearer ${e.apiKey}` }, {
504
- personalizations: t.map(Ce),
419
+ sendBatch: (t) => U("sendgrid", "https://api.sendgrid.com/v3/mail/send", { authorization: `Bearer ${e.apiKey}` }, {
420
+ personalizations: t.map(je),
505
421
  from: { email: t[0]?.from },
506
422
  subject: t[0]?.subject,
507
- content: Se(t[0] ?? {})
423
+ content: Ae(t[0] ?? {})
508
424
  }, async (e) => {
509
425
  let n = e.headers.get("x-message-id") ?? "sendgrid";
510
426
  return t.map((e, t) => ({
@@ -513,35 +429,35 @@ var b = class extends u.TaggedError()("MailError", {
513
429
  }));
514
430
  })
515
431
  };
516
- }, Ee = (e, t) => {
432
+ }, Pe = (e, t) => {
517
433
  let n = t, r = n.response?.status;
518
- return V(e, {
434
+ return H(e, {
519
435
  ...typeof r == "number" ? { status: r } : {},
520
436
  message: n.message ?? String(t)
521
437
  });
522
- }, De = (e, t, n) => r.gen(function* () {
523
- let i = yield* (yield* v.HttpClient).execute(t);
438
+ }, Fe = (e, t, r) => n.gen(function* () {
439
+ let i = yield* (yield* te.HttpClient).execute(t);
524
440
  if (i.status < 200 || i.status >= 300) {
525
- let t = yield* i.text.pipe(r.catchAll(() => r.succeed("")));
526
- return yield* r.fail(V(e, {
441
+ let t = yield* i.text.pipe(n.catchAll(() => n.succeed("")));
442
+ return yield* n.fail(H(e, {
527
443
  status: i.status,
528
444
  message: t || `HTTP ${i.status}`
529
445
  }));
530
446
  }
531
- return yield* n(i);
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
+ 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) => {
533
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");
534
450
  return {
535
451
  authorization: `AWS4-HMAC-SHA256 Credential=${e.accessKeyId}/${p}, SignedHeaders=${d}, Signature=${h}`,
536
452
  "x-amz-date": c,
537
453
  ...e.sessionToken ? { "x-amz-security-token": e.sessionToken } : {}
538
454
  };
539
- } }), ke = (e) => JSON.stringify({
455
+ } }), Le = (e) => JSON.stringify({
540
456
  FromEmailAddress: e.from,
541
457
  Destination: {
542
- ToAddresses: w(e.to),
543
- ...e.cc ? { CcAddresses: w(e.cc) } : {},
544
- ...e.bcc ? { BccAddresses: w(e.bcc) } : {}
458
+ ToAddresses: E(e.to),
459
+ ...e.cc ? { CcAddresses: E(e.cc) } : {},
460
+ ...e.bcc ? { BccAddresses: E(e.bcc) } : {}
545
461
  },
546
462
  ...e.replyTo ? { ReplyToAddresses: [e.replyTo] } : {},
547
463
  Content: { Simple: {
@@ -560,51 +476,51 @@ var b = class extends u.TaggedError()("MailError", {
560
476
  } } : {}
561
477
  }
562
478
  } }
563
- }), Ae = (e) => {
564
- let t = Oe(e), n = `https://email.${e.region}.amazonaws.com`, i = "/v2/email/outbound-emails";
479
+ }), Re = (e) => {
480
+ let t = Ie(e), r = `https://email.${e.region}.amazonaws.com`, i = "/v2/email/outbound-emails";
565
481
  return {
566
482
  name: "ses",
567
- send: (e) => r.gen(function* () {
568
- let a = ke(e), o = yield* r.tryPromise({
569
- try: () => t.sign(n, i, a),
570
- catch: (e) => V("ses", { message: e?.message ?? String(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) })
571
487
  });
572
- return yield* De("ses", y.post(`${n}${i}`).pipe(y.setHeaders({
488
+ return yield* Fe("ses", S.post(`${r}${i}`).pipe(S.setHeaders({
573
489
  "content-type": "application/json",
574
490
  ...o
575
- }), y.bodyUnsafeJson(JSON.parse(a))), (e) => e.json.pipe(r.map((e) => ({
491
+ }), S.bodyUnsafeJson(JSON.parse(a))), (e) => e.json.pipe(n.map((e) => ({
576
492
  id: e.MessageId ?? "ses",
577
493
  provider: "ses"
578
494
  }))));
579
495
  })
580
496
  };
581
- }, je = (e) => {
497
+ }, ze = (e) => {
582
498
  let t = [["from", e.from], ["subject", e.subject]];
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]);
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]);
586
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]);
587
503
  if (e.tags) for (let n of e.tags) t.push(["o:tag", n]);
588
504
  return e.scheduledAt && t.push(["o:deliverytime", e.scheduledAt.toUTCString()]), t;
589
- }, Me = (e) => {
505
+ }, Be = (e) => {
590
506
  let t = `${e.baseUrl ?? "https://api.mailgun.net"}/v3/${e.domain}/messages`;
591
507
  return {
592
508
  name: "mailgun",
593
509
  supportsScheduling: !0,
594
- send: (n) => {
510
+ send: (r) => {
595
511
  let i = new URLSearchParams();
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
+ 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) => ({
598
514
  id: e.id ?? "mailgun",
599
515
  provider: "mailgun"
600
516
  }))));
601
517
  }
602
518
  };
603
- }, W = (e) => {
519
+ }, G = (e) => {
604
520
  let t = null;
605
521
  return {
606
522
  name: "smtp",
607
- send: (n) => r.tryPromise({
523
+ send: (r) => n.tryPromise({
608
524
  try: async () => {
609
525
  if (!t) {
610
526
  let n = await import("nodemailer"), r = n.createTransport ?? n.default?.createTransport;
@@ -618,16 +534,16 @@ var b = class extends u.TaggedError()("MailError", {
618
534
  }
619
535
  return {
620
536
  id: (await t.sendMail({
621
- from: n.from,
622
- to: w(n.to),
623
- cc: n.cc ? w(n.cc) : void 0,
624
- bcc: n.bcc ? w(n.bcc) : void 0,
625
- replyTo: n.replyTo,
626
- subject: n.subject,
627
- html: n.html,
628
- text: n.text,
629
- headers: n.headers,
630
- attachments: n.attachments?.map((e) => ({
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) => ({
631
547
  filename: e.filename,
632
548
  content: e.content,
633
549
  encoding: "base64",
@@ -639,53 +555,53 @@ var b = class extends u.TaggedError()("MailError", {
639
555
  },
640
556
  catch: (e) => {
641
557
  let t = e;
642
- return V("smtp", {
558
+ return H("smtp", {
643
559
  ...typeof t.responseCode == "number" ? { status: t.responseCode } : {},
644
560
  message: t.message ?? String(e)
645
561
  });
646
562
  }
647
563
  })
648
564
  };
649
- }, Ne = (e) => ({
565
+ }, Ve = (e) => ({
650
566
  name: "console",
651
- send: (t) => r.sync(() => {
652
- let n = `✉ to=${w(t.to).join(",")} subject=${JSON.stringify(t.subject)}`;
567
+ send: (t) => n.sync(() => {
568
+ let n = `✉ to=${E(t.to).join(",")} subject=${JSON.stringify(t.subject)}`;
653
569
  return e ? e(n, { from: t.from }) : process.stdout.write(`[mail:console] ${n}\n`), {
654
- id: B("console"),
570
+ id: Se("console"),
655
571
  provider: "console"
656
572
  };
657
573
  })
658
- }), G = [], Pe = () => [...G], Fe = () => {
659
- G.length = 0;
660
- }, Ie = () => ({
574
+ }), K = [], He = () => [...K], Ue = () => {
575
+ K.length = 0;
576
+ }, We = () => ({
661
577
  name: "memory",
662
- send: (e) => r.sync(() => (G.push(e), {
663
- id: B("mem"),
578
+ send: (e) => n.sync(() => (K.push(e), {
579
+ id: Se("mem"),
664
580
  provider: "memory"
665
581
  }))
666
- }), K = m({ scope: "@voltro/plugin-mail" }), Le = (e) => {
582
+ }), q = d({ scope: "@voltro/plugin-mail" }), Ge = (e) => {
667
583
  if (e.provider !== void 0 && typeof e.provider != "string") return e.provider;
668
584
  let t = e.env ?? {}, n = e.provider ?? t.MAIL_PROVIDER ?? "console", r = (e, t) => {
669
585
  if (!t) throw Error(`@voltro/plugin-mail: provider '${n}' needs apiKey or ${e}`);
670
586
  return t;
671
587
  };
672
588
  switch (n) {
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({
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({
678
594
  region: r("AWS_REGION", t.AWS_REGION),
679
595
  accessKeyId: r("AWS_ACCESS_KEY_ID", t.AWS_ACCESS_KEY_ID),
680
596
  secretAccessKey: r("AWS_SECRET_ACCESS_KEY", t.AWS_SECRET_ACCESS_KEY),
681
597
  ...t.AWS_SESSION_TOKEN ? { sessionToken: t.AWS_SESSION_TOKEN } : {}
682
598
  });
683
- case "mailgun": return Me({
599
+ case "mailgun": return Be({
684
600
  apiKey: r("MAILGUN_API_KEY", e.apiKey ?? t.MAILGUN_API_KEY),
685
601
  domain: r("MAILGUN_DOMAIN", t.MAILGUN_DOMAIN),
686
602
  ...t.MAILGUN_BASE_URL ? { baseUrl: t.MAILGUN_BASE_URL } : {}
687
603
  });
688
- case "smtp": return t.SMTP_URL ? W({ url: t.SMTP_URL }) : W({
604
+ case "smtp": return t.SMTP_URL ? G({ url: t.SMTP_URL }) : G({
689
605
  host: r("SMTP_URL/SMTP_HOST", t.SMTP_HOST),
690
606
  ...t.SMTP_PORT ? { port: Number(t.SMTP_PORT) } : {},
691
607
  secure: t.SMTP_SECURE === "true",
@@ -694,50 +610,50 @@ var b = class extends u.TaggedError()("MailError", {
694
610
  pass: t.SMTP_PASS
695
611
  } } : {}
696
612
  });
697
- case "memory": return Ie();
698
- default: return Ne((e, t) => K.info(e, t));
613
+ case "memory": return We();
614
+ default: return Ve((e, t) => q.info(e, t));
699
615
  }
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();
702
- for (let t of e.templates ?? []) c.set(t.locale ? `${t.name}:${t.locale}` : t.name, t);
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) => {
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) => {
704
620
  if (d.set(e, t), d.size > 500) {
705
621
  let e = d.keys().next().value;
706
622
  e !== void 0 && d.delete(e);
707
623
  }
708
- }, m = e.idempotency ?? I(), h = z({
709
- store: e.schedule ?? L(),
624
+ }, m = e.idempotency ?? B(), h = be({
625
+ store: e.schedule ?? V(),
710
626
  providerSchedules: e.provider.supportsScheduling === !0,
711
- flush: (e) => y(e)
627
+ flush: (e) => v(e)
712
628
  }), 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({
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({
716
632
  provider: e.provider.name,
717
633
  message: `a broadcast needs \`unsubscribe: { oneClickUrl }\` (an https URL) — '${e.provider.name}' does not unsubscribe recipients itself`,
718
634
  transient: !1
719
635
  }));
720
- let i = [`<${n.oneClickUrl}>`, ...n.mailto === void 0 ? [] : [`<mailto:${n.mailto.replace(/^mailto:/, "")}>`]];
721
- return r.succeed({
636
+ let i = [`<${r.oneClickUrl}>`, ...r.mailto === void 0 ? [] : [`<mailto:${r.mailto.replace(/^mailto:/, "")}>`]];
637
+ return n.succeed({
722
638
  "List-Unsubscribe": i.join(", "),
723
639
  "List-Unsubscribe-Post": "List-Unsubscribe=One-Click"
724
640
  });
725
- }, _ = (t) => r.gen(function* () {
641
+ }, _ = (t) => n.gen(function* () {
726
642
  let i = t.from ?? e.from;
727
- if (!i) return yield* r.fail(new b({
643
+ if (!i) return yield* n.fail(new C({
728
644
  provider: e.provider.name,
729
645
  message: "no `from` — set mailPlugin({ from }) or pass message.from",
730
646
  transient: !1
731
647
  }));
732
- let s = w(t.to);
648
+ let o = E(t.to);
733
649
  if (a) {
734
- let n = s.filter(o);
735
- if (n.length === 0) return K.warn("mail skipped — no recipient in allowlist (non-production)", {
736
- to: s.join(","),
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(","),
737
653
  subject: t.subject
738
654
  }), e.onSent?.({
739
655
  id: "skipped-allowlist",
740
- to: s,
656
+ to: o,
741
657
  from: i,
742
658
  subject: t.subject,
743
659
  provider: "skipped",
@@ -750,16 +666,16 @@ var b = class extends u.TaggedError()("MailError", {
750
666
  provider: "skipped"
751
667
  }
752
668
  };
753
- s = n;
669
+ o = n;
754
670
  }
755
671
  let c = t.tenantId ?? null, l = [];
756
- for (let e of s) (yield* n.isSuppressed(c, e)) || l.push(e);
757
- if (l.length === 0) return K.warn("mail skipped — all recipients suppressed", {
758
- to: s.join(","),
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(","),
759
675
  subject: t.subject
760
676
  }), e.onSent?.({
761
677
  id: "skipped-suppressed",
762
- to: s,
678
+ to: o,
763
679
  from: i,
764
680
  subject: t.subject,
765
681
  provider: "suppressed",
@@ -772,7 +688,7 @@ var b = class extends u.TaggedError()("MailError", {
772
688
  provider: "suppressed"
773
689
  }
774
690
  };
775
- s = l;
691
+ o = l;
776
692
  let u = t.replyTo ?? e.replyTo, d = yield* g(t), f = d === void 0 ? t.headers : {
777
693
  ...t.headers,
778
694
  ...d
@@ -782,35 +698,35 @@ var b = class extends u.TaggedError()("MailError", {
782
698
  message: {
783
699
  ...t,
784
700
  from: i,
785
- to: s,
701
+ to: o,
786
702
  ...u === void 0 ? {} : { replyTo: u },
787
703
  ...f === void 0 ? {} : { headers: f }
788
704
  }
789
705
  };
790
- }), v = (t) => {
791
- let n = w(t.to);
792
- return e.provider.send(t).pipe(r.retry(s), r.tap((i) => r.sync(() => {
793
- K.info("mail sent", {
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", {
794
710
  provider: i.provider,
795
711
  id: i.id,
796
- to: n.join(",")
712
+ to: r.join(",")
797
713
  }), e.onSent?.({
798
714
  id: i.id,
799
- to: n,
715
+ to: r,
800
716
  from: t.from,
801
717
  subject: t.subject,
802
718
  provider: i.provider,
803
719
  ok: !0,
804
720
  ts: Date.now()
805
721
  });
806
- })), r.tapError((i) => r.sync(() => {
807
- K.warn("mail failed", {
722
+ })), n.tapError((i) => n.sync(() => {
723
+ q.warn("mail failed", {
808
724
  provider: i.provider,
809
725
  status: i.status,
810
726
  message: i.message
811
727
  }), e.onSent?.({
812
728
  id: `err-${Date.now()}`,
813
- to: n,
729
+ to: r,
814
730
  from: t.from,
815
731
  subject: t.subject,
816
732
  provider: i.provider,
@@ -819,14 +735,14 @@ var b = class extends u.TaggedError()("MailError", {
819
735
  ts: Date.now()
820
736
  });
821
737
  })));
822
- }, y = (t) => r.gen(function* () {
738
+ }, v = (t) => n.gen(function* () {
823
739
  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)", {
740
+ return n.kind === "skip" ? n.result : h.shouldHold(t.scheduledAt) ? (yield* h.hold(n.message), q.info("mail scheduled (held for later flush)", {
825
741
  subject: n.message.subject,
826
742
  dueAt: t.scheduledAt?.toISOString()
827
743
  }), e.onSent?.({
828
744
  id: "scheduled",
829
- to: w(n.message.to),
745
+ to: E(n.message.to),
830
746
  from: n.message.from,
831
747
  subject: n.message.subject,
832
748
  provider: "scheduled",
@@ -835,24 +751,24 @@ var b = class extends u.TaggedError()("MailError", {
835
751
  }), {
836
752
  id: "scheduled",
837
753
  provider: "scheduled"
838
- }) : yield* v(n.message);
839
- }), x = (e, t, n) => t === void 0 ? n : r.gen(function* () {
840
- let r = yield* m.reserve(e, t);
841
- if (!r.won) return K.info("mail idempotent replay — skipped", { key: t }), r.prior ?? {
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 ?? {
842
758
  id: "idempotent-replay",
843
759
  provider: "idempotent"
844
760
  };
845
- let i = yield* n;
761
+ let i = yield* r;
846
762
  return yield* m.record(e, t, i), i;
847
- }), S = (e) => r.suspend(() => {
763
+ }), b = (e) => n.suspend(() => {
848
764
  let t = u(e.template, e.locale);
849
- if (!t) return r.fail(new b({
765
+ if (!t) return n.fail(new C({
850
766
  provider: "template",
851
767
  message: `unknown template '${e.template}'${e.locale ? ` (locale '${e.locale}')` : ""}`,
852
768
  transient: !1
853
769
  }));
854
- let n = f(t, e.props), i = d.get(n);
855
- return (i ? r.succeed(i) : O(t, e.props).pipe(r.tap((e) => r.sync(() => p(n, e))))).pipe(r.map((t) => ({
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) => ({
856
772
  to: e.to,
857
773
  subject: t.subject,
858
774
  html: t.html,
@@ -871,31 +787,31 @@ var b = class extends u.TaggedError()("MailError", {
871
787
  ...e.unsubscribe === void 0 ? {} : { unsubscribe: e.unsubscribe },
872
788
  ...e.metadata === void 0 ? {} : { metadata: e.metadata }
873
789
  })));
874
- }), C = (e) => "template" in e ? S(e) : r.succeed(e);
790
+ }), x = (e) => "template" in e ? b(e) : n.succeed(e);
875
791
  return {
876
792
  service: {
877
- send: (e) => r.gen(function* () {
878
- let t = yield* C(e);
879
- return yield* x(t.tenantId ?? null, t.idempotencyKey, y(t));
793
+ send: (e) => n.gen(function* () {
794
+ let t = yield* x(e);
795
+ return yield* y(t.tenantId ?? null, t.idempotencyKey, v(t));
880
796
  }),
881
- sendBatch: (t) => r.gen(function* () {
882
- let n = yield* r.forEach(t, C), i = n.some((e) => e.idempotencyKey !== void 0 || h.shouldHold(e.scheduledAt));
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));
883
799
  if (e.provider.sendBatch && !i) {
884
800
  let t = [], i = [];
885
- for (let e of n) {
801
+ for (let e of r) {
886
802
  let n = yield* _(e);
887
803
  n.kind === "skip" ? i.push(n.result) : (i.push(t.length), t.push(n.message));
888
804
  }
889
- let a = t.length > 0 ? yield* e.provider.sendBatch(t).pipe(r.retry(s), r.tap((n) => r.sync(() => {
890
- t.forEach((t, r) => {
891
- let i = n[r];
892
- i && (K.info("mail sent (batch)", {
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)", {
893
809
  provider: i.provider,
894
810
  id: i.id,
895
- to: w(t.to).join(",")
811
+ to: E(t.to).join(",")
896
812
  }), e.onSent?.({
897
813
  id: i.id,
898
- to: w(t.to),
814
+ to: E(t.to),
899
815
  from: t.from,
900
816
  subject: t.subject,
901
817
  provider: i.provider,
@@ -903,7 +819,7 @@ var b = class extends u.TaggedError()("MailError", {
903
819
  ts: Date.now()
904
820
  }));
905
821
  });
906
- })), r.tapError((e) => r.sync(() => K.warn("mail batch failed", {
822
+ })), n.tapError((e) => n.sync(() => q.warn("mail batch failed", {
907
823
  provider: e.provider,
908
824
  status: e.status,
909
825
  message: e.message
@@ -913,11 +829,11 @@ var b = class extends u.TaggedError()("MailError", {
913
829
  provider: e.provider.name
914
830
  } : t);
915
831
  }
916
- return yield* r.forEach(n, (e) => x(e.tenantId ?? null, e.idempotencyKey, y(e)));
832
+ return yield* n.forEach(r, (e) => y(e.tenantId ?? null, e.idempotencyKey, v(e)));
917
833
  }),
918
- suppress: n.suppress,
919
- unsuppress: n.unsuppress,
920
- isSuppressed: n.isSuppressed,
834
+ suppress: r.suppress,
835
+ unsuppress: r.unsuppress,
836
+ isSuppressed: r.isSuppressed,
921
837
  provider: {
922
838
  name: e.provider.name,
923
839
  managesUnsubscribe: e.provider.managesUnsubscribe === !0
@@ -925,7 +841,7 @@ var b = class extends u.TaggedError()("MailError", {
925
841
  },
926
842
  scheduler: h
927
843
  };
928
- }, Re = (e) => q(e).service, ze = (e) => a.succeed(C, Re(e)), J = (e, t, n, r, i, a = {}) => {
844
+ }, Ke = (e) => J(e).service, qe = (e) => i.succeed(ne, Ke(e)), Y = (e, t, n, r, i, a = {}) => {
929
845
  if (!t) return null;
930
846
  let o = a.metadata !== null && typeof a.metadata == "object" ? Object.fromEntries(Object.entries(a.metadata).filter(([, e]) => typeof e == "string")) : void 0;
931
847
  return {
@@ -937,36 +853,36 @@ var b = class extends u.TaggedError()("MailError", {
937
853
  ...a.eventId === void 0 ? {} : { eventId: a.eventId },
938
854
  ...o === void 0 ? {} : { metadata: o }
939
855
  };
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) => {
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) => {
941
857
  let n = [], r = (e) => {
942
858
  e && n.push(e);
943
859
  };
944
860
  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));
861
+ for (let e of t) r(Y(Qe(String(e.event)), e.email, "sendgrid", e.sg_message_id, e));
946
862
  return n;
947
863
  }
948
864
  let i = t ?? {};
949
865
  if (e === "lettermint") {
950
866
  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, {
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, {
952
868
  eventId: o,
953
869
  metadata: a.metadata
954
870
  })), n);
955
871
  }
956
872
  if (e === "resend") {
957
873
  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;
874
+ return r(Y(Xe(String(i.type)), a, "resend", e.email_id, t)), n;
959
875
  }
960
876
  if (e === "postmark") {
961
877
  let e = i.Email ?? i.Recipient;
962
- return r(J(Ue(String(i.RecordType)), e, "postmark", i.MessageID, t, { metadata: i.Metadata })), n;
878
+ return r(Y(Ze(String(i.RecordType)), e, "postmark", i.MessageID, t, { metadata: i.Metadata })), n;
963
879
  }
964
880
  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) => {
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) => {
970
886
  let t = [];
971
887
  return {
972
888
  record: (n) => {
@@ -977,18 +893,18 @@ var b = class extends u.TaggedError()("MailError", {
977
893
  t.length = 0;
978
894
  }
979
895
  };
980
- }, qe = {
896
+ }, tt = {
981
897
  lettermint: "api.lettermint.co",
982
898
  resend: "api.resend.com",
983
899
  postmark: "api.postmarkapp.com",
984
900
  sendgrid: "api.sendgrid.com"
985
- }, Je = (e) => {
901
+ }, nt = (e) => {
986
902
  try {
987
903
  return new URL(e).host || void 0;
988
904
  } catch {
989
905
  return;
990
906
  }
991
- }, Ye = (e) => {
907
+ }, rt = (e) => {
992
908
  let t = e.env ?? {}, n = (e) => [{
993
909
  destination: e,
994
910
  purpose: "outgoing email: recipients, subject and body",
@@ -999,15 +915,15 @@ var b = class extends u.TaggedError()("MailError", {
999
915
  let t = e.provider.name;
1000
916
  return t === "console" || t === "memory" ? [] : n(`runtime:the '${t}' mail provider`);
1001
917
  }
1002
- let r = e.provider ?? t.MAIL_PROVIDER ?? "console", i = qe[r];
918
+ let r = e.provider ?? t.MAIL_PROVIDER ?? "console", i = tt[r];
1003
919
  if (i !== void 0) return n(i);
1004
920
  switch (r) {
1005
921
  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");
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");
1008
924
  default: return [];
1009
925
  }
1010
- }, X = (e, t) => {
926
+ }, Z = (e, t) => {
1011
927
  if (t > 6) return null;
1012
928
  switch (e._tag) {
1013
929
  case "StringKeyword": return "";
@@ -1017,17 +933,17 @@ var b = class extends u.TaggedError()("MailError", {
1017
933
  case "Enums": return e.enums.length > 0 ? e.enums[0][1] : null;
1018
934
  case "TypeLiteral": {
1019
935
  let n = {};
1020
- for (let r of e.propertySignatures) typeof r.name == "string" && (n[r.name] = X(r.type, t + 1));
936
+ for (let r of e.propertySignatures) typeof r.name == "string" && (n[r.name] = Z(r.type, t + 1));
1021
937
  return n;
1022
938
  }
1023
939
  case "TupleType": 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);
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);
1028
944
  default: return null;
1029
945
  }
1030
- }, Xe = (e) => X(e.ast, 0), Z = p([
946
+ }, it = (e) => Z(e.ast, 0), Q = u([
1031
947
  {
1032
948
  name: "MAIL_PROVIDER",
1033
949
  required: !1,
@@ -1147,101 +1063,29 @@ var b = class extends u.TaggedError()("MailError", {
1147
1063
  required: !1,
1148
1064
  secret: !1,
1149
1065
  description: "Comma-separated recipient allowlist; in dev, mail to addresses outside it is skipped."
1150
- },
1151
- {
1152
- name: "DB_URL",
1153
- required: !1,
1154
- secret: !0,
1155
- description: "Postgres connection URL for the postgres suppression store (credential-carrying). Falls back to DB_PRIMARY_URL."
1156
- },
1157
- {
1158
- name: "DB_PRIMARY_URL",
1159
- required: !1,
1160
- secret: !0,
1161
- description: "Fallback Postgres connection URL for the postgres suppression store (credential-carrying)."
1162
- },
1163
- {
1164
- name: "DB_HOST",
1165
- required: !1,
1166
- secret: !1,
1167
- description: "Postgres host for the postgres suppression store when no URL is set. Falls back to PG_HOST, then localhost."
1168
- },
1169
- {
1170
- name: "PG_HOST",
1171
- required: !1,
1172
- secret: !1,
1173
- description: "Fallback Postgres host for the postgres suppression store."
1174
- },
1175
- {
1176
- name: "DB_PORT",
1177
- required: !1,
1178
- secret: !1,
1179
- description: "Postgres port for the postgres suppression store when no URL is set. Falls back to PG_PORT, then 5432."
1180
- },
1181
- {
1182
- name: "PG_PORT",
1183
- required: !1,
1184
- secret: !1,
1185
- description: "Fallback Postgres port for the postgres suppression store."
1186
- },
1187
- {
1188
- name: "DB_USER",
1189
- required: !1,
1190
- secret: !1,
1191
- description: "Postgres username for the postgres suppression store when no URL is set. Falls back to PG_USER, then \"app\"."
1192
- },
1193
- {
1194
- name: "PG_USER",
1195
- required: !1,
1196
- secret: !1,
1197
- description: "Fallback Postgres username for the postgres suppression store."
1198
- },
1199
- {
1200
- name: "DB_PASSWORD",
1201
- required: !1,
1202
- secret: !0,
1203
- description: "Postgres password for the postgres suppression store when no URL is set. Falls back to PG_PASSWORD."
1204
- },
1205
- {
1206
- name: "PG_PASSWORD",
1207
- required: !1,
1208
- secret: !0,
1209
- description: "Fallback Postgres password for the postgres suppression store."
1210
- },
1211
- {
1212
- name: "DB_DATABASE",
1213
- required: !1,
1214
- secret: !1,
1215
- description: "Postgres database name for the postgres suppression store when no URL is set. Falls back to PG_DATABASE, then \"app\"."
1216
- },
1217
- {
1218
- name: "PG_DATABASE",
1219
- required: !1,
1220
- secret: !1,
1221
- description: "Fallback Postgres database name for the postgres suppression store."
1222
1066
  }
1223
- ]), Q = "@voltro/plugin-mail", $ = (e, t) => ({
1067
+ ]), at = "@voltro/plugin-mail", $ = (e, t) => ({
1224
1068
  status: e,
1225
1069
  contentType: "application/json",
1226
1070
  body: JSON.stringify(t)
1227
- }), Ze = (e, t, n, i) => {
1071
+ }), ot = (e, t, r, i) => {
1228
1072
  let a = e.webhook;
1229
1073
  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");
1074
+ let o = r.secret ?? Q.read("MAIL_WEBHOOK_SECRET");
1231
1075
  if (!o && i) throw Error("@voltro/plugin-mail: mailPlugin({ webhook }) needs a signing secret in production — set MAIL_WEBHOOK_SECRET");
1232
1076
  return {
1233
1077
  method: "POST",
1234
- path: n.path ?? "/webhooks/mail",
1078
+ path: r.path ?? "/webhooks/mail",
1235
1079
  handle: async (i) => {
1236
1080
  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)));
1081
+ let s = await n.runPromise(n.either(a.verify(i.headers, new TextDecoder().decode(i.rawBody), o)));
1238
1082
  if (s._tag === "Left") return $(401, { error: s.left.message });
1239
- let c = Y(e.name, s.right);
1083
+ let c = X(e.name, s.right);
1240
1084
  try {
1241
1085
  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;
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;
1245
1089
  }
1246
1090
  } catch (e) {
1247
1091
  return {
@@ -1252,46 +1096,54 @@ var b = class extends u.TaggedError()("MailError", {
1252
1096
  return $(200, { received: c.length });
1253
1097
  }
1254
1098
  };
1255
- }, Qe = (e = {}) => {
1256
- let t = process.env, o = Le({
1099
+ }, st = (e = {}) => {
1100
+ let a = process.env, s = Ge({
1257
1101
  ...e.provider === void 0 ? {} : { provider: e.provider },
1258
1102
  ...e.apiKey === void 0 ? {} : { apiKey: e.apiKey },
1259
- env: t
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({
1261
- provider: o,
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,
1262
1114
  suppression: p,
1263
1115
  idempotency: m,
1264
1116
  schedule: h,
1265
- onSent: g.record,
1117
+ onSent: v.record,
1266
1118
  ...e.from === void 0 ? {} : { from: e.from },
1267
1119
  ...e.replyTo === void 0 ? {} : { replyTo: e.replyTo },
1268
- ...c === void 0 ? {} : { allowlist: c },
1120
+ ...d === void 0 ? {} : { allowlist: d },
1269
1121
  ...e.attempts === void 0 ? {} : { attempts: e.attempts },
1270
1122
  ...e.templates === void 0 ? {} : { templates: e.templates },
1271
- production: u
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) => ({
1123
+ production: f
1124
+ }), b = i.succeed(ne, y.service), x = s.supportsScheduling !== !0, te = e.scheduleTickMs ?? 1e3, S, C = /* @__PURE__ */ new Map();
1125
+ for (let t of e.templates ?? []) C.set(t.locale ? `${t.name}:${t.locale}` : t.name, t);
1126
+ let w = (e, t) => (t ? C.get(`${e}:${t}`) : void 0) ?? C.get(e) ?? j(e, t), T = (e) => ({
1275
1127
  name: e.name,
1276
1128
  ...e.locale ? { locale: e.locale } : {},
1277
- props: e.preview ?? Xe(e.props)
1129
+ props: e.preview ?? it(e.props)
1278
1130
  }), E = () => {
1279
1131
  let t = /* @__PURE__ */ new Map();
1280
1132
  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);
1133
+ for (let e of se()) {
1134
+ let n = j(e.name, e.locale);
1283
1135
  n && t.set(e.locale ? `${e.name}:${e.locale}` : e.name, T(n));
1284
1136
  }
1285
1137
  return [...t.values()];
1286
- }, D = e.webhook === void 0 ? [] : [Ze(o, p, e.webhook, u)], k = [
1138
+ }, D = e.webhook === void 0 ? [] : [ot(s, p, e.webhook, f)], O = [
1287
1139
  {
1288
1140
  method: "GET",
1289
1141
  path: "/outbox",
1290
1142
  scope: "process",
1291
1143
  description: "Recent sent mail (dev capture buffer), newest first",
1292
- handler: () => r.succeed({
1144
+ handler: () => n.succeed({
1293
1145
  kind: "json",
1294
- data: { sends: g.list() }
1146
+ data: { sends: v.list() }
1295
1147
  })
1296
1148
  },
1297
1149
  {
@@ -1299,7 +1151,7 @@ var b = class extends u.TaggedError()("MailError", {
1299
1151
  path: "/templates",
1300
1152
  scope: "declaration",
1301
1153
  description: "Discovered + registered email templates",
1302
- handler: () => r.succeed({
1154
+ handler: () => n.succeed({
1303
1155
  kind: "json",
1304
1156
  data: { templates: E() }
1305
1157
  })
@@ -1308,7 +1160,7 @@ var b = class extends u.TaggedError()("MailError", {
1308
1160
  method: "POST",
1309
1161
  path: "/preview",
1310
1162
  description: "Render a template to html + text without sending — body { template, props, locale? }",
1311
- handler: (e) => r.gen(function* () {
1163
+ handler: (e) => n.gen(function* () {
1312
1164
  let t = {};
1313
1165
  try {
1314
1166
  t = JSON.parse(e.body || "{}");
@@ -1324,11 +1176,11 @@ var b = class extends u.TaggedError()("MailError", {
1324
1176
  status: 400,
1325
1177
  data: { error: "missing \"template\"" }
1326
1178
  };
1327
- let n = w(t.template, t.locale);
1328
- return n ? yield* O(n, t.props).pipe(r.map((e) => ({
1179
+ let r = w(t.template, t.locale);
1180
+ return r ? yield* k(r, t.props).pipe(n.map((e) => ({
1329
1181
  kind: "json",
1330
1182
  data: e
1331
- })), r.catchAll((e) => r.succeed({
1183
+ })), n.catchAll((e) => n.succeed({
1332
1184
  kind: "json",
1333
1185
  status: 400,
1334
1186
  data: { error: e.message }
@@ -1341,48 +1193,52 @@ var b = class extends u.TaggedError()("MailError", {
1341
1193
  }
1342
1194
  ];
1343
1195
  return {
1344
- ...d({
1345
- name: f({
1346
- base: Q,
1196
+ ...c({
1197
+ name: l({
1198
+ base: at,
1347
1199
  alias: e.alias,
1348
1200
  instance: e.name
1349
1201
  }),
1350
- baseName: Q,
1202
+ baseName: at,
1351
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.",
1352
1204
  permissions: [
1353
1205
  "network:outbound:*",
1354
1206
  "inspect:read",
1355
1207
  "inspect:write"
1356
1208
  ],
1357
- declaredEnv: Z.declared,
1358
- egress: Ye({
1209
+ declaredEnv: Q.declared,
1210
+ egress: rt({
1359
1211
  ...e.provider === void 0 ? {} : { provider: e.provider },
1360
- env: t
1212
+ env: a
1361
1213
  }),
1362
- services: v,
1363
- inspectEndpoints: k,
1214
+ services: b,
1215
+ inspectEndpoints: O,
1364
1216
  ...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);
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);
1369
1225
  }
1370
- t.logger.info("mail active", {
1371
- provider: o.name,
1226
+ r.logger.info("mail active", {
1227
+ provider: s.name,
1372
1228
  from: e.from ?? null,
1373
- allowlist: c?.length ?? 0,
1229
+ allowlist: d?.length ?? 0,
1374
1230
  suppression: typeof e.suppression == "string" ? e.suppression : e.suppression ? "custom" : "memory",
1375
1231
  idempotency: typeof e.idempotency == "string" ? e.idempotency : e.idempotency ? "custom" : "memory",
1376
- scheduling: o.supportsScheduling === !0 ? "native" : "held",
1377
- production: u
1232
+ scheduling: s.supportsScheduling === !0 ? "native" : "held",
1233
+ production: f
1378
1234
  });
1379
1235
  }),
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()));
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()));
1382
1238
  })
1383
1239
  }),
1384
- service: _.service
1240
+ service: y.service
1385
1241
  };
1386
1242
  };
1387
1243
  //#endregion
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 };
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 };