@growth-labs/mailer 0.1.3

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 (132) hide show
  1. package/README.md +89 -0
  2. package/dist/components/index.d.ts +3 -0
  3. package/dist/components/index.d.ts.map +1 -0
  4. package/dist/components/index.js +3 -0
  5. package/dist/components/index.js.map +1 -0
  6. package/dist/index.d.ts +14 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +65 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/middleware/tracking.d.ts +3 -0
  11. package/dist/middleware/tracking.d.ts.map +1 -0
  12. package/dist/middleware/tracking.js +13 -0
  13. package/dist/middleware/tracking.js.map +1 -0
  14. package/dist/options.d.ts +160 -0
  15. package/dist/options.d.ts.map +1 -0
  16. package/dist/options.js +51 -0
  17. package/dist/options.js.map +1 -0
  18. package/dist/queue/consumer.d.ts +8 -0
  19. package/dist/queue/consumer.d.ts.map +1 -0
  20. package/dist/queue/consumer.js +83 -0
  21. package/dist/queue/consumer.js.map +1 -0
  22. package/dist/routes/confirm.d.ts +3 -0
  23. package/dist/routes/confirm.d.ts.map +1 -0
  24. package/dist/routes/confirm.js +59 -0
  25. package/dist/routes/confirm.js.map +1 -0
  26. package/dist/routes/subscribe.d.ts +3 -0
  27. package/dist/routes/subscribe.d.ts.map +1 -0
  28. package/dist/routes/subscribe.js +87 -0
  29. package/dist/routes/subscribe.js.map +1 -0
  30. package/dist/routes/track-click.d.ts +3 -0
  31. package/dist/routes/track-click.d.ts.map +1 -0
  32. package/dist/routes/track-click.js +45 -0
  33. package/dist/routes/track-click.js.map +1 -0
  34. package/dist/routes/track-open.d.ts +3 -0
  35. package/dist/routes/track-open.d.ts.map +1 -0
  36. package/dist/routes/track-open.js +40 -0
  37. package/dist/routes/track-open.js.map +1 -0
  38. package/dist/routes/unsubscribe.d.ts +4 -0
  39. package/dist/routes/unsubscribe.d.ts.map +1 -0
  40. package/dist/routes/unsubscribe.js +81 -0
  41. package/dist/routes/unsubscribe.js.map +1 -0
  42. package/dist/routes/webhook.d.ts +3 -0
  43. package/dist/routes/webhook.d.ts.map +1 -0
  44. package/dist/routes/webhook.js +30 -0
  45. package/dist/routes/webhook.js.map +1 -0
  46. package/dist/schema.d.ts +564 -0
  47. package/dist/schema.d.ts.map +1 -0
  48. package/dist/schema.js +47 -0
  49. package/dist/schema.js.map +1 -0
  50. package/dist/types.d.ts +106 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +3 -0
  53. package/dist/types.js.map +1 -0
  54. package/dist/utils/bindings.d.ts +20 -0
  55. package/dist/utils/bindings.d.ts.map +1 -0
  56. package/dist/utils/bindings.js +19 -0
  57. package/dist/utils/bindings.js.map +1 -0
  58. package/dist/utils/bounce.d.ts +29 -0
  59. package/dist/utils/bounce.d.ts.map +1 -0
  60. package/dist/utils/bounce.js +59 -0
  61. package/dist/utils/bounce.js.map +1 -0
  62. package/dist/utils/index.d.ts +12 -0
  63. package/dist/utils/index.d.ts.map +1 -0
  64. package/dist/utils/index.js +9 -0
  65. package/dist/utils/index.js.map +1 -0
  66. package/dist/utils/providers.d.ts +31 -0
  67. package/dist/utils/providers.d.ts.map +1 -0
  68. package/dist/utils/providers.js +109 -0
  69. package/dist/utils/providers.js.map +1 -0
  70. package/dist/utils/scheduling.d.ts +89 -0
  71. package/dist/utils/scheduling.d.ts.map +1 -0
  72. package/dist/utils/scheduling.js +110 -0
  73. package/dist/utils/scheduling.js.map +1 -0
  74. package/dist/utils/send.d.ts +42 -0
  75. package/dist/utils/send.d.ts.map +1 -0
  76. package/dist/utils/send.js +193 -0
  77. package/dist/utils/send.js.map +1 -0
  78. package/dist/utils/subscribers.d.ts +23 -0
  79. package/dist/utils/subscribers.d.ts.map +1 -0
  80. package/dist/utils/subscribers.js +200 -0
  81. package/dist/utils/subscribers.js.map +1 -0
  82. package/dist/utils/templates.d.ts +16 -0
  83. package/dist/utils/templates.d.ts.map +1 -0
  84. package/dist/utils/templates.js +426 -0
  85. package/dist/utils/templates.js.map +1 -0
  86. package/dist/utils/tokens.d.ts +13 -0
  87. package/dist/utils/tokens.d.ts.map +1 -0
  88. package/dist/utils/tokens.js +62 -0
  89. package/dist/utils/tokens.js.map +1 -0
  90. package/dist/utils/tracking.d.ts +26 -0
  91. package/dist/utils/tracking.d.ts.map +1 -0
  92. package/dist/utils/tracking.js +49 -0
  93. package/dist/utils/tracking.js.map +1 -0
  94. package/dist/utils/urls.d.ts +7 -0
  95. package/dist/utils/urls.d.ts.map +1 -0
  96. package/dist/utils/urls.js +34 -0
  97. package/dist/utils/urls.js.map +1 -0
  98. package/dist/vite-plugin.d.ts +4 -0
  99. package/dist/vite-plugin.d.ts.map +1 -0
  100. package/dist/vite-plugin.js +18 -0
  101. package/dist/vite-plugin.js.map +1 -0
  102. package/package.json +85 -0
  103. package/src/astro.d.ts +4 -0
  104. package/src/components/PreferenceCenter.astro +147 -0
  105. package/src/components/SubscribeForm.astro +161 -0
  106. package/src/components/index.ts +2 -0
  107. package/src/index.ts +101 -0
  108. package/src/middleware/tracking.ts +18 -0
  109. package/src/options.ts +65 -0
  110. package/src/queue/consumer.ts +99 -0
  111. package/src/routes/confirm.ts +68 -0
  112. package/src/routes/preferences.astro +137 -0
  113. package/src/routes/subscribe.ts +107 -0
  114. package/src/routes/track-click.ts +57 -0
  115. package/src/routes/track-open.ts +51 -0
  116. package/src/routes/unsubscribe.ts +96 -0
  117. package/src/routes/webhook.ts +48 -0
  118. package/src/schema.ts +56 -0
  119. package/src/types.ts +145 -0
  120. package/src/utils/bindings.ts +28 -0
  121. package/src/utils/bounce.ts +77 -0
  122. package/src/utils/index.ts +47 -0
  123. package/src/utils/providers.ts +141 -0
  124. package/src/utils/scheduling.ts +188 -0
  125. package/src/utils/send.ts +282 -0
  126. package/src/utils/subscribers.ts +277 -0
  127. package/src/utils/templates.ts +459 -0
  128. package/src/utils/tokens.ts +91 -0
  129. package/src/utils/tracking.ts +58 -0
  130. package/src/utils/urls.ts +49 -0
  131. package/src/virtual.d.ts +32 -0
  132. package/src/vite-plugin.ts +21 -0
