@growth-labs/mailer 0.2.1 → 0.3.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 (57) hide show
  1. package/README.md +5 -1
  2. package/dist/_internal/schema-probe.d.ts +30 -0
  3. package/dist/_internal/schema-probe.d.ts.map +1 -0
  4. package/dist/_internal/schema-probe.js +68 -0
  5. package/dist/_internal/schema-probe.js.map +1 -0
  6. package/dist/options.d.ts +58 -0
  7. package/dist/options.d.ts.map +1 -1
  8. package/dist/options.js +22 -0
  9. package/dist/options.js.map +1 -1
  10. package/dist/queue/consumer.d.ts.map +1 -1
  11. package/dist/queue/consumer.js +12 -0
  12. package/dist/queue/consumer.js.map +1 -1
  13. package/dist/routes/confirm.d.ts.map +1 -1
  14. package/dist/routes/confirm.js +5 -0
  15. package/dist/routes/confirm.js.map +1 -1
  16. package/dist/routes/subscribe.d.ts.map +1 -1
  17. package/dist/routes/subscribe.js +6 -0
  18. package/dist/routes/subscribe.js.map +1 -1
  19. package/dist/routes/track-click.d.ts.map +1 -1
  20. package/dist/routes/track-click.js +16 -9
  21. package/dist/routes/track-click.js.map +1 -1
  22. package/dist/routes/track-open.d.ts.map +1 -1
  23. package/dist/routes/track-open.js +15 -9
  24. package/dist/routes/track-open.js.map +1 -1
  25. package/dist/routes/unsubscribe.d.ts.map +1 -1
  26. package/dist/routes/unsubscribe.js +10 -0
  27. package/dist/routes/unsubscribe.js.map +1 -1
  28. package/dist/routes/webhook.d.ts.map +1 -1
  29. package/dist/routes/webhook.js +24 -1
  30. package/dist/routes/webhook.js.map +1 -1
  31. package/dist/utils/analytics.d.ts +1 -1
  32. package/dist/utils/analytics.d.ts.map +1 -1
  33. package/dist/utils/analytics.js +1 -0
  34. package/dist/utils/analytics.js.map +1 -1
  35. package/dist/utils/index.d.ts +1 -0
  36. package/dist/utils/index.d.ts.map +1 -1
  37. package/dist/utils/index.js +1 -0
  38. package/dist/utils/index.js.map +1 -1
  39. package/dist/utils/webhook-signature.d.ts +6 -0
  40. package/dist/utils/webhook-signature.d.ts.map +1 -0
  41. package/dist/utils/webhook-signature.js +59 -0
  42. package/dist/utils/webhook-signature.js.map +1 -0
  43. package/migrations/0001_create_gl_mailer_tables.sql +48 -0
  44. package/package.json +2 -1
  45. package/src/_internal/schema-probe.ts +89 -0
  46. package/src/options.ts +22 -0
  47. package/src/queue/consumer.ts +13 -0
  48. package/src/routes/confirm.ts +6 -0
  49. package/src/routes/subscribe.ts +7 -0
  50. package/src/routes/track-click.ts +21 -14
  51. package/src/routes/track-open.ts +20 -14
  52. package/src/routes/unsubscribe.ts +9 -0
  53. package/src/routes/webhook.ts +25 -1
  54. package/src/utils/analytics.ts +2 -0
  55. package/src/utils/index.ts +1 -0
  56. package/src/utils/webhook-signature.ts +91 -0
  57. package/src/virtual.d.ts +14 -0
package/README.md CHANGED
@@ -39,6 +39,10 @@ mailer({
39
39
  },
40
40
  analyticsEnabled: true, // Emit email events to @growth-labs/analytics
41
41
  analyticsBinding: 'ANALYTICS', // WAE binding used when analytics is enabled
42
+ webhookSignature: {
43
+ enabled: true,
44
+ secret: import.meta.env.MAILER_WEBHOOK_SECRET,
45
+ },
42
46
  })
