@growth-labs/mailer 0.6.5 → 0.8.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 (48) hide show
  1. package/dist/_internal/tracking-update.d.ts +21 -0
  2. package/dist/_internal/tracking-update.d.ts.map +1 -0
  3. package/dist/_internal/tracking-update.js +44 -0
  4. package/dist/_internal/tracking-update.js.map +1 -0
  5. package/dist/options.d.ts +15 -221
  6. package/dist/options.d.ts.map +1 -1
  7. package/dist/options.js +6 -6
  8. package/dist/options.js.map +1 -1
  9. package/dist/queue/consumer.d.ts.map +1 -1
  10. package/dist/queue/consumer.js +8 -2
  11. package/dist/queue/consumer.js.map +1 -1
  12. package/dist/routes/track-click.d.ts.map +1 -1
  13. package/dist/routes/track-click.js +20 -12
  14. package/dist/routes/track-click.js.map +1 -1
  15. package/dist/routes/track-open.d.ts.map +1 -1
  16. package/dist/routes/track-open.js +20 -12
  17. package/dist/routes/track-open.js.map +1 -1
  18. package/dist/types.d.ts +2 -0
  19. package/dist/types.d.ts.map +1 -1
  20. package/dist/utils/broadcast.d.ts.map +1 -1
  21. package/dist/utils/broadcast.js +5 -3
  22. package/dist/utils/broadcast.js.map +1 -1
  23. package/dist/utils/index.d.ts +1 -0
  24. package/dist/utils/index.d.ts.map +1 -1
  25. package/dist/utils/index.js +1 -0
  26. package/dist/utils/index.js.map +1 -1
  27. package/dist/utils/providers.d.ts.map +1 -1
  28. package/dist/utils/providers.js +13 -0
  29. package/dist/utils/providers.js.map +1 -1
  30. package/dist/utils/send.d.ts.map +1 -1
  31. package/dist/utils/send.js +3 -0
  32. package/dist/utils/send.js.map +1 -1
  33. package/dist/utils/utm.d.ts +26 -0
  34. package/dist/utils/utm.d.ts.map +1 -0
  35. package/dist/utils/utm.js +63 -0
  36. package/dist/utils/utm.js.map +1 -0
  37. package/package.json +2 -2
  38. package/src/_internal/tracking-update.ts +67 -0
  39. package/src/options.ts +6 -6
  40. package/src/queue/consumer.ts +12 -2
  41. package/src/routes/track-click.ts +20 -12
  42. package/src/routes/track-open.ts +20 -12
  43. package/src/types.ts +2 -0
  44. package/src/utils/broadcast.ts +9 -3
  45. package/src/utils/index.ts +1 -0
  46. package/src/utils/providers.ts +11 -0
  47. package/src/utils/send.ts +3 -0
  48. package/src/utils/utm.ts +73 -0