@@ -0,0 +1,426 @@
1
+ // ─── Inline style map (~40 utility classes → CSS) ───
2
+ const INLINE_STYLES = {
3
+ 'text-center': 'text-align: center;',
4
+ 'text-left': 'text-align: left;',
5
+ 'text-right': 'text-align: right;',
6
+ 'font-bold': 'font-weight: 700;',
7
+ 'font-normal': 'font-weight: 400;',
8
+ 'text-sm': 'font-size: 14px; line-height: 20px;',
9
+ 'text-base': 'font-size: 16px; line-height: 24px;',
10
+ 'text-lg': 'font-size: 18px; line-height: 28px;',
11
+ 'text-xl': 'font-size: 20px; line-height: 28px;',
12
+ 'text-2xl': 'font-size: 24px; line-height: 32px;',
13
+ 'text-3xl': 'font-size: 30px; line-height: 36px;',
14
+ 'p-2': 'padding: 8px;',
15
+ 'p-3': 'padding: 12px;',
16
+ 'p-4': 'padding: 16px;',
17
+ 'p-6': 'padding: 24px;',
18
+ 'p-8': 'padding: 32px;',
19
+ 'px-2': 'padding-left: 8px; padding-right: 8px;',
20
+ 'px-3': 'padding-left: 12px; padding-right: 12px;',
21
+ 'px-4': 'padding-left: 16px; padding-right: 16px;',
22
+ 'px-6': 'padding-left: 24px; padding-right: 24px;',
23
+ 'px-8': 'padding-left: 32px; padding-right: 32px;',
24
+ 'py-2': 'padding-top: 8px; padding-bottom: 8px;',
25
+ 'py-3': 'padding-top: 12px; padding-bottom: 12px;',
26
+ 'py-4': 'padding-top: 16px; padding-bottom: 16px;',
27
+ 'py-6': 'padding-top: 24px; padding-bottom: 24px;',
28
+ 'py-8': 'padding-top: 32px; padding-bottom: 32px;',
29
+ 'm-0': 'margin: 0;',
30
+ 'm-2': 'margin: 8px;',
31
+ 'm-3': 'margin: 12px;',
32
+ 'm-4': 'margin: 16px;',
33
+ 'mx-auto': 'margin-left: auto; margin-right: auto;',
34
+ 'my-2': 'margin-top: 8px; margin-bottom: 8px;',
35
+ 'my-3': 'margin-top: 12px; margin-bottom: 12px;',
36
+ 'my-4': 'margin-top: 16px; margin-bottom: 16px;',
37
+ 'w-full': 'width: 100%;',
38
+ 'max-w-xl': 'max-width: 576px;',
39
+ 'max-w-2xl': 'max-width: 672px;',
40
+ rounded: 'border-radius: 4px;',
41
+ 'rounded-lg': 'border-radius: 8px;',
42
+ border: 'border: 1px solid #e5e7eb;',
43
+ block: 'display: block;',
44
+ 'inline-block': 'display: inline-block;',
45
+ hidden: 'display: none;',
46
+ 'leading-relaxed': 'line-height: 1.625;',
47
+ };
48
+ // ─── Helpers ───
49
+ function escapeHtml(str) {
50
+ return str
51
+ .replace(/&/g, '&')
52
+ .replace(/</g, '&lt;')
53
+ .replace(/>/g, '&gt;')
54
+ .replace(/"/g, '&quot;')
55
+ .replace(/'/g, '&#39;');
56
+ }
57
+ function resolveValue(data, path) {
58
+ return path.split('.').reduce((obj, key) => {
59
+ if (obj !== null && obj !== undefined && typeof obj === 'object') {
60
+ return obj[key];
61
+ }
62
+ return undefined;
63
+ }, data);
64
+ }
65
+ // ─── Conditional block processing ───
66
+ /**
67
+ * Process {{#key}}...{{/key}} conditional blocks.
68
+ * If the resolved value is truthy the inner content is kept;
69
+ * otherwise the entire block (including delimiters) is removed.
70
+ * Supports nested dot paths like {{#brand.logoUrl}}...{{/brand.logoUrl}}.
71
+ */
72
+ export function processConditionals(template, data) {
73
+ return template.replace(/\{\{#([^}]+)\}\}([\s\S]*?)\{\{\/\1\}\}/g, (_, key, content) => {
74
+ const value = resolveValue(data, key.trim());
75
+ return value ? content : '';
76
+ });
77
+ }
78
+ // ─── Template interpolation ───
79
+ export function interpolate(template, data) {
80
+ // Conditional blocks first
81
+ let result = processConditionals(template, data);
82
+ // Triple-brace: raw unescaped output
83
+ result = result.replace(/\{\{\{([^}]+)\}\}\}/g, (_, key) => {
84
+ const value = resolveValue(data, key.trim());
85
+ return value !== undefined && value !== null ? String(value) : '';
86
+ });
87
+ // Double-brace: HTML-escaped output
88
+ result = result.replace(/\{\{([^}]+)\}\}/g, (_, key) => {
89
+ const value = resolveValue(data, key.trim());
90
+ return value !== undefined && value !== null ? escapeHtml(String(value)) : '';
91
+ });
92
+ return result;
93
+ }
94
+ // ─── Style inlining ───
95
+ export function inlineStyles(html, brand) {
96
+ return html.replace(/class="([^"]*)"/g, (_, classAttr) => {
97
+ const classes = classAttr.split(/\s+/).filter(Boolean);
98
+ const styles = [];
99
+ for (const cls of classes) {
100
+ if (INLINE_STYLES[cls]) {
101
+ styles.push(INLINE_STYLES[cls]);
102
+ }
103
+ else if (brand) {
104
+ if (cls === 'text-primary') {
105
+ styles.push(`color: ${brand.primaryColor};`);
106
+ }
107
+ else if (cls === 'text-accent') {
108
+ styles.push(`color: ${brand.accentColor};`);
109
+ }
110
+ else if (cls === 'bg-primary') {
111
+ styles.push(`background-color: ${brand.primaryColor};`);
112
+ }
113
+ else if (cls === 'bg-accent') {
114
+ styles.push(`background-color: ${brand.accentColor};`);
115
+ }
116
+ // Unrecognized classes without brand match are dropped silently
117
+ }
118
+ // Unrecognized classes are dropped silently
119
+ }
120
+ if (styles.length === 0)
121
+ return '';
122
+ return `style="${styles.join(' ')}"`;
123
+ });
124
+ }
125
+ // ─── Digest item renderer ───
126
+ export function renderDigestItems(items) {
127
+ return items
128
+ .map((item) => {
129
+ const thumbnail = item.imageUrl
130
+ ? `<td style="width: 120px; padding-right: 16px; vertical-align: top;"><a href="${item.url}"><img src="${item.imageUrl}" alt="" width="120" style="display: block; border-radius: 4px; width: 120px;" /></a></td>`
131
+ : '';
132
+ const meta = [];
133
+ if (item.author)
134
+ meta.push(item.author);
135
+ if (item.publishedAt)
136
+ meta.push(item.publishedAt);
137
+ const metaLine = meta.length > 0
138
+ ? `<p style="font-size: 12px; line-height: 16px; color: #6b7280; margin: 4px 0 0 0;">${escapeHtml(meta.join(' &middot; '))}</p>`
139
+ : '';
140
+ const description = item.description
141
+ ? `<p style="font-size: 14px; line-height: 20px; color: #374151; margin: 4px 0 0 0;">${escapeHtml(item.description)}</p>`
142
+ : '';
143
+ return `<table width="100%" cellpadding="0" cellspacing="0" border="0" style="margin-bottom: 24px;"><tr>${thumbnail}<td style="vertical-align: top;"><a href="${item.url}" style="font-size: 16px; line-height: 24px; color: #111827; font-weight: 700; text-decoration: none;">${escapeHtml(item.title)}</a>${description}${metaLine}</td></tr></table>`;
144
+ })
145
+ .join('\n');
146
+ }
147
+ // ─── Built-in templates ───
148
+ const BUILT_IN_TEMPLATES = {
149
+ confirmation: [
150
+ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"',
151
+ ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
152
+ '<html xmlns="http://www.w3.org/1999/xhtml">',
153
+ '<head>',
154
+ '<meta charset="utf-8" />',
155
+ '<meta name="viewport" content="width=device-width, initial-scale=1.0" />',
156
+ '<title>Confirm your {{senderName}} subscription</title>',
157
+ '</head>',
158
+ '<body style="margin: 0; padding: 0;">',
159
+ '<table width="100%" cellpadding="0" cellspacing="0" border="0"',
160
+ ' class="w-full" style="background-color: #f4f4f5;">',
161
+ '<tr><td class="text-center p-4">',
162
+ '<table cellpadding="0" cellspacing="0" border="0"',
163
+ ' class="mx-auto" style="max-width: 600px; width: 100%;">',
164
+ // Header
165
+ '<tr><td class="bg-primary p-6 text-center rounded-lg"' +
166
+ ' style="border-radius: 8px 8px 0 0;">',
167
+ '{{#brand.logoUrl}}<img src="{{brand.logoUrl}}" alt="{{senderName}}"' +
168
+ ' style="max-height: 48px; margin-bottom: 8px;" />{{/brand.logoUrl}}',
169
+ '<p class="text-xl font-bold"' + ' style="color: #ffffff; margin: 0;">{{senderName}}</p>',
170
+ '</td></tr>',
171
+ // Body
172
+ '<tr><td style="background-color: #ffffff; padding: 32px 24px;">',
173
+ '<h1 class="text-2xl font-bold"' + ' style="margin: 0 0 16px 0; color: #111827;">',
174
+ 'Confirm your subscription</h1>',
175
+ '<p class="text-base leading-relaxed"' + ' style="margin: 0 0 24px 0; color: #374151;">',
176
+ 'Thanks for subscribing! Please confirm your email address',
177
+ ' by clicking the button below.</p>',
178
+ '<table cellpadding="0" cellspacing="0" border="0"><tr>',
179
+ '<td class="bg-primary rounded text-center"' + ' style="padding: 12px 24px;">',
180
+ '<a href="{{confirmUrl}}" style="color: #ffffff;' +
181
+ ' text-decoration: none; font-weight: 700;' +
182
+ ' font-size: 16px;">Confirm Subscription</a>',
183
+ '</td></tr></table>',
184
+ '<p class="text-sm"' + ' style="margin: 24px 0 0 0; color: #6b7280;">',
185
+ "If you didn't subscribe to {{senderName}},",
186
+ ' you can safely ignore this email.</p>',
187
+ '</td></tr>',
188
+ // Footer
189
+ '<tr><td class="text-center p-4">',
190
+ '{{#brand.footerText}}<p class="text-sm" style="color: #9ca3af; margin: 0 0 8px 0;">',
191
+ '{{brand.footerText}}</p>{{/brand.footerText}}',
192
+ '<p class="text-sm" style="color: #9ca3af; margin: 0;">',
193
+ '{{senderName}}</p>',
194
+ '</td></tr>',
195
+ '</table>',
196
+ '</td></tr></table>',
197
+ '</body></html>',
198
+ ].join('\n'),
199
+ welcome: [
200
+ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"',
201
+ ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
202
+ '<html xmlns="http://www.w3.org/1999/xhtml">',
203
+ '<head>',
204
+ '<meta charset="utf-8" />',
205
+ '<meta name="viewport" content="width=device-width, initial-scale=1.0" />',
206
+ '<title>Welcome to {{senderName}}</title>',
207
+ '</head>',
208
+ '<body style="margin: 0; padding: 0;">',
209
+ '<table width="100%" cellpadding="0" cellspacing="0" border="0"',
210
+ ' class="w-full" style="background-color: #f4f4f5;">',
211
+ '<tr><td class="text-center p-4">',
212
+ '<table cellpadding="0" cellspacing="0" border="0"',
213
+ ' class="mx-auto" style="max-width: 600px; width: 100%;">',
214
+ // Header
215
+ '<tr><td class="bg-primary p-6 text-center"' + ' style="border-radius: 8px 8px 0 0;">',
216
+ '{{#brand.logoUrl}}<img src="{{brand.logoUrl}}" alt="{{senderName}}"' +
217
+ ' style="max-height: 48px; margin-bottom: 8px;" />{{/brand.logoUrl}}',
218
+ '<p class="text-xl font-bold"' + ' style="color: #ffffff; margin: 0;">{{senderName}}</p>',
219
+ '</td></tr>',
220
+ // Body
221
+ '<tr><td style="background-color: #ffffff; padding: 32px 24px;">',
222
+ '<h1 class="text-2xl font-bold"' + ' style="margin: 0 0 16px 0; color: #111827;">',
223
+ "You're confirmed!</h1>",
224
+ '<p class="text-base leading-relaxed"' + ' style="margin: 0 0 24px 0; color: #374151;">',
225
+ '{{{welcomeMessage}}}</p>',
226
+ '<table cellpadding="0" cellspacing="0" border="0"><tr>',
227
+ '<td class="bg-primary rounded text-center"' + ' style="padding: 12px 24px;">',
228
+ '<a href="{{siteUrl}}" style="color: #ffffff;' +
229
+ ' text-decoration: none; font-weight: 700;' +
230
+ ' font-size: 16px;">Visit {{senderName}}</a>',
231
+ '</td></tr></table>',
232
+ '</td></tr>',
233
+ // Footer
234
+ '<tr><td class="text-center p-4">',
235
+ '{{#brand.footerText}}<p class="text-sm" style="color: #9ca3af; margin: 0 0 8px 0;">',
236
+ '{{brand.footerText}}</p>{{/brand.footerText}}',
237
+ '<p class="text-sm" style="color: #9ca3af; margin: 0 0 8px 0;">',
238
+ '{{senderName}}</p>',
239
+ '{{#unsubscribeUrl}}<p class="text-sm" style="color: #9ca3af; margin: 0;">',
240
+ '<a href="{{unsubscribeUrl}}"' + ' style="color: #9ca3af;">Unsubscribe</a>',
241
+ ' &middot; ',
242
+ '<a href="{{preferencesUrl}}"' + ' style="color: #9ca3af;">Preferences</a>',
243
+ '</p>{{/unsubscribeUrl}}',
244
+ '</td></tr>',
245
+ '</table>',
246
+ '</td></tr></table>',
247
+ '</body></html>',
248
+ ].join('\n'),
249
+ campaign: [
250
+ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"',
251
+ ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
252
+ '<html xmlns="http://www.w3.org/1999/xhtml">',
253
+ '<head>',
254
+ '<meta charset="utf-8" />',
255
+ '<meta name="viewport" content="width=device-width, initial-scale=1.0" />',
256
+ '<title>{{subject}}</title>',
257
+ '</head>',
258
+ '<body style="margin: 0; padding: 0;">',
259
+ '<table width="100%" cellpadding="0" cellspacing="0" border="0"',
260
+ ' class="w-full" style="background-color: #f4f4f5;">',
261
+ '<tr><td class="text-center p-4">',
262
+ '<table cellpadding="0" cellspacing="0" border="0"',
263
+ ' class="mx-auto" style="max-width: 600px; width: 100%;">',
264
+ // Header
265
+ '<tr><td class="bg-primary p-6 text-center"' + ' style="border-radius: 8px 8px 0 0;">',
266
+ '{{#brand.logoUrl}}<img src="{{brand.logoUrl}}" alt="{{senderName}}"' +
267
+ ' style="max-height: 48px; margin-bottom: 8px;" />{{/brand.logoUrl}}',
268
+ '<p class="text-xl font-bold"' + ' style="color: #ffffff; margin: 0;">{{senderName}}</p>',
269
+ '</td></tr>',
270
+ // Body
271
+ '<tr><td style="background-color: #ffffff; padding: 32px 24px;">',
272
+ '{{{content}}}',
273
+ '</td></tr>',
274
+ // Footer
275
+ '<tr><td class="text-center p-4">',
276
+ '{{#brand.footerText}}<p class="text-sm" style="color: #9ca3af; margin: 0 0 8px 0;">',
277
+ '{{brand.footerText}}</p>{{/brand.footerText}}',
278
+ '<p class="text-sm" style="color: #9ca3af; margin: 0 0 8px 0;">',
279
+ '{{senderName}}</p>',
280
+ '{{#unsubscribeUrl}}<p class="text-sm" style="color: #9ca3af; margin: 0;">',
281
+ '<a href="{{unsubscribeUrl}}"' + ' style="color: #9ca3af;">Unsubscribe</a>',
282
+ ' &middot; ',
283
+ '<a href="{{preferencesUrl}}"' + ' style="color: #9ca3af;">Preferences</a>',
284
+ '</p>{{/unsubscribeUrl}}',
285
+ '</td></tr>',
286
+ '</table>',
287
+ '</td></tr></table>',
288
+ '{{#trackingPixelUrl}}<img src="{{trackingPixelUrl}}" width="1" height="1" alt=""' +
289
+ ' style="display:block;width:1px;height:1px;border:0;" />{{/trackingPixelUrl}}',
290
+ '</body></html>',
291
+ ].join('\n'),
292
+ digest: [
293
+ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"',
294
+ ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
295
+ '<html xmlns="http://www.w3.org/1999/xhtml">',
296
+ '<head>',
297
+ '<meta charset="utf-8" />',
298
+ '<meta name="viewport" content="width=device-width, initial-scale=1.0" />',
299
+ '<title>{{subject}}</title>',
300
+ '</head>',
301
+ '<body style="margin: 0; padding: 0;">',
302
+ '<table width="100%" cellpadding="0" cellspacing="0" border="0"',
303
+ ' class="w-full" style="background-color: #f4f4f5;">',
304
+ '<tr><td class="text-center p-4">',
305
+ '<table cellpadding="0" cellspacing="0" border="0"',
306
+ ' class="mx-auto" style="max-width: 600px; width: 100%;">',
307
+ // Header
308
+ '<tr><td class="bg-primary p-6 text-center"' + ' style="border-radius: 8px 8px 0 0;">',
309
+ '{{#brand.logoUrl}}<img src="{{brand.logoUrl}}" alt="{{senderName}}"' +
310
+ ' style="max-height: 48px; margin-bottom: 8px;" />{{/brand.logoUrl}}',
311
+ '<p class="text-xl font-bold"' + ' style="color: #ffffff; margin: 0;">{{senderName}}</p>',
312
+ '</td></tr>',
313
+ // Body
314
+ '<tr><td style="background-color: #ffffff; padding: 32px 24px;">',
315
+ '{{#introText}}<p class="text-base leading-relaxed" style="margin: 0 0 24px 0; color: #374151;">',
316
+ '{{{introText}}}</p>{{/introText}}',
317
+ '{{{digestItems}}}',
318
+ '</td></tr>',
319
+ // Footer
320
+ '<tr><td class="text-center p-4">',
321
+ '{{#brand.footerText}}<p class="text-sm" style="color: #9ca3af; margin: 0 0 8px 0;">',
322
+ '{{brand.footerText}}</p>{{/brand.footerText}}',
323
+ '<p class="text-sm" style="color: #9ca3af; margin: 0 0 8px 0;">',
324
+ '{{senderName}}</p>',
325
+ '{{#unsubscribeUrl}}<p class="text-sm" style="color: #9ca3af; margin: 0;">',
326
+ '<a href="{{unsubscribeUrl}}"' + ' style="color: #9ca3af;">Unsubscribe</a>',
327
+ ' &middot; ',
328
+ '<a href="{{preferencesUrl}}"' + ' style="color: #9ca3af;">Preferences</a>',
329
+ '</p>{{/unsubscribeUrl}}',
330
+ '</td></tr>',
331
+ '</table>',
332
+ '</td></tr></table>',
333
+ '{{#trackingPixelUrl}}<img src="{{trackingPixelUrl}}" width="1" height="1" alt=""' +
334
+ ' style="display:block;width:1px;height:1px;border:0;" />{{/trackingPixelUrl}}',
335
+ '</body></html>',
336
+ ].join('\n'),
337
+ transactional: [
338
+ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"',
339
+ ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
340
+ '<html xmlns="http://www.w3.org/1999/xhtml">',
341
+ '<head>',
342
+ '<meta charset="utf-8" />',
343
+ '<meta name="viewport" content="width=device-width, initial-scale=1.0" />',
344
+ '<title>{{subject}}</title>',
345
+ '</head>',
346
+ '<body style="margin: 0; padding: 0;">',
347
+ '<table width="100%" cellpadding="0" cellspacing="0" border="0"',
348
+ ' class="w-full" style="background-color: #f4f4f5;">',
349
+ '<tr><td class="text-center p-4">',
350
+ '<table cellpadding="0" cellspacing="0" border="0"',
351
+ ' class="mx-auto" style="max-width: 600px; width: 100%;">',
352
+ // Header — minimal, text only
353
+ '<tr><td class="p-4 text-center">',
354
+ '<p class="text-lg font-bold"' + ' style="color: #374151; margin: 0;">{{senderName}}</p>',
355
+ '</td></tr>',
356
+ // Body
357
+ '<tr><td style="background-color: #ffffff; padding: 32px 24px;">',
358
+ '{{{content}}}',
359
+ '</td></tr>',
360
+ // Footer — minimal, no unsubscribe
361
+ '<tr><td class="text-center p-4">',
362
+ '<p class="text-sm" style="color: #9ca3af; margin: 0;">',
363
+ '{{senderName}}</p>',
364
+ '</td></tr>',
365
+ '</table>',
366
+ '</td></tr></table>',
367
+ '</body></html>',
368
+ ].join('\n'),
369
+ 'unsubscribe-confirm': [
370
+ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"',
371
+ ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
372
+ '<html xmlns="http://www.w3.org/1999/xhtml">',
373
+ '<head>',
374
+ '<meta charset="utf-8" />',
375
+ '<meta name="viewport" content="width=device-width, initial-scale=1.0" />',
376
+ '<title>Unsubscribed from {{senderName}}</title>',
377
+ '</head>',
378
+ '<body style="margin: 0; padding: 0;">',
379
+ '<table width="100%" cellpadding="0" cellspacing="0" border="0"',
380
+ ' class="w-full" style="background-color: #f4f4f5;">',
381
+ '<tr><td class="text-center p-4">',
382
+ '<table cellpadding="0" cellspacing="0" border="0"',
383
+ ' class="mx-auto" style="max-width: 600px; width: 100%;">',
384
+ // Header
385
+ '<tr><td class="bg-primary p-6 text-center"' + ' style="border-radius: 8px 8px 0 0;">',
386
+ '{{#brand.logoUrl}}<img src="{{brand.logoUrl}}" alt="{{senderName}}"' +
387
+ ' style="max-height: 48px; margin-bottom: 8px;" />{{/brand.logoUrl}}',
388
+ '<p class="text-xl font-bold"' + ' style="color: #ffffff; margin: 0;">{{senderName}}</p>',
389
+ '</td></tr>',
390
+ // Body
391
+ '<tr><td style="background-color: #ffffff; padding: 32px 24px;">',
392
+ '<h1 class="text-2xl font-bold"' + ' style="margin: 0 0 16px 0; color: #111827;">',
393
+ "You've been unsubscribed</h1>",
394
+ '<p class="text-base leading-relaxed"' + ' style="margin: 0 0 24px 0; color: #374151;">',
395
+ "You won't receive any more emails from {{senderName}}.</p>",
396
+ '<table cellpadding="0" cellspacing="0" border="0"><tr>',
397
+ '<td class="bg-primary rounded text-center"' + ' style="padding: 12px 24px;">',
398
+ '<a href="{{resubscribeUrl}}" style="color: #ffffff;' +
399
+ ' text-decoration: none; font-weight: 700;' +
400
+ ' font-size: 16px;">Re-subscribe</a>',
401
+ '</td></tr></table>',
402
+ '</td></tr>',
403
+ // Footer
404
+ '<tr><td class="text-center p-4">',
405
+ '{{#brand.footerText}}<p class="text-sm" style="color: #9ca3af; margin: 0 0 8px 0;">',
406
+ '{{brand.footerText}}</p>{{/brand.footerText}}',
407
+ '<p class="text-sm" style="color: #9ca3af; margin: 0;">',
408
+ '{{senderName}}</p>',
409
+ '</td></tr>',
410
+ '</table>',
411
+ '</td></tr></table>',
412
+ '</body></html>',
413
+ ].join('\n'),
414
+ };
415
+ // ─── Main render function ───
416
+ export function renderEmail(template, data) {
417
+ const templateStr = template in BUILT_IN_TEMPLATES ? BUILT_IN_TEMPLATES[template] : template;
418
+ // Provide default welcome message if not supplied
419
+ const templateData = { ...data };
420
+ if (template === 'welcome' && !templateData.welcomeMessage) {
421
+ templateData.welcomeMessage = `Welcome to ${data.senderName}! You're now subscribed and will receive our latest updates directly in your inbox.`;
422
+ }
423
+ const interpolated = interpolate(templateStr, templateData);
424
+ return inlineStyles(interpolated, data.brand);
425
+ }
426
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/utils/templates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ interface TokenPayload {
2
+ subscriberId: string;
3
+ action: 'confirm' | 'unsubscribe' | 'preferences';
4
+ exp?: number;
5
+ }
6
+ export declare function generateToken(secret: string, payload: TokenPayload): Promise<string>;
7
+ export declare function verifyToken(secret: string, token: string): Promise<{
8
+ subscriberId: string;
9
+ action: string;
10
+ exp?: number;
11
+ } | null>;
12
+ export {};
13
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"AA0CA,UAAU,YAAY;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,aAAa,CAAA;IACjD,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ;AAID,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAU1F;AAED,wBAAsB,WAAW,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAyBxE"}
@@ -0,0 +1,62 @@
1
+ const encoder = new TextEncoder();
2
+ function toBase64Url(buffer) {
3
+ const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
4
+ return btoa(String.fromCharCode(...bytes))
5
+ .replace(/\+/g, '-')
6
+ .replace(/\//g, '_')
7
+ .replace(/=+$/, '');
8
+ }
9
+ function fromBase64Url(str) {
10
+ const padded = str.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (str.length % 4)) % 4);
11
+ const binary = atob(padded);
12
+ const bytes = new Uint8Array(binary.length);
13
+ for (let i = 0; i < binary.length; i++) {
14
+ bytes[i] = binary.charCodeAt(i);
15
+ }
16
+ return bytes;
17
+ }
18
+ async function importKey(secret) {
19
+ return crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify']);
20
+ }
21
+ async function hmacSign(secret, data) {
22
+ const key = await importKey(secret);
23
+ const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(data));
24
+ return toBase64Url(signature);
25
+ }
26
+ async function hmacVerify(secret, data, signature) {
27
+ const key = await importKey(secret);
28
+ const sigBytes = fromBase64Url(signature);
29
+ return crypto.subtle.verify('HMAC', key, sigBytes.buffer, encoder.encode(data));
30
+ }
31
+ const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
32
+ export async function generateToken(secret, payload) {
33
+ const finalPayload = { ...payload };
34
+ if (finalPayload.action === 'confirm' && finalPayload.exp === undefined) {
35
+ finalPayload.exp = Date.now() + SEVEN_DAYS_MS;
36
+ }
37
+ const payloadB64 = toBase64Url(encoder.encode(JSON.stringify(finalPayload)));
38
+ const signature = await hmacSign(secret, payloadB64);
39
+ return `${payloadB64}.${signature}`;
40
+ }
41
+ export async function verifyToken(secret, token) {
42
+ const dotIndex = token.indexOf('.');
43
+ if (dotIndex === -1)
44
+ return null;
45
+ const payloadB64 = token.slice(0, dotIndex);
46
+ const signature = token.slice(dotIndex + 1);
47
+ if (!payloadB64 || !signature)
48
+ return null;
49
+ const valid = await hmacVerify(secret, payloadB64, signature);
50
+ if (!valid)
51
+ return null;
52
+ try {
53
+ const decoded = JSON.parse(new TextDecoder().decode(fromBase64Url(payloadB64)));
54
+ if (decoded.exp !== undefined && decoded.exp < Date.now())
55
+ return null;
56
+ return decoded;
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export declare const TRANSPARENT_GIF: Uint8Array<ArrayBuffer>;
2
+ /**
3
+ * Inject a tracking pixel before the closing </body> tag.
4
+ * If no </body> tag exists, the pixel is appended to the end.
5
+ * The {{TRACKING_ID}} placeholder is substituted per-recipient at send time.
6
+ *
7
+ * Only used for campaign/digest emails — never transactional.
8
+ */
9
+ export declare function injectTrackingPixel(html: string, trackOpenUrl: string): string;
10
+ /**
11
+ * Rewrite all <a href="..."> links to pass through a tracking redirect.
12
+ * The rewritten href becomes:
13
+ * {trackClickUrl}/{{TRACKING_ID}}?url={encodeURIComponent(originalUrl)}
14
+ *
15
+ * Skipped links:
16
+ * - Template placeholders: {{UNSUBSCRIBE_URL}}, {{PREFERENCES_URL}}
17
+ * - mailto: links
18
+ * - Anchor links starting with #
19
+ * - Empty href or missing href
20
+ *
21
+ * The {{TRACKING_ID}} placeholder is substituted per-recipient at send time.
22
+ *
23
+ * Only used for campaign/digest emails — never transactional.
24
+ */
25
+ export declare function rewriteLinksForTracking(html: string, trackClickUrl: string): string;
26
+ //# sourceMappingURL=tracking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracking.d.ts","sourceRoot":"","sources":["../../src/utils/tracking.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,eAAe,yBAI1B,CAAA;AAEF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAS9E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAiBnF"}
@@ -0,0 +1,49 @@
1
+ // 43-byte 1×1 transparent GIF
2
+ export const TRANSPARENT_GIF = new Uint8Array([
3
+ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0xff, 0xff, 0xff,
4
+ 0x00, 0x00, 0x00, 0x21, 0xf9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
5
+ 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b,
6
+ ]);
7
+ /**
8
+ * Inject a tracking pixel before the closing </body> tag.
9
+ * If no </body> tag exists, the pixel is appended to the end.
10
+ * The {{TRACKING_ID}} placeholder is substituted per-recipient at send time.
11
+ *
12
+ * Only used for campaign/digest emails — never transactional.
13
+ */
14
+ export function injectTrackingPixel(html, trackOpenUrl) {
15
+ const pixel = `<img src="${trackOpenUrl}/{{TRACKING_ID}}" width="1" height="1" alt="" style="display:block;width:1px;height:1px;border:0;" />`;
16
+ const bodyCloseIdx = html.lastIndexOf('</body>');
17
+ if (bodyCloseIdx !== -1) {
18
+ return html.slice(0, bodyCloseIdx) + pixel + html.slice(bodyCloseIdx);
19
+ }
20
+ return html + pixel;
21
+ }
22
+ /**
23
+ * Rewrite all <a href="..."> links to pass through a tracking redirect.
24
+ * The rewritten href becomes:
25
+ * {trackClickUrl}/{{TRACKING_ID}}?url={encodeURIComponent(originalUrl)}
26
+ *
27
+ * Skipped links:
28
+ * - Template placeholders: {{UNSUBSCRIBE_URL}}, {{PREFERENCES_URL}}
29
+ * - mailto: links
30
+ * - Anchor links starting with #
31
+ * - Empty href or missing href
32
+ *
33
+ * The {{TRACKING_ID}} placeholder is substituted per-recipient at send time.
34
+ *
35
+ * Only used for campaign/digest emails — never transactional.
36
+ */
37
+ export function rewriteLinksForTracking(html, trackClickUrl) {
38
+ return html.replace(/<a\s([^>]*?)href\s*=\s*"([^"]*)"([^>]*?)>/gi, (_match, before, href, after) => {
39
+ if (!href || href.startsWith('#') || href.startsWith('mailto:')) {
40
+ return _match;
41
+ }
42
+ if (href.includes('{{UNSUBSCRIBE_URL}}') || href.includes('{{PREFERENCES_URL}}')) {
43
+ return _match;
44
+ }
45
+ const tracked = `${trackClickUrl}/{{TRACKING_ID}}` + `?url=${encodeURIComponent(href)}`;
46
+ return `<a ${before}href="${tracked}"${after}>`;
47
+ });
48
+ }
49
+ //# sourceMappingURL=tracking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracking.js","sourceRoot":"","sources":["../../src/utils/tracking.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,UAAU,CAAC;IAC7C,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;IAC9F,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;IAC9F,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;CAChE,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,YAAoB;IACrE,MAAM,KAAK,GAAG,aAAa,YAAY,uGAAuG,CAAA;IAE9I,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAA;IAChD,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IACtE,CAAC;IAED,OAAO,IAAI,GAAG,KAAK,CAAA;AACpB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY,EAAE,aAAqB;IAC1E,OAAO,IAAI,CAAC,OAAO,CAClB,6CAA6C,EAC7C,CAAC,MAAM,EAAE,MAAc,EAAE,IAAY,EAAE,KAAa,EAAE,EAAE;QACvD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACjE,OAAO,MAAM,CAAA;QACd,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,CAAC,qBAAqB,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAClF,OAAO,MAAM,CAAA;QACd,CAAC;QAED,MAAM,OAAO,GAAG,GAAG,aAAa,kBAAkB,GAAG,QAAQ,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAA;QAEvF,OAAO,MAAM,MAAM,SAAS,OAAO,IAAI,KAAK,GAAG,CAAA;IAChD,CAAC,CACD,CAAA;AACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { ResolvedMailerOptions } from '../options.js';
2
+ export declare function buildSiteUrl(siteUrl: string, path: string, searchParams?: Record<string, string | boolean | undefined>): string;
3
+ export declare function buildSubscriberManageUrls(options: Pick<ResolvedMailerOptions, 'preferencesPath' | 'siteUrl' | 'signingSecret' | 'unsubscribePath'>, subscriberId?: string): Promise<{
4
+ preferencesUrl: string;
5
+ unsubscribeUrl: string;
6
+ }>;
7
+ //# sourceMappingURL=urls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"urls.d.ts","sourceRoot":"","sources":["../../src/utils/urls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAG1D,wBAAgB,YAAY,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,GACzD,MAAM,CASR;AAED,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,IAAI,CACZ,qBAAqB,EACrB,iBAAiB,GAAG,SAAS,GAAG,eAAe,GAAG,iBAAiB,CACnE,EACD,YAAY,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB7D"}