@voltro/plugin-mail 0.82.0 → 0.83.1

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