@@ -0,0 +1,21 @@
1
+ export declare const GL_MAILER_TRACKING_ROW_MISSING = "GL_MAILER_TRACKING_ROW_MISSING";
2
+ export declare const GL_MAILER_TRACKING_UPDATE_FAILED = "GL_MAILER_TRACKING_UPDATE_FAILED";
3
+ interface D1PreparedStatementLike {
4
+ bind(...values: unknown[]): {
5
+ first<T = unknown>(): Promise<T | null>;
6
+ };
7
+ }
8
+ interface D1Like {
9
+ prepare(query: string): D1PreparedStatementLike;
10
+ }
11
+ /** Rows changed by a D1 write, or undefined when the result carries no count. */
12
+ export declare function changedRows(result: unknown): number | undefined;
13
+ /**
14
+ * Call after a tracking UPDATE. Does nothing when the update changed a row.
15
+ * On zero rows, checks the row exists: absent logs GL_MAILER_TRACKING_ROW_MISSING.
16
+ * Never throws; the pixel and the redirect must still respond.
17
+ */
18
+ export declare function reportIfNoRowUpdated(d1: D1Like, bindingName: string, trackingId: string, route: 'open' | 'click', updateResult: unknown): Promise<void>;
19
+ export declare function logTrackingUpdateFailed(route: 'open' | 'click', trackingId: string, bindingName: string, err: unknown): void;
20
+ export {};
21
+ //# sourceMappingURL=tracking-update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracking-update.d.ts","sourceRoot":"","sources":["../../src/_internal/tracking-update.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,8BAA8B,mCAAmC,CAAA;AAC9E,eAAO,MAAM,gCAAgC,qCAAqC,CAAA;AAElF,UAAU,uBAAuB;IAChC,IAAI,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG;QAAE,KAAK,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;KAAE,CAAA;CACvE;AAED,UAAU,MAAM;IACf,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,uBAAuB,CAAA;CAC/C;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAG/D;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACzC,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GAAG,OAAO,EACvB,YAAY,EAAE,OAAO,GACnB,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,MAAM,GAAG,OAAO,EACvB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,OAAO,GACV,IAAI,CAKN"}
@@ -0,0 +1,44 @@
1
+ // Loud failure for open/click tracking updates.
2
+ //
3
+ // The open pixel and click redirect update one gl_email_sends row by
4
+ // tracking id. When the route is bound to the wrong D1 (a producer that
5
+ // writes sends elsewhere), the UPDATE matches zero rows and every send looks
6
+ // unopened forever. That silent drift is what left 745 Fronts sends at
7
+ // opened=0 while WAE counted hundreds of opens, so a zero-row update now
8
+ // distinguishes "row already past this status" (fine) from "no such row in
9
+ // this D1" (an error with a stable code, visible in Workers observability).
10
+ export const GL_MAILER_TRACKING_ROW_MISSING = 'GL_MAILER_TRACKING_ROW_MISSING';
11
+ export const GL_MAILER_TRACKING_UPDATE_FAILED = 'GL_MAILER_TRACKING_UPDATE_FAILED';
12
+ /** Rows changed by a D1 write, or undefined when the result carries no count. */
13
+ export function changedRows(result) {
14
+ const changes = result?.meta?.changes;
15
+ return typeof changes === 'number' ? changes : undefined;
16
+ }
17
+ /**
18
+ * Call after a tracking UPDATE. Does nothing when the update changed a row.
19
+ * On zero rows, checks the row exists: absent logs GL_MAILER_TRACKING_ROW_MISSING.
20
+ * Never throws; the pixel and the redirect must still respond.
21
+ */
22
+ export async function reportIfNoRowUpdated(d1, bindingName, trackingId, route, updateResult) {
23
+ if (changedRows(updateResult) !== 0)
24
+ return;
25
+ try {
26
+ const row = await d1
27
+ .prepare('SELECT status FROM gl_email_sends WHERE tracking_id = ? LIMIT 1')
28
+ .bind(trackingId)
29
+ .first();
30
+ if (row)
31
+ return;
32
+ console.error(`[mailer] ${GL_MAILER_TRACKING_ROW_MISSING}: track-${route} for trackingId=${trackingId} ` +
33
+ `matched no gl_email_sends row in env.${bindingName}. The send was recorded in a ` +
34
+ 'different D1, or the id is unknown: set sendsD1Binding to the D1 that holds the sends.');
35
+ }
36
+ catch (err) {
37
+ logTrackingUpdateFailed(route, trackingId, bindingName, err);
38
+ }
39
+ }
40
+ export function logTrackingUpdateFailed(route, trackingId, bindingName, err) {
41
+ console.error(`[mailer] ${GL_MAILER_TRACKING_UPDATE_FAILED}: track-${route} for trackingId=${trackingId} ` +
42
+ `could not update env.${bindingName}: ${err instanceof Error ? err.message : String(err)}`);
43
+ }
44
+ //# sourceMappingURL=tracking-update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracking-update.js","sourceRoot":"","sources":["../../src/_internal/tracking-update.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,qEAAqE;AACrE,wEAAwE;AACxE,6EAA6E;AAC7E,uEAAuE;AACvE,yEAAyE;AACzE,2EAA2E;AAC3E,4EAA4E;AAE5E,MAAM,CAAC,MAAM,8BAA8B,GAAG,gCAAgC,CAAA;AAC9E,MAAM,CAAC,MAAM,gCAAgC,GAAG,kCAAkC,CAAA;AAUlF,iFAAiF;AACjF,MAAM,UAAU,WAAW,CAAC,MAAe;IAC1C,MAAM,OAAO,GAAI,MAA8D,EAAE,IAAI,EAAE,OAAO,CAAA;IAC9F,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;AACzD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,EAAU,EACV,WAAmB,EACnB,UAAkB,EAClB,KAAuB,EACvB,YAAqB;IAErB,IAAI,WAAW,CAAC,YAAY,CAAC,KAAK,CAAC;QAAE,OAAM;IAC3C,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,MAAM,EAAE;aAClB,OAAO,CAAC,iEAAiE,CAAC;aAC1E,IAAI,CAAC,UAAU,CAAC;aAChB,KAAK,EAAsB,CAAA;QAC7B,IAAI,GAAG;YAAE,OAAM;QACf,OAAO,CAAC,KAAK,CACZ,YAAY,8BAA8B,WAAW,KAAK,mBAAmB,UAAU,GAAG;YACzF,wCAAwC,WAAW,+BAA+B;YAClF,wFAAwF,CACzF,CAAA;IACF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,uBAAuB,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,CAAC,CAAA;IAC7D,CAAC;AACF,CAAC;AAED,MAAM,UAAU,uBAAuB,CACtC,KAAuB,EACvB,UAAkB,EAClB,WAAmB,EACnB,GAAY;IAEZ,OAAO,CAAC,KAAK,CACZ,YAAY,gCAAgC,WAAW,KAAK,mBAAmB,UAAU,GAAG;QAC3F,wBAAwB,WAAW,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC3F,CAAA;AACF,CAAC"}
package/dist/options.d.ts CHANGED
@@ -3,18 +3,18 @@ import type { SiteConfigLookup } from './types.js';
3
3
  export declare const mailerOptionsSchema: z.ZodObject<{
4
4
  siteId: z.ZodString;
5
5
  senderName: z.ZodString;
6
- fromAddress: z.ZodString;
7
- replyTo: z.ZodOptional<z.ZodString>;
6
+ fromAddress: z.ZodEmail;
7
+ replyTo: z.ZodOptional<z.ZodEmail>;
8
8
  d1Binding: z.ZodDefault<z.ZodString>;
9
9
  sendsD1Binding: z.ZodOptional<z.ZodString>;
10
10
  queueBinding: z.ZodDefault<z.ZodString>;
11
11
  senderBinding: z.ZodDefault<z.ZodString>;
12
- siteConfigLookup: z.ZodOptional<z.ZodType<SiteConfigLookup, z.ZodTypeDef, SiteConfigLookup>>;
12
+ siteConfigLookup: z.ZodOptional<z.ZodCustom<SiteConfigLookup, SiteConfigLookup>>;
13
13
  turnstileSiteKey: z.ZodString;
14
14
  turnstileSecretBinding: z.ZodDefault<z.ZodString>;
15
15
  turnstileSecretKey: z.ZodOptional<z.ZodString>;
16
16
  doubleOptIn: z.ZodDefault<z.ZodBoolean>;
17
- topics: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
17
+ topics: z.ZodOptional<z.ZodArray<z.ZodString>>;
18
18
  signingSecretBinding: z.ZodDefault<z.ZodString>;
19
19
  signingSecret: z.ZodOptional<z.ZodString>;
20
20
  subscribePath: z.ZodDefault<z.ZodString>;
@@ -22,244 +22,38 @@ export declare const mailerOptionsSchema: z.ZodObject<{
22
22
  unsubscribePath: z.ZodDefault<z.ZodString>;
23
23
  preferencesPath: z.ZodDefault<z.ZodString>;
24
24
  webhookPath: z.ZodDefault<z.ZodString>;
25
- webhookSignature: z.ZodDefault<z.ZodDiscriminatedUnion<"enabled", [z.ZodObject<{
25
+ webhookSignature: z.ZodPrefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
26
26
  enabled: z.ZodLiteral<false>;
27
27
  header: z.ZodDefault<z.ZodString>;
28
28
  timestampHeader: z.ZodDefault<z.ZodString>;
29
29
  toleranceSeconds: z.ZodDefault<z.ZodNumber>;
30
- }, "strip", z.ZodTypeAny, {
31
- enabled: false;
32
- header: string;
33
- timestampHeader: string;
34
- toleranceSeconds: number;
35
- }, {
36
- enabled: false;
37
- header?: string | undefined;
38
- timestampHeader?: string | undefined;
39
- toleranceSeconds?: number | undefined;
40
- }>, z.ZodObject<{
30
+ }, z.core.$strip>, z.ZodObject<{
41
31
  enabled: z.ZodLiteral<true>;
42
32
  secret: z.ZodString;
43
33
  header: z.ZodDefault<z.ZodString>;
44
34
  timestampHeader: z.ZodDefault<z.ZodString>;
45
35
  toleranceSeconds: z.ZodDefault<z.ZodNumber>;
46
- }, "strip", z.ZodTypeAny, {
47
- enabled: true;
48
- header: string;
49
- timestampHeader: string;
50
- toleranceSeconds: number;
51
- secret: string;
52
- }, {
53
- enabled: true;
54
- secret: string;
55
- header?: string | undefined;
56
- timestampHeader?: string | undefined;
57
- toleranceSeconds?: number | undefined;
58
- }>]>>;
36
+ }, z.core.$strip>], "enabled">>;
59
37
  trackOpenPath: z.ZodDefault<z.ZodString>;
