@growth-labs/mailer 0.4.10 → 0.5.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.
Files changed (54) hide show
  1. package/README.md +19 -2
  2. package/dist/options.d.ts +12 -6
  3. package/dist/options.d.ts.map +1 -1
  4. package/dist/options.js +4 -2
  5. package/dist/options.js.map +1 -1
  6. package/dist/routes/confirm.d.ts.map +1 -1
  7. package/dist/routes/confirm.js +6 -2
  8. package/dist/routes/confirm.js.map +1 -1
  9. package/dist/routes/subscribe.d.ts.map +1 -1
  10. package/dist/routes/subscribe.js +11 -3
  11. package/dist/routes/subscribe.js.map +1 -1
  12. package/dist/routes/unsubscribe.d.ts.map +1 -1
  13. package/dist/routes/unsubscribe.js +14 -3
  14. package/dist/routes/unsubscribe.js.map +1 -1
  15. package/dist/utils/broadcast.d.ts.map +1 -1
  16. package/dist/utils/broadcast.js +10 -2
  17. package/dist/utils/broadcast.js.map +1 -1
  18. package/dist/utils/providers.d.ts +4 -5
  19. package/dist/utils/providers.d.ts.map +1 -1
  20. package/dist/utils/providers.js +10 -3
  21. package/dist/utils/providers.js.map +1 -1
  22. package/dist/utils/runtime-secrets.d.ts +25 -0
  23. package/dist/utils/runtime-secrets.d.ts.map +1 -0
  24. package/dist/utils/runtime-secrets.js +96 -0
  25. package/dist/utils/runtime-secrets.js.map +1 -0
  26. package/dist/utils/send.d.ts.map +1 -1
  27. package/dist/utils/send.js +18 -6
  28. package/dist/utils/send.js.map +1 -1
  29. package/dist/utils/tokens.d.ts +3 -1
  30. package/dist/utils/tokens.d.ts.map +1 -1
  31. package/dist/utils/tokens.js +14 -5
  32. package/dist/utils/tokens.js.map +1 -1
  33. package/dist/utils/urls.d.ts +1 -1
  34. package/dist/utils/urls.d.ts.map +1 -1
  35. package/dist/utils/urls.js +9 -3
  36. package/dist/utils/urls.js.map +1 -1
  37. package/dist/vite-plugin.d.ts +1 -0
  38. package/dist/vite-plugin.d.ts.map +1 -1
  39. package/dist/vite-plugin.js +13 -1
  40. package/dist/vite-plugin.js.map +1 -1
  41. package/package.json +4 -4
  42. package/src/options.ts +4 -2
  43. package/src/routes/confirm.ts +9 -2
  44. package/src/routes/preferences.astro +12 -3
  45. package/src/routes/subscribe.ts +15 -3
  46. package/src/routes/unsubscribe.ts +16 -3
  47. package/src/utils/broadcast.ts +9 -2
  48. package/src/utils/providers.ts +15 -5
  49. package/src/utils/runtime-secrets.ts +144 -0
  50. package/src/utils/send.ts +21 -5
  51. package/src/utils/tokens.ts +16 -5
  52. package/src/utils/urls.ts +9 -6
  53. package/src/virtual.d.ts +4 -2
  54. package/src/vite-plugin.ts +18 -1
