@growth-labs/mailer 0.4.7 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +3 -0
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +5 -0
- package/dist/options.js.map +1 -1
- package/dist/routes/track-click.d.ts.map +1 -1
- package/dist/routes/track-click.js +3 -2
- package/dist/routes/track-click.js.map +1 -1
- package/dist/routes/track-open.d.ts.map +1 -1
- package/dist/routes/track-open.js +3 -2
- package/dist/routes/track-open.js.map +1 -1
- package/dist/routes/unsubscribe.d.ts.map +1 -1
- package/dist/routes/unsubscribe.js +3 -4
- package/dist/routes/unsubscribe.js.map +1 -1
- package/dist/routes/webhook.d.ts.map +1 -1
- package/dist/routes/webhook.js +8 -1
- package/dist/routes/webhook.js.map +1 -1
- package/dist/schema/sends.d.ts +19 -0
- package/dist/schema/sends.d.ts.map +1 -1
- package/dist/schema/sends.js +2 -0
- package/dist/schema/sends.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/broadcast.d.ts +82 -0
- package/dist/utils/broadcast.d.ts.map +1 -0
- package/dist/utils/broadcast.js +261 -0
- package/dist/utils/broadcast.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/send.d.ts.map +1 -1
- package/dist/utils/send.js +18 -10
- package/dist/utils/send.js.map +1 -1
- package/migrations/0002_add_site_id_to_email_sends.sql +4 -0
- package/package.json +2 -2
- package/src/components/SubscribeForm.astro +78 -65
- package/src/index.ts +6 -0
- package/src/options.ts +5 -0
- package/src/routes/track-click.ts +3 -2
- package/src/routes/track-open.ts +3 -2
- package/src/routes/unsubscribe.ts +3 -5
- package/src/routes/webhook.ts +9 -1
- package/src/schema/sends.ts +2 -0
- package/src/types.ts +2 -2
- package/src/utils/broadcast.ts +404 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/send.ts +23 -9
- package/src/virtual.d.ts +1 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { and, eq, inArray } from 'drizzle-orm';
|
|
2
|
+
import { drizzle } from 'drizzle-orm/d1';
|
|
3
|
+
import { ulid } from 'ulidx';
|
|
4
|
+
import { emailSends } from '../schema/sends.js';
|
|
5
|
+
import { subscribers } from '../schema/subscribers.js';
|
|
6
|
+
import { sleep } from './providers.js';
|
|
7
|
+
import { generateToken } from './tokens.js';
|
|
8
|
+
import { injectTrackingPixel, rewriteLinksForTracking } from './tracking.js';
|
|
9
|
+
/**
|
|
10
|
+
* Statuses that mean "do not send" — the suppression list. A broadcast skips
|
|
11
|
+
* every recipient whose `gl_subscribers` row carries one of these. `active` is
|
|
12
|
+
* the ONLY sendable status; everything else (including `pending`, i.e. an
|
|
13
|
+
* unconfirmed double-opt-in subscriber) is suppressed so a one-off blast can
|
|
14
|
+
* never re-permission or re-mail a suppressed address.
|
|
15
|
+
*/
|
|
16
|
+
const SUPPRESSED_STATUSES = new Set([
|
|
17
|
+
'unsubscribed',
|
|
18
|
+
'bounced',
|
|
19
|
+
'complained',
|
|
20
|
+
'pending',
|
|
21
|
+
]);
|
|
22
|
+
function resolveProducerBindings(env, options) {
|
|
23
|
+
const d1 = env[options.d1Binding];
|
|
24
|
+
if (!d1) {
|
|
25
|
+
throw new Error(`[mailer] env.${options.d1Binding} is undefined; ` +
|
|
26
|
+
'pass d1Binding option to the mailer integration or bind that name in wrangler.toml.');
|
|
27
|
+
}
|
|
28
|
+
const sendsBindingName = options.sendsD1Binding ?? options.d1Binding;
|
|
29
|
+
const sendsD1 = env[sendsBindingName];
|
|
30
|
+
if (!sendsD1) {
|
|
31
|
+
throw new Error(`[mailer] env.${sendsBindingName} is undefined; ` +
|
|
32
|
+
'pass sendsD1Binding option to the mailer integration or bind that name in wrangler.toml.');
|
|
33
|
+
}
|
|
34
|
+
const queue = env[options.queueBinding];
|
|
35
|
+
if (!queue) {
|
|
36
|
+
throw new Error(`[mailer] env.${options.queueBinding} is undefined; ` +
|
|
37
|
+
'pass queueBinding option to the mailer integration or bind that name in wrangler.toml.');
|
|
38
|
+
}
|
|
39
|
+
return { db: drizzle(d1), sendsDb: drizzle(sendsD1), queue };
|
|
40
|
+
}
|
|
41
|
+
function normalizeRecipient(r) {
|
|
42
|
+
return typeof r === 'string' ? { email: r } : r;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolve each requested recipient against `gl_subscribers`:
|
|
46
|
+
* - dedupe by email (a list may repeat an address)
|
|
47
|
+
* - attach the canonical subscriberId + status
|
|
48
|
+
* - drop emails with no subscriber row (cannot honour unsubscribe → suppress)
|
|
49
|
+
* Returns the resolved sendable-candidate set plus the suppressed count.
|
|
50
|
+
*/
|
|
51
|
+
async function resolveAgainstSubscribers(db, requested) {
|
|
52
|
+
// Dedupe requested emails (case-insensitive, trimmed) preserving first-seen order.
|
|
53
|
+
const byEmail = new Map();
|
|
54
|
+
for (const r of requested) {
|
|
55
|
+
const key = r.email.trim().toLowerCase();
|
|
56
|
+
if (key.length === 0)
|
|
57
|
+
continue;
|
|
58
|
+
if (!byEmail.has(key))
|
|
59
|
+
byEmail.set(key, { ...r, email: r.email.trim() });
|
|
60
|
+
}
|
|
61
|
+
const emails = [...byEmail.values()].map((r) => r.email);
|
|
62
|
+
if (emails.length === 0)
|
|
63
|
+
return { candidates: [], suppressedCount: 0 };
|
|
64
|
+
// Look up subscriber rows for all requested emails.
|
|
65
|
+
const rows = await db
|
|
66
|
+
.select({
|
|
67
|
+
id: subscribers.id,
|
|
68
|
+
email: subscribers.email,
|
|
69
|
+
status: subscribers.status,
|
|
70
|
+
})
|
|
71
|
+
.from(subscribers)
|
|
72
|
+
.where(inArray(subscribers.email, emails));
|
|
73
|
+
const rowByEmail = new Map();
|
|
74
|
+
for (const row of rows) {
|
|
75
|
+
rowByEmail.set(row.email.toLowerCase(), {
|
|
76
|
+
id: row.id,
|
|
77
|
+
status: row.status,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const candidates = [];
|
|
81
|
+
let suppressedCount = 0;
|
|
82
|
+
for (const r of byEmail.values()) {
|
|
83
|
+
const row = rowByEmail.get(r.email.toLowerCase());
|
|
84
|
+
// No subscriber row, or a suppressed status → skip. Never send to an
|
|
85
|
+
// address we cannot honour an unsubscribe for, and never to a
|
|
86
|
+
// bounced/complained/unsubscribed/pending one.
|
|
87
|
+
if (!row || SUPPRESSED_STATUSES.has(row.status)) {
|
|
88
|
+
suppressedCount++;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
candidates.push({ email: r.email, subscriberId: r.subscriberId ?? row.id, status: row.status });
|
|
92
|
+
}
|
|
93
|
+
return { candidates, suppressedCount };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Of the suppression-cleared candidates, drop any that already have a
|
|
97
|
+
* `gl_email_sends` row for this campaignId — the idempotency guard that makes a
|
|
98
|
+
* re-run a no-op for already-processed recipients.
|
|
99
|
+
*/
|
|
100
|
+
async function filterAlreadySent(db, campaignId, candidates) {
|
|
101
|
+
if (candidates.length === 0)
|
|
102
|
+
return { fresh: [], alreadySentCount: 0 };
|
|
103
|
+
const candidateEmails = candidates.map((c) => c.email);
|
|
104
|
+
const existing = await db
|
|
105
|
+
.select({ email: emailSends.email })
|
|
106
|
+
.from(emailSends)
|
|
107
|
+
.where(and(eq(emailSends.campaignId, campaignId), inArray(emailSends.email, candidateEmails)));
|
|
108
|
+
const sentEmails = new Set(existing.map((row) => row.email.toLowerCase()));
|
|
109
|
+
const fresh = [];
|
|
110
|
+
let alreadySentCount = 0;
|
|
111
|
+
for (const c of candidates) {
|
|
112
|
+
if (sentEmails.has(c.email.toLowerCase())) {
|
|
113
|
+
alreadySentCount++;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
fresh.push(c);
|
|
117
|
+
}
|
|
118
|
+
return { fresh, alreadySentCount };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Build the broadcast HTML with the tracking pixel + click-rewrite applied
|
|
122
|
+
* once (the per-recipient {{TRACKING_ID}} is substituted in the consumer).
|
|
123
|
+
*/
|
|
124
|
+
function prepareBroadcastHtml(html, options) {
|
|
125
|
+
const trackOpenUrl = `${options.siteUrl}${options.trackOpenPath}`;
|
|
126
|
+
const trackClickUrl = `${options.siteUrl}${options.trackClickPath}`;
|
|
127
|
+
return rewriteLinksForTracking(injectTrackingPixel(html, trackOpenUrl), trackClickUrl);
|
|
128
|
+
}
|
|
129
|
+
function chunk(items, size) {
|
|
130
|
+
const out = [];
|
|
131
|
+
for (let i = 0; i < items.length; i += size) {
|
|
132
|
+
out.push(items.slice(i, i + size));
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Send a one-off broadcast blast (Customer.io one-off-blast parity).
|
|
138
|
+
*
|
|
139
|
+
* - Takes an explicit recipient list, or a saved-segment name resolved via
|
|
140
|
+
* `resolveRecipients` (segmentation is NOT built here — WS5 bead 06).
|
|
141
|
+
* - Enforces the suppression list: only `active` subscribers are sent;
|
|
142
|
+
* unsubscribed / bounced / complained / pending / unknown addresses skip.
|
|
143
|
+
* - Idempotent on `campaignId`: a re-run never double-sends to a recipient that
|
|
144
|
+
* already has a `gl_email_sends` row for the campaign.
|
|
145
|
+
* - Writes a per-recipient `gl_email_sends` row (`type='broadcast'`, status
|
|
146
|
+
* `queued`) before enqueueing.
|
|
147
|
+
* - Fans out in batches sized for Cloudflare queue/send-rate limits, with
|
|
148
|
+
* optional `messagesPerSecond` pacing.
|
|
149
|
+
* - `dryRun` resolves + suppresses + dedupes and returns the recipient count
|
|
150
|
+
* WITHOUT writing rows or enqueueing anything.
|
|
151
|
+
*/
|
|
152
|
+
export async function sendBroadcast(env, options, params) {
|
|
153
|
+
const campaignId = params.campaignId ?? ulid();
|
|
154
|
+
const dryRun = params.dryRun ?? false;
|
|
155
|
+
const maxRecipientsPerMessage = Math.max(1, Math.min(params.maxRecipientsPerMessage ?? options.batchSize, 500));
|
|
156
|
+
const messagesPerSecond = params.messagesPerSecond ?? 0;
|
|
157
|
+
const { db, sendsDb, queue } = resolveProducerBindings(env, options);
|
|
158
|
+
// 1. Resolve the recipient list — explicit list and/or a resolved segment.
|
|
159
|
+
const requestedRaw = [...(params.recipients ?? [])];
|
|
160
|
+
if (params.segmentName) {
|
|
161
|
+
if (!params.resolveRecipients) {
|
|
162
|
+
throw new Error('[mailer] sendBroadcast: segmentName given without resolveRecipients; ' +
|
|
163
|
+
'segmentation is resolved by the caller (WS5 bead 06), not by the mailer.');
|
|
164
|
+
}
|
|
165
|
+
const resolved = await params.resolveRecipients(params.segmentName);
|
|
166
|
+
requestedRaw.push(...resolved);
|
|
167
|
+
}
|
|
168
|
+
if (requestedRaw.length === 0) {
|
|
169
|
+
throw new Error('[mailer] sendBroadcast: no recipients or segmentName provided');
|
|
170
|
+
}
|
|
171
|
+
const requested = requestedRaw.map(normalizeRecipient);
|
|
172
|
+
// 2. Suppression enforcement + dedupe — reads gl_subscribers (subscribers db).
|
|
173
|
+
const { candidates, suppressedCount } = await resolveAgainstSubscribers(db, requested);
|
|
174
|
+
// 3. Idempotency — drop recipients already recorded for this campaign (sends db).
|
|
175
|
+
const { fresh, alreadySentCount } = await filterAlreadySent(sendsDb, campaignId, candidates);
|
|
176
|
+
// Dry-run: report the counts and stop before any write or enqueue.
|
|
177
|
+
if (dryRun) {
|
|
178
|
+
return {
|
|
179
|
+
campaignId,
|
|
180
|
+
recipientCount: fresh.length,
|
|
181
|
+
suppressedCount,
|
|
182
|
+
alreadySentCount,
|
|
183
|
+
batchCount: 0,
|
|
184
|
+
dryRun: true,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (fresh.length === 0) {
|
|
188
|
+
return {
|
|
189
|
+
campaignId,
|
|
190
|
+
recipientCount: 0,
|
|
191
|
+
suppressedCount,
|
|
192
|
+
alreadySentCount,
|
|
193
|
+
batchCount: 0,
|
|
194
|
+
dryRun: false,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const html = prepareBroadcastHtml(params.html, options);
|
|
198
|
+
const now = new Date().toISOString();
|
|
199
|
+
// 4. Per-recipient row write + queue-recipient build.
|
|
200
|
+
const queueRecipients = [];
|
|
201
|
+
for (const recipient of fresh) {
|
|
202
|
+
const trackingId = ulid();
|
|
203
|
+
const [unsubscribeToken, preferencesToken] = await Promise.all([
|
|
204
|
+
generateToken(options.signingSecret, {
|
|
205
|
+
subscriberId: recipient.subscriberId,
|
|
206
|
+
action: 'unsubscribe',
|
|
207
|
+
}),
|
|
208
|
+
generateToken(options.signingSecret, {
|
|
209
|
+
subscriberId: recipient.subscriberId,
|
|
210
|
+
action: 'preferences',
|
|
211
|
+
}),
|
|
212
|
+
]);
|
|
213
|
+
await sendsDb.insert(emailSends).values({
|
|
214
|
+
id: ulid(),
|
|
215
|
+
subscriberId: recipient.subscriberId,
|
|
216
|
+
siteId: options.siteId,
|
|
217
|
+
campaignId,
|
|
218
|
+
email: recipient.email,
|
|
219
|
+
subject: params.subject,
|
|
220
|
+
type: 'broadcast',
|
|
221
|
+
status: 'queued',
|
|
222
|
+
trackingId,
|
|
223
|
+
createdAt: now,
|
|
224
|
+
});
|
|
225
|
+
queueRecipients.push({
|
|
226
|
+
email: recipient.email,
|
|
227
|
+
subscriberId: recipient.subscriberId,
|
|
228
|
+
trackingId,
|
|
229
|
+
unsubscribeToken,
|
|
230
|
+
preferencesToken,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
// 5. Batched fan-out, paced to the configured send rate.
|
|
234
|
+
const batches = chunk(queueRecipients, maxRecipientsPerMessage);
|
|
235
|
+
const intervalMs = messagesPerSecond > 0 ? Math.ceil(1000 / messagesPerSecond) : 0;
|
|
236
|
+
for (let i = 0; i < batches.length; i++) {
|
|
237
|
+
const message = {
|
|
238
|
+
siteId: options.siteId,
|
|
239
|
+
type: 'broadcast',
|
|
240
|
+
recipients: batches[i],
|
|
241
|
+
subject: params.subject,
|
|
242
|
+
htmlTemplate: html,
|
|
243
|
+
from: `${options.senderName} <${options.fromAddress}>`,
|
|
244
|
+
replyTo: options.replyTo,
|
|
245
|
+
campaignId,
|
|
246
|
+
};
|
|
247
|
+
await queue.send(message);
|
|
248
|
+
if (intervalMs > 0 && i < batches.length - 1) {
|
|
249
|
+
await sleep(intervalMs);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
campaignId,
|
|
254
|
+
recipientCount: fresh.length,
|
|
255
|
+
suppressedCount,
|
|
256
|
+
alreadySentCount,
|
|
257
|
+
batchCount: batches.length,
|
|
258
|
+
dryRun: false,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=broadcast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"broadcast.js","sourceRoot":"","sources":["../../src/utils/broadcast.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { buildMailerAnalyticsDataPoint, emitMailerAnalyticsEvent, type MailerAnalyticsEvent, } from './analytics.js';
|
|
2
2
|
export { handleBounce, handleComplaint, handleDelivery, updateSendStatus, } from './bounce.js';
|
|
3
|
+
export type { BroadcastRecipient, SendBroadcastParams, SendBroadcastResult, } from './broadcast.js';
|
|
4
|
+
export { sendBroadcast } from './broadcast.js';
|
|
3
5
|
export type { CloudflareEmailSender } from './providers.js';
|
|
4
6
|
export { CloudflareEmailProvider, getProvider, sleep } from './providers.js';
|
|
5
7
|
export type { CampaignSchedule, DigestSchedule } from './scheduling.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,6BAA6B,EAC7B,wBAAwB,EACxB,KAAK,oBAAoB,GACzB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACN,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,GAChB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAC3D,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC5E,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACvE,OAAO,EACN,uBAAuB,EACvB,qBAAqB,EACrB,eAAe,EACf,aAAa,EACb,kBAAkB,GAClB,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACvE,OAAO,EACN,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,GACjB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACN,YAAY,EACZ,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,WAAW,GACX,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EACN,mBAAmB,EACnB,uBAAuB,EACvB,eAAe,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,6BAA6B,EAC7B,wBAAwB,EACxB,KAAK,oBAAoB,GACzB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACN,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,GAChB,MAAM,aAAa,CAAA;AACpB,YAAY,EACX,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC9C,YAAY,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAC3D,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC5E,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACvE,OAAO,EACN,uBAAuB,EACvB,qBAAqB,EACrB,eAAe,EACf,aAAa,EACb,kBAAkB,GAClB,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACvE,OAAO,EACN,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,GACjB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACN,YAAY,EACZ,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,WAAW,GACX,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EACN,mBAAmB,EACnB,uBAAuB,EACvB,eAAe,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA"}
|
package/dist/utils/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { buildMailerAnalyticsDataPoint, emitMailerAnalyticsEvent, } from './analytics.js';
|
|
2
2
|
export { handleBounce, handleComplaint, handleDelivery, updateSendStatus, } from './bounce.js';
|
|
3
|
+
export { sendBroadcast } from './broadcast.js';
|
|
3
4
|
export { CloudflareEmailProvider, getProvider, sleep } from './providers.js';
|
|
4
5
|
export { executeCampaignSchedule, executeDigestSchedule, prepareCampaign, prepareDigest, sendBatchCampaigns, } from './scheduling.js';
|
|
5
6
|
export { sendCampaign, sendDigest, sendTransactional } from './send.js';
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,6BAA6B,EAC7B,wBAAwB,GAExB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACN,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,GAChB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,6BAA6B,EAC7B,wBAAwB,GAExB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACN,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,GAChB,MAAM,aAAa,CAAA;AAMpB,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE9C,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAE5E,OAAO,EACN,uBAAuB,EACvB,qBAAqB,EACrB,eAAe,EACf,aAAa,EACb,kBAAkB,GAClB,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACvE,OAAO,EACN,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,GACjB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACN,YAAY,EACZ,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,WAAW,GACX,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EACN,mBAAmB,EACnB,uBAAuB,EACvB,eAAe,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA"}
|
package/dist/utils/send.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/utils/send.js
CHANGED
|
@@ -13,16 +13,22 @@ function resolveProducerBindings(env, options) {
|
|
|
13
13
|
throw new Error(`[mailer] env.${options.d1Binding} is undefined; ` +
|
|
14
14
|
'pass d1Binding option to the mailer integration or bind that name in wrangler.toml.');
|
|
15
15
|
}
|
|
16
|
+
const sendsBindingName = options.sendsD1Binding ?? options.d1Binding;
|
|
17
|
+
const sendsD1 = env[sendsBindingName];
|
|
18
|
+
if (!sendsD1) {
|
|
19
|
+
throw new Error(`[mailer] env.${sendsBindingName} is undefined; ` +
|
|
20
|
+
'pass sendsD1Binding option to the mailer integration or bind that name in wrangler.toml.');
|
|
21
|
+
}
|
|
16
22
|
const queue = env[options.queueBinding];
|
|
17
23
|
if (!queue) {
|
|
18
24
|
throw new Error(`[mailer] env.${options.queueBinding} is undefined; ` +
|
|
19
25
|
'pass queueBinding option to the mailer integration or bind that name in wrangler.toml.');
|
|
20
26
|
}
|
|
21
|
-
return { db: drizzle(d1), queue };
|
|
27
|
+
return { db: drizzle(d1), sendsDb: drizzle(sendsD1), queue };
|
|
22
28
|
}
|
|
23
29
|
// ─── sendTransactional ───
|
|
24
30
|
export async function sendTransactional(env, options, params) {
|
|
25
|
-
const { db, queue } = resolveProducerBindings(env, options);
|
|
31
|
+
const { db, sendsDb, queue } = resolveProducerBindings(env, options);
|
|
26
32
|
const trackingId = ulid();
|
|
27
33
|
const subscriberId = params.subscriberId ?? ulid();
|
|
28
34
|
// Render HTML
|
|
@@ -44,10 +50,11 @@ export async function sendTransactional(env, options, params) {
|
|
|
44
50
|
else {
|
|
45
51
|
throw new Error('Either template or html must be provided');
|
|
46
52
|
}
|
|
47
|
-
// Record the send
|
|
48
|
-
await
|
|
53
|
+
// Record the send (in the sends DB — may differ from the subscribers DB)
|
|
54
|
+
await sendsDb.insert(emailSends).values({
|
|
49
55
|
id: ulid(),
|
|
50
56
|
subscriberId,
|
|
57
|
+
siteId: options.siteId,
|
|
51
58
|
email: params.to,
|
|
52
59
|
subject: params.subject,
|
|
53
60
|
type: 'transactional',
|
|
@@ -77,7 +84,7 @@ export async function sendTransactional(env, options, params) {
|
|
|
77
84
|
return { trackingId };
|
|
78
85
|
}
|
|
79
86
|
// ─── Fan-out helper (shared by campaign + digest) ───
|
|
80
|
-
async function fanOutToSubscribers(queue, db, options, params) {
|
|
87
|
+
async function fanOutToSubscribers(queue, db, sendsDb, options, params) {
|
|
81
88
|
let cursor = null;
|
|
82
89
|
let recipientCount = 0;
|
|
83
90
|
const filter = params.filter ?? { status: ['active'] };
|
|
@@ -98,9 +105,10 @@ async function fanOutToSubscribers(queue, db, options, params) {
|
|
|
98
105
|
subscriberId: sub.id,
|
|
99
106
|
action: 'preferences',
|
|
100
107
|
});
|
|
101
|
-
await
|
|
108
|
+
await sendsDb.insert(emailSends).values({
|
|
102
109
|
id: ulid(),
|
|
103
110
|
subscriberId: sub.id,
|
|
111
|
+
siteId: options.siteId,
|
|
104
112
|
campaignId: params.campaignId,
|
|
105
113
|
email: sub.email,
|
|
106
114
|
subject: params.subject,
|
|
@@ -136,7 +144,7 @@ async function fanOutToSubscribers(queue, db, options, params) {
|
|
|
136
144
|
// ─── sendCampaign ───
|
|
137
145
|
export async function sendCampaign(env, options, params) {
|
|
138
146
|
const campaignId = params.campaignId ?? ulid();
|
|
139
|
-
const { db, queue } = resolveProducerBindings(env, options);
|
|
147
|
+
const { db, sendsDb, queue } = resolveProducerBindings(env, options);
|
|
140
148
|
// Render HTML
|
|
141
149
|
let html;
|
|
142
150
|
if (params.template) {
|
|
@@ -160,7 +168,7 @@ export async function sendCampaign(env, options, params) {
|
|
|
160
168
|
const trackClickUrl = `${options.siteUrl}${options.trackClickPath}`;
|
|
161
169
|
html = injectTrackingPixel(html, trackOpenUrl);
|
|
162
170
|
html = rewriteLinksForTracking(html, trackClickUrl);
|
|
163
|
-
const recipientCount = await fanOutToSubscribers(queue, db, options, {
|
|
171
|
+
const recipientCount = await fanOutToSubscribers(queue, db, sendsDb, options, {
|
|
164
172
|
subject: params.subject,
|
|
165
173
|
html,
|
|
166
174
|
campaignId,
|
|
@@ -172,7 +180,7 @@ export async function sendCampaign(env, options, params) {
|
|
|
172
180
|
// ─── sendDigest ───
|
|
173
181
|
export async function sendDigest(env, options, params) {
|
|
174
182
|
const campaignId = params.campaignId ?? ulid();
|
|
175
|
-
const { db, queue } = resolveProducerBindings(env, options);
|
|
183
|
+
const { db, sendsDb, queue } = resolveProducerBindings(env, options);
|
|
176
184
|
// Render digest items then full template
|
|
177
185
|
const itemsHtml = renderDigestItems(params.items);
|
|
178
186
|
let html = renderEmail('digest', {
|
|
@@ -189,7 +197,7 @@ export async function sendDigest(env, options, params) {
|
|
|
189
197
|
const trackClickUrl = `${options.siteUrl}${options.trackClickPath}`;
|
|
190
198
|
html = injectTrackingPixel(html, trackOpenUrl);
|
|
191
199
|
html = rewriteLinksForTracking(html, trackClickUrl);
|
|
192
|
-
const recipientCount = await fanOutToSubscribers(queue, db, options, {
|
|
200
|
+
const recipientCount = await fanOutToSubscribers(queue, db, sendsDb, options, {
|
|
193
201
|
subject: params.subject,
|
|
194
202
|
html,
|
|
195
203
|
campaignId,
|
package/dist/utils/send.js.map
CHANGED
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/utils/send.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
-- Adds site_id to gl_email_sends so the send-record table can be centralized
|
|
2
|
+
-- in a realm D1 shared across sites (the mail-platform consumer model).
|
|
3
|
+
ALTER TABLE gl_email_sends ADD COLUMN site_id TEXT;
|
|
4
|
+
CREATE INDEX IF NOT EXISTS idx_sends_site_campaign ON gl_email_sends(site_id, campaign_id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growth-labs/mailer",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"drizzle-kit": "^0.30.0",
|
|
90
90
|
"typescript": "^5.7.0",
|
|
91
91
|
"vite": "^7.3.2",
|
|
92
|
-
"vitest": "^
|
|
92
|
+
"vitest": "^4.1.8"
|
|
93
93
|
},
|
|
94
94
|
"scripts": {
|
|
95
95
|
"build": "tsc",
|
|
@@ -30,6 +30,7 @@ const formId = `gl-subscribe-${Math.random().toString(36).slice(2, 8)}`;
|
|
|
30
30
|
method="POST"
|
|
31
31
|
action={config.subscribePath}
|
|
32
32
|
data-gl-subscribe-form
|
|
33
|
+
data-subscribe-path={config.subscribePath}
|
|
33
34
|
>
|
|
34
35
|
<div class="gl-subscribe-fields">
|
|
35
36
|
{showNameField && (
|
|
@@ -87,75 +88,87 @@ const formId = `gl-subscribe-${Math.random().toString(36).slice(2, 8)}`;
|
|
|
87
88
|
</form>
|
|
88
89
|
</div>
|
|
89
90
|
|
|
90
|
-
<script
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// Load Turnstile
|
|
91
|
+
<script>
|
|
92
|
+
// Hoisted (bundled) module shared by every SubscribeForm on the page.
|
|
93
|
+
// Previously this used `define:vars`, which Astro cannot bundle, so the
|
|
94
|
+
// whole handler was re-emitted inline per form instance. Now it wires all
|
|
95
|
+
// forms from one cached script, reading per-form config from data-* attrs.
|
|
96
|
+
function initGlSubscribeForms() {
|
|
97
|
+
const forms = document.querySelectorAll<HTMLFormElement>(
|
|
98
|
+
"[data-gl-subscribe-form]",
|
|
99
|
+
);
|
|
100
|
+
if (forms.length === 0) return;
|
|
101
|
+
|
|
102
|
+
// Load Turnstile once per page (de-duped) when a subscribe form exists.
|
|
102
103
|
if (!document.querySelector('script[src*="turnstile"]')) {
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
document.head.appendChild(
|
|
104
|
+
const turnstile = document.createElement("script");
|
|
105
|
+
turnstile.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
|
|
106
|
+
turnstile.async = true;
|
|
107
|
+
document.head.appendChild(turnstile);
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
110
|
+
for (const form of forms) {
|
|
111
|
+
if (form.dataset.glWired) continue;
|
|
112
|
+
form.dataset.glWired = "1";
|
|
113
|
+
|
|
114
|
+
const subscribePath = form.getAttribute("data-subscribe-path") || "";
|
|
115
|
+
const submitBtn = form.querySelector("[data-gl-submit]");
|
|
116
|
+
const messageEl = form.querySelector("[data-gl-message]");
|
|
117
|
+
const successEl = form.querySelector("[data-gl-success]");
|
|
118
|
+
const errorEl = form.querySelector("[data-gl-error]");
|
|
119
|
+
|
|
120
|
+
form.addEventListener("submit", async (e) => {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
if (!submitBtn) return;
|
|
123
|
+
|
|
124
|
+
submitBtn.setAttribute("disabled", "true");
|
|
125
|
+
messageEl?.setAttribute("hidden", "");
|
|
126
|
+
successEl?.setAttribute("hidden", "");
|
|
127
|
+
errorEl?.setAttribute("hidden", "");
|
|
128
|
+
|
|
129
|
+
const formData = new FormData(form);
|
|
130
|
+
const preferences = formData.getAll("preferences");
|
|
131
|
+
const turnstileInput = form.querySelector<HTMLInputElement>(
|
|
132
|
+
'[name="cf-turnstile-response"]',
|
|
133
|
+
);
|
|
134
|
+
const turnstileToken = turnstileInput?.value ?? "";
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const res = await fetch(subscribePath, {
|
|
138
|
+
method: "POST",
|
|
139
|
+
headers: { "Content-Type": "application/json" },
|
|
140
|
+
body: JSON.stringify({
|
|
141
|
+
email: formData.get("email"),
|
|
142
|
+
name: formData.get("name") || undefined,
|
|
143
|
+
source: formData.get("source"),
|
|
144
|
+
preferences: preferences.length ? preferences : undefined,
|
|
145
|
+
turnstileToken,
|
|
146
|
+
}),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const data = await res.json();
|
|
150
|
+
|
|
151
|
+
messageEl?.removeAttribute("hidden");
|
|
152
|
+
if (data.success) {
|
|
153
|
+
successEl?.removeAttribute("hidden");
|
|
154
|
+
form.reset();
|
|
155
|
+
} else if (errorEl) {
|
|
156
|
+
errorEl.textContent = data.error || "Something went wrong";
|
|
157
|
+
errorEl.removeAttribute("hidden");
|
|
158
|
+
}
|
|
159
|
+
} catch {
|
|
160
|
+
messageEl?.removeAttribute("hidden");
|
|
145
161
|
if (errorEl) {
|
|
146
|
-
errorEl.textContent =
|
|
147
|
-
errorEl.removeAttribute(
|
|
162
|
+
errorEl.textContent = "Network error. Please try again.";
|
|
163
|
+
errorEl.removeAttribute("hidden");
|
|
148
164
|
}
|
|
165
|
+
} finally {
|
|
166
|
+
submitBtn.removeAttribute("disabled");
|
|
149
167
|
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
} finally {
|
|
157
|
-
submitBtn.removeAttribute('disabled')
|
|
158
|
-
}
|
|
159
|
-
})
|
|
160
|
-
})()
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
initGlSubscribeForms();
|
|
173
|
+
document.addEventListener("astro:page-load", initGlSubscribeForms);
|
|
161
174
|
</script>
|