60
38
  trackClickPath: z.ZodDefault<z.ZodString>;
61
- siteUrl: z.ZodString;
62
- brand: z.ZodDefault<z.ZodObject<{
63
- logoUrl: z.ZodOptional<z.ZodString>;
39
+ siteUrl: z.ZodURL;
40
+ brand: z.ZodPrefault<z.ZodObject<{
41
+ logoUrl: z.ZodOptional<z.ZodURL>;
64
42
  primaryColor: z.ZodDefault<z.ZodString>;
65
43
  accentColor: z.ZodDefault<z.ZodString>;
66
44
  footerText: z.ZodOptional<z.ZodString>;
67
- }, "strip", z.ZodTypeAny, {
68
- primaryColor: string;
69
- accentColor: string;
70
- logoUrl?: string | undefined;
71
- footerText?: string | undefined;
72
- }, {
73
- logoUrl?: string | undefined;
74
- primaryColor?: string | undefined;
75
- accentColor?: string | undefined;
76
- footerText?: string | undefined;
77
- }>>;
45
+ }, z.core.$strip>>;
78
46
  batchSize: z.ZodDefault<z.ZodNumber>;
79
47
  analyticsEnabled: z.ZodDefault<z.ZodBoolean>;
80
48
  analyticsBinding: z.ZodDefault<z.ZodString>;