@@ -0,0 +1,96 @@
1
+ export const GL_MAILER_TURNSTILE_UNCONFIGURED = 'GL_MAILER_TURNSTILE_UNCONFIGURED';
2
+ export const GL_MAILER_SIGNING_UNCONFIGURED = 'GL_MAILER_SIGNING_UNCONFIGURED';
3
+ const TURNSTILE_TEST_SECRETS = new Set([
4
+ '1x0000000000000000000000000000000AA',
5
+ '2x0000000000000000000000000000000AA',
6
+ '3x0000000000000000000000000000000AA',
7
+ ]);
8
+ const secretStoreCache = new WeakMap();
9
+ function isSecretStoreBinding(value) {
10
+ return (typeof value === 'object' &&
11
+ value !== null &&
12
+ 'get' in value &&
13
+ typeof value.get === 'function');
14
+ }
15
+ async function readBinding(value) {
16
+ if (typeof value === 'string')
17
+ return value.trim() || undefined;
18
+ if (!isSecretStoreBinding(value))
19
+ return undefined;
20
+ let pending = secretStoreCache.get(value);
21
+ if (!pending) {
22
+ pending = Promise.resolve()
23
+ .then(() => value.get())
24
+ .then((secret) => {
25
+ const resolved = typeof secret === 'string' ? secret.trim() || undefined : undefined;
26
+ if (!resolved)
27
+ secretStoreCache.delete(value);
28
+ return resolved;
29
+ })
30
+ .catch(() => {
31
+ secretStoreCache.delete(value);
32
+ return undefined;
33
+ });
34
+ secretStoreCache.set(value, pending);
35
+ }
36
+ return pending;
37
+ }
38
+ function hostFromUrl(url) {
39
+ try {
40
+ const parsed = new URL(url);
41
+ return {
42
+ host: parsed.host.toLowerCase(),
43
+ isLocal: parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1',
44
+ };
45
+ }
46
+ catch {
47
+ return { host: 'unknown', isLocal: false };
48
+ }
49
+ }
50
+ function failureCode(kind) {
51
+ return kind === 'turnstile' ? GL_MAILER_TURNSTILE_UNCONFIGURED : GL_MAILER_SIGNING_UNCONFIGURED;
52
+ }
53
+ export async function resolveMailerRuntimeSecret({ bindingsEnv, bindingName, legacyValue, requestUrl, kind, }) {
54
+ const requestHost = hostFromUrl(requestUrl);
55
+ let secret = await readBinding(bindingsEnv[bindingName]);
56
+ // Build-time literals are a localhost-only development escape hatch. A
57
+ // production request must always resolve its secret from the Worker env.
58
+ if (!secret && requestHost.isLocal)
59
+ secret = legacyValue?.trim() || undefined;
60
+ if (kind === 'turnstile' &&
61
+ secret &&
62
+ TURNSTILE_TEST_SECRETS.has(secret) &&
63
+ !requestHost.isLocal) {
64
+ secret = undefined;
65
+ }
66
+ if (secret)
67
+ return { ok: true, secret };
68
+ const code = failureCode(kind);
69
+ console.error(`[${code}] @growth-labs/mailer: host=${requestHost.host} binding=${bindingName}`);
70
+ return { ok: false, code };
71
+ }
72
+ export async function resolveTurnstileSecret(bindingsEnv, options, requestUrl) {
73
+ return resolveMailerRuntimeSecret({
74
+ bindingsEnv,
75
+ bindingName: options.turnstileSecretBinding,
76
+ legacyValue: options.turnstileSecretKey,
77
+ requestUrl,
78
+ kind: 'turnstile',
79
+ });
80
+ }
81
+ export async function resolveSigningSecret(bindingsEnv, options, requestUrl) {
82
+ return resolveMailerRuntimeSecret({
83
+ bindingsEnv,
84
+ bindingName: options.signingSecretBinding,
85
+ legacyValue: options.signingSecret,
86
+ requestUrl,
87
+ kind: 'signing',
88
+ });
89
+ }
90
+ export function runtimeSecretErrorResponse(failure) {
91
+ return Response.json({ error: failure.code }, { status: 503, headers: { 'Cache-Control': 'no-store' } });
92
+ }
93
+ export function siteHost(siteUrl) {
94
+ return new URL(siteUrl).host.toLowerCase();
95
+ }
96
+ //# sourceMappingURL=runtime-secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-secrets.js","sourceRoot":"","sources":["../../src/utils/runtime-secrets.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../src/utils/send.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAExC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAE1D,OAAO,KAAK,EACX,UAAU,EAGV,gBAAgB,EAEhB,YAAY,EACZ,MAAM,aAAa,CAAA;AAOpB,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAA;AAI3C;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAyC/C,wBAAsB,iBAAiB,CACtC,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE;IACP,EAAE,EAAE,MAAM,CAAA;IACV,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC9B,GACC,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CA0DjC;AAiFD,wBAAsB,YAAY,CACjC,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE;IACP,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,UAAU,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,MAAM,CAAC,EAAE,gBAAgB,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;CACnB,GACC,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CAoCzD;AAID,wBAAsB,UAAU,CAC/B,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE;IACP,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,gBAAgB,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;CACnB,GACC,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CA+BzD;AAID,wBAAsB,gBAAgB,CACrC,EAAE,EAAE,SAAS,EACb,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GACpC,OAAO,CAAC,IAAI,CAAC,CAKf"}
1
+ {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../src/utils/send.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAExC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAE1D,OAAO,KAAK,EACX,UAAU,EAGV,gBAAgB,EAEhB,YAAY,EACZ,MAAM,aAAa,CAAA;AAQpB,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAA;AAI3C;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAkD/C,wBAAsB,iBAAiB,CACtC,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE;IACP,EAAE,EAAE,MAAM,CAAA;IACV,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC9B,GACC,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CA2DjC;AAoFD,wBAAsB,YAAY,CACjC,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE;IACP,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,UAAU,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,MAAM,CAAC,EAAE,gBAAgB,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;CACnB,GACC,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CAqCzD;AAID,wBAAsB,UAAU,CAC/B,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE;IACP,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,gBAAgB,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;CACnB,GACC,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CAgCzD;AAID,wBAAsB,gBAAgB,CACrC,EAAE,EAAE,SAAS,EACb,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GACpC,OAAO,CAAC,IAAI,CAAC,CAKf"}
@@ -2,11 +2,18 @@ import { eq } from 'drizzle-orm';
2
2
  import { drizzle } from 'drizzle-orm/d1';
3
3
  import { ulid } from 'ulidx';
4
4
  import { emailSends } from '../schema/sends.js';
5
+ import { resolveSigningSecret, siteHost } from './runtime-secrets.js';
5
6
  import { getSubscriberBatch } from './subscribers.js';
6
7
  import { renderDigestItems, renderEmail } from './templates.js';
7
8
  import { generateToken } from './tokens.js';
8
9
  import { injectTrackingPixel, rewriteLinksForTracking } from './tracking.js';
9
10
  import { buildSubscriberManageUrls } from './urls.js';
11
+ async function requireSigningSecret(env, options) {
12
+ const result = await resolveSigningSecret(env, options, options.siteUrl);
13
+ if (!result.ok)
14
+ throw new Error(result.code);
15
+ return result.secret;
16
+ }
10
17
  function resolveProducerBindings(env, options) {
11
18
  const d1 = env[options.d1Binding];
12
19
  if (!d1) {
@@ -28,13 +35,14 @@ function resolveProducerBindings(env, options) {
28
35
  }
29
36
  // ─── sendTransactional ───
30
37
  export async function sendTransactional(env, options, params) {
38
+ const signingSecret = params.subscriberId ? await requireSigningSecret(env, options) : undefined;
31
39
  const { sendsDb, queue } = resolveProducerBindings(env, options);
32
40
  const trackingId = ulid();
33
41
  const subscriberId = params.subscriberId ?? ulid();
34
42
  // Render HTML
35
43
  let html;
36
44
  if (params.template) {
37
- const manageUrls = await buildSubscriberManageUrls(options, params.subscriberId);
45
+ const manageUrls = await buildSubscriberManageUrls(options, params.subscriberId, signingSecret);
38
46
  html = renderEmail(params.template, {
39
47
  ...params.data,
40
48
  senderName: options.senderName,
@@ -84,7 +92,7 @@ export async function sendTransactional(env, options, params) {
84
92
  return { trackingId };
85
93
  }
86
94
  // ─── Fan-out helper (shared by campaign + digest) ───
87
- async function fanOutToSubscribers(queue, db, sendsDb, options, params) {
95
+ async function fanOutToSubscribers(queue, db, sendsDb, options, signingSecret, params) {
88
96
  let cursor = null;
89
97
  let recipientCount = 0;
90
98
  const filter = params.filter ?? { status: ['active'] };
@@ -97,13 +105,15 @@ async function fanOutToSubscribers(queue, db, sendsDb, options, params) {
97
105
  const recipients = [];
98
106
  for (const sub of batch) {
99
107
  const trackingId = ulid();
100
- const unsubscribeToken = await generateToken(options.signingSecret, {
108
+ const unsubscribeToken = await generateToken(signingSecret, {
101
109
  subscriberId: sub.id,
102
110
  action: 'unsubscribe',
111
+ host: siteHost(options.siteUrl),
103
112
  });
104
- const preferencesToken = await generateToken(options.signingSecret, {
113
+ const preferencesToken = await generateToken(signingSecret, {
105
114
  subscriberId: sub.id,
106
115
  action: 'preferences',
116
+ host: siteHost(options.siteUrl),
107
117
  });
108
118
  await sendsDb.insert(emailSends).values({
109
119
  id: ulid(),
@@ -144,6 +154,7 @@ async function fanOutToSubscribers(queue, db, sendsDb, options, params) {
144
154
  // ─── sendCampaign ───
145
155
  export async function sendCampaign(env, options, params) {
146
156
  const campaignId = params.campaignId ?? ulid();
157
+ const signingSecret = await requireSigningSecret(env, options);
147
158
  const { db, sendsDb, queue } = resolveProducerBindings(env, options);
148
159
  // Render HTML
149
160
  let html;
@@ -168,7 +179,7 @@ export async function sendCampaign(env, options, params) {
168
179
  const trackClickUrl = `${options.siteUrl}${options.trackClickPath}`;
169
180
  html = injectTrackingPixel(html, trackOpenUrl);
170
181
  html = rewriteLinksForTracking(html, trackClickUrl);
171
- const recipientCount = await fanOutToSubscribers(queue, db, sendsDb, options, {
182
+ const recipientCount = await fanOutToSubscribers(queue, db, sendsDb, options, signingSecret, {
172
183
  subject: params.subject,
173
184
  html,
174
185
  campaignId,
@@ -180,6 +191,7 @@ export async function sendCampaign(env, options, params) {
180
191
  // ─── sendDigest ───
181
192
  export async function sendDigest(env, options, params) {
182
193
  const campaignId = params.campaignId ?? ulid();
194
+ const signingSecret = await requireSigningSecret(env, options);
183
195
  const { db, sendsDb, queue } = resolveProducerBindings(env, options);
184
196
  // Render digest items then full template
185
197
  const itemsHtml = renderDigestItems(params.items);
@@ -197,7 +209,7 @@ export async function sendDigest(env, options, params) {
197
209
  const trackClickUrl = `${options.siteUrl}${options.trackClickPath}`;
198
210
  html = injectTrackingPixel(html, trackOpenUrl);
199
211
  html = rewriteLinksForTracking(html, trackClickUrl);
200
- const recipientCount = await fanOutToSubscribers(queue, db, sendsDb, options, {
212
+ const recipientCount = await fanOutToSubscribers(queue, db, sendsDb, options, signingSecret, {
201
213
  subject: params.subject,
202
214
  html,
203
215
  campaignId,
@@ -1 +1 @@
1
- {"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/utils/send.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAE5B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAS/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAsBrD,SAAS,uBAAuB,CAC/B,GAAc,EACd,OAA8B;IAE9B,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,CAA2B,CAAA;IAC3D,IAAI,CAAC,EAAE,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACd,gBAAgB,OAAO,CAAC,SAAS,iBAAiB;YACjD,qFAAqF,CACtF,CAAA;IACF,CAAC;IACD,MAAM,gBAAgB,GAAG,OAAO,CAAC,cAAc,IAAI,OAAO,CAAC,SAAS,CAAA;IACpE,MAAM,OAAO,GAAG,GAAG,CAAC,gBAAgB,CAA2B,CAAA;IAC/D,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACd,gBAAgB,gBAAgB,iBAAiB;YAChD,0FAA0F,CAC3F,CAAA;IACF,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,CAAsB,CAAA;IAC5D,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACd,gBAAgB,OAAO,CAAC,YAAY,iBAAiB;YACpD,wFAAwF,CACzF,CAAA;IACF,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAA;AAC7D,CAAC;AAED,4BAA4B;AAE5B,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,GAAc,EACd,OAA8B,EAC9B,MAOC;IAED,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,uBAAuB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAChE,MAAM,UAAU,GAAG,IAAI,EAAE,CAAA;IACzB,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,IAAI,IAAI,EAAE,CAAA;IAElD,cAAc;IACd,IAAI,IAAY,CAAA;IAChB,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,UAAU,GAAG,MAAM,yBAAyB,CAAC,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;QAChF,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE;YACnC,GAAG,MAAM,CAAC,IAAI;YACd,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,cAAc,EAAE,UAAU,CAAC,cAAc;YACzC,cAAc,EAAE,UAAU,CAAC,cAAc;YACzC,KAAK,EAAE,OAAO,CAAC,KAAK;SACJ,CAAC,CAAA;IACnB,CAAC;SAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;IACnB,CAAC;SAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;IAC5D,CAAC;IAED,yEAAyE;IACzE,MAAM,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;QACvC,EAAE,EAAE,IAAI,EAAE;QACV,YAAY;QACZ,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,MAAM,CAAC,EAAE;QAChB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,QAAQ;QAChB,UAAU;QACV,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACnC,CAAC,CAAA;IAEF,gCAAgC;IAChC,MAAM,OAAO,GAAsB;QAClC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,IAAI,EAAE,eAAe;QACrB,UAAU,EAAE;YACX;gBACC,KAAK,EAAE,MAAM,CAAC,EAAE;gBAChB,YAAY;gBACZ,UAAU;gBACV,gBAAgB,EAAE,EAAE;gBACpB,gBAAgB,EAAE,EAAE;aACpB;SACD;QACD,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,YAAY,EAAE,IAAI;QAClB,IAAI,EAAE,GAAG,OAAO,CAAC,UAAU,KAAK,OAAO,CAAC,WAAW,GAAG;QACtD,OAAO,EAAE,OAAO,CAAC,OAAO;KACxB,CAAA;IAED,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAEzB,OAAO,EAAE,UAAU,EAAE,CAAA;AACtB,CAAC;AAED,uDAAuD;AAEvD,KAAK,UAAU,mBAAmB,CACjC,KAAY,EACZ,EAAa,EACb,OAAkB,EAClB,OAA8B,EAC9B,MAMC;IAED,IAAI,MAAM,GAAkB,IAAI,CAAA;IAChC,IAAI,cAAc,GAAG,CAAC,CAAA;IACtB,MAAM,MAAM,GAAqB,MAAM,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAA;IACxE,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,MAAM,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAA;IAE9C,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;QAC7E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,MAAK;QAE7B,MAAM,UAAU,GAAqB,EAAE,CAAA;QACvC,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,UAAU,GAAG,IAAI,EAAE,CAAA;YACzB,MAAM,gBAAgB,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,aAAa,EAAE;gBACnE,YAAY,EAAE,GAAG,CAAC,EAAE;gBACpB,MAAM,EAAE,aAAa;aACrB,CAAC,CAAA;YACF,MAAM,gBAAgB,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,aAAa,EAAE;gBACnE,YAAY,EAAE,GAAG,CAAC,EAAE;gBACpB,MAAM,EAAE,aAAa;aACrB,CAAC,CAAA;YAEF,MAAM,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;gBACvC,EAAE,EAAE,IAAI,EAAE;gBACV,YAAY,EAAE,GAAG,CAAC,EAAE;gBACpB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,MAAM,EAAE,QAAQ;gBAChB,UAAU;gBACV,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACnC,CAAC,CAAA;YAEF,UAAU,CAAC,IAAI,CAAC;gBACf,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,YAAY,EAAE,GAAG,CAAC,EAAE;gBACpB,UAAU;gBACV,gBAAgB;gBAChB,gBAAgB;aAChB,CAAC,CAAA;QACH,CAAC;QAED,MAAM,OAAO,GAAsB;YAClC,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,UAAU;YACV,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,YAAY,EAAE,MAAM,CAAC,IAAI;YACzB,IAAI,EAAE,GAAG,OAAO,CAAC,UAAU,KAAK,OAAO,CAAC,WAAW,GAAG;YACtD,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,UAAU,EAAE,MAAM,CAAC,UAAU;SAC7B,CAAA;QACD,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAEzB,cAAc,IAAI,KAAK,CAAC,MAAM,CAAA;QAC9B,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IACpC,CAAC;IAED,OAAO,cAAc,CAAA;AACtB,CAAC;AAED,uBAAuB;AAEvB,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,GAAc,EACd,OAA8B,EAC9B,MAOC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,IAAI,EAAE,CAAA;IAC9C,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,uBAAuB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAEpE,cAAc;IACd,IAAI,IAAY,CAAA;IAChB,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrB,IAAI,GAAG,WAAW,CAAC,UAAU,EAAE;YAC9B,GAAG,MAAM,CAAC,IAAI;YACd,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,cAAc,EAAE,qBAAqB;YACrC,cAAc,EAAE,qBAAqB;YACrC,KAAK,EAAE,OAAO,CAAC,KAAK;SACJ,CAAC,CAAA;IACnB,CAAC;SAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;IACnB,CAAC;SAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;IAC5D,CAAC;IAED,kBAAkB;IAClB,MAAM,YAAY,GAAG,GAAG,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,aAAa,EAAE,CAAA;IACjE,MAAM,aAAa,GAAG,GAAG,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAA;IACnE,IAAI,GAAG,mBAAmB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;IAC9C,IAAI,GAAG,uBAAuB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;IAEnD,MAAM,cAAc,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE;QAC7E,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,IAAI;QACJ,UAAU;QACV,IAAI,EAAE,UAAU;QAChB,MAAM,EAAE,MAAM,CAAC,MAAM;KACrB,CAAC,CAAA;IAEF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,CAAA;AACtC,CAAC;AAED,qBAAqB;AAErB,MAAM,CAAC,KAAK,UAAU,UAAU,CAC/B,GAAc,EACd,OAA8B,EAC9B,MAMC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,IAAI,EAAE,CAAA;IAC9C,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,uBAAuB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAEpE,yCAAyC;IACzC,MAAM,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACjD,IAAI,IAAI,GAAG,WAAW,CAAC,QAAQ,EAAE;QAChC,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,cAAc,EAAE,qBAAqB;QACrC,cAAc,EAAE,qBAAqB;QACrC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE;QACjC,WAAW,EAAE,SAAS;KACN,CAAC,CAAA;IAElB,kBAAkB;IAClB,MAAM,YAAY,GAAG,GAAG,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,aAAa,EAAE,CAAA;IACjE,MAAM,aAAa,GAAG,GAAG,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAA;IACnE,IAAI,GAAG,mBAAmB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;IAC9C,IAAI,GAAG,uBAAuB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;IAEnD,MAAM,cAAc,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE;QAC7E,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,IAAI;QACJ,UAAU;QACV,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,MAAM,CAAC,MAAM;KACrB,CAAC,CAAA;IAEF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,CAAA;AACtC,CAAC;AAED,2BAA2B;AAE3B,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACrC,EAAa,EACb,UAAkB,EAClB,MAAc,EACd,MAAsC;IAEtC,MAAM,EAAE;SACN,MAAM,CAAC,UAAU,CAAC;SAClB,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;SAC1B,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAA;AAC/C,CAAC"}
1
+ {"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/utils/send.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,14 @@
1
1
  interface TokenPayload {
2
2
  subscriberId: string;
3
3
  action: 'confirm' | 'unsubscribe' | 'preferences';
4
+ host: string;
4
5
  exp?: number;
5
6
  }
6
7
  export declare function generateToken(secret: string, payload: TokenPayload): Promise<string>;
7
- export declare function verifyToken(secret: string, token: string): Promise<{
8
+ export declare function verifyToken(secret: string, token: string, expectedHost: string): Promise<{
8
9
  subscriberId: string;
9
10
  action: string;
11
+ host: string;
10
12
  exp?: number;
11
13
  } | null>;
12
14
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"AA0CA,UAAU,YAAY;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,aAAa,CAAA;IACjD,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ;AAID,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAU1F;AAED,wBAAsB,WAAW,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAyBxE"}
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"AA0CA,UAAU,YAAY;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,aAAa,CAAA;IACjD,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ;AAID,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAc1F;AAED,wBAAsB,WAAW,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA8BtF"}
@@ -30,6 +30,9 @@ async function hmacVerify(secret, data, signature) {
30
30
  }
31
31
  const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
32
32
  export async function generateToken(secret, payload) {
33
+ if (typeof payload.host !== 'string' || !payload.host.trim()) {
34
+ throw new Error('GL_MAILER_TOKEN_HOST_REQUIRED');
35
+ }
33
36
  const finalPayload = { ...payload };
34
37
  if (finalPayload.action === 'confirm' && finalPayload.exp === undefined) {
35
38
  finalPayload.exp = Date.now() + SEVEN_DAYS_MS;
@@ -38,7 +41,9 @@ export async function generateToken(secret, payload) {
38
41
  const signature = await hmacSign(secret, payloadB64);
39
42
  return `${payloadB64}.${signature}`;
40
43
  }
41
- export async function verifyToken(secret, token) {
44
+ export async function verifyToken(secret, token, expectedHost) {
45
+ if (typeof expectedHost !== 'string' || !expectedHost.trim())
46
+ return null;
42
47
  const dotIndex = token.indexOf('.');
43
48
  if (dotIndex === -1)
44
49
  return null;
@@ -46,14 +51,18 @@ export async function verifyToken(secret, token) {
46
51
  const signature = token.slice(dotIndex + 1);
47
52
  if (!payloadB64 || !signature)
48
53
  return null;
49
- const valid = await hmacVerify(secret, payloadB64, signature);
50
- if (!valid)
51
- return null;
52
54
  try {
55
+ const valid = await hmacVerify(secret, payloadB64, signature);
56
+ if (!valid)
57
+ return null;
53
58
  const decoded = JSON.parse(new TextDecoder().decode(fromBase64Url(payloadB64)));
54
59
  if (decoded.exp !== undefined && decoded.exp < Date.now())
55
60
  return null;
56
- return decoded;
61
+ if (typeof decoded.host !== 'string' || !decoded.host.trim())
62
+ return null;
63
+ if (decoded.host !== expectedHost)
64
+ return null;
65
+ return { ...decoded, host: decoded.host };
57
66
  }
58
67
  catch {
59
68
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import type { ResolvedMailerOptions } from '../options.js';
2
2
  export declare function buildSiteUrl(siteUrl: string, path: string, searchParams?: Record<string, string | boolean | undefined>): string;
3
- export declare function buildSubscriberManageUrls(options: Pick<ResolvedMailerOptions, 'preferencesPath' | 'siteUrl' | 'signingSecret' | 'unsubscribePath'>, subscriberId?: string): Promise<{
3
+ export declare function buildSubscriberManageUrls(options: Pick<ResolvedMailerOptions, 'preferencesPath' | 'siteUrl' | 'unsubscribePath'>, subscriberId?: string, signingSecret?: string): Promise<{
4
4
  preferencesUrl: string;
5
5
  unsubscribeUrl: string;
6
6
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"urls.d.ts","sourceRoot":"","sources":["../../src/utils/urls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAG1D,wBAAgB,YAAY,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,GACzD,MAAM,CASR;AAED,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,IAAI,CACZ,qBAAqB,EACrB,iBAAiB,GAAG,SAAS,GAAG,eAAe,GAAG,iBAAiB,CACnE,EACD,YAAY,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB7D"}
1
+ {"version":3,"file":"urls.d.ts","sourceRoot":"","sources":["../../src/utils/urls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAI1D,wBAAgB,YAAY,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,GACzD,MAAM,CASR;AAED,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,iBAAiB,GAAG,SAAS,GAAG,iBAAiB,CAAC,EACvF,YAAY,CAAC,EAAE,MAAM,EACrB,aAAa,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CA4B7D"}
@@ -1,3 +1,4 @@
1
+ import { siteHost } from './runtime-secrets.js';
1
2
  import { generateToken } from './tokens.js';
2
3
  export function buildSiteUrl(siteUrl, path, searchParams) {
3
4
  const url = new URL(path, siteUrl);
@@ -8,18 +9,23 @@ export function buildSiteUrl(siteUrl, path, searchParams) {
8
9
  }
9
10
  return url.toString();
10
11
  }
11
- export async function buildSubscriberManageUrls(options, subscriberId) {
12
+ export async function buildSubscriberManageUrls(options, subscriberId, signingSecret) {
12
13
  if (!subscriberId) {
13
14
  return { preferencesUrl: '', unsubscribeUrl: '' };
14
15
  }
16
+ if (!signingSecret)
17
+ throw new Error('GL_MAILER_SIGNING_UNCONFIGURED');
18
+ const host = siteHost(options.siteUrl);
15
19
  const [unsubscribeToken, preferencesToken] = await Promise.all([
16
- generateToken(options.signingSecret, {
20
+ generateToken(signingSecret, {
17
21
  subscriberId,
18
22
  action: 'unsubscribe',
23
+ host,
19
24
  }),
20
- generateToken(options.signingSecret, {
25
+ generateToken(signingSecret, {
21
26
  subscriberId,
22
27
  action: 'preferences',
28
+ host,
23
29
  }),
24
30
  ]);
25
31
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"urls.js","sourceRoot":"","sources":["../../src/utils/urls.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,MAAM,UAAU,YAAY,CAC3B,OAAe,EACf,IAAY,EACZ,YAA2D;IAE3D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAElC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IAC9E,CAAC;IAED,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC9C,OAGC,EACD,YAAqB;IAErB,IAAI,CAAC,YAAY,EAAE,CAAC;QACnB,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,CAAA;IAClD,CAAC;IAED,MAAM,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC9D,aAAa,CAAC,OAAO,CAAC,aAAa,EAAE;YACpC,YAAY;YACZ,MAAM,EAAE,aAAa;SACrB,CAAC;QACF,aAAa,CAAC,OAAO,CAAC,aAAa,EAAE;YACpC,YAAY;YACZ,MAAM,EAAE,aAAa;SACrB,CAAC;KACF,CAAC,CAAA;IAEF,OAAO;QACN,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,EAAE;YACtE,KAAK,EAAE,gBAAgB;SACvB,CAAC;QACF,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,EAAE;YACtE,KAAK,EAAE,gBAAgB;SACvB,CAAC;KACF,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"urls.js","sourceRoot":"","sources":["../../src/utils/urls.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,MAAM,UAAU,YAAY,CAC3B,OAAe,EACf,IAAY,EACZ,YAA2D;IAE3D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAElC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IAC9E,CAAC;IAED,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC9C,OAAuF,EACvF,YAAqB,EACrB,aAAsB;IAEtB,IAAI,CAAC,YAAY,EAAE,CAAC;QACnB,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,CAAA;IAClD,CAAC;IAED,IAAI,CAAC,aAAa;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;IACrE,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IACtC,MAAM,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC9D,aAAa,CAAC,aAAa,EAAE;YAC5B,YAAY;YACZ,MAAM,EAAE,aAAa;YACrB,IAAI;SACJ,CAAC;QACF,aAAa,CAAC,aAAa,EAAE;YAC5B,YAAY;YACZ,MAAM,EAAE,aAAa;YACrB,IAAI;SACJ,CAAC;KACF,CAAC,CAAA;IAEF,OAAO;QACN,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,EAAE;YACtE,KAAK,EAAE,gBAAgB;SACvB,CAAC;QACF,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,EAAE;YACtE,KAAK,EAAE,gBAAgB;SACvB,CAAC;KACF,CAAA;AACF,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import type { Plugin } from 'vite';
2
2
  import type { ResolvedMailerOptions } from './options.js';
3
+ export declare function renderMailerConfigModule(config: ResolvedMailerOptions, command: 'build' | 'serve'): string;
3
4
  export declare function growthLabsMailerPlugin(config: ResolvedMailerOptions): Plugin;
4
5
  //# sourceMappingURL=vite-plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAClC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAKzD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAc5E"}
1
+ {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAClC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAKzD,wBAAgB,wBAAwB,CACvC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,OAAO,GAAG,OAAO,GACxB,MAAM,CAOR;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAmB5E"}
@@ -1,8 +1,20 @@
1
1
  const VIRTUAL_MODULE_ID = 'virtual:growth-labs/mailer/config';
2
2
  const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
3
+ export function renderMailerConfigModule(config, command) {
4
+ const serializableConfig = { ...config };
5
+ if (command !== 'serve') {
6
+ delete serializableConfig.turnstileSecretKey;
7
+ delete serializableConfig.signingSecret;
8
+ }
9
+ return `export const config = ${JSON.stringify(serializableConfig)};`;
10
+ }
3
11
  export function growthLabsMailerPlugin(config) {
12
+ let command = 'build';
4
13
  return {
5
14
  name: 'growth-labs-mailer-config',
15
+ configResolved(resolvedConfig) {
16
+ command = resolvedConfig.command;
17
+ },
6
18
  resolveId(id) {
7
19
  if (id === VIRTUAL_MODULE_ID) {
8
20
  return RESOLVED_VIRTUAL_MODULE_ID;
@@ -10,7 +22,7 @@ export function growthLabsMailerPlugin(config) {
10
22
  },
11
23
  load(id) {
12
24
  if (id === RESOLVED_VIRTUAL_MODULE_ID) {
13
- return `export const config = ${JSON.stringify(config)};`;
25
+ return renderMailerConfigModule(config, command);
14
26
  }
15
27
  },
16
28
  };
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAGA,MAAM,iBAAiB,GAAG,mCAAmC,CAAA;AAC7D,MAAM,0BAA0B,GAAG,KAAK,iBAAiB,EAAE,CAAA;AAE3D,MAAM,UAAU,sBAAsB,CAAC,MAA6B;IACnE,OAAO;QACN,IAAI,EAAE,2BAA2B;QACjC,SAAS,CAAC,EAAE;YACX,IAAI,EAAE,KAAK,iBAAiB,EAAE,CAAC;gBAC9B,OAAO,0BAA0B,CAAA;YAClC,CAAC;QACF,CAAC;QACD,IAAI,CAAC,EAAE;YACN,IAAI,EAAE,KAAK,0BAA0B,EAAE,CAAC;gBACvC,OAAO,yBAAyB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAA;YAC1D,CAAC;QACF,CAAC;KACD,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAGA,MAAM,iBAAiB,GAAG,mCAAmC,CAAA;AAC7D,MAAM,0BAA0B,GAAG,KAAK,iBAAiB,EAAE,CAAA;AAE3D,MAAM,UAAU,wBAAwB,CACvC,MAA6B,EAC7B,OAA0B;IAE1B,MAAM,kBAAkB,GAAG,EAAE,GAAG,MAAM,EAAE,CAAA;IACxC,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACzB,OAAO,kBAAkB,CAAC,kBAAkB,CAAA;QAC5C,OAAO,kBAAkB,CAAC,aAAa,CAAA;IACxC,CAAC;IACD,OAAO,yBAAyB,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,GAAG,CAAA;AACtE,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAA6B;IACnE,IAAI,OAAO,GAAsB,OAAO,CAAA;IAExC,OAAO;QACN,IAAI,EAAE,2BAA2B;QACjC,cAAc,CAAC,cAAc;YAC5B,OAAO,GAAG,cAAc,CAAC,OAAO,CAAA;QACjC,CAAC;QACD,SAAS,CAAC,EAAE;YACX,IAAI,EAAE,KAAK,iBAAiB,EAAE,CAAC;gBAC9B,OAAO,0BAA0B,CAAA;YAClC,CAAC;QACF,CAAC;QACD,IAAI,CAAC,EAAE;YACN,IAAI,EAAE,KAAK,0BAA0B,EAAE,CAAC;gBACvC,OAAO,wBAAwB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;YACjD,CAAC;QACF,CAAC;KACD,CAAA;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growth-labs/mailer",
3
- "version": "0.4.10",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "exports": {
@@ -71,7 +71,7 @@
71
71
  "registry": "https://registry.npmjs.org/"
72
72
  },
73
73
  "peerDependencies": {
74
- "astro": "^6.1.10"
74
+ "astro": "^6.1.10 || ^7.0.0"
75
75
  },
76
76
  "peerDependenciesMeta": {
77
77
  "@growth-labs/analytics": {
@@ -85,10 +85,10 @@
85
85
  },
86
86
  "devDependencies": {
87
87
  "@cloudflare/workers-types": "^4.20260402.1",
88
- "astro": "^6.1.10",
88
+ "astro": "^6.4.6",
89
89
  "drizzle-kit": "^0.30.0",
90
90
  "typescript": "^5.7.0",
91
- "vite": "^7.3.2",
91
+ "vite": "^7.3.5",
92
92
  "vitest": "^4.1.8"
93
93
  },
94
94
  "scripts": {
package/src/options.ts CHANGED
@@ -25,14 +25,16 @@ export const mailerOptionsSchema = z.object({
25
25
 
26
26
  // ─── Turnstile (subscribe form) ───
27
27
  turnstileSiteKey: z.string(),
28
- turnstileSecretKey: z.string(),
28
+ turnstileSecretBinding: z.string().min(1).default('TURNSTILE_SECRET_KEY'),
29
+ turnstileSecretKey: z.string().optional(),
29
30
 
30
31
  // ─── Subscriber lifecycle ───
31
32
  doubleOptIn: z.boolean().default(true),
32
33
  topics: z.array(z.string()).optional(),
33
34
 
34
35
  // ─── Token signing ───
35
- signingSecret: z.string(),
36
+ signingSecretBinding: z.string().min(1).default('MAILER_SIGNING_SECRET'),
37
+ signingSecret: z.string().optional(),
36
38
 
37
39
  // ─── Routes ───
38
40
  subscribePath: z.string().default('/api/newsletter/subscribe'),
@@ -4,6 +4,11 @@ import type { APIRoute } from 'astro'
4
4
  import { drizzle } from 'drizzle-orm/d1'
5
5
  import { probeMailerSchema, schemaMissingResponse } from '../_internal/schema-probe.js'
6
6
  import { emitMailerAnalyticsEvent } from '../utils/analytics.js'
7
+ import {
8
+ resolveSigningSecret,
9
+ runtimeSecretErrorResponse,
10
+ siteHost,
11
+ } from '../utils/runtime-secrets.js'
7
12
  import { sendTransactional } from '../utils/send.js'
8
13
  import { confirmSubscriber, getSubscriberById } from '../utils/subscribers.js'
9
14
  import { verifyToken } from '../utils/tokens.js'
@@ -14,15 +19,17 @@ export const GET: APIRoute = async (context) => {
14
19
  if (!token) {
15
20
  return Response.json({ error: 'Missing token' }, { status: 400 })
16
21
  }
22
+ const bindingsEnv = cloudflareEnv as Record<string, unknown>
23
+ const signingSecret = await resolveSigningSecret(bindingsEnv, config, url.toString())
24
+ if (!signingSecret.ok) return runtimeSecretErrorResponse(signingSecret)
17
25
 
18
26
  // Verify token
19
- const payload = await verifyToken(config.signingSecret, token)
27
+ const payload = await verifyToken(signingSecret.secret, token, siteHost(config.siteUrl))
20
28
  if (!payload || payload.action !== 'confirm') {
21
29
  return Response.json({ error: 'Invalid or expired token' }, { status: 400 })
22
30
  }
23
31
 
24
32
  // Resolve bindings
25
- const bindingsEnv = cloudflareEnv as Record<string, unknown>
26
33
  const d1 = bindingsEnv[config.d1Binding] as D1Database
27
34
 
28
35
  // Schema probe — return 503 with GL_MAILER_SCHEMA_MISSING on miss.
@@ -8,6 +8,11 @@ import {
8
8
  unsubscribeSubscriber,
9
9
  updatePreferences,
10
10
  } from "../utils/subscribers.js";
11
+ import {
12
+ resolveSigningSecret,
13
+ runtimeSecretErrorResponse,
14
+ siteHost,
15
+ } from "../utils/runtime-secrets.js";
11
16
  import { generateToken, verifyToken } from "../utils/tokens.js";
12
17
  import { buildSiteUrl } from "../utils/urls.js";
13
18
 
@@ -27,15 +32,20 @@ if (!token) {
27
32
  return new Response("Missing token", { status: 400 });
28
33
  }
29
34
 
30
- const payload = await verifyToken(config.signingSecret, token);
35
+ const bindingsEnv = cloudflareEnv as Record<string, unknown>;
36
+ const signingSecret = await resolveSigningSecret(bindingsEnv, config, Astro.request.url);
37
+ if (!signingSecret.ok) return runtimeSecretErrorResponse(signingSecret);
38
+
39
+ const payload = await verifyToken(signingSecret.secret, token, siteHost(config.siteUrl));
31
40
  if (!payload || !["preferences", "unsubscribe"].includes(payload.action)) {
32
41
  return new Response("Invalid or expired token", { status: 400 });
33
42
  }
34
43
 
35
44
  if (Astro.request.method === "GET" && payload.action === "unsubscribe") {
36
- const preferencesToken = await generateToken(config.signingSecret, {
45
+ const preferencesToken = await generateToken(signingSecret.secret, {
37
46
  subscriberId: payload.subscriberId,
38
47
  action: "preferences",
48
+ host: siteHost(config.siteUrl),
39
49
  });
40
50
 
41
51
  return Astro.redirect(
@@ -51,7 +61,6 @@ if (Astro.request.method === "POST" && payload.action !== "preferences") {
51
61
  return new Response("Invalid or expired token", { status: 400 });
52
62
  }
53
63
 
54
- const bindingsEnv = cloudflareEnv as Record<string, unknown>;
55
64
  const d1 = bindingsEnv[config.d1Binding] as D1Database;
56
65
  if (!d1) {
57
66
  return new Response("D1 binding not available", { status: 500 });
@@ -4,6 +4,12 @@ import type { APIRoute } from 'astro'
4
4
  import { drizzle } from 'drizzle-orm/d1'
5
5
  import { probeMailerSchema, schemaMissingResponse } from '../_internal/schema-probe.js'
6
6
  import { emitMailerAnalyticsEvent } from '../utils/analytics.js'
7
+ import {
8
+ resolveSigningSecret,
9
+ resolveTurnstileSecret,
10
+ runtimeSecretErrorResponse,
11
+ siteHost,
12
+ } from '../utils/runtime-secrets.js'
7
13
  import { sendTransactional } from '../utils/send.js'
8
14
  import { createSubscriber } from '../utils/subscribers.js'
9
15
  import { generateToken } from '../utils/tokens.js'
@@ -29,6 +35,12 @@ export const POST: APIRoute = async (context) => {
29
35
  return Response.json({ error: 'Invalid email format' }, { status: 400 })
30
36
  }
31
37
 
38
+ const bindingsEnv = cloudflareEnv as Record<string, unknown>
39
+ const turnstileSecret = await resolveTurnstileSecret(bindingsEnv, config, request.url)
40
+ if (!turnstileSecret.ok) return runtimeSecretErrorResponse(turnstileSecret)
41
+ const signingSecret = await resolveSigningSecret(bindingsEnv, config, request.url)
42
+ if (!signingSecret.ok) return runtimeSecretErrorResponse(signingSecret)
43
+
32
44
  // 4. Validate Turnstile token
33
45
  const turnstileResponse = await fetch(
34
46
  'https://challenges.cloudflare.com/turnstile/v0/siteverify',
@@ -36,7 +48,7 @@ export const POST: APIRoute = async (context) => {
36
48
  method: 'POST',
37
49
  headers: { 'Content-Type': 'application/json' },
38
50
  body: JSON.stringify({
39
- secret: config.turnstileSecretKey,
51
+ secret: turnstileSecret.secret,
40
52
  response: body.turnstileToken,
41
53
  remoteip: request.headers.get('cf-connecting-ip') ?? undefined,
42
54
  }),
@@ -50,7 +62,6 @@ export const POST: APIRoute = async (context) => {
50
62
  }
51
63
 
52
64
  // 5. Resolve bindings
53
- const bindingsEnv = cloudflareEnv as Record<string, unknown>
54
65
  const d1 = bindingsEnv[config.d1Binding] as D1Database
55
66
 
56
67
  // 5a. Schema probe — runs once per Worker instance. On miss, return 503
@@ -72,9 +83,10 @@ export const POST: APIRoute = async (context) => {
72
83
 
73
84
  // 7. Send confirmation or welcome email
74
85
  if (config.doubleOptIn && (isNew || subscriber.status === 'pending')) {
75
- const confirmToken = await generateToken(config.signingSecret, {
86
+ const confirmToken = await generateToken(signingSecret.secret, {
76
87
  subscriberId: subscriber.id,
77
88
  action: 'confirm',
89
+ host: siteHost(config.siteUrl),
78
90
  })
79
91
  const confirmUrl = `${config.siteUrl}${config.confirmPath}?token=${confirmToken}`
80
92