43
47
  ```
44
48
 
@@ -49,7 +53,7 @@ mailer({
49
53
  - `GET /api/newsletter/confirm?token=...` — Double opt-in confirmation
50
54
  - `GET /api/newsletter/unsubscribe?token=...` — One-click unsubscribe
51
55
  - `GET/POST /email/preferences?token=...` — Preference center page
52
- - `POST /api/email/webhook` — ESP webhook receiver (delivery, bounce, complaint)
56
+ - `POST /api/email/webhook` — ESP webhook receiver (delivery, bounce, complaint), optional HMAC signature verification
53
57
  - `GET /api/email/open/:trackingId` — Open tracking pixel
54
58
  - `GET /api/email/click/:trackingId` — Click tracking redirect
55
59
 
@@ -0,0 +1,30 @@
1
+ export declare const GL_MAILER_SCHEMA_MISSING = "GL_MAILER_SCHEMA_MISSING";
2
+ export type ProbeResult = {
3
+ ok: boolean;
4
+ };
5
+ interface D1PreparedStatement {
6
+ first<T = unknown>(): Promise<T | null>;
7
+ }
8
+ interface D1DatabaseLike {
9
+ prepare(query: string): D1PreparedStatement;
10
+ }
11
+ /** @internal Reset the module-scoped cache — tests only. */
12
+ export declare function _resetSchemaProbeCache(): void;
13
+ /**
14
+ * Probe the configured D1 binding for `gl_subscribers` AND `gl_email_sends`.
15
+ * Returns `{ ok: true }` on success, `{ ok: false }` on failure (either table
16
+ * missing). Caches the result on the first call per Worker instance.
17
+ *
18
+ * Does not throw. A missing binding is treated as `ok: false` and logged —
19
+ * mailer cannot function without D1, so unlike analytics this is loud rather
20
+ * than silent.
21
+ */
22
+ export declare function probeMailerSchema(db: D1DatabaseLike | undefined, d1Binding: string): Promise<ProbeResult>;
23
+ /**
24
+ * Helper: build the standard 503 response mailer routes return on schema
25
+ * miss. The body shape is stable so observability dashboards can match on
26
+ * `code === 'GL_MAILER_SCHEMA_MISSING'`.
27
+ */
28
+ export declare function schemaMissingResponse(): Response;
29
+ export {};
30
+ //# sourceMappingURL=schema-probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-probe.d.ts","sourceRoot":"","sources":["../../src/_internal/schema-probe.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,wBAAwB,6BAA6B,CAAA;AAElE,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,OAAO,CAAA;CAAE,CAAA;AAEzC,UAAU,mBAAmB;IAC5B,KAAK,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;CACvC;AAED,UAAU,cAAc;IACvB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAAA;CAC3C;AAID,4DAA4D;AAC5D,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACtC,EAAE,EAAE,cAAc,GAAG,SAAS,EAC9B,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CAkBtB;AAgBD;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,QAAQ,CAQhD"}
@@ -0,0 +1,68 @@
1
+ // One-shot D1 schema probe with module-scoped cache. Mailer routes that touch
2
+ // gl_subscribers / gl_email_sends call this before doing D1 work. On miss the
3
+ // route returns 503 with the GL_MAILER_SCHEMA_MISSING code instead of letting
4
+ // drizzle throw and falling into Astro's SSR error template.
5
+ //
6
+ // Does NOT throw. A throw here would recreate the silent-500 cascade that
7
+ // motivates this release. The contract: schema missing → mailer disabled
8
+ // for this Worker's lifetime, affected routes return 503 with a diagnostic
9
+ // body, observability gets one loud error per instance startup.
10
+ export const GL_MAILER_SCHEMA_MISSING = 'GL_MAILER_SCHEMA_MISSING';
11
+ let _cached = null;
12
+ /** @internal Reset the module-scoped cache — tests only. */
13
+ export function _resetSchemaProbeCache() {
14
+ _cached = null;
15
+ }
16
+ /**
17
+ * Probe the configured D1 binding for `gl_subscribers` AND `gl_email_sends`.
18
+ * Returns `{ ok: true }` on success, `{ ok: false }` on failure (either table
19
+ * missing). Caches the result on the first call per Worker instance.
20
+ *
21
+ * Does not throw. A missing binding is treated as `ok: false` and logged —
22
+ * mailer cannot function without D1, so unlike analytics this is loud rather
23
+ * than silent.
24
+ */
25
+ export async function probeMailerSchema(db, d1Binding) {
26
+ if (_cached)
27
+ return _cached;
28
+ if (!db) {
29
+ logSchemaMissing(d1Binding, 'D1 binding is not bound');
30
+ _cached = { ok: false };
31
+ return _cached;
32
+ }
33
+ try {
34
+ await db.prepare('SELECT 1 FROM gl_subscribers LIMIT 1').first();
35
+ await db.prepare('SELECT 1 FROM gl_email_sends LIMIT 1').first();
36
+ _cached = { ok: true };
37
+ return _cached;
38
+ }
39
+ catch (err) {
40
+ const message = err instanceof Error ? err.message : String(err);
41
+ logSchemaMissing(d1Binding, message);
42
+ _cached = { ok: false };
43
+ return _cached;
44
+ }
45
+ }
46
+ function logSchemaMissing(d1Binding, underlying) {
47
+ console.error(`[${GL_MAILER_SCHEMA_MISSING}] @growth-labs/mailer: D1 binding "${d1Binding}" is ` +
48
+ 'missing one or both of the gl_subscribers / gl_email_sends tables.\n' +
49
+ 'Remediation:\n' +
50
+ ' 1. Add to wrangler.toml under your [[d1_databases]] block:\n' +
51
+ ' migrations_dir = "node_modules/@growth-labs/mailer/migrations"\n' +
52
+ ` 2. Run: pnpm exec wrangler d1 migrations apply ${d1Binding} --remote\n` +
53
+ 'See packages-docs/mailer-d1-migrations.md for the full guide.\n' +
54
+ 'Mailer routes return 503 until the schema is present. ' +
55
+ `Underlying error: ${underlying}`);
56
+ }
57
+ /**
58
+ * Helper: build the standard 503 response mailer routes return on schema
59
+ * miss. The body shape is stable so observability dashboards can match on
60
+ * `code === 'GL_MAILER_SCHEMA_MISSING'`.
61
+ */
62
+ export function schemaMissingResponse() {
63
+ return Response.json({
64
+ error: 'Mailer schema is not initialized',
65
+ code: GL_MAILER_SCHEMA_MISSING,
66
+ }, { status: 503 });
67
+ }
68
+ //# sourceMappingURL=schema-probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-probe.js","sourceRoot":"","sources":["../../src/_internal/schema-probe.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,8EAA8E;AAC9E,8EAA8E;AAC9E,6DAA6D;AAC7D,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,2EAA2E;AAC3E,gEAAgE;AAEhE,MAAM,CAAC,MAAM,wBAAwB,GAAG,0BAA0B,CAAA;AAYlE,IAAI,OAAO,GAAuB,IAAI,CAAA;AAEtC,4DAA4D;AAC5D,MAAM,UAAU,sBAAsB;IACrC,OAAO,GAAG,IAAI,CAAA;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,EAA8B,EAC9B,SAAiB;IAEjB,IAAI,OAAO;QAAE,OAAO,OAAO,CAAA;IAC3B,IAAI,CAAC,EAAE,EAAE,CAAC;QACT,gBAAgB,CAAC,SAAS,EAAE,yBAAyB,CAAC,CAAA;QACtD,OAAO,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,CAAA;QACvB,OAAO,OAAO,CAAA;IACf,CAAC;IACD,IAAI,CAAC;QACJ,MAAM,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,KAAK,EAAE,CAAA;QAChE,MAAM,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,KAAK,EAAE,CAAA;QAChE,OAAO,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;QACtB,OAAO,OAAO,CAAA;IACf,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAChE,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QACpC,OAAO,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,CAAA;QACvB,OAAO,OAAO,CAAA;IACf,CAAC;AACF,CAAC;AAED,SAAS,gBAAgB,CAAC,SAAiB,EAAE,UAAkB;IAC9D,OAAO,CAAC,KAAK,CACZ,IAAI,wBAAwB,sCAAsC,SAAS,OAAO;QACjF,sEAAsE;QACtE,gBAAgB;QAChB,gEAAgE;QAChE,yEAAyE;QACzE,oDAAoD,SAAS,aAAa;QAC1E,iEAAiE;QACjE,wDAAwD;QACxD,qBAAqB,UAAU,EAAE,CAClC,CAAA;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB;IACpC,OAAO,QAAQ,CAAC,IAAI,CACnB;QACC,KAAK,EAAE,kCAAkC;QACzC,IAAI,EAAE,wBAAwB;KAC9B,EACD,EAAE,MAAM,EAAE,GAAG,EAAE,CACf,CAAA;AACF,CAAC"}
package/dist/options.d.ts CHANGED
@@ -15,6 +15,40 @@ export declare const mailerOptionsSchema: z.ZodObject<{
15
15
  unsubscribePath: z.ZodDefault<z.ZodString>;
16
16
  preferencesPath: z.ZodDefault<z.ZodString>;
17
17
  webhookPath: z.ZodDefault<z.ZodString>;
18
+ webhookSignature: z.ZodDefault<z.ZodDiscriminatedUnion<"enabled", [z.ZodObject<{
19
+ enabled: z.ZodLiteral<false>;
20
+ header: z.ZodDefault<z.ZodString>;
21
+ timestampHeader: z.ZodDefault<z.ZodString>;
22
+ toleranceSeconds: z.ZodDefault<z.ZodNumber>;
23
+ }, "strip", z.ZodTypeAny, {
24
+ enabled: false;
25
+ header: string;
26
+ timestampHeader: string;
27
+ toleranceSeconds: number;
28
+ }, {
29
+ enabled: false;
30
+ header?: string | undefined;
31
+ timestampHeader?: string | undefined;
32
+ toleranceSeconds?: number | undefined;
33
+ }>, z.ZodObject<{
34
+ enabled: z.ZodLiteral<true>;
35
+ secret: z.ZodString;
36
+ header: z.ZodDefault<z.ZodString>;
37
+ timestampHeader: z.ZodDefault<z.ZodString>;
38
+ toleranceSeconds: z.ZodDefault<z.ZodNumber>;
39
+ }, "strip", z.ZodTypeAny, {
40
+ enabled: true;
41
+ header: string;
42
+ timestampHeader: string;
43
+ toleranceSeconds: number;
44
+ secret: string;
45
+ }, {
46
+ enabled: true;
47
+ secret: string;
48
+ header?: string | undefined;
49
+ timestampHeader?: string | undefined;
50
+ toleranceSeconds?: number | undefined;
51
+ }>]>>;
18
52
  trackOpenPath: z.ZodDefault<z.ZodString>;
19
53
  trackClickPath: z.ZodDefault<z.ZodString>;
20
54
  siteUrl: z.ZodString;
@@ -51,6 +85,18 @@ export declare const mailerOptionsSchema: z.ZodObject<{
51
85
  unsubscribePath: string;
52
86
  preferencesPath: string;
53
87
  webhookPath: string;
88
+ webhookSignature: {
89
+ enabled: false;
90
+ header: string;
91
+ timestampHeader: string;
92
+ toleranceSeconds: number;
93
+ } | {
94
+ enabled: true;
95
+ header: string;
96
+ timestampHeader: string;
97
+ toleranceSeconds: number;
98
+ secret: string;
99
+ };
54
100
  trackOpenPath: string;
55
101
  trackClickPath: string;
56
102
  siteUrl: string;
@@ -82,6 +128,18 @@ export declare const mailerOptionsSchema: z.ZodObject<{
82
128
  unsubscribePath?: string | undefined;
83
129
  preferencesPath?: string | undefined;
84
130
  webhookPath?: string | undefined;
131
+ webhookSignature?: {
132
+ enabled: false;
133
+ header?: string | undefined;
134
+ timestampHeader?: string | undefined;
135
+ toleranceSeconds?: number | undefined;
136
+ } | {
137
+ enabled: true;
138
+ secret: string;
139
+ header?: string | undefined;
140
+ timestampHeader?: string | undefined;
141
+ toleranceSeconds?: number | undefined;
142
+ } | undefined;
85
143
  trackOpenPath?: string | undefined;
86
144
  trackClickPath?: string | undefined;
87
145
  brand?: {
@@ -1 +1 @@
1
- {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmD9B,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAA"}
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyE9B,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAA"}
package/dist/options.js CHANGED
@@ -22,6 +22,28 @@ export const mailerOptionsSchema = z.object({
22
22
  unsubscribePath: z.string().default('/api/newsletter/unsubscribe'),
23
23
  preferencesPath: z.string().default('/email/preferences'),
24
24
  webhookPath: z.string().default('/api/email/webhook'),
25
+ webhookSignature: z
26
+ .discriminatedUnion('enabled', [
27
+ z.object({
28
+ enabled: z.literal(false),
29
+ header: z.string().default('x-gl-mailer-signature'),
30
+ timestampHeader: z.string().default('x-gl-mailer-timestamp'),
31
+ toleranceSeconds: z.number().min(0).default(300),
32
+ }),
33
+ z.object({
34
+ enabled: z.literal(true),
35
+ secret: z.string().min(32),
36
+ header: z.string().default('x-gl-mailer-signature'),
37
+ timestampHeader: z.string().default('x-gl-mailer-timestamp'),
38
+ toleranceSeconds: z.number().min(0).default(300),
39
+ }),
40
+ ])
41
+ .default({
42
+ enabled: false,
43
+ header: 'x-gl-mailer-signature',
44
+ timestampHeader: 'x-gl-mailer-timestamp',
45
+ toleranceSeconds: 300,
46
+ }),
25
47
  // ─── Tracking ───
26
48
  trackOpenPath: z.string().default('/api/email/open'),
27
49
  trackClickPath: z.string().default('/api/email/click'),
@@ -1 +1 @@
1
- {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,mBAAmB;IACnB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;IAE/B,mBAAmB;IACnB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE;IAEtC,8BAA8B;IAC9B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;IACxC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC;IAE/C,qCAAqC;IACrC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE;IAC5B,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE;IAE9B,+BAA+B;IAC/B,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACtC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAEtC,wBAAwB;IACxB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IAEzB,iBAAiB;IACjB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,2BAA2B,CAAC;IAC9D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,yBAAyB,CAAC;IAC1D,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,6BAA6B,CAAC;IAClE,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAC;IACzD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAC;IAErD,mBAAmB;IACnB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,iBAAiB,CAAC;IACpD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC;IACtD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IAEzB,4CAA4C;IAC5C,KAAK,EAAE,CAAC;SACN,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;QACpC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;QAC3C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;QAC1C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KACjC,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;IAEb,yBAAyB;IACzB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;IAElD,mCAAmC;IACnC,gBAAgB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC5C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC;CACjD,CAAC,CAAA"}
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../src/queue/consumer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,KAAK,EAAiB,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAGnE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAGlE,wBAAsB,gBAAgB,CACrC,KAAK,EAAE,YAAY,CAAC,iBAAiB,CAAC,EACtC,GAAG,EAAE;IACJ,EAAE,EAAE,UAAU,CAAA;IACd,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACtB,EACD,OAAO,EAAE,qBAAqB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA2Ff"}
1
+ {"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../src/queue/consumer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,KAAK,EAAiB,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAGnE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAGlE,wBAAsB,gBAAgB,CACrC,KAAK,EAAE,YAAY,CAAC,iBAAiB,CAAC,EACtC,GAAG,EAAE;IACJ,EAAE,EAAE,UAAU,CAAA;IACd,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACtB,EACD,OAAO,EAAE,qBAAqB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAuGf"}
@@ -1,8 +1,20 @@
1
1
  import { drizzle } from 'drizzle-orm/d1';
2
+ import { probeMailerSchema } from '../_internal/schema-probe.js';
2
3
  import { emitMailerAnalyticsEvent } from '../utils/analytics.js';
3
4
  import { updateSendStatus } from '../utils/bounce.js';
4
5
  import { CloudflareEmailProvider, sleep } from '../utils/providers.js';
5
6
  export async function handleEmailQueue(batch, env, options) {
7
+ // Schema probe — runs once per Worker instance. On miss the probe logs
8
+ // GL_MAILER_SCHEMA_MISSING and we ack every message in the batch without
9
+ // retry. Re-queueing without the schema would cycle indefinitely and burn
10
+ // Cloudflare Queue retry budget.
11
+ const schemaProbe = await probeMailerSchema(env.DB, options.d1Binding);
12
+ if (!schemaProbe.ok) {
13
+ for (const message of batch.messages) {
14
+ message.ack();
15
+ }
16
+ return;
17
+ }
6
18
  const db = drizzle(env.DB);
7
19
  const provider = env.EMAIL_SENDER
8
20
  ? new CloudflareEmailProvider(env.EMAIL_SENDER)
@@ -1 +1 @@
1
- {"version":3,"file":"consumer.js","sourceRoot":"","sources":["../../src/queue/consumer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"consumer.js","sourceRoot":"","sources":["../../src/queue/consumer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAGhE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErD,OAAO,EAAE,uBAAuB,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAEtE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACrC,KAAsC,EACtC,GAIC,EACD,OAA8B;IAE9B,uEAAuE;IACvE,yEAAyE;IACzE,0EAA0E;IAC1E,iCAAiC;IACjC,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;IACtE,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC;QACrB,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACtC,OAAO,CAAC,GAAG,EAAE,CAAA;QACd,CAAC;QACD,OAAM;IACP,CAAC;IAED,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC1B,MAAM,QAAQ,GAAkB,GAAG,CAAC,YAAY;QAC/C,CAAC,CAAC,IAAI,uBAAuB,CAAC,GAAG,CAAC,YAAY,CAAC;QAC/C,CAAC,CAAC,IAAI,uBAAuB,CAAC;YAC5B,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;SACnE,CAAC,CAAA;IAEJ,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACtC,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAA;QAExF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACpC,IAAI,IAAI,GAAG,YAAY,CAAA;YACvB,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;gBAC9B,MAAM,cAAc,GAAG,GAAG,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,eAAe,UAAU,SAAS,CAAC,gBAAgB,EAAE,CAAA;gBACzG,MAAM,cAAc,GAAG,GAAG,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,eAAe,UAAU,SAAS,CAAC,gBAAgB,EAAE,CAAA;gBACzG,IAAI,GAAG,IAAI;qBACT,UAAU,CAAC,iBAAiB,EAAE,SAAS,CAAC,UAAU,CAAC;qBACnD,UAAU,CAAC,qBAAqB,EAAE,cAAc,CAAC;qBACjD,UAAU,CAAC,qBAAqB,EAAE,cAAc,CAAC,CAAA;YACpD,CAAC;YAED,MAAM,gBAAgB,GACrB,IAAI,KAAK,eAAe;gBACvB,CAAC,CAAC;oBACA,GAAG,OAAO;oBACV,kBAAkB,EAAE,IAAI,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,eAAe,UAAU,SAAS,CAAC,gBAAgB,GAAG;oBACxG,uBAAuB,EAAE,4BAA4B;iBACrD;gBACF,CAAC,CAAC,OAAO,CAAA;YAEX,IAAI,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC;gBAChC,EAAE,EAAE,SAAS,CAAC,KAAK;gBACnB,IAAI;gBACJ,OAAO;gBACP,OAAO;gBACP,IAAI;gBACJ,OAAO,EAAE,gBAAgB;aACzB,CAAC,CAAA;YAEF,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACzC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;oBAC/C,MAAM,KAAK,CAAC,CAAC,IAAI,OAAO,GAAG,IAAI,CAAC,CAAA;oBAChC,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC;wBAC5B,EAAE,EAAE,SAAS,CAAC,KAAK;wBACnB,IAAI;wBACJ,OAAO;wBACP,OAAO;wBACP,IAAI;wBACJ,OAAO,EAAE,gBAAgB;qBACzB,CAAC,CAAA;oBACF,IAAI,MAAM,CAAC,OAAO;wBAAE,MAAK;gBAC1B,CAAC;YACF,CAAC;YAED,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,gBAAgB,CAAC,EAAE,EAAE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE;oBACxD,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBAChC,CAAC,CAAA;gBACF,wBAAwB,CAAC,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE;oBACzD,WAAW,EAAE,SAAS,CAAC,UAAU;oBACjC,KAAK,EAAE;wBACN,UAAU,EAAE,SAAS,CAAC,UAAU;wBAChC,YAAY,EAAE,SAAS,CAAC,YAAY;wBACpC,KAAK,EAAE,SAAS,CAAC,KAAK;wBACtB,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,UAAU;wBACnC,IAAI;qBACJ;iBACD,CAAC,CAAA;YACH,CAAC;iBAAM,CAAC;gBACP,MAAM,gBAAgB,CAAC,EAAE,EAAE,SAAS,CAAC,UAAU,EAAE,SAAS,EAAE;oBAC3D,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,UAAU,EAAE,MAAM;iBAClB,CAAC,CAAA;gBACF,wBAAwB,CAAC,OAAO,EAAE,GAAG,EAAE,wBAAwB,EAAE;oBAChE,WAAW,EAAE,SAAS,CAAC,UAAU;oBACjC,KAAK,EAAE;wBACN,UAAU,EAAE,SAAS,CAAC,UAAU;wBAChC,YAAY,EAAE,SAAS,CAAC,YAAY;wBACpC,KAAK,EAAE,SAAS,CAAC,KAAK;wBACtB,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,UAAU;wBACnC,IAAI;wBACJ,KAAK,EAAE,MAAM,CAAC,KAAK;qBACnB;iBACD,CAAC,CAAA;gBACF,OAAO,CAAC,KAAK,CAAC,4BAA4B,SAAS,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;YAC9E,CAAC;QACF,CAAC;QAED,OAAO,CAAC,GAAG,EAAE,CAAA;IACd,CAAC;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"confirm.d.ts","sourceRoot":"","sources":["../../src/routes/confirm.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAQrC,eAAO,MAAM,GAAG,EAAE,QAoEjB,CAAA"}
1
+ {"version":3,"file":"confirm.d.ts","sourceRoot":"","sources":["../../src/routes/confirm.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AASrC,eAAO,MAAM,GAAG,EAAE,QAyEjB,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { env as cloudflareEnv } from 'cloudflare:workers';
2
2
  import { config } from 'virtual:growth-labs/mailer/config';
3
3
  import { drizzle } from 'drizzle-orm/d1';
4
+ import { probeMailerSchema, schemaMissingResponse } from '../_internal/schema-probe.js';
4
5
  import { emitMailerAnalyticsEvent } from '../utils/analytics.js';
5
6
  import { sendTransactional } from '../utils/send.js';
6
7
  import { confirmSubscriber, getSubscriberById } from '../utils/subscribers.js';
@@ -20,6 +21,10 @@ export const GET = async (context) => {
20
21
  const bindingsEnv = cloudflareEnv;
21
22
  const d1 = bindingsEnv[config.d1Binding];
22
23
  const queue = bindingsEnv[config.queueBinding];
24
+ // Schema probe — return 503 with GL_MAILER_SCHEMA_MISSING on miss.
25
+ const schema = await probeMailerSchema(d1, config.d1Binding);
26
+ if (!schema.ok)
27
+ return schemaMissingResponse();
23
28
  const db = drizzle(d1);
24
29
  const env = { DB: d1, QUEUE: queue };
25
30
  // Get subscriber to check status
@@ -1 +1 @@
1
- {"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../src/routes/confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAE1D,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAEhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEhD,MAAM,CAAC,MAAM,GAAG,GAAa,KAAK,EAAE,OAAO,EAAE,EAAE;IAC9C,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;IACvB,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAClE,CAAC;IAED,eAAe;IACf,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,aAAa,EAAE,KAAK,CAAC,CAAA;IAC9D,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9C,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAC7E,CAAC;IAED,mBAAmB;IACnB,MAAM,WAAW,GAAG,aAAwC,CAAA;IAC5D,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,SAAS,CAAe,CAAA;IACtD,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,YAAY,CAAU,CAAA;IACvD,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA;IACtB,MAAM,GAAG,GAAc,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IAE/C,iCAAiC;IACjC,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IACpE,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACzE,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACpC,yCAAyC;QACzC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;YACzB,MAAM,EAAE,GAAG;YACX,OAAO,EAAE;gBACR,QAAQ,EAAE,GAAG,MAAM,CAAC,OAAO,yBAAyB;aACpD;SACD,CAAC,CAAA;IACH,CAAC;IAED,qBAAqB;IACrB,IAAI,CAAC;QACJ,MAAM,iBAAiB,CAAC,EAAE,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IAClD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACxE,CAAC;IAED,qBAAqB;IACrB,MAAM,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE;QACpC,EAAE,EAAE,UAAU,CAAC,KAAK;QACpB,YAAY,EAAE,UAAU,CAAC,EAAE;QAC3B,OAAO,EAAE,cAAc,MAAM,CAAC,UAAU,EAAE;QAC1C,QAAQ,EAAE,SAAS;QACnB,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE;KAC/B,CAAC,CAAA;IAEF,wBAAwB,CAAC,MAAM,EAAE,WAAW,EAAE,sBAAsB,EAAE;QACrE,OAAO;QACP,WAAW,EAAE,UAAU,CAAC,EAAE;QAC1B,KAAK,EAAE;YACN,YAAY,EAAE,UAAU,CAAC,EAAE;YAC3B,KAAK,EAAE,UAAU,CAAC,KAAK;SACvB;KACD,CAAC,CAAA;IAEF,uCAAuC;IACvC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACzB,MAAM,EAAE,GAAG;QACX,OAAO,EAAE;YACR,QAAQ,EAAE,GAAG,MAAM,CAAC,OAAO,iBAAiB;SAC5C;KACD,CAAC,CAAA;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../src/routes/confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAE1D,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAEhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEhD,MAAM,CAAC,MAAM,GAAG,GAAa,KAAK,EAAE,OAAO,EAAE,EAAE;IAC9C,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;IACvB,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAClE,CAAC;IAED,eAAe;IACf,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,aAAa,EAAE,KAAK,CAAC,CAAA;IAC9D,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9C,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAC7E,CAAC;IAED,mBAAmB;IACnB,MAAM,WAAW,GAAG,aAAwC,CAAA;IAC5D,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,SAAS,CAAe,CAAA;IACtD,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,YAAY,CAAU,CAAA;IAEvD,mEAAmE;IACnE,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;IAC5D,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,qBAAqB,EAAE,CAAA;IAE9C,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA;IACtB,MAAM,GAAG,GAAc,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IAE/C,iCAAiC;IACjC,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IACpE,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACzE,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACpC,yCAAyC;QACzC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;YACzB,MAAM,EAAE,GAAG;YACX,OAAO,EAAE;gBACR,QAAQ,EAAE,GAAG,MAAM,CAAC,OAAO,yBAAyB;aACpD;SACD,CAAC,CAAA;IACH,CAAC;IAED,qBAAqB;IACrB,IAAI,CAAC;QACJ,MAAM,iBAAiB,CAAC,EAAE,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IAClD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACxE,CAAC;IAED,qBAAqB;IACrB,MAAM,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE;QACpC,EAAE,EAAE,UAAU,CAAC,KAAK;QACpB,YAAY,EAAE,UAAU,CAAC,EAAE;QAC3B,OAAO,EAAE,cAAc,MAAM,CAAC,UAAU,EAAE;QAC1C,QAAQ,EAAE,SAAS;QACnB,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE;KAC/B,CAAC,CAAA;IAEF,wBAAwB,CAAC,MAAM,EAAE,WAAW,EAAE,sBAAsB,EAAE;QACrE,OAAO;QACP,WAAW,EAAE,UAAU,CAAC,EAAE;QAC1B,KAAK,EAAE;YACN,YAAY,EAAE,UAAU,CAAC,EAAE;YAC3B,KAAK,EAAE,UAAU,CAAC,KAAK;SACvB;KACD,CAAC,CAAA;IAEF,uCAAuC;IACvC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACzB,MAAM,EAAE,GAAG;QACX,OAAO,EAAE;YACR,QAAQ,EAAE,GAAG,MAAM,CAAC,OAAO,iBAAiB;SAC5C;KACD,CAAC,CAAA;AACH,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["../../src/routes/subscribe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAQrC,eAAO,MAAM,IAAI,EAAE,QAgHlB,CAAA"}
1
+ {"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["../../src/routes/subscribe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AASrC,eAAO,MAAM,IAAI,EAAE,QAsHlB,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { env as cloudflareEnv } from 'cloudflare:workers';
2
2
  import { config } from 'virtual:growth-labs/mailer/config';
3
3
  import { drizzle } from 'drizzle-orm/d1';
4
+ import { probeMailerSchema, schemaMissingResponse } from '../_internal/schema-probe.js';
4
5
  import { emitMailerAnalyticsEvent } from '../utils/analytics.js';
5
6
  import { sendTransactional } from '../utils/send.js';
6
7
  import { createSubscriber } from '../utils/subscribers.js';
@@ -35,6 +36,11 @@ export const POST = async (context) => {
35
36
  const bindingsEnv = cloudflareEnv;
36
37
  const d1 = bindingsEnv[config.d1Binding];
37
38
  const queue = bindingsEnv[config.queueBinding];
39
+ // 5a. Schema probe — runs once per Worker instance. On miss, return 503
40
+ // with GL_MAILER_SCHEMA_MISSING so the consumer's site doesn't 500-cascade.
41
+ const schema = await probeMailerSchema(d1, config.d1Binding);
42
+ if (!schema.ok)
43
+ return schemaMissingResponse();
38
44
  const db = drizzle(d1);
39
45
  const env = { DB: d1, QUEUE: queue };
40
46
  // 6. Create subscriber
@@ -1 +1 @@
1
- {"version":3,"file":"subscribe.js","sourceRoot":"","sources":["../../src/routes/subscribe.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"subscribe.js","sourceRoot":"","sources":["../../src/routes/subscribe.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"track-click.d.ts","sourceRoot":"","sources":["../../src/routes/track-click.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAMrC,eAAO,MAAM,GAAG,EAAE,QAsDjB,CAAA"}
1
+ {"version":3,"file":"track-click.d.ts","sourceRoot":"","sources":["../../src/routes/track-click.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAOrC,eAAO,MAAM,GAAG,EAAE,QA4DjB,CAAA"}
@@ -2,6 +2,7 @@ import { env as cloudflareEnv } from 'cloudflare:workers';
2
2
  import { config } from 'virtual:growth-labs/mailer/config';
3
3
  import { and, eq, inArray } from 'drizzle-orm';
4
4
  import { drizzle } from 'drizzle-orm/d1';
5
+ import { probeMailerSchema } from '../_internal/schema-probe.js';
5
6
  import { emailSends } from '../schema.js';
6
7
  import { emitMailerAnalyticsEvent } from '../utils/analytics.js';
7
8
  export const GET = async (context) => {
@@ -21,19 +22,25 @@ export const GET = async (context) => {
21
22
  catch {
22
23
  return new Response('Invalid URL', { status: 400 });
23
24
  }
24
- // Update status to 'clicked' only when it hasn't already reached 'clicked'
25
+ // Update status to 'clicked' only when it hasn't already reached 'clicked'.
26
+ // On schema miss the probe logs GL_MAILER_SCHEMA_MISSING and we skip the
27
+ // D1 update — but always still 302 to the destination so we don't break
28
+ // the user's actual click intent.
25
29
  try {
26
30
  const bindingsEnv = cloudflareEnv;
27
31
  if (bindingsEnv) {
28
32
  const d1 = bindingsEnv[config.d1Binding];
29
- const db = drizzle(d1);
30
- await db
31
- .update(emailSends)
32
- .set({
33
- status: 'clicked',
34
- clickedAt: new Date().toISOString(),
35
- })
36
- .where(and(eq(emailSends.trackingId, trackingId), inArray(emailSends.status, ['sent', 'delivered', 'opened'])));
33
+ const schema = await probeMailerSchema(d1, config.d1Binding);
34
+ if (schema.ok) {
35
+ const db = drizzle(d1);
36
+ await db
37
+ .update(emailSends)
38
+ .set({
39
+ status: 'clicked',
40
+ clickedAt: new Date().toISOString(),
41
+ })
42
+ .where(and(eq(emailSends.trackingId, trackingId), inArray(emailSends.status, ['sent', 'delivered', 'opened'])));
43
+ }
37
44
  }
38
45
  }
39
46
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"track-click.js","sourceRoot":"","sources":["../../src/routes/track-click.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAE1D,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAEhE,MAAM,CAAC,MAAM,GAAG,GAAa,KAAK,EAAE,OAAO,EAAE,EAAE;IAC9C,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;IACxC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAA;IACpC,MAAM,WAAW,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAE/C,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,OAAO,IAAI,QAAQ,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACpD,CAAC;IAED,qEAAqE;IACrE,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAA;QACpC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrD,OAAO,IAAI,QAAQ,CAAC,kBAAkB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QACzD,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,QAAQ,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACpD,CAAC;IAED,2EAA2E;IAC3E,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,aAAwC,CAAA;QAC5D,IAAI,WAAW,EAAE,CAAC;YACjB,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,SAAS,CAAe,CAAA;YACtD,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA;YACtB,MAAM,EAAE;iBACN,MAAM,CAAC,UAAU,CAAC;iBAClB,GAAG,CAAC;gBACJ,MAAM,EAAE,SAAS;gBACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACnC,CAAC;iBACD,KAAK,CACL,GAAG,CACF,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,EACrC,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC,CAC3D,CACD,CAAA;QACH,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,uCAAuC;IACxC,CAAC;IAED,wBAAwB,CAAC,MAAM,EAAE,aAAwC,EAAE,oBAAoB,EAAE;QAChG,OAAO;QACP,OAAO;QACP,WAAW,EAAE,UAAU;QACvB,KAAK,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE;KAClC,CAAC,CAAA;IAEF,uCAAuC;IACvC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACzB,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE;KAClC,CAAC,CAAA;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"track-click.js","sourceRoot":"","sources":["../../src/routes/track-click.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAE1D,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAEhE,MAAM,CAAC,MAAM,GAAG,GAAa,KAAK,EAAE,OAAO,EAAE,EAAE;IAC9C,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;IACxC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAA;IACpC,MAAM,WAAW,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAE/C,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,OAAO,IAAI,QAAQ,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACpD,CAAC;IAED,qEAAqE;IACrE,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAA;QACpC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrD,OAAO,IAAI,QAAQ,CAAC,kBAAkB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QACzD,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,QAAQ,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACpD,CAAC;IAED,4EAA4E;IAC5E,yEAAyE;IACzE,wEAAwE;IACxE,kCAAkC;IAClC,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,aAAwC,CAAA;QAC5D,IAAI,WAAW,EAAE,CAAC;YACjB,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,SAAS,CAAe,CAAA;YACtD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;YAC5D,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA;gBACtB,MAAM,EAAE;qBACN,MAAM,CAAC,UAAU,CAAC;qBAClB,GAAG,CAAC;oBACJ,MAAM,EAAE,SAAS;oBACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBACnC,CAAC;qBACD,KAAK,CACL,GAAG,CACF,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,EACrC,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC,CAC3D,CACD,CAAA;YACH,CAAC;QACF,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,uCAAuC;IACxC,CAAC;IAED,wBAAwB,CAAC,MAAM,EAAE,aAAwC,EAAE,oBAAoB,EAAE;QAChG,OAAO;QACP,OAAO;QACP,WAAW,EAAE,UAAU;QACvB,KAAK,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE;KAClC,CAAC,CAAA;IAEF,uCAAuC;IACvC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACzB,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE;KAClC,CAAC,CAAA;AACH,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"track-open.d.ts","sourceRoot":"","sources":["../../src/routes/track-open.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAOrC,eAAO,MAAM,GAAG,EAAE,QA+CjB,CAAA"}
1
+ {"version":3,"file":"track-open.d.ts","sourceRoot":"","sources":["../../src/routes/track-open.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAQrC,eAAO,MAAM,GAAG,EAAE,QAoDjB,CAAA"}
@@ -2,6 +2,7 @@ import { env as cloudflareEnv } from 'cloudflare:workers';
2
2
  import { config } from 'virtual:growth-labs/mailer/config';
3
3
  import { and, eq, inArray } from 'drizzle-orm';
4
4
  import { drizzle } from 'drizzle-orm/d1';
5
+ import { probeMailerSchema } from '../_internal/schema-probe.js';
5
6
  import { emailSends } from '../schema.js';
6
7
  import { emitMailerAnalyticsEvent } from '../utils/analytics.js';
7
8
  import { TRANSPARENT_GIF } from '../utils/tracking.js';
@@ -12,19 +13,24 @@ export const GET = async (context) => {
12
13
  return new Response(null, { status: 400 });
13
14
  }
14
15
  // Update status to 'opened' only when currently 'sent' or 'delivered'
15
- // to avoid downgrading from 'clicked'.
16
+ // to avoid downgrading from 'clicked'. On schema miss, the probe logs
17
+ // GL_MAILER_SCHEMA_MISSING and we skip the D1 work — but always still
18
+ // return the transparent GIF so we don't break email rendering.
16
19
  try {
17
20
  const bindingsEnv = cloudflareEnv;
18
21
  if (bindingsEnv) {
19
22
  const d1 = bindingsEnv[config.d1Binding];
20
- const db = drizzle(d1);
21
- await db
22
- .update(emailSends)
23
- .set({
24
- status: 'opened',
25
- openedAt: new Date().toISOString(),
26
- })
27
- .where(and(eq(emailSends.trackingId, trackingId), inArray(emailSends.status, ['sent', 'delivered'])));
23
+ const schema = await probeMailerSchema(d1, config.d1Binding);
24
+ if (schema.ok) {
25
+ const db = drizzle(d1);
26
+ await db
27
+ .update(emailSends)
28
+ .set({
29
+ status: 'opened',
30
+ openedAt: new Date().toISOString(),
31
+ })
32
+ .where(and(eq(emailSends.trackingId, trackingId), inArray(emailSends.status, ['sent', 'delivered'])));
33
+ }
28
34
  }
29
35
  }
30
36
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"track-open.js","sourceRoot":"","sources":["../../src/routes/track-open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAE1D,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,CAAC,MAAM,GAAG,GAAa,KAAK,EAAE,OAAO,EAAE,EAAE;IAC9C,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IACnC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAA;IACpC,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAC3C,CAAC;IAED,sEAAsE;IACtE,uCAAuC;IACvC,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,aAAwC,CAAA;QAC5D,IAAI,WAAW,EAAE,CAAC;YACjB,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,SAAS,CAAe,CAAA;YACtD,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA;YACtB,MAAM,EAAE;iBACN,MAAM,CAAC,UAAU,CAAC;iBAClB,GAAG,CAAC;gBACJ,MAAM,EAAE,QAAQ;gBAChB,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aAClC,CAAC;iBACD,KAAK,CACL,GAAG,CACF,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,EACrC,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CACjD,CACD,CAAA;QACH,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,6CAA6C;IAC9C,CAAC;IAED,wBAAwB,CAAC,MAAM,EAAE,aAAwC,EAAE,mBAAmB,EAAE;QAC/F,OAAO;QACP,OAAO;QACP,WAAW,EAAE,UAAU;QACvB,KAAK,EAAE,EAAE,UAAU,EAAE;KACrB,CAAC,CAAA;IAEF,sBAAsB;IACtB,OAAO,IAAI,QAAQ,CAAC,eAAe,EAAE;QACpC,MAAM,EAAE,GAAG;QACX,OAAO,EAAE;YACR,cAAc,EAAE,WAAW;YAC3B,eAAe,EAAE,qCAAqC;YACtD,gBAAgB,EAAE,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;SAChD;KACD,CAAC,CAAA;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"track-open.js","sourceRoot":"","sources":["../../src/routes/track-open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAE1D,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,CAAC,MAAM,GAAG,GAAa,KAAK,EAAE,OAAO,EAAE,EAAE;IAC9C,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IACnC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAA;IACpC,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAC3C,CAAC;IAED,sEAAsE;IACtE,sEAAsE;IACtE,sEAAsE;IACtE,gEAAgE;IAChE,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,aAAwC,CAAA;QAC5D,IAAI,WAAW,EAAE,CAAC;YACjB,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,SAAS,CAAe,CAAA;YACtD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;YAC5D,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA;gBACtB,MAAM,EAAE;qBACN,MAAM,CAAC,UAAU,CAAC;qBAClB,GAAG,CAAC;oBACJ,MAAM,EAAE,QAAQ;oBAChB,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBAClC,CAAC;qBACD,KAAK,CACL,GAAG,CACF,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,EACrC,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CACjD,CACD,CAAA;YACH,CAAC;QACF,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,6CAA6C;IAC9C,CAAC;IAED,wBAAwB,CAAC,MAAM,EAAE,aAAwC,EAAE,mBAAmB,EAAE;QAC/F,OAAO;QACP,OAAO;QACP,WAAW,EAAE,UAAU;QACvB,KAAK,EAAE,EAAE,UAAU,EAAE;KACrB,CAAC,CAAA;IAEF,sBAAsB;IACtB,OAAO,IAAI,QAAQ,CAAC,eAAe,EAAE;QACpC,MAAM,EAAE,GAAG;QACX,OAAO,EAAE;YACR,cAAc,EAAE,WAAW;YAC3B,eAAe,EAAE,qCAAqC;YACtD,gBAAgB,EAAE,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;SAChD;KACD,CAAC,CAAA;AACH,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"unsubscribe.d.ts","sourceRoot":"","sources":["../../src/routes/unsubscribe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAiErC,eAAO,MAAM,GAAG,EAAE,QA0BjB,CAAA;AAGD,eAAO,MAAM,IAAI,EAAE,QAgBlB,CAAA"}
1
+ {"version":3,"file":"unsubscribe.d.ts","sourceRoot":"","sources":["../../src/routes/unsubscribe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAwErC,eAAO,MAAM,GAAG,EAAE,QA2BjB,CAAA;AAGD,eAAO,MAAM,IAAI,EAAE,QAiBlB,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { env as cloudflareEnv } from 'cloudflare:workers';
2
2
  import { config } from 'virtual:growth-labs/mailer/config';
3
3
  import { drizzle } from 'drizzle-orm/d1';
4
+ import { probeMailerSchema, schemaMissingResponse } from '../_internal/schema-probe.js';
4
5
  import { emitMailerAnalyticsEvent } from '../utils/analytics.js';
5
6
  import { sendTransactional } from '../utils/send.js';
6
7
  import { getSubscriberById, unsubscribeSubscriber } from '../utils/subscribers.js';
@@ -16,6 +17,11 @@ async function processUnsubscribe(token, context, request) {
16
17
  const bindingsEnv = cloudflareEnv;
17
18
  const d1 = bindingsEnv[config.d1Binding];
18
19
  const queue = bindingsEnv[config.queueBinding];
20
+ // Schema probe — short-circuit with a Response on miss; both GET and POST
21
+ // handlers below forward it unchanged.
22
+ const schema = await probeMailerSchema(d1, config.d1Binding);
23
+ if (!schema.ok)
24
+ return { schemaMissing: true };
19
25
  const db = drizzle(d1);
20
26
  const env = { DB: d1, QUEUE: queue };
21
27
  // Get subscriber
@@ -58,6 +64,8 @@ export const GET = async (context) => {
58
64
  return Response.json({ error: 'Missing token' }, { status: 400 });
59
65
  }
60
66
  const result = await processUnsubscribe(token, context, request);
67
+ if ('schemaMissing' in result)
68
+ return schemaMissingResponse();
61
69
  if ('error' in result) {
62
70
  return Response.json({ error: result.error }, { status: result.status });
63
71
  }
@@ -86,6 +94,8 @@ export const POST = async (context) => {
86
94
  return new Response('Missing token', { status: 400 });
87
95
  }
88
96
  const result = await processUnsubscribe(token, context, request);
97
+ if ('schemaMissing' in result)
98
+ return schemaMissingResponse();
89
99
  if ('error' in result) {
90
100
  return new Response(result.error, { status: result.status });
91
101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"unsubscribe.js","sourceRoot":"","sources":["../../src/routes/unsubscribe.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"unsubscribe.js","sourceRoot":"","sources":["../../src/routes/unsubscribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAE1D,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAEhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AAClF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAE/C,KAAK,UAAU,kBAAkB,CAChC,KAAa,EACb,OAAiC,EACjC,OAAiB;IAEjB,eAAe;IACf,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,aAAa,EAAE,KAAK,CAAC,CAAA;IAC9D,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QAClD,OAAO,EAAE,KAAK,EAAE,0BAA0B,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IAC1D,CAAC;IAED,mBAAmB;IACnB,MAAM,WAAW,GAAG,aAAwC,CAAA;IAC5D,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,SAAS,CAAe,CAAA;IACtD,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,YAAY,CAAU,CAAA;IAEvD,0EAA0E;IAC1E,uCAAuC;IACvC,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;IAC5D,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,aAAa,EAAE,IAAa,EAAE,CAAA;IAEvD,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA;IACtB,MAAM,GAAG,GAAc,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IAE/C,iBAAiB;IACjB,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IACpE,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO,EAAE,KAAK,EAAE,sBAAsB,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IACtD,CAAC;IAED,cAAc;IACd,MAAM,qBAAqB,CAAC,EAAE,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IAErD,0BAA0B;IAC1B,MAAM,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE;QACpC,EAAE,EAAE,UAAU,CAAC,KAAK;QACpB,YAAY,EAAE,UAAU,CAAC,EAAE;QAC3B,OAAO,EAAE,iCAAiC,MAAM,CAAC,UAAU,EAAE;QAC7D,QAAQ,EAAE,qBAAqB;QAC/B,IAAI,EAAE;YACL,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,cAAc,EAAE,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,aAAa,CAAC;SAClE;KACD,CAAC,CAAA;IAEF,wBAAwB,CAAC,MAAM,EAAE,WAAW,EAAE,yBAAyB,EAAE;QACxE,OAAO;QACP,OAAO;QACP,WAAW,EAAE,UAAU,CAAC,EAAE;QAC1B,KAAK,EAAE;YACN,YAAY,EAAE,UAAU,CAAC,EAAE;YAC3B,KAAK,EAAE,UAAU,CAAC,KAAK;SACvB;KACD,CAAC,CAAA;IAEF,OAAO;QACN,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,OAAO,CAAC,YAAY;KAClC,CAAA;AACF,CAAC;AAED,8BAA8B;AAC9B,MAAM,CAAC,MAAM,GAAG,GAAa,KAAK,EAAE,OAAO,EAAE,EAAE;IAC9C,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IAChC,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAClE,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IAChE,IAAI,eAAe,IAAI,MAAM;QAAE,OAAO,qBAAqB,EAAE,CAAA;IAC7D,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;QACvB,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;IACzE,CAAC;IAED,8CAA8C;IAC9C,MAAM,gBAAgB,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,aAAa,EAAE;QAClE,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,MAAM,EAAE,aAAa;KACrB,CAAC,CAAA;IACF,MAAM,cAAc,GAAG,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,eAAe,EAAE;QAC3E,KAAK,EAAE,gBAAgB;QACvB,YAAY,EAAE,IAAI;KAClB,CAAC,CAAA;IAEF,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACzB,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE;KACrC,CAAC,CAAA;AACH,CAAC,CAAA;AAED,uCAAuC;AACvC,MAAM,CAAC,MAAM,IAAI,GAAa,KAAK,EAAE,OAAO,EAAE,EAAE;IAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IAC3B,iDAAiD;IACjD,+CAA+C;IAC/C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAChC,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,IAAI,QAAQ,CAAC,eAAe,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACtD,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IAChE,IAAI,eAAe,IAAI,MAAM;QAAE,OAAO,qBAAqB,EAAE,CAAA;IAC7D,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;QACvB,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;IAC7D,CAAC;IAED,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;AAC3C,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/routes/webhook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAarC,eAAO,MAAM,IAAI,EAAE,QAyClB,CAAA"}
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/routes/webhook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAcrC,eAAO,MAAM,IAAI,EAAE,QAgElB,CAAA"}
@@ -3,9 +3,21 @@ import { config } from 'virtual:growth-labs/mailer/config';
3
3
  import { drizzle } from 'drizzle-orm/d1';
4
4
  import { emitMailerAnalyticsEvent } from '../utils/analytics.js';
5
5
  import { handleBounce, handleComplaint, handleDelivery } from '../utils/bounce.js';
6
+ import { verifyWebhookSignature } from '../utils/webhook-signature.js';
6
7
  export const POST = async (context) => {
7
8
  const { request } = context;
8
- const body = (await request.json());
9
+ const rawBody = await request.text();
10
+ const signatureValid = await verifyWebhookSignature(config.webhookSignature, request, rawBody);
11
+ if (!signatureValid) {
12
+ return Response.json({ error: 'Invalid webhook signature' }, { status: 401 });
13
+ }
14
+ let body;
15
+ try {
16
+ body = JSON.parse(rawBody);
17
+ }
18
+ catch {
19
+ return Response.json({ error: 'Invalid payload' }, { status: 400 });
20
+ }
9
21
  if (!body.type || !body.email) {
10
22
  return Response.json({ error: 'Invalid payload' }, { status: 400 });
11
23
  }
@@ -15,6 +27,17 @@ export const POST = async (context) => {
15
27
  return Response.json({ error: 'D1 binding not available' }, { status: 500 });
16
28
  }
17
29
  const db = drizzle(d1);
30
+ emitMailerAnalyticsEvent(config, bindingsEnv, 'newsletter_webhook_received', {
31
+ request,
32
+ context,
33
+ contentSlug: body.trackingId ?? body.email,
34
+ label: {
35
+ type: body.type,
36
+ email: body.email,
37
+ trackingId: body.trackingId,
38
+ bounceType: body.bounceType,
39
+ },
40
+ });
18
41
  switch (body.type) {
19
42
  case 'delivery':
20
43
  if (body.trackingId) {