81
- }, "strip", z.ZodTypeAny, {
82
- senderName: string;
83
- siteUrl: string;
84
- brand: {
85
- primaryColor: string;
86
- accentColor: string;
87
- logoUrl?: string | undefined;
88
- footerText?: string | undefined;
89
- };
90
- siteId: string;
91
- fromAddress: string;
92
- d1Binding: string;
93
- queueBinding: string;
94
- senderBinding: string;
95
- turnstileSiteKey: string;
96
- turnstileSecretBinding: string;
97
- doubleOptIn: boolean;
98
- signingSecretBinding: string;
99
- subscribePath: string;
100
- confirmPath: string;
101
- unsubscribePath: string;
102
- preferencesPath: string;
103
- webhookPath: string;
104
- webhookSignature: {
105
- enabled: false;
106
- header: string;
107
- timestampHeader: string;
108
- toleranceSeconds: number;
109
- } | {
110
- enabled: true;
111
- header: string;
112
- timestampHeader: string;
113
- toleranceSeconds: number;
114
- secret: string;
115
- };
116
- trackOpenPath: string;
117
- trackClickPath: string;
118
- batchSize: number;
119
- analyticsEnabled: boolean;
120
- analyticsBinding: string;
121
- replyTo?: string | undefined;
122
- sendsD1Binding?: string | undefined;
123
- siteConfigLookup?: SiteConfigLookup | undefined;
124
- turnstileSecretKey?: string | undefined;
125
- topics?: string[] | undefined;
126
- signingSecret?: string | undefined;
127
- }, {
128
- senderName: string;
129
- siteUrl: string;
130
- siteId: string;
131
- fromAddress: string;
132
- turnstileSiteKey: string;
133
- brand?: {
134
- logoUrl?: string | undefined;
135
- primaryColor?: string | undefined;
136
- accentColor?: string | undefined;
137
- footerText?: string | undefined;
138
- } | undefined;
139
- replyTo?: string | undefined;
140
- d1Binding?: string | undefined;
141
- sendsD1Binding?: string | undefined;
142
- queueBinding?: string | undefined;
143
- senderBinding?: string | undefined;
144
- siteConfigLookup?: SiteConfigLookup | undefined;
145
- turnstileSecretBinding?: string | undefined;
146
- turnstileSecretKey?: string | undefined;
147
- doubleOptIn?: boolean | undefined;
148
- topics?: string[] | undefined;
149
- signingSecretBinding?: string | undefined;
150
- signingSecret?: string | undefined;
151
- subscribePath?: string | undefined;
152
- confirmPath?: string | undefined;
153
- unsubscribePath?: string | undefined;
154
- preferencesPath?: string | undefined;
155
- webhookPath?: string | undefined;
156
- webhookSignature?: {
157
- enabled: false;
158
- header?: string | undefined;
159
- timestampHeader?: string | undefined;
160
- toleranceSeconds?: number | undefined;
161
- } | {
162
- enabled: true;
163
- secret: string;
164
- header?: string | undefined;
165
- timestampHeader?: string | undefined;
166
- toleranceSeconds?: number | undefined;
167
- } | undefined;
168
- trackOpenPath?: string | undefined;
169
- trackClickPath?: string | undefined;
170
- batchSize?: number | undefined;
171
- analyticsEnabled?: boolean | undefined;
172
- analyticsBinding?: string | undefined;
173
- }>;
49
+ }, z.core.$strip>;
174
50
  export type MailerOptions = z.input<typeof mailerOptionsSchema>;
