@shogo-ai/email 1.4.1
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/LICENSE +21 -0
- package/README.md +29 -0
- package/dist/chunk-VPWVZHAC.js +882 -0
- package/dist/index.cjs +923 -0
- package/dist/index.d.cts +136 -0
- package/dist/index.d.ts +136 -0
- package/dist/index.js +37 -0
- package/dist/server.cjs +1059 -0
- package/dist/server.d.cts +109 -0
- package/dist/server.d.ts +109 -0
- package/dist/server.js +404 -0
- package/dist/types-CCHryvHe.d.cts +196 -0
- package/dist/types-CCHryvHe.d.ts +196 -0
- package/package.json +74 -0
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
function formatEmailAddress(address) {
|
|
3
|
+
if (typeof address === "string") {
|
|
4
|
+
return address;
|
|
5
|
+
}
|
|
6
|
+
return address.name ? `"${address.name}" <${address.email}>` : address.email;
|
|
7
|
+
}
|
|
8
|
+
var EmailError = class _EmailError extends Error {
|
|
9
|
+
constructor(code, message, cause) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "EmailError";
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.cause = cause;
|
|
14
|
+
}
|
|
15
|
+
static configMissing(field) {
|
|
16
|
+
return new _EmailError("config_missing", `Missing required config: ${field}`);
|
|
17
|
+
}
|
|
18
|
+
static templateNotFound(name) {
|
|
19
|
+
return new _EmailError("template_not_found", `Template not found: ${name}`);
|
|
20
|
+
}
|
|
21
|
+
static providerNotConfigured() {
|
|
22
|
+
return new _EmailError("provider_not_configured", "Email provider is not configured");
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// src/templates.ts
|
|
27
|
+
function interpolate(template, data) {
|
|
28
|
+
return template.replace(/\{\{(\w+(?:\.\w+)*)\}\}/g, (match, path) => {
|
|
29
|
+
const value = getNestedValue(data, path);
|
|
30
|
+
return value !== void 0 ? String(value) : match;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function getNestedValue(obj, path) {
|
|
34
|
+
const keys = path.split(".");
|
|
35
|
+
let current = obj;
|
|
36
|
+
for (const key of keys) {
|
|
37
|
+
if (current === null || current === void 0) {
|
|
38
|
+
return void 0;
|
|
39
|
+
}
|
|
40
|
+
current = current[key];
|
|
41
|
+
}
|
|
42
|
+
return current;
|
|
43
|
+
}
|
|
44
|
+
function htmlToText(html) {
|
|
45
|
+
return html.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n\n").replace(/<\/div>/gi, "\n").replace(/<\/h[1-6]>/gi, "\n\n").replace(/<\/li>/gi, "\n").replace(/<[^>]+>/g, "").replace(/ /gi, " ").replace(/&/gi, "&").replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, '"').replace(/'/gi, "'").replace(/[ \t]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
|
|
46
|
+
}
|
|
47
|
+
var EmailTemplateRegistry = class {
|
|
48
|
+
constructor() {
|
|
49
|
+
this.templates = {};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Register a template
|
|
53
|
+
*/
|
|
54
|
+
register(template) {
|
|
55
|
+
this.templates[template.name] = template;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get a template by name
|
|
59
|
+
*/
|
|
60
|
+
get(name) {
|
|
61
|
+
return this.templates[name];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if a template exists
|
|
65
|
+
*/
|
|
66
|
+
has(name) {
|
|
67
|
+
return name in this.templates;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Render a template with data
|
|
71
|
+
*/
|
|
72
|
+
render(name, data) {
|
|
73
|
+
const template = this.templates[name];
|
|
74
|
+
if (!template) {
|
|
75
|
+
throw EmailError.templateNotFound(name);
|
|
76
|
+
}
|
|
77
|
+
const mergedData = { ...template.defaults, ...data };
|
|
78
|
+
const allData = {
|
|
79
|
+
...mergedData,
|
|
80
|
+
currentYear: (/* @__PURE__ */ new Date()).getFullYear()
|
|
81
|
+
};
|
|
82
|
+
const subject = interpolate(template.subject, allData);
|
|
83
|
+
const html = interpolate(template.html, allData);
|
|
84
|
+
const text = template.text ? interpolate(template.text, allData) : htmlToText(html);
|
|
85
|
+
return { subject, html, text };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* List all registered template names
|
|
89
|
+
*/
|
|
90
|
+
list() {
|
|
91
|
+
return Object.keys(this.templates);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var welcomeTemplate = {
|
|
95
|
+
name: "welcome",
|
|
96
|
+
subject: "Welcome to {{appName}}!",
|
|
97
|
+
html: `<!DOCTYPE html>
|
|
98
|
+
<html>
|
|
99
|
+
<head>
|
|
100
|
+
<meta charset="UTF-8">
|
|
101
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
102
|
+
</head>
|
|
103
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background-color: #f4f4f5;">
|
|
104
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse;">
|
|
105
|
+
<tr>
|
|
106
|
+
<td align="center" style="padding: 40px 20px;">
|
|
107
|
+
<table role="presentation" style="width: 100%; max-width: 600px; background: #ffffff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
|
|
108
|
+
<tr>
|
|
109
|
+
<td style="padding: 40px;">
|
|
110
|
+
<h1 style="margin: 0 0 24px; font-size: 24px; color: #18181b;">
|
|
111
|
+
Welcome to {{appName}}! \u{1F389}
|
|
112
|
+
</h1>
|
|
113
|
+
<p style="margin: 0 0 16px; font-size: 16px; line-height: 1.6; color: #3f3f46;">
|
|
114
|
+
Hi {{name}},
|
|
115
|
+
</p>
|
|
116
|
+
<p style="margin: 0 0 24px; font-size: 16px; line-height: 1.6; color: #3f3f46;">
|
|
117
|
+
Thanks for signing up. We're excited to have you on board!
|
|
118
|
+
</p>
|
|
119
|
+
{{#if loginUrl}}
|
|
120
|
+
<table role="presentation" style="margin: 32px 0;">
|
|
121
|
+
<tr>
|
|
122
|
+
<td>
|
|
123
|
+
<a href="{{loginUrl}}" style="display: inline-block; padding: 12px 24px; background: #6366f1; color: #ffffff; text-decoration: none; font-weight: 600; border-radius: 6px;">
|
|
124
|
+
Get Started
|
|
125
|
+
</a>
|
|
126
|
+
</td>
|
|
127
|
+
</tr>
|
|
128
|
+
</table>
|
|
129
|
+
{{/if}}
|
|
130
|
+
<p style="margin: 24px 0 0; font-size: 14px; color: #71717a;">
|
|
131
|
+
If you have any questions, just reply to this email.
|
|
132
|
+
</p>
|
|
133
|
+
</td>
|
|
134
|
+
</tr>
|
|
135
|
+
<tr>
|
|
136
|
+
<td style="padding: 20px 40px; background: #fafafa; border-top: 1px solid #e4e4e7; border-radius: 0 0 8px 8px;">
|
|
137
|
+
<p style="margin: 0; font-size: 13px; color: #a1a1aa; text-align: center;">
|
|
138
|
+
\xA9 {{currentYear}} {{appName}}
|
|
139
|
+
</p>
|
|
140
|
+
</td>
|
|
141
|
+
</tr>
|
|
142
|
+
</table>
|
|
143
|
+
</td>
|
|
144
|
+
</tr>
|
|
145
|
+
</table>
|
|
146
|
+
</body>
|
|
147
|
+
</html>`,
|
|
148
|
+
defaults: {
|
|
149
|
+
appName: "Our App"
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var passwordResetTemplate = {
|
|
153
|
+
name: "password-reset",
|
|
154
|
+
subject: "Reset your {{appName}} password",
|
|
155
|
+
html: `<!DOCTYPE html>
|
|
156
|
+
<html>
|
|
157
|
+
<head>
|
|
158
|
+
<meta charset="UTF-8">
|
|
159
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
160
|
+
</head>
|
|
161
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background-color: #f4f4f5;">
|
|
162
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse;">
|
|
163
|
+
<tr>
|
|
164
|
+
<td align="center" style="padding: 40px 20px;">
|
|
165
|
+
<table role="presentation" style="width: 100%; max-width: 600px; background: #ffffff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
|
|
166
|
+
<tr>
|
|
167
|
+
<td style="padding: 40px;">
|
|
168
|
+
<h1 style="margin: 0 0 24px; font-size: 24px; color: #18181b;">
|
|
169
|
+
Reset Your Password
|
|
170
|
+
</h1>
|
|
171
|
+
<p style="margin: 0 0 16px; font-size: 16px; line-height: 1.6; color: #3f3f46;">
|
|
172
|
+
{{#if name}}Hi {{name}},{{/if}}
|
|
173
|
+
</p>
|
|
174
|
+
<p style="margin: 0 0 24px; font-size: 16px; line-height: 1.6; color: #3f3f46;">
|
|
175
|
+
We received a request to reset your password. Click the button below to choose a new one:
|
|
176
|
+
</p>
|
|
177
|
+
<table role="presentation" style="margin: 32px 0;">
|
|
178
|
+
<tr>
|
|
179
|
+
<td>
|
|
180
|
+
<a href="{{resetUrl}}" style="display: inline-block; padding: 12px 24px; background: #6366f1; color: #ffffff; text-decoration: none; font-weight: 600; border-radius: 6px;">
|
|
181
|
+
Reset Password
|
|
182
|
+
</a>
|
|
183
|
+
</td>
|
|
184
|
+
</tr>
|
|
185
|
+
</table>
|
|
186
|
+
<p style="margin: 16px 0; font-size: 14px; color: #71717a;">
|
|
187
|
+
This link will expire in {{expiresIn}}.
|
|
188
|
+
</p>
|
|
189
|
+
<p style="margin: 24px 0 0; font-size: 14px; color: #71717a;">
|
|
190
|
+
If you didn't request this, you can safely ignore this email.
|
|
191
|
+
</p>
|
|
192
|
+
</td>
|
|
193
|
+
</tr>
|
|
194
|
+
<tr>
|
|
195
|
+
<td style="padding: 20px 40px; background: #fafafa; border-top: 1px solid #e4e4e7; border-radius: 0 0 8px 8px;">
|
|
196
|
+
<p style="margin: 0; font-size: 13px; color: #a1a1aa; text-align: center;">
|
|
197
|
+
\xA9 {{currentYear}} {{appName}}
|
|
198
|
+
</p>
|
|
199
|
+
</td>
|
|
200
|
+
</tr>
|
|
201
|
+
</table>
|
|
202
|
+
</td>
|
|
203
|
+
</tr>
|
|
204
|
+
</table>
|
|
205
|
+
</body>
|
|
206
|
+
</html>`,
|
|
207
|
+
defaults: {
|
|
208
|
+
appName: "Our App",
|
|
209
|
+
expiresIn: "1 hour"
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var invitationTemplate = {
|
|
213
|
+
name: "invitation",
|
|
214
|
+
subject: "{{inviterName}} invited you to join {{resourceName}}",
|
|
215
|
+
html: `<!DOCTYPE html>
|
|
216
|
+
<html>
|
|
217
|
+
<head>
|
|
218
|
+
<meta charset="UTF-8">
|
|
219
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
220
|
+
</head>
|
|
221
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background-color: #f4f4f5;">
|
|
222
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse;">
|
|
223
|
+
<tr>
|
|
224
|
+
<td align="center" style="padding: 40px 20px;">
|
|
225
|
+
<table role="presentation" style="width: 100%; max-width: 600px; background: #ffffff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
|
|
226
|
+
<tr>
|
|
227
|
+
<td style="padding: 40px;">
|
|
228
|
+
<h1 style="margin: 0 0 24px; font-size: 24px; color: #18181b;">
|
|
229
|
+
You're Invited! \u{1F389}
|
|
230
|
+
</h1>
|
|
231
|
+
<p style="margin: 0 0 24px; font-size: 16px; line-height: 1.6; color: #3f3f46;">
|
|
232
|
+
<strong>{{inviterName}}</strong> has invited you to join
|
|
233
|
+
<strong>{{resourceName}}</strong>{{#if role}} as a <strong>{{role}}</strong>{{/if}}.
|
|
234
|
+
</p>
|
|
235
|
+
<table role="presentation" style="margin: 32px 0;">
|
|
236
|
+
<tr>
|
|
237
|
+
<td>
|
|
238
|
+
<a href="{{acceptUrl}}" style="display: inline-block; padding: 12px 24px; background: #6366f1; color: #ffffff; text-decoration: none; font-weight: 600; border-radius: 6px;">
|
|
239
|
+
Accept Invitation
|
|
240
|
+
</a>
|
|
241
|
+
</td>
|
|
242
|
+
</tr>
|
|
243
|
+
</table>
|
|
244
|
+
<p style="margin: 24px 0 0; font-size: 14px; color: #71717a;">
|
|
245
|
+
If you weren't expecting this invitation, you can safely ignore this email.
|
|
246
|
+
</p>
|
|
247
|
+
</td>
|
|
248
|
+
</tr>
|
|
249
|
+
<tr>
|
|
250
|
+
<td style="padding: 20px 40px; background: #fafafa; border-top: 1px solid #e4e4e7; border-radius: 0 0 8px 8px;">
|
|
251
|
+
<p style="margin: 0; font-size: 13px; color: #a1a1aa; text-align: center;">
|
|
252
|
+
\xA9 {{currentYear}} {{appName}}
|
|
253
|
+
</p>
|
|
254
|
+
</td>
|
|
255
|
+
</tr>
|
|
256
|
+
</table>
|
|
257
|
+
</td>
|
|
258
|
+
</tr>
|
|
259
|
+
</table>
|
|
260
|
+
</body>
|
|
261
|
+
</html>`,
|
|
262
|
+
defaults: {
|
|
263
|
+
appName: "Our App",
|
|
264
|
+
resourceType: "workspace"
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
var notificationTemplate = {
|
|
268
|
+
name: "notification",
|
|
269
|
+
subject: "{{title}}",
|
|
270
|
+
html: `<!DOCTYPE html>
|
|
271
|
+
<html>
|
|
272
|
+
<head>
|
|
273
|
+
<meta charset="UTF-8">
|
|
274
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
275
|
+
</head>
|
|
276
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background-color: #f4f4f5;">
|
|
277
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse;">
|
|
278
|
+
<tr>
|
|
279
|
+
<td align="center" style="padding: 40px 20px;">
|
|
280
|
+
<table role="presentation" style="width: 100%; max-width: 600px; background: #ffffff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
|
|
281
|
+
<tr>
|
|
282
|
+
<td style="padding: 40px;">
|
|
283
|
+
<h1 style="margin: 0 0 24px; font-size: 24px; color: #18181b;">
|
|
284
|
+
{{title}}
|
|
285
|
+
</h1>
|
|
286
|
+
<p style="margin: 0 0 24px; font-size: 16px; line-height: 1.6; color: #3f3f46;">
|
|
287
|
+
{{message}}
|
|
288
|
+
</p>
|
|
289
|
+
{{#if actionUrl}}
|
|
290
|
+
<table role="presentation" style="margin: 32px 0;">
|
|
291
|
+
<tr>
|
|
292
|
+
<td>
|
|
293
|
+
<a href="{{actionUrl}}" style="display: inline-block; padding: 12px 24px; background: #6366f1; color: #ffffff; text-decoration: none; font-weight: 600; border-radius: 6px;">
|
|
294
|
+
{{actionText}}
|
|
295
|
+
</a>
|
|
296
|
+
</td>
|
|
297
|
+
</tr>
|
|
298
|
+
</table>
|
|
299
|
+
{{/if}}
|
|
300
|
+
</td>
|
|
301
|
+
</tr>
|
|
302
|
+
<tr>
|
|
303
|
+
<td style="padding: 20px 40px; background: #fafafa; border-top: 1px solid #e4e4e7; border-radius: 0 0 8px 8px;">
|
|
304
|
+
<p style="margin: 0; font-size: 13px; color: #a1a1aa; text-align: center;">
|
|
305
|
+
\xA9 {{currentYear}} {{appName}}
|
|
306
|
+
</p>
|
|
307
|
+
</td>
|
|
308
|
+
</tr>
|
|
309
|
+
</table>
|
|
310
|
+
</td>
|
|
311
|
+
</tr>
|
|
312
|
+
</table>
|
|
313
|
+
</body>
|
|
314
|
+
</html>`,
|
|
315
|
+
defaults: {
|
|
316
|
+
appName: "Our App",
|
|
317
|
+
actionText: "View Details"
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
var builtinTemplates = [
|
|
321
|
+
welcomeTemplate,
|
|
322
|
+
passwordResetTemplate,
|
|
323
|
+
invitationTemplate,
|
|
324
|
+
notificationTemplate
|
|
325
|
+
];
|
|
326
|
+
function createTemplateRegistry() {
|
|
327
|
+
const registry = new EmailTemplateRegistry();
|
|
328
|
+
for (const template of builtinTemplates) {
|
|
329
|
+
registry.register(template);
|
|
330
|
+
}
|
|
331
|
+
return registry;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/templates/_layout.ts
|
|
335
|
+
var EMAIL_CONSTANTS = {
|
|
336
|
+
APP_NAME: "Shogo",
|
|
337
|
+
APP_URL: "https://shogo.ai",
|
|
338
|
+
SUPPORT_EMAIL: "support@shogo.ai",
|
|
339
|
+
LOGO_URL: "https://shogo.ai/assets/images/shogo-logo-email.png",
|
|
340
|
+
LOGO_DARK_URL: "https://shogo.ai/assets/images/shogo-logo-dark-email.png",
|
|
341
|
+
FOOTER_TEXT: "© {{currentYear}} Shogo. All rights reserved"
|
|
342
|
+
};
|
|
343
|
+
var LIGHT_STYLES = `
|
|
344
|
+
body, html { margin: 0; padding: 0; }
|
|
345
|
+
.email-body {
|
|
346
|
+
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
347
|
+
background-color: #fafafa;
|
|
348
|
+
color: #2a2929;
|
|
349
|
+
line-height: 1.6;
|
|
350
|
+
}
|
|
351
|
+
.email-wrapper { padding: 40px 20px; }
|
|
352
|
+
.email-container {
|
|
353
|
+
max-width: 560px;
|
|
354
|
+
margin: 0 auto;
|
|
355
|
+
background: #ffffff;
|
|
356
|
+
border-radius: 16px;
|
|
357
|
+
border: 1px solid #e5e7eb;
|
|
358
|
+
overflow: hidden;
|
|
359
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
|
360
|
+
}
|
|
361
|
+
.email-header {
|
|
362
|
+
background: #ffffff;
|
|
363
|
+
padding: 24px 32px;
|
|
364
|
+
border-bottom: 1px solid #e5e7eb;
|
|
365
|
+
}
|
|
366
|
+
.email-logo {
|
|
367
|
+
font-size: 18px;
|
|
368
|
+
font-weight: 700;
|
|
369
|
+
color: #2a2929;
|
|
370
|
+
text-decoration: none;
|
|
371
|
+
letter-spacing: -0.3px;
|
|
372
|
+
}
|
|
373
|
+
.email-content { padding: 32px; }
|
|
374
|
+
.email-h1 {
|
|
375
|
+
font-size: 22px;
|
|
376
|
+
font-weight: 700;
|
|
377
|
+
color: #2a2929;
|
|
378
|
+
margin: 0 0 16px;
|
|
379
|
+
}
|
|
380
|
+
.email-text {
|
|
381
|
+
font-size: 15px;
|
|
382
|
+
color: #555;
|
|
383
|
+
margin: 0 0 16px;
|
|
384
|
+
}
|
|
385
|
+
.email-muted {
|
|
386
|
+
font-size: 13px;
|
|
387
|
+
color: #888;
|
|
388
|
+
margin: 0 0 12px;
|
|
389
|
+
}
|
|
390
|
+
.email-btn {
|
|
391
|
+
display: inline-block;
|
|
392
|
+
padding: 12px 32px;
|
|
393
|
+
background: #e8853d;
|
|
394
|
+
color: #ffffff;
|
|
395
|
+
text-decoration: none;
|
|
396
|
+
font-weight: 600;
|
|
397
|
+
font-size: 14px;
|
|
398
|
+
border-radius: 9999px;
|
|
399
|
+
margin: 8px 0 16px;
|
|
400
|
+
}
|
|
401
|
+
.email-btn-outline {
|
|
402
|
+
display: inline-block;
|
|
403
|
+
padding: 12px 32px;
|
|
404
|
+
background: #ffffff;
|
|
405
|
+
color: #2a2929;
|
|
406
|
+
text-decoration: none;
|
|
407
|
+
font-weight: 600;
|
|
408
|
+
font-size: 14px;
|
|
409
|
+
border-radius: 9999px;
|
|
410
|
+
border: 1px solid #d4d4d4;
|
|
411
|
+
margin: 8px 0 16px;
|
|
412
|
+
}
|
|
413
|
+
.email-btn-danger {
|
|
414
|
+
display: inline-block;
|
|
415
|
+
padding: 12px 32px;
|
|
416
|
+
background: #ef4444;
|
|
417
|
+
color: #ffffff;
|
|
418
|
+
text-decoration: none;
|
|
419
|
+
font-weight: 600;
|
|
420
|
+
font-size: 14px;
|
|
421
|
+
border-radius: 9999px;
|
|
422
|
+
margin: 8px 0 16px;
|
|
423
|
+
}
|
|
424
|
+
.email-divider {
|
|
425
|
+
border: none;
|
|
426
|
+
border-top: 1px solid #f1f5f9;
|
|
427
|
+
margin: 24px 0;
|
|
428
|
+
}
|
|
429
|
+
.email-footer {
|
|
430
|
+
padding: 20px 32px;
|
|
431
|
+
background: #ffffff;
|
|
432
|
+
border-top: 1px solid #e5e7eb;
|
|
433
|
+
text-align: center;
|
|
434
|
+
}
|
|
435
|
+
.email-footer-text {
|
|
436
|
+
font-size: 12px;
|
|
437
|
+
color: #888;
|
|
438
|
+
margin: 0;
|
|
439
|
+
}
|
|
440
|
+
.email-badge {
|
|
441
|
+
display: inline-block;
|
|
442
|
+
padding: 4px 10px;
|
|
443
|
+
background: #fff7ed;
|
|
444
|
+
color: #e8853d;
|
|
445
|
+
font-size: 12px;
|
|
446
|
+
font-weight: 600;
|
|
447
|
+
border-radius: 9999px;
|
|
448
|
+
border: 1px solid rgba(232,133,61,0.15);
|
|
449
|
+
}
|
|
450
|
+
.email-detail-row {
|
|
451
|
+
display: flex;
|
|
452
|
+
justify-content: space-between;
|
|
453
|
+
padding: 8px 0;
|
|
454
|
+
border-bottom: 1px solid #f4f4f5;
|
|
455
|
+
}
|
|
456
|
+
.email-detail-label {
|
|
457
|
+
font-size: 13px;
|
|
458
|
+
color: #888;
|
|
459
|
+
}
|
|
460
|
+
.email-detail-value {
|
|
461
|
+
font-size: 13px;
|
|
462
|
+
font-weight: 600;
|
|
463
|
+
color: #2a2929;
|
|
464
|
+
}
|
|
465
|
+
.email-logo-dark { display: none !important; }`;
|
|
466
|
+
var DARK_STYLES = `
|
|
467
|
+
@media (prefers-color-scheme: dark) {
|
|
468
|
+
.email-body { background-color: #1a1a1e; color: #e4e4e7; }
|
|
469
|
+
.email-container { background: #27272a; border-color: #3f3f46; box-shadow: 0 1px 4px rgba(0,0,0,0.3); }
|
|
470
|
+
.email-header { background: #27272a; border-bottom-color: #3f3f46; }
|
|
471
|
+
.email-logo { color: #fafafa; }
|
|
472
|
+
.email-h1 { color: #fafafa; }
|
|
473
|
+
.email-text { color: #d4d4d8; }
|
|
474
|
+
.email-muted { color: #a1a1aa; }
|
|
475
|
+
.email-btn-outline { background: #3f3f46; color: #fafafa; border-color: #52525b; }
|
|
476
|
+
.email-divider { border-top-color: #3f3f46; }
|
|
477
|
+
.email-footer { background: #27272a; border-top-color: #3f3f46; }
|
|
478
|
+
.email-footer-text { color: #71717a; }
|
|
479
|
+
.email-badge { background: rgba(232,133,61,0.15); color: #f0a970; border-color: rgba(232,133,61,0.25); }
|
|
480
|
+
.email-detail-row { border-bottom-color: #3f3f46; }
|
|
481
|
+
.email-detail-label { color: #a1a1aa; }
|
|
482
|
+
.email-detail-value { color: #fafafa; }
|
|
483
|
+
.email-logo-light { display: none !important; }
|
|
484
|
+
.email-logo-dark { display: block !important; }
|
|
485
|
+
}`;
|
|
486
|
+
var STYLES = `
|
|
487
|
+
<style>${LIGHT_STYLES}
|
|
488
|
+
${DARK_STYLES}
|
|
489
|
+
</style>`;
|
|
490
|
+
var DARK_STYLE_OVERRIDES = DARK_STYLES.replace(
|
|
491
|
+
"@media (prefers-color-scheme: dark) {",
|
|
492
|
+
""
|
|
493
|
+
).replace(/\}\s*$/, "");
|
|
494
|
+
function wrapInLayout(content) {
|
|
495
|
+
return `<!DOCTYPE html>
|
|
496
|
+
<html lang="en">
|
|
497
|
+
<head>
|
|
498
|
+
<meta charset="UTF-8">
|
|
499
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
500
|
+
${STYLES}
|
|
501
|
+
</head>
|
|
502
|
+
<body class="email-body">
|
|
503
|
+
<div class="email-wrapper">
|
|
504
|
+
<div class="email-container">
|
|
505
|
+
<div class="email-header">
|
|
506
|
+
<a href="${EMAIL_CONSTANTS.APP_URL}" style="display:inline-block;text-decoration:none;">
|
|
507
|
+
<img class="email-logo-light" src="${EMAIL_CONSTANTS.LOGO_URL}" alt="${EMAIL_CONSTANTS.APP_NAME}" width="169" height="58" style="display:block;height:40px;width:auto;" />
|
|
508
|
+
<img class="email-logo-dark" src="${EMAIL_CONSTANTS.LOGO_DARK_URL}" alt="${EMAIL_CONSTANTS.APP_NAME}" width="169" height="58" style="display:none;height:40px;width:auto;" />
|
|
509
|
+
</a>
|
|
510
|
+
</div>
|
|
511
|
+
<div class="email-content">
|
|
512
|
+
${content}
|
|
513
|
+
</div>
|
|
514
|
+
<div class="email-footer">
|
|
515
|
+
<p class="email-footer-text">© {{currentYear}} Shogo. All rights reserved</p>
|
|
516
|
+
</div>
|
|
517
|
+
</div>
|
|
518
|
+
</div>
|
|
519
|
+
</body>
|
|
520
|
+
</html>`;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/templates/auth/welcome.ts
|
|
524
|
+
var welcomeTemplate2 = {
|
|
525
|
+
name: "welcome",
|
|
526
|
+
subject: "Welcome to {{appName}}!",
|
|
527
|
+
html: wrapInLayout(`
|
|
528
|
+
<h1 class="email-h1">Welcome to {{appName}}!</h1>
|
|
529
|
+
<p class="email-text">Hi {{name}},</p>
|
|
530
|
+
<p class="email-text">
|
|
531
|
+
Thanks for signing up. You now have access to build, deploy, and
|
|
532
|
+
collaborate on AI-powered apps \u2014 all from one place.
|
|
533
|
+
</p>
|
|
534
|
+
<a href="{{loginUrl}}" class="email-btn" style="color:#ffffff;text-decoration:none;">Get Started</a>
|
|
535
|
+
<hr class="email-divider">
|
|
536
|
+
<p class="email-muted">
|
|
537
|
+
If you have any questions, just reply to this email \u2014 we're happy to help.
|
|
538
|
+
</p>
|
|
539
|
+
`),
|
|
540
|
+
defaults: {
|
|
541
|
+
appName: EMAIL_CONSTANTS.APP_NAME,
|
|
542
|
+
loginUrl: EMAIL_CONSTANTS.APP_URL
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
// src/templates/auth/password-reset.ts
|
|
547
|
+
var passwordResetTemplate2 = {
|
|
548
|
+
name: "password-reset",
|
|
549
|
+
subject: "Reset your {{appName}} password",
|
|
550
|
+
html: wrapInLayout(`
|
|
551
|
+
<h1 class="email-h1">Reset Your Password</h1>
|
|
552
|
+
<p class="email-text">
|
|
553
|
+
We received a request to reset the password for your account.
|
|
554
|
+
Click the button below to choose a new one:
|
|
555
|
+
</p>
|
|
556
|
+
<a href="{{resetUrl}}" class="email-btn" style="color:#ffffff;text-decoration:none;">Reset Password</a>
|
|
557
|
+
<hr class="email-divider">
|
|
558
|
+
<p class="email-muted">This link expires in {{expiresIn}}.</p>
|
|
559
|
+
<p class="email-muted">
|
|
560
|
+
If you didn't request this, you can safely ignore this email.
|
|
561
|
+
Your password will remain unchanged.
|
|
562
|
+
</p>
|
|
563
|
+
`),
|
|
564
|
+
defaults: {
|
|
565
|
+
appName: EMAIL_CONSTANTS.APP_NAME,
|
|
566
|
+
expiresIn: "1 hour"
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
// src/templates/auth/email-verification.ts
|
|
571
|
+
var emailVerificationTemplate = {
|
|
572
|
+
name: "email-verification",
|
|
573
|
+
subject: "Verify your email for {{appName}}",
|
|
574
|
+
html: wrapInLayout(`
|
|
575
|
+
<h1 class="email-h1">Verify Your Email</h1>
|
|
576
|
+
<p class="email-text">
|
|
577
|
+
Please confirm your email address by clicking the button below.
|
|
578
|
+
This helps us keep your account secure.
|
|
579
|
+
</p>
|
|
580
|
+
<a href="{{verifyUrl}}" class="email-btn" style="color:#ffffff;text-decoration:none;">Verify Email</a>
|
|
581
|
+
<hr class="email-divider">
|
|
582
|
+
<p class="email-muted">This link expires in {{expiresIn}}.</p>
|
|
583
|
+
<p class="email-muted">
|
|
584
|
+
If you didn't create an account, you can safely ignore this email.
|
|
585
|
+
</p>
|
|
586
|
+
`),
|
|
587
|
+
defaults: {
|
|
588
|
+
appName: EMAIL_CONSTANTS.APP_NAME,
|
|
589
|
+
expiresIn: "24 hours"
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
// src/templates/invitation/workspace-invite.ts
|
|
594
|
+
var workspaceInviteTemplate = {
|
|
595
|
+
name: "workspace-invite",
|
|
596
|
+
subject: "{{inviterName}} invited you to {{workspaceName}}",
|
|
597
|
+
html: wrapInLayout(`
|
|
598
|
+
<h1 class="email-h1">You're Invited!</h1>
|
|
599
|
+
<p class="email-text">
|
|
600
|
+
<strong>{{inviterName}}</strong> has invited you to join the
|
|
601
|
+
<strong>{{workspaceName}}</strong> workspace as
|
|
602
|
+
<span class="email-badge">{{role}}</span>.
|
|
603
|
+
</p>
|
|
604
|
+
<p class="email-text">
|
|
605
|
+
Accept the invitation to start collaborating on projects, share
|
|
606
|
+
resources, and build together.
|
|
607
|
+
</p>
|
|
608
|
+
<a href="{{acceptUrl}}" class="email-btn" style="color:#ffffff;text-decoration:none;">Accept Invitation</a>
|
|
609
|
+
<hr class="email-divider">
|
|
610
|
+
<p class="email-muted">
|
|
611
|
+
If you weren't expecting this, you can safely ignore this email.
|
|
612
|
+
</p>
|
|
613
|
+
`),
|
|
614
|
+
defaults: {
|
|
615
|
+
appName: EMAIL_CONSTANTS.APP_NAME,
|
|
616
|
+
role: "Editor"
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
// src/templates/invitation/project-invite.ts
|
|
621
|
+
var projectInviteTemplate = {
|
|
622
|
+
name: "project-invite",
|
|
623
|
+
subject: "{{inviterName}} invited you to {{projectName}}",
|
|
624
|
+
html: wrapInLayout(`
|
|
625
|
+
<h1 class="email-h1">Project Invitation</h1>
|
|
626
|
+
<p class="email-text">
|
|
627
|
+
<strong>{{inviterName}}</strong> has invited you to the
|
|
628
|
+
<strong>{{projectName}}</strong> project as
|
|
629
|
+
<span class="email-badge">{{role}}</span>.
|
|
630
|
+
</p>
|
|
631
|
+
<a href="{{acceptUrl}}" class="email-btn" style="color:#ffffff;text-decoration:none;">Accept Invitation</a>
|
|
632
|
+
<hr class="email-divider">
|
|
633
|
+
<p class="email-muted">
|
|
634
|
+
If you weren't expecting this, you can safely ignore this email.
|
|
635
|
+
</p>
|
|
636
|
+
`),
|
|
637
|
+
defaults: {
|
|
638
|
+
appName: EMAIL_CONSTANTS.APP_NAME,
|
|
639
|
+
role: "Editor"
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
// src/templates/invitation/invite-accepted.ts
|
|
644
|
+
var inviteAcceptedTemplate = {
|
|
645
|
+
name: "invite-accepted",
|
|
646
|
+
subject: "{{inviteeName}} accepted your invitation to {{resourceName}}",
|
|
647
|
+
html: wrapInLayout(`
|
|
648
|
+
<h1 class="email-h1">Invitation Accepted</h1>
|
|
649
|
+
<p class="email-text">
|
|
650
|
+
<strong>{{inviteeName}}</strong> ({{inviteeEmail}}) has accepted your
|
|
651
|
+
invitation to join <strong>{{resourceName}}</strong>.
|
|
652
|
+
</p>
|
|
653
|
+
<p class="email-text">
|
|
654
|
+
They now have access to the {{resourceType}} and can start collaborating.
|
|
655
|
+
</p>
|
|
656
|
+
<a href="{{dashboardUrl}}" class="email-btn-outline">View Team</a>
|
|
657
|
+
`),
|
|
658
|
+
defaults: {
|
|
659
|
+
appName: EMAIL_CONSTANTS.APP_NAME,
|
|
660
|
+
resourceType: "workspace"
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
// src/templates/billing/plan-upgraded.ts
|
|
665
|
+
var planUpgradedTemplate = {
|
|
666
|
+
name: "plan-upgraded",
|
|
667
|
+
subject: "{{workspaceName}} has been upgraded to {{planName}}",
|
|
668
|
+
html: wrapInLayout(`
|
|
669
|
+
<h1 class="email-h1">Plan Upgraded!</h1>
|
|
670
|
+
<p class="email-text">
|
|
671
|
+
Your workspace <strong>{{workspaceName}}</strong> is now on the
|
|
672
|
+
<span class="email-badge">{{planName}}</span> plan.
|
|
673
|
+
</p>
|
|
674
|
+
<table width="100%" cellpadding="0" cellspacing="0">
|
|
675
|
+
<tr>
|
|
676
|
+
<td class="email-detail-label">Plan</td>
|
|
677
|
+
<td class="email-detail-value" align="right">{{planName}}</td>
|
|
678
|
+
</tr>
|
|
679
|
+
<tr>
|
|
680
|
+
<td class="email-detail-label">Billing</td>
|
|
681
|
+
<td class="email-detail-value" align="right">{{billingInterval}}</td>
|
|
682
|
+
</tr>
|
|
683
|
+
<tr>
|
|
684
|
+
<td class="email-detail-label">Seats</td>
|
|
685
|
+
<td class="email-detail-value" align="right">{{seats}}</td>
|
|
686
|
+
</tr>
|
|
687
|
+
<tr>
|
|
688
|
+
<td class="email-detail-label">Monthly included usage</td>
|
|
689
|
+
<td class="email-detail-value" align="right">{{includedUsdTotal}}</td>
|
|
690
|
+
</tr>
|
|
691
|
+
</table>
|
|
692
|
+
<p class="email-text" style="margin-top:18px;">
|
|
693
|
+
Every AI request is billed at the provider's raw cost plus a flat 20% markup \u2014
|
|
694
|
+
no credits, no unit conversions. You can add or remove seats anytime from
|
|
695
|
+
Settings → Billing; we prorate the change automatically.
|
|
696
|
+
</p>
|
|
697
|
+
<br>
|
|
698
|
+
<a href="{{dashboardUrl}}" class="email-btn" style="color:#ffffff;text-decoration:none;">Go to Dashboard</a>
|
|
699
|
+
<hr class="email-divider">
|
|
700
|
+
<p class="email-muted">
|
|
701
|
+
Manage your subscription anytime from Settings → Billing.
|
|
702
|
+
</p>
|
|
703
|
+
`),
|
|
704
|
+
defaults: {
|
|
705
|
+
appName: EMAIL_CONSTANTS.APP_NAME,
|
|
706
|
+
billingInterval: "Monthly",
|
|
707
|
+
seats: "1"
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
// src/templates/billing/payment-receipt.ts
|
|
712
|
+
var paymentReceiptTemplate = {
|
|
713
|
+
name: "payment-receipt",
|
|
714
|
+
subject: "Payment receipt for {{workspaceName}}",
|
|
715
|
+
html: wrapInLayout(`
|
|
716
|
+
<h1 class="email-h1">Payment Received</h1>
|
|
717
|
+
<p class="email-text">
|
|
718
|
+
Thank you! Here's your receipt for <strong>{{workspaceName}}</strong>.
|
|
719
|
+
</p>
|
|
720
|
+
<table width="100%" cellpadding="0" cellspacing="0">
|
|
721
|
+
<tr>
|
|
722
|
+
<td class="email-detail-label">Plan</td>
|
|
723
|
+
<td class="email-detail-value" align="right">{{planName}}</td>
|
|
724
|
+
</tr>
|
|
725
|
+
<tr>
|
|
726
|
+
<td class="email-detail-label">Amount</td>
|
|
727
|
+
<td class="email-detail-value" align="right">{{currency}}{{amount}}</td>
|
|
728
|
+
</tr>
|
|
729
|
+
<tr>
|
|
730
|
+
<td class="email-detail-label">Date</td>
|
|
731
|
+
<td class="email-detail-value" align="right">{{invoiceDate}}</td>
|
|
732
|
+
</tr>
|
|
733
|
+
</table>
|
|
734
|
+
<br>
|
|
735
|
+
<a href="{{invoiceUrl}}" class="email-btn-outline">View Invoice</a>
|
|
736
|
+
<hr class="email-divider">
|
|
737
|
+
<p class="email-muted">
|
|
738
|
+
If you have billing questions, reply to this email or contact support.
|
|
739
|
+
</p>
|
|
740
|
+
`),
|
|
741
|
+
defaults: {
|
|
742
|
+
appName: EMAIL_CONSTANTS.APP_NAME,
|
|
743
|
+
currency: "$"
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
// src/templates/billing/payment-failed.ts
|
|
748
|
+
var paymentFailedTemplate = {
|
|
749
|
+
name: "payment-failed",
|
|
750
|
+
subject: "Payment failed for {{workspaceName}}",
|
|
751
|
+
html: wrapInLayout(`
|
|
752
|
+
<h1 class="email-h1">Payment Failed</h1>
|
|
753
|
+
<p class="email-text">
|
|
754
|
+
We were unable to process the payment of <strong>{{currency}}{{amount}}</strong>
|
|
755
|
+
for <strong>{{workspaceName}}</strong> ({{planName}} plan).
|
|
756
|
+
</p>
|
|
757
|
+
<p class="email-text">
|
|
758
|
+
Please update your payment method to keep your plan active and avoid
|
|
759
|
+
any interruption to your workspace.
|
|
760
|
+
</p>
|
|
761
|
+
<a href="{{retryUrl}}" class="email-btn-danger" style="color:#ffffff;text-decoration:none;">Update Payment Method</a>
|
|
762
|
+
<hr class="email-divider">
|
|
763
|
+
<p class="email-muted">
|
|
764
|
+
If you believe this is a mistake, please reply to this email or
|
|
765
|
+
contact support.
|
|
766
|
+
</p>
|
|
767
|
+
`),
|
|
768
|
+
defaults: {
|
|
769
|
+
appName: EMAIL_CONSTANTS.APP_NAME,
|
|
770
|
+
currency: "$"
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
// src/templates/workspace/member-joined.ts
|
|
775
|
+
var memberJoinedTemplate = {
|
|
776
|
+
name: "member-joined",
|
|
777
|
+
subject: "{{memberName}} joined {{workspaceName}}",
|
|
778
|
+
html: wrapInLayout(`
|
|
779
|
+
<h1 class="email-h1">New Team Member</h1>
|
|
780
|
+
<p class="email-text">
|
|
781
|
+
<strong>{{memberName}}</strong> ({{memberEmail}}) has joined
|
|
782
|
+
<strong>{{workspaceName}}</strong> as
|
|
783
|
+
<span class="email-badge">{{role}}</span>.
|
|
784
|
+
</p>
|
|
785
|
+
<a href="{{dashboardUrl}}" class="email-btn-outline">View Team</a>
|
|
786
|
+
`),
|
|
787
|
+
defaults: {
|
|
788
|
+
appName: EMAIL_CONSTANTS.APP_NAME,
|
|
789
|
+
role: "Editor"
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
// src/templates/workspace/member-removed.ts
|
|
794
|
+
var memberRemovedTemplate = {
|
|
795
|
+
name: "member-removed",
|
|
796
|
+
subject: "You have been removed from {{workspaceName}}",
|
|
797
|
+
html: wrapInLayout(`
|
|
798
|
+
<h1 class="email-h1">Workspace Access Removed</h1>
|
|
799
|
+
<p class="email-text">
|
|
800
|
+
You no longer have access to the <strong>{{workspaceName}}</strong>
|
|
801
|
+
workspace. All associated projects and data in that workspace are no
|
|
802
|
+
longer accessible from your account.
|
|
803
|
+
</p>
|
|
804
|
+
<hr class="email-divider">
|
|
805
|
+
<p class="email-muted">
|
|
806
|
+
If you believe this was a mistake, please contact the workspace owner
|
|
807
|
+
or reply to this email.
|
|
808
|
+
</p>
|
|
809
|
+
`),
|
|
810
|
+
defaults: {
|
|
811
|
+
appName: EMAIL_CONSTANTS.APP_NAME
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
// src/templates/workspace/account-deleted.ts
|
|
816
|
+
var accountDeletedTemplate = {
|
|
817
|
+
name: "account-deleted",
|
|
818
|
+
subject: "Your {{appName}} account has been deleted",
|
|
819
|
+
html: wrapInLayout(`
|
|
820
|
+
<h1 class="email-h1">Account Deleted</h1>
|
|
821
|
+
<p class="email-text">
|
|
822
|
+
Your {{appName}} account associated with <strong>{{email}}</strong>
|
|
823
|
+
has been permanently deleted. All your data, workspaces, and projects
|
|
824
|
+
have been removed.
|
|
825
|
+
</p>
|
|
826
|
+
<p class="email-text">
|
|
827
|
+
If you ever want to come back, you can create a new account at any
|
|
828
|
+
time.
|
|
829
|
+
</p>
|
|
830
|
+
<hr class="email-divider">
|
|
831
|
+
<p class="email-muted">
|
|
832
|
+
If you didn't request this deletion, please contact us immediately
|
|
833
|
+
by replying to this email.
|
|
834
|
+
</p>
|
|
835
|
+
`),
|
|
836
|
+
defaults: {
|
|
837
|
+
appName: EMAIL_CONSTANTS.APP_NAME
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
// src/templates/index.ts
|
|
842
|
+
var allTemplates = [
|
|
843
|
+
welcomeTemplate2,
|
|
844
|
+
passwordResetTemplate2,
|
|
845
|
+
emailVerificationTemplate,
|
|
846
|
+
workspaceInviteTemplate,
|
|
847
|
+
projectInviteTemplate,
|
|
848
|
+
inviteAcceptedTemplate,
|
|
849
|
+
planUpgradedTemplate,
|
|
850
|
+
paymentReceiptTemplate,
|
|
851
|
+
paymentFailedTemplate,
|
|
852
|
+
memberJoinedTemplate,
|
|
853
|
+
memberRemovedTemplate,
|
|
854
|
+
accountDeletedTemplate
|
|
855
|
+
];
|
|
856
|
+
function createShogoTemplateRegistry() {
|
|
857
|
+
const registry = new EmailTemplateRegistry();
|
|
858
|
+
for (const template of allTemplates) {
|
|
859
|
+
registry.register(template);
|
|
860
|
+
}
|
|
861
|
+
return registry;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
export {
|
|
865
|
+
formatEmailAddress,
|
|
866
|
+
EmailError,
|
|
867
|
+
interpolate,
|
|
868
|
+
htmlToText,
|
|
869
|
+
EmailTemplateRegistry,
|
|
870
|
+
welcomeTemplate,
|
|
871
|
+
passwordResetTemplate,
|
|
872
|
+
invitationTemplate,
|
|
873
|
+
notificationTemplate,
|
|
874
|
+
builtinTemplates,
|
|
875
|
+
createTemplateRegistry,
|
|
876
|
+
EMAIL_CONSTANTS,
|
|
877
|
+
DARK_STYLE_OVERRIDES,
|
|
878
|
+
wrapInLayout,
|
|
879
|
+
allTemplates,
|
|
880
|
+
createShogoTemplateRegistry
|
|
881
|
+
};
|
|
882
|
+
//# sourceMappingURL=chunk-VPWVZHAC.js.map
|