175
51
  export type ResolvedMailerOptions = z.output<typeof mailerOptionsSchema>;
176
- export declare const mailerComponentsOptionsSchema: z.ZodObject<Pick<{
177
- siteId: z.ZodString;
178
- senderName: z.ZodString;
179
- fromAddress: z.ZodString;
180
- replyTo: z.ZodOptional<z.ZodString>;
181
- d1Binding: z.ZodDefault<z.ZodString>;
182
- sendsD1Binding: z.ZodOptional<z.ZodString>;
183
- queueBinding: z.ZodDefault<z.ZodString>;
184
- senderBinding: z.ZodDefault<z.ZodString>;
185
- siteConfigLookup: z.ZodOptional<z.ZodType<SiteConfigLookup, z.ZodTypeDef, SiteConfigLookup>>;
52
+ export declare const mailerComponentsOptionsSchema: z.ZodObject<{
186
53
  turnstileSiteKey: z.ZodString;
187
- turnstileSecretBinding: z.ZodDefault<z.ZodString>;
188
- turnstileSecretKey: z.ZodOptional<z.ZodString>;
189
- doubleOptIn: z.ZodDefault<z.ZodBoolean>;
190
- topics: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
191
- signingSecretBinding: z.ZodDefault<z.ZodString>;
192
- signingSecret: z.ZodOptional<z.ZodString>;
54
+ topics: z.ZodOptional<z.ZodArray<z.ZodString>>;
193
55
  subscribePath: z.ZodDefault<z.ZodString>;
194
- confirmPath: z.ZodDefault<z.ZodString>;
195
- unsubscribePath: z.ZodDefault<z.ZodString>;
196
- preferencesPath: z.ZodDefault<z.ZodString>;
197
- webhookPath: z.ZodDefault<z.ZodString>;
198
- webhookSignature: z.ZodDefault<z.ZodDiscriminatedUnion<"enabled", [z.ZodObject<{
199
- enabled: z.ZodLiteral<false>;
200
- header: z.ZodDefault<z.ZodString>;
201
- timestampHeader: z.ZodDefault<z.ZodString>;
202
- toleranceSeconds: z.ZodDefault<z.ZodNumber>;
203
- }, "strip", z.ZodTypeAny, {
204
- enabled: false;
205
- header: string;
206
- timestampHeader: string;
207
- toleranceSeconds: number;
208
- }, {
209
- enabled: false;
210
- header?: string | undefined;
211
- timestampHeader?: string | undefined;
212
- toleranceSeconds?: number | undefined;
213
- }>, z.ZodObject<{
214
- enabled: z.ZodLiteral<true>;
215
- secret: z.ZodString;
216
- header: z.ZodDefault<z.ZodString>;
217
- timestampHeader: z.ZodDefault<z.ZodString>;
218
- toleranceSeconds: z.ZodDefault<z.ZodNumber>;
219
- }, "strip", z.ZodTypeAny, {
220
- enabled: true;
221
- header: string;
222
- timestampHeader: string;
223
- toleranceSeconds: number;
224
- secret: string;
225
- }, {
226
- enabled: true;
227
- secret: string;
228
- header?: string | undefined;
229
- timestampHeader?: string | undefined;
230
- toleranceSeconds?: number | undefined;
231
- }>]>>;
232
- trackOpenPath: z.ZodDefault<z.ZodString>;
233
- trackClickPath: z.ZodDefault<z.ZodString>;
234
- siteUrl: z.ZodString;
235
- brand: z.ZodDefault<z.ZodObject<{
236
- logoUrl: z.ZodOptional<z.ZodString>;
237
- primaryColor: z.ZodDefault<z.ZodString>;
238
- accentColor: z.ZodDefault<z.ZodString>;
239
- footerText: z.ZodOptional<z.ZodString>;
240
- }, "strip", z.ZodTypeAny, {
241
- primaryColor: string;
242
- accentColor: string;
243
- logoUrl?: string | undefined;
244
- footerText?: string | undefined;
245
- }, {
246
- logoUrl?: string | undefined;
247
- primaryColor?: string | undefined;
248
- accentColor?: string | undefined;
249
- footerText?: string | undefined;
250
- }>>;
251
- batchSize: z.ZodDefault<z.ZodNumber>;
252
- analyticsEnabled: z.ZodDefault<z.ZodBoolean>;
253
- analyticsBinding: z.ZodDefault<z.ZodString>;
254
- }, "turnstileSiteKey" | "topics" | "subscribePath">, "strict", z.ZodTypeAny, {
255
- turnstileSiteKey: string;
256
- subscribePath: string;
257
- topics?: string[] | undefined;
258
- }, {
259
- turnstileSiteKey: string;
260
- topics?: string[] | undefined;
261
- subscribePath?: string | undefined;
262
- }>;
56
+ }, z.core.$strict>;
263
57
  export type MailerComponentsOptions = z.input<typeof mailerComponentsOptionsSchema>;
264
58
  export type ResolvedMailerComponentsOptions = z.output<typeof mailerComponentsOptionsSchema>;
265
59
  //# sourceMappingURL=options.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqF9B,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;AAIxE,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEhC,CAAA;AAEV,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAA;AACnF,MAAM,MAAM,+BAA+B,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,6BAA6B,CAAC,CAAA"}
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqF9B,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;AAIxE,eAAO,MAAM,6BAA6B;;;;kBAEhC,CAAA;AAEV,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAA;AACnF,MAAM,MAAM,+BAA+B,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,6BAA6B,CAAC,CAAA"}
package/dist/options.js CHANGED
@@ -3,9 +3,9 @@ export const mailerOptionsSchema = z.object({
3
3
  // ─── Required ───
4
4
  siteId: z.string().min(1),
5
5
  senderName: z.string(),
6
- fromAddress: z.string().email(),
6
+ fromAddress: z.email(),
7
7
  // ─── Reply-to ───
8
- replyTo: z.string().email().optional(),
8
+ replyTo: z.email().optional(),
9
9
  // ─── Cloudflare bindings ───
10
10
  d1Binding: z.string().default('SITE_DB'),
11
11
  // D1 binding holding gl_email_sends (the send-record table). Defaults to
@@ -49,7 +49,7 @@ export const mailerOptionsSchema = z.object({
49
49
  toleranceSeconds: z.number().min(0).default(300),
50
50
  }),
51
51
  ])
52
- .default({
52
+ .prefault({
53
53
  enabled: false,
54
54
  header: 'x-gl-mailer-signature',
55
55
  timestampHeader: 'x-gl-mailer-timestamp',
@@ -58,16 +58,16 @@ export const mailerOptionsSchema = z.object({
58
58
  // ─── Tracking ───
59
59
  trackOpenPath: z.string().default('/api/email/open'),
60
60
  trackClickPath: z.string().default('/api/email/click'),
61
- siteUrl: z.string().url(),
61
+ siteUrl: z.url(),
62
62
  // ─── Branding (for built-in templates) ───
63
63
  brand: z
64
64
  .object({
65
- logoUrl: z.string().url().optional(),
65
+ logoUrl: z.url().optional(),
66
66
  primaryColor: z.string().default('#1a365d'),
67
67
  accentColor: z.string().default('#e53e3e'),
68
68
  footerText: z.string().optional(),
69
69
  })
70
- .default({}),
70
+ .prefault({}),
71
71
  // ─── Queue batching ───
72
72
  batchSize: z.number().min(1).max(500).default(100),
73
73
  // ─── Optional peer: analytics ───
@@ -1 +1 @@
1
- {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
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":"AAEA,OAAO,KAAK,EACX,iBAAiB,EAEjB,gBAAgB,EAEhB,sBAAsB,EACtB,MAAM,aAAa,CAAA;AASpB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAI7F,MAAM,WAAW,qBAAqB;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gBAAgB,EAAE,gBAAgB,CAAA;IAClC;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAA;CAC/C;AAkED,wBAAsB,gBAAgB,CACrC,KAAK,EAAE,YAAY,CAAC,iBAAiB,CAAC,EACtC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,OAAO,EAAE,qBAAqB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAgNf"}
1
+ {"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../src/queue/consumer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,iBAAiB,EAEjB,gBAAgB,EAEhB,sBAAsB,EACtB,MAAM,aAAa,CAAA;AASpB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAI7F,MAAM,WAAW,qBAAqB;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gBAAgB,EAAE,gBAAgB,CAAA;IAClC;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAA;CAC/C;AAkED,wBAAsB,gBAAgB,CACrC,KAAK,EAAE,YAAY,CAAC,iBAAiB,CAAC,EACtC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,OAAO,EAAE,qBAAqB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA0Nf"}
@@ -185,9 +185,13 @@ export async function handleEmailQueue(batch, env, options) {
185
185
  });
186
186
  }
187
187
  else {
188
+ // Record what the provider said: `hard` for a permanent refusal, `soft` for
189
+ // a transient failure that exhausted its retries, suffixed with the
190
+ // provider's own error code when it gave one (`hard:E_RECIPIENT_REJECTED`).
191
+ const bounceType = `${result.retryable ? 'soft' : 'hard'}${result.errorCode ? `:${result.errorCode}` : ''}`;
188
192
  await updateSendStatus(db, recipient.trackingId, 'bounced', {
189
193
  bouncedAt: new Date().toISOString(),
190
- bounceType: 'hard',
194
+ bounceType,
191
195
  });
192
196
  emitMailerAnalyticsEvent(analyticsOptions(options, effective, message.body.siteId), env, 'newsletter_send_failed', {
193
197
  contentSlug: recipient.trackingId,
@@ -199,9 +203,11 @@ export async function handleEmailQueue(batch, env, options) {
199
203
  type,
200
204
  siteId: message.body.siteId,
201
205
  error: result.error,
206
+ errorCode: result.errorCode,
207
+ bounceType,
202
208
  },
203
209
  });
204
- console.error(`[mailer] Send failed for ${recipient.email}: ${result.error}`);
210
+ console.error(`[mailer] Send failed for ${recipient.email} (${bounceType}): ${result.error}`);
205
211
  }
206
212
  }
207
213
  message.ack();
@@ -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":"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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;AAOrC,eAAO,MAAM,GAAG,EAAE,QA6DjB,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;AAQrC,eAAO,MAAM,GAAG,EAAE,QAoEjB,CAAA"}
@@ -1,8 +1,9 @@
1
1
  import { env as cloudflareEnv } from 'cloudflare:workers';
2
2
  import { config } from 'virtual:growth-labs/mailer/config';
3
- import { and, eq, inArray } from 'drizzle-orm';
3
+ import { and, eq, inArray, sql } from 'drizzle-orm';
4
4
  import { drizzle } from 'drizzle-orm/d1';
5
- import { probeMailerSchema } from '../_internal/schema-probe.js';
5
+ import { probeMailerSendsSchema } from '../_internal/schema-probe.js';
6
+ import { logTrackingUpdateFailed, reportIfNoRowUpdated } from '../_internal/tracking-update.js';
6
7
  import { emailSends } from '../schema/sends.js';
7
8
  import { emitMailerAnalyticsEvent } from '../utils/analytics.js';
8
9
  export const GET = async (context) => {
@@ -23,29 +24,36 @@ export const GET = async (context) => {
23
24
  return new Response('Invalid URL', { status: 400 });
24
25
  }
25
26
  // 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.
27
+ // A click proves the message was delivered and read, so delivered_at and
28
+ // opened_at are filled when nothing else set them (image-blocking clients
29
+ // never fire the pixel). Only gl_email_sends is touched, so only that table
30
+ // is probed. On schema miss the probe logs GL_MAILER_SCHEMA_MISSING and we
31
+ // skip the D1 update; a zero-row update or a D1 error is logged loudly. We
32
+ // always still 302 to the destination so the user's click intent is kept.
33
+ const bindingsEnv = cloudflareEnv;
34
+ const sendsBindingName = config.sendsD1Binding ?? config.d1Binding;
29
35
  try {
30
- const bindingsEnv = cloudflareEnv;
31
36
  if (bindingsEnv) {
32
- const sendsBindingName = config.sendsD1Binding ?? config.d1Binding;
33
37
  const d1 = bindingsEnv[sendsBindingName];
34
- const schema = await probeMailerSchema(d1, sendsBindingName);
38
+ const schema = await probeMailerSendsSchema(d1, sendsBindingName);
35
39
  if (schema.ok) {
36
40
  const db = drizzle(d1);
37
- await db
41
+ const now = new Date().toISOString();
42
+ const result = await db
38
43
  .update(emailSends)
39
44
  .set({
40
45
  status: 'clicked',
41
- clickedAt: new Date().toISOString(),
46
+ clickedAt: now,
47
+ openedAt: sql `coalesce(${emailSends.openedAt}, ${now})`,
48
+ deliveredAt: sql `coalesce(${emailSends.deliveredAt}, ${now})`,
42
49
  })
43
50
  .where(and(eq(emailSends.trackingId, trackingId), inArray(emailSends.status, ['sent', 'delivered', 'opened'])));
51
+ await reportIfNoRowUpdated(d1, sendsBindingName, trackingId, 'click', result);
44
52
  }
45
53
  }
46
54
  }
47
- catch {
48
- // Never fail the redirect on DB errors
55
+ catch (err) {
56
+ logTrackingUpdateFailed('click', trackingId, sendsBindingName, err);
49
57
  }
50
58
  emitMailerAnalyticsEvent(config, cloudflareEnv, 'newsletter_clicked', {
51
59
  request,
@@ -1 +1 @@
1
- {"version":3,"file":"track-click.js","sourceRoot":"","sources":["../../src/routes/track-click.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"track-click.js","sourceRoot":"","sources":["../../src/routes/track-click.ts"],"names":[],"mappings":"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"}
@@ -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;AAQrC,eAAO,MAAM,GAAG,EAAE,QAqDjB,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;AASrC,eAAO,MAAM,GAAG,EAAE,QA4DjB,CAAA"}
@@ -1,8 +1,9 @@
1
1
  import { env as cloudflareEnv } from 'cloudflare:workers';
2
2
  import { config } from 'virtual:growth-labs/mailer/config';
3
- import { and, eq, inArray } from 'drizzle-orm';
3
+ import { and, eq, inArray, sql } from 'drizzle-orm';
4
4
  import { drizzle } from 'drizzle-orm/d1';
5
- import { probeMailerSchema } from '../_internal/schema-probe.js';
5
+ import { probeMailerSendsSchema } from '../_internal/schema-probe.js';
6
+ import { logTrackingUpdateFailed, reportIfNoRowUpdated } from '../_internal/tracking-update.js';
6
7
  import { emailSends } from '../schema/sends.js';
7
8
  import { emitMailerAnalyticsEvent } from '../utils/analytics.js';
8
9
  import { TRANSPARENT_GIF } from '../utils/tracking.js';
@@ -13,29 +14,36 @@ export const GET = async (context) => {
13
14
  return new Response(null, { status: 400 });
14
15
  }
15
16
  // Update status to 'opened' only when currently 'sent' or 'delivered'
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.
17
+ // to avoid downgrading from 'clicked'. An open proves delivery, so
18
+ // delivered_at is filled when no delivery webhook set it. Only
19
+ // gl_email_sends is touched, so only that table is probed: the sends D1 of
20
+ // a realm holds no gl_subscribers. On schema miss, the probe logs
21
+ // GL_MAILER_SCHEMA_MISSING and we skip the D1 work; a zero-row update or a
22
+ // D1 error is logged loudly. The transparent GIF is returned regardless so
23
+ // email rendering never breaks.
24
+ const bindingsEnv = cloudflareEnv;
25
+ const sendsBindingName = config.sendsD1Binding ?? config.d1Binding;
19
26
  try {
20
- const bindingsEnv = cloudflareEnv;
21
27
  if (bindingsEnv) {
22
- const sendsBindingName = config.sendsD1Binding ?? config.d1Binding;
23
28
  const d1 = bindingsEnv[sendsBindingName];
24
- const schema = await probeMailerSchema(d1, sendsBindingName);
29
+ const schema = await probeMailerSendsSchema(d1, sendsBindingName);
25
30
  if (schema.ok) {
26
31
  const db = drizzle(d1);
27
- await db
32
+ const now = new Date().toISOString();
33
+ const result = await db
28
34
  .update(emailSends)
29
35
  .set({
30
36
  status: 'opened',
31
- openedAt: new Date().toISOString(),
37
+ openedAt: now,
38
+ deliveredAt: sql `coalesce(${emailSends.deliveredAt}, ${now})`,
32
39
  })
33
40
  .where(and(eq(emailSends.trackingId, trackingId), inArray(emailSends.status, ['sent', 'delivered'])));
41
+ await reportIfNoRowUpdated(d1, sendsBindingName, trackingId, 'open', result);
34
42
  }
35
43
  }
36
44
  }
37
- catch {
38
- // Never fail the pixel response on DB errors
45
+ catch (err) {
46
+ logTrackingUpdateFailed('open', trackingId, sendsBindingName, err);
39
47
  }
40
48
  emitMailerAnalyticsEvent(config, cloudflareEnv, 'newsletter_opened', {
41
49
  request,