payload-plugin-newsletter 0.8.7 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/CLAUDE.md +8 -2
- package/README.md +86 -0
- package/dist/client.d.cts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/components.cjs +777 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +28 -2
- package/dist/components.d.ts +28 -2
- package/dist/components.js +764 -0
- package/dist/components.js.map +1 -1
- package/dist/fields.cjs +80 -0
- package/dist/fields.cjs.map +1 -0
- package/dist/fields.d.cts +13 -0
- package/dist/fields.d.ts +13 -0
- package/dist/fields.js +65 -0
- package/dist/fields.js.map +1 -0
- package/dist/index.cjs +2228 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2246 -17
- package/dist/index.js.map +1 -1
- package/dist/types.cjs +271 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +620 -1
- package/dist/types.d.ts +620 -1
- package/dist/types.js +255 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.cjs +337 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.cts +43 -0
- package/dist/utils.d.ts +43 -0
- package/dist/utils.js +298 -0
- package/dist/utils.js.map +1 -0
- package/package.json +21 -1
package/dist/types.js
CHANGED
|
@@ -1 +1,256 @@
|
|
|
1
|
+
// src/types/newsletter.ts
|
|
2
|
+
var NewsletterStatus = /* @__PURE__ */ ((NewsletterStatus2) => {
|
|
3
|
+
NewsletterStatus2["DRAFT"] = "draft";
|
|
4
|
+
NewsletterStatus2["SCHEDULED"] = "scheduled";
|
|
5
|
+
NewsletterStatus2["SENDING"] = "sending";
|
|
6
|
+
NewsletterStatus2["SENT"] = "sent";
|
|
7
|
+
NewsletterStatus2["FAILED"] = "failed";
|
|
8
|
+
NewsletterStatus2["PAUSED"] = "paused";
|
|
9
|
+
NewsletterStatus2["CANCELED"] = "canceled";
|
|
10
|
+
return NewsletterStatus2;
|
|
11
|
+
})(NewsletterStatus || {});
|
|
12
|
+
var NewsletterProviderError = class extends Error {
|
|
13
|
+
constructor(message, code, provider, details) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.provider = provider;
|
|
17
|
+
this.details = details;
|
|
18
|
+
this.name = "NewsletterProviderError";
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var NewsletterErrorCode = /* @__PURE__ */ ((NewsletterErrorCode2) => {
|
|
22
|
+
NewsletterErrorCode2["NOT_SUPPORTED"] = "NOT_SUPPORTED";
|
|
23
|
+
NewsletterErrorCode2["INVALID_STATUS"] = "INVALID_STATUS";
|
|
24
|
+
NewsletterErrorCode2["PROVIDER_ERROR"] = "PROVIDER_ERROR";
|
|
25
|
+
NewsletterErrorCode2["VALIDATION_ERROR"] = "VALIDATION_ERROR";
|
|
26
|
+
NewsletterErrorCode2["NOT_FOUND"] = "NOT_FOUND";
|
|
27
|
+
NewsletterErrorCode2["PERMISSION_DENIED"] = "PERMISSION_DENIED";
|
|
28
|
+
NewsletterErrorCode2["RATE_LIMITED"] = "RATE_LIMITED";
|
|
29
|
+
NewsletterErrorCode2["CONFIGURATION_ERROR"] = "CONFIGURATION_ERROR";
|
|
30
|
+
return NewsletterErrorCode2;
|
|
31
|
+
})(NewsletterErrorCode || {});
|
|
32
|
+
|
|
33
|
+
// src/types/broadcast.ts
|
|
34
|
+
var BroadcastStatus = /* @__PURE__ */ ((BroadcastStatus2) => {
|
|
35
|
+
BroadcastStatus2["DRAFT"] = "draft";
|
|
36
|
+
BroadcastStatus2["SCHEDULED"] = "scheduled";
|
|
37
|
+
BroadcastStatus2["SENDING"] = "sending";
|
|
38
|
+
BroadcastStatus2["SENT"] = "sent";
|
|
39
|
+
BroadcastStatus2["FAILED"] = "failed";
|
|
40
|
+
BroadcastStatus2["PAUSED"] = "paused";
|
|
41
|
+
BroadcastStatus2["CANCELED"] = "canceled";
|
|
42
|
+
return BroadcastStatus2;
|
|
43
|
+
})(BroadcastStatus || {});
|
|
44
|
+
var BroadcastProviderError = class extends Error {
|
|
45
|
+
constructor(message, code, provider, details) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.code = code;
|
|
48
|
+
this.provider = provider;
|
|
49
|
+
this.details = details;
|
|
50
|
+
this.name = "BroadcastProviderError";
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var BroadcastErrorCode = /* @__PURE__ */ ((BroadcastErrorCode2) => {
|
|
54
|
+
BroadcastErrorCode2["NOT_SUPPORTED"] = "NOT_SUPPORTED";
|
|
55
|
+
BroadcastErrorCode2["INVALID_STATUS"] = "INVALID_STATUS";
|
|
56
|
+
BroadcastErrorCode2["PROVIDER_ERROR"] = "PROVIDER_ERROR";
|
|
57
|
+
BroadcastErrorCode2["VALIDATION_ERROR"] = "VALIDATION_ERROR";
|
|
58
|
+
BroadcastErrorCode2["NOT_FOUND"] = "NOT_FOUND";
|
|
59
|
+
BroadcastErrorCode2["PERMISSION_DENIED"] = "PERMISSION_DENIED";
|
|
60
|
+
BroadcastErrorCode2["RATE_LIMITED"] = "RATE_LIMITED";
|
|
61
|
+
BroadcastErrorCode2["CONFIGURATION_ERROR"] = "CONFIGURATION_ERROR";
|
|
62
|
+
BroadcastErrorCode2["CHANNEL_NOT_FOUND"] = "CHANNEL_NOT_FOUND";
|
|
63
|
+
BroadcastErrorCode2["INVALID_CHANNEL"] = "INVALID_CHANNEL";
|
|
64
|
+
return BroadcastErrorCode2;
|
|
65
|
+
})(BroadcastErrorCode || {});
|
|
66
|
+
|
|
67
|
+
// src/types/providers.ts
|
|
68
|
+
var BaseBroadcastProvider = class {
|
|
69
|
+
constructor(config) {
|
|
70
|
+
this.config = config;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Schedule a broadcast - default implementation throws not supported
|
|
74
|
+
*/
|
|
75
|
+
async schedule(id, scheduledAt) {
|
|
76
|
+
const capabilities = this.getCapabilities();
|
|
77
|
+
if (!capabilities.supportsScheduling) {
|
|
78
|
+
throw new BroadcastProviderError(
|
|
79
|
+
"Scheduling is not supported by this provider",
|
|
80
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
81
|
+
this.name
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
throw new Error("Method not implemented");
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Cancel scheduled broadcast - default implementation throws not supported
|
|
88
|
+
*/
|
|
89
|
+
async cancelSchedule(id) {
|
|
90
|
+
const capabilities = this.getCapabilities();
|
|
91
|
+
if (!capabilities.supportsScheduling) {
|
|
92
|
+
throw new BroadcastProviderError(
|
|
93
|
+
"Scheduling is not supported by this provider",
|
|
94
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
95
|
+
this.name
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
throw new Error("Method not implemented");
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get analytics - default implementation returns zeros
|
|
102
|
+
*/
|
|
103
|
+
async getAnalytics(id) {
|
|
104
|
+
const capabilities = this.getCapabilities();
|
|
105
|
+
if (!capabilities.supportsAnalytics) {
|
|
106
|
+
throw new BroadcastProviderError(
|
|
107
|
+
"Analytics are not supported by this provider",
|
|
108
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
109
|
+
this.name
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
sent: 0,
|
|
114
|
+
delivered: 0,
|
|
115
|
+
opened: 0,
|
|
116
|
+
clicked: 0,
|
|
117
|
+
bounced: 0,
|
|
118
|
+
complained: 0,
|
|
119
|
+
unsubscribed: 0
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Helper method to validate required fields
|
|
124
|
+
*/
|
|
125
|
+
validateRequiredFields(data, fields) {
|
|
126
|
+
const missing = fields.filter((field) => !data[field]);
|
|
127
|
+
if (missing.length > 0) {
|
|
128
|
+
throw new BroadcastProviderError(
|
|
129
|
+
`Missing required fields: ${missing.join(", ")}`,
|
|
130
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
131
|
+
this.name
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Helper method to check if a status transition is allowed
|
|
137
|
+
*/
|
|
138
|
+
canEditInStatus(status) {
|
|
139
|
+
const capabilities = this.getCapabilities();
|
|
140
|
+
return capabilities.editableStatuses.includes(status);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Helper to build pagination response
|
|
144
|
+
*/
|
|
145
|
+
buildListResponse(items, total, options = {}) {
|
|
146
|
+
const limit = options.limit || 20;
|
|
147
|
+
const offset = options.offset || 0;
|
|
148
|
+
return {
|
|
149
|
+
items,
|
|
150
|
+
total,
|
|
151
|
+
limit,
|
|
152
|
+
offset,
|
|
153
|
+
hasMore: offset + items.length < total
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
var BaseNewsletterProvider = class {
|
|
158
|
+
constructor(config) {
|
|
159
|
+
this.config = config;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Schedule a newsletter - default implementation throws not supported
|
|
163
|
+
*/
|
|
164
|
+
async schedule(id, scheduledAt) {
|
|
165
|
+
const capabilities = this.getCapabilities();
|
|
166
|
+
if (!capabilities.supportsScheduling) {
|
|
167
|
+
throw new NewsletterProviderError(
|
|
168
|
+
"Scheduling is not supported by this provider",
|
|
169
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
170
|
+
this.name
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
throw new Error("Method not implemented");
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Cancel scheduled newsletter - default implementation throws not supported
|
|
177
|
+
*/
|
|
178
|
+
async cancelSchedule(id) {
|
|
179
|
+
const capabilities = this.getCapabilities();
|
|
180
|
+
if (!capabilities.supportsScheduling) {
|
|
181
|
+
throw new NewsletterProviderError(
|
|
182
|
+
"Scheduling is not supported by this provider",
|
|
183
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
184
|
+
this.name
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
throw new Error("Method not implemented");
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get analytics - default implementation returns zeros
|
|
191
|
+
*/
|
|
192
|
+
async getAnalytics(id) {
|
|
193
|
+
const capabilities = this.getCapabilities();
|
|
194
|
+
if (!capabilities.supportsAnalytics) {
|
|
195
|
+
throw new NewsletterProviderError(
|
|
196
|
+
"Analytics are not supported by this provider",
|
|
197
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
198
|
+
this.name
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
sent: 0,
|
|
203
|
+
delivered: 0,
|
|
204
|
+
opened: 0,
|
|
205
|
+
clicked: 0,
|
|
206
|
+
bounced: 0,
|
|
207
|
+
complained: 0,
|
|
208
|
+
unsubscribed: 0
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Helper method to validate required fields
|
|
213
|
+
*/
|
|
214
|
+
validateRequiredFields(data, fields) {
|
|
215
|
+
const missing = fields.filter((field) => !data[field]);
|
|
216
|
+
if (missing.length > 0) {
|
|
217
|
+
throw new NewsletterProviderError(
|
|
218
|
+
`Missing required fields: ${missing.join(", ")}`,
|
|
219
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
220
|
+
this.name
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Helper method to check if a status transition is allowed
|
|
226
|
+
*/
|
|
227
|
+
canEditInStatus(status) {
|
|
228
|
+
const capabilities = this.getCapabilities();
|
|
229
|
+
return capabilities.editableStatuses.includes(status);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Helper to build pagination response
|
|
233
|
+
*/
|
|
234
|
+
buildListResponse(items, total, options = {}) {
|
|
235
|
+
const limit = options.limit || 20;
|
|
236
|
+
const offset = options.offset || 0;
|
|
237
|
+
return {
|
|
238
|
+
items,
|
|
239
|
+
total,
|
|
240
|
+
limit,
|
|
241
|
+
offset,
|
|
242
|
+
hasMore: offset + items.length < total
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
export {
|
|
247
|
+
BaseBroadcastProvider,
|
|
248
|
+
BaseNewsletterProvider,
|
|
249
|
+
BroadcastErrorCode,
|
|
250
|
+
BroadcastProviderError,
|
|
251
|
+
BroadcastStatus,
|
|
252
|
+
NewsletterErrorCode,
|
|
253
|
+
NewsletterProviderError,
|
|
254
|
+
NewsletterStatus
|
|
255
|
+
};
|
|
1
256
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/types/newsletter.ts","../src/types/broadcast.ts","../src/types/providers.ts"],"sourcesContent":["/**\n * Core types for newsletter management functionality\n */\n\n/**\n * Represents a newsletter/broadcast in the system\n */\nexport interface Newsletter {\n id: string;\n name: string;\n subject: string;\n preheader?: string;\n content: string; // HTML content\n status: NewsletterStatus;\n trackOpens: boolean;\n trackClicks: boolean;\n replyTo?: string;\n recipientCount?: number;\n sentAt?: Date;\n scheduledAt?: Date;\n createdAt: Date;\n updatedAt: Date;\n // Provider-specific data stored here\n providerData?: Record<string, any>;\n // Provider information\n providerId?: string;\n providerType?: 'broadcast' | 'resend';\n}\n\n/**\n * Possible statuses for a newsletter\n */\nexport enum NewsletterStatus {\n DRAFT = 'draft',\n SCHEDULED = 'scheduled',\n SENDING = 'sending',\n SENT = 'sent',\n FAILED = 'failed',\n PAUSED = 'paused',\n CANCELED = 'canceled'\n}\n\n/**\n * Options for listing newsletters\n */\nexport interface ListNewsletterOptions {\n limit?: number;\n offset?: number;\n status?: NewsletterStatus;\n sortBy?: 'createdAt' | 'updatedAt' | 'sentAt' | 'name';\n sortOrder?: 'asc' | 'desc';\n}\n\n/**\n * Response from listing newsletters\n */\nexport interface ListNewsletterResponse<T = Newsletter> {\n items: T[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n/**\n * Input for creating a new newsletter\n */\nexport interface CreateNewsletterInput {\n name: string;\n subject: string;\n preheader?: string;\n content: string;\n trackOpens?: boolean;\n trackClicks?: boolean;\n replyTo?: string;\n audienceIds?: string[]; // Maps to segments/audiences\n}\n\n/**\n * Input for updating an existing newsletter\n */\nexport interface UpdateNewsletterInput {\n name?: string;\n subject?: string;\n preheader?: string;\n content?: string;\n trackOpens?: boolean;\n trackClicks?: boolean;\n replyTo?: string;\n audienceIds?: string[];\n}\n\n/**\n * Options for sending a newsletter\n */\nexport interface SendNewsletterOptions {\n audienceIds?: string[]; // Target specific audiences\n testMode?: boolean; // Send test email\n testRecipients?: string[]; // Email addresses for test send\n}\n\n/**\n * Analytics data for a newsletter\n */\nexport interface NewsletterAnalytics {\n sent: number;\n delivered: number;\n opened: number;\n clicked: number;\n bounced: number;\n complained: number;\n unsubscribed: number;\n deliveryRate?: number;\n openRate?: number;\n clickRate?: number;\n bounceRate?: number;\n}\n\n/**\n * Capabilities that a newsletter provider supports\n */\nexport interface NewsletterProviderCapabilities {\n supportsScheduling: boolean;\n supportsSegmentation: boolean;\n supportsAnalytics: boolean;\n supportsABTesting: boolean;\n supportsTemplates: boolean;\n supportsPersonalization: boolean;\n maxRecipientsPerSend?: number;\n editableStatuses: NewsletterStatus[];\n supportedContentTypes: ('html' | 'text' | 'react')[];\n}\n\n/**\n * Error types specific to newsletter operations\n */\nexport class NewsletterProviderError extends Error {\n constructor(\n message: string,\n public code: NewsletterErrorCode,\n public provider: string,\n public details?: any\n ) {\n super(message);\n this.name = 'NewsletterProviderError';\n }\n}\n\nexport enum NewsletterErrorCode {\n NOT_SUPPORTED = 'NOT_SUPPORTED',\n INVALID_STATUS = 'INVALID_STATUS',\n PROVIDER_ERROR = 'PROVIDER_ERROR',\n VALIDATION_ERROR = 'VALIDATION_ERROR',\n NOT_FOUND = 'NOT_FOUND',\n PERMISSION_DENIED = 'PERMISSION_DENIED',\n RATE_LIMITED = 'RATE_LIMITED',\n CONFIGURATION_ERROR = 'CONFIGURATION_ERROR'\n}\n\n/**\n * Newsletter template for reusable content\n */\nexport interface NewsletterTemplate {\n id: string;\n name: string;\n description?: string;\n content: string;\n variables?: NewsletterTemplateVariable[];\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface NewsletterTemplateVariable {\n name: string;\n type: 'text' | 'html' | 'image' | 'url';\n defaultValue?: string;\n required?: boolean;\n}","/**\n * Core types for broadcast management functionality\n */\n\n/**\n * Represents a broadcast (individual email campaign) in the system\n */\nexport interface Broadcast {\n id: string;\n channelId: string; // Reference to the channel this broadcast belongs to\n name: string;\n subject: string;\n preheader?: string;\n content: string; // HTML content\n status: BroadcastStatus;\n trackOpens: boolean;\n trackClicks: boolean;\n replyTo?: string;\n recipientCount?: number;\n sentAt?: Date;\n scheduledAt?: Date;\n createdAt: Date;\n updatedAt: Date;\n // Provider-specific data stored here\n providerData?: Record<string, any>;\n // Provider information\n providerId?: string;\n providerType?: 'broadcast' | 'resend';\n}\n\n/**\n * Possible statuses for a broadcast\n */\nexport enum BroadcastStatus {\n DRAFT = 'draft',\n SCHEDULED = 'scheduled',\n SENDING = 'sending',\n SENT = 'sent',\n FAILED = 'failed',\n PAUSED = 'paused',\n CANCELED = 'canceled'\n}\n\n/**\n * Options for listing broadcasts\n */\nexport interface ListBroadcastOptions {\n limit?: number;\n offset?: number;\n status?: BroadcastStatus;\n channelId?: string; // Filter by channel\n sortBy?: 'createdAt' | 'updatedAt' | 'sentAt' | 'name';\n sortOrder?: 'asc' | 'desc';\n}\n\n/**\n * Response from listing broadcasts\n */\nexport interface ListBroadcastResponse<T = Broadcast> {\n items: T[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n/**\n * Input for creating a new broadcast\n */\nexport interface CreateBroadcastInput {\n channelId: string; // Which channel to send to\n name: string;\n subject: string;\n preheader?: string;\n content: string;\n trackOpens?: boolean;\n trackClicks?: boolean;\n replyTo?: string;\n audienceIds?: string[]; // Maps to segments/audiences within the channel\n}\n\n/**\n * Input for updating an existing broadcast\n */\nexport interface UpdateBroadcastInput {\n name?: string;\n subject?: string;\n preheader?: string;\n content?: string;\n trackOpens?: boolean;\n trackClicks?: boolean;\n replyTo?: string;\n audienceIds?: string[];\n}\n\n/**\n * Options for sending a broadcast\n */\nexport interface SendBroadcastOptions {\n audienceIds?: string[]; // Target specific audiences within the channel\n testMode?: boolean; // Send test email\n testRecipients?: string[]; // Email addresses for test send\n}\n\n/**\n * Analytics data for a broadcast\n */\nexport interface BroadcastAnalytics {\n sent: number;\n delivered: number;\n opened: number;\n clicked: number;\n bounced: number;\n complained: number;\n unsubscribed: number;\n deliveryRate?: number;\n openRate?: number;\n clickRate?: number;\n bounceRate?: number;\n}\n\n/**\n * Capabilities that a broadcast provider supports\n */\nexport interface BroadcastProviderCapabilities {\n supportsScheduling: boolean;\n supportsSegmentation: boolean;\n supportsAnalytics: boolean;\n supportsABTesting: boolean;\n supportsTemplates: boolean;\n supportsPersonalization: boolean;\n maxRecipientsPerSend?: number;\n editableStatuses: BroadcastStatus[];\n supportedContentTypes: ('html' | 'text' | 'react')[];\n // Channel-specific capabilities\n supportsMultipleChannels: boolean;\n supportsChannelSegmentation: boolean;\n}\n\n/**\n * Error types specific to broadcast operations\n */\nexport class BroadcastProviderError extends Error {\n constructor(\n message: string,\n public code: BroadcastErrorCode,\n public provider: string,\n public details?: any\n ) {\n super(message);\n this.name = 'BroadcastProviderError';\n }\n}\n\nexport enum BroadcastErrorCode {\n NOT_SUPPORTED = 'NOT_SUPPORTED',\n INVALID_STATUS = 'INVALID_STATUS',\n PROVIDER_ERROR = 'PROVIDER_ERROR',\n VALIDATION_ERROR = 'VALIDATION_ERROR',\n NOT_FOUND = 'NOT_FOUND',\n PERMISSION_DENIED = 'PERMISSION_DENIED',\n RATE_LIMITED = 'RATE_LIMITED',\n CONFIGURATION_ERROR = 'CONFIGURATION_ERROR',\n CHANNEL_NOT_FOUND = 'CHANNEL_NOT_FOUND',\n INVALID_CHANNEL = 'INVALID_CHANNEL'\n}\n\n/**\n * Broadcast template for reusable content\n */\nexport interface BroadcastTemplate {\n id: string;\n channelId?: string; // Optional channel-specific template\n name: string;\n description?: string;\n content: string;\n variables?: BroadcastTemplateVariable[];\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface BroadcastTemplateVariable {\n name: string;\n type: 'text' | 'html' | 'image' | 'url';\n defaultValue?: string;\n required?: boolean;\n}\n\n// Re-export newsletter types with deprecation notice for backwards compatibility\nexport {\n NewsletterStatus,\n type ListNewsletterOptions,\n type ListNewsletterResponse,\n type CreateNewsletterInput,\n type UpdateNewsletterInput,\n type SendNewsletterOptions,\n type NewsletterAnalytics,\n type NewsletterProviderCapabilities,\n NewsletterProviderError,\n NewsletterErrorCode,\n type NewsletterTemplate,\n type NewsletterTemplateVariable\n} from './newsletter';","/**\n * Provider interfaces for broadcast management\n */\n\n// Import broadcast types\nimport type {\n Broadcast,\n BroadcastStatus,\n ListBroadcastOptions,\n ListBroadcastResponse,\n CreateBroadcastInput,\n UpdateBroadcastInput,\n SendBroadcastOptions,\n BroadcastAnalytics,\n BroadcastProviderCapabilities\n} from './broadcast'\n\nimport {\n BroadcastProviderError,\n BroadcastErrorCode\n} from './broadcast'\n\n// Import channel types\nimport type {\n Channel,\n CreateChannelInput,\n UpdateChannelInput,\n ListChannelsOptions,\n ListChannelsResponse\n} from './channel'\n\n// Import legacy newsletter types for backwards compatibility\nimport type {\n Newsletter,\n NewsletterStatus,\n ListNewsletterOptions,\n ListNewsletterResponse,\n CreateNewsletterInput,\n UpdateNewsletterInput,\n SendNewsletterOptions,\n NewsletterAnalytics,\n NewsletterProviderCapabilities\n} from './newsletter'\n\nimport {\n NewsletterProviderError,\n NewsletterErrorCode\n} from './newsletter'\n\n/**\n * Main interface for broadcast providers\n */\nexport interface BroadcastProvider {\n /**\n * Get the provider name\n */\n readonly name: string;\n\n // Channel management methods\n /**\n * List channels (newsletter types/publications)\n */\n listChannels(options?: ListChannelsOptions): Promise<ListChannelsResponse>;\n \n /**\n * Get a specific channel by ID\n */\n getChannel(id: string): Promise<Channel>;\n \n /**\n * Create a new channel\n */\n createChannel(data: CreateChannelInput): Promise<Channel>;\n \n /**\n * Update an existing channel\n */\n updateChannel(id: string, data: UpdateChannelInput): Promise<Channel>;\n \n /**\n * Delete a channel\n */\n deleteChannel(id: string): Promise<void>;\n\n // Broadcast management methods\n /**\n * List broadcasts with pagination\n */\n list(options?: ListBroadcastOptions): Promise<ListBroadcastResponse<Broadcast>>;\n \n /**\n * Get a specific broadcast by ID\n */\n get(id: string): Promise<Broadcast>;\n \n /**\n * Create a new broadcast\n */\n create(data: CreateBroadcastInput): Promise<Broadcast>;\n \n /**\n * Update an existing broadcast\n */\n update(id: string, data: UpdateBroadcastInput): Promise<Broadcast>;\n \n /**\n * Delete a broadcast\n */\n delete(id: string): Promise<void>;\n \n /**\n * Send a broadcast immediately or to test recipients\n */\n send(id: string, options?: SendBroadcastOptions): Promise<Broadcast>;\n \n /**\n * Schedule a broadcast for future sending\n */\n schedule(id: string, scheduledAt: Date): Promise<Broadcast>;\n \n /**\n * Cancel a scheduled broadcast\n */\n cancelSchedule(id: string): Promise<Broadcast>;\n \n /**\n * Get analytics for a broadcast\n */\n getAnalytics(id: string): Promise<BroadcastAnalytics>;\n \n /**\n * Get provider capabilities\n */\n getCapabilities(): BroadcastProviderCapabilities;\n \n /**\n * Validate that the provider is properly configured\n */\n validateConfiguration(): Promise<boolean>;\n}\n\n/**\n * Legacy newsletter provider interface for backwards compatibility\n * @deprecated Use BroadcastProvider instead\n */\nexport interface NewsletterProvider {\n /**\n * Get the provider name\n */\n readonly name: string;\n\n /**\n * List newsletters with pagination\n */\n list(options?: ListNewsletterOptions): Promise<ListNewsletterResponse<Newsletter>>;\n \n /**\n * Get a specific newsletter by ID\n */\n get(id: string): Promise<Newsletter>;\n \n /**\n * Create a new newsletter\n */\n create(data: CreateNewsletterInput): Promise<Newsletter>;\n \n /**\n * Update an existing newsletter\n */\n update(id: string, data: UpdateNewsletterInput): Promise<Newsletter>;\n \n /**\n * Delete a newsletter\n */\n delete(id: string): Promise<void>;\n \n /**\n * Send a newsletter immediately or to test recipients\n */\n send(id: string, options?: SendNewsletterOptions): Promise<Newsletter>;\n \n /**\n * Schedule a newsletter for future sending\n */\n schedule(id: string, scheduledAt: Date): Promise<Newsletter>;\n \n /**\n * Cancel a scheduled newsletter\n */\n cancelSchedule(id: string): Promise<Newsletter>;\n \n /**\n * Get analytics for a newsletter\n */\n getAnalytics(id: string): Promise<NewsletterAnalytics>;\n \n /**\n * Get provider capabilities\n */\n getCapabilities(): NewsletterProviderCapabilities;\n \n /**\n * Validate that the provider is properly configured\n */\n validateConfiguration(): Promise<boolean>;\n}\n\n/**\n * Base abstract class for broadcast providers\n */\nexport abstract class BaseBroadcastProvider implements BroadcastProvider {\n abstract readonly name: string;\n \n constructor(protected config: any) {}\n \n // Channel management - abstract methods\n abstract listChannels(options?: ListChannelsOptions): Promise<ListChannelsResponse>;\n abstract getChannel(id: string): Promise<Channel>;\n abstract createChannel(data: CreateChannelInput): Promise<Channel>;\n abstract updateChannel(id: string, data: UpdateChannelInput): Promise<Channel>;\n abstract deleteChannel(id: string): Promise<void>;\n \n // Broadcast management - abstract methods\n abstract list(options?: ListBroadcastOptions): Promise<ListBroadcastResponse<Broadcast>>;\n abstract get(id: string): Promise<Broadcast>;\n abstract create(data: CreateBroadcastInput): Promise<Broadcast>;\n abstract update(id: string, data: UpdateBroadcastInput): Promise<Broadcast>;\n abstract delete(id: string): Promise<void>;\n abstract send(id: string, options?: SendBroadcastOptions): Promise<Broadcast>;\n abstract getCapabilities(): BroadcastProviderCapabilities;\n abstract validateConfiguration(): Promise<boolean>;\n \n /**\n * Schedule a broadcast - default implementation throws not supported\n */\n async schedule(id: string, scheduledAt: Date): Promise<Broadcast> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsScheduling) {\n throw new BroadcastProviderError(\n 'Scheduling is not supported by this provider',\n BroadcastErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n throw new Error('Method not implemented');\n }\n \n /**\n * Cancel scheduled broadcast - default implementation throws not supported\n */\n async cancelSchedule(id: string): Promise<Broadcast> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsScheduling) {\n throw new BroadcastProviderError(\n 'Scheduling is not supported by this provider',\n BroadcastErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n throw new Error('Method not implemented');\n }\n \n /**\n * Get analytics - default implementation returns zeros\n */\n async getAnalytics(id: string): Promise<BroadcastAnalytics> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsAnalytics) {\n throw new BroadcastProviderError(\n 'Analytics are not supported by this provider',\n BroadcastErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n \n return {\n sent: 0,\n delivered: 0,\n opened: 0,\n clicked: 0,\n bounced: 0,\n complained: 0,\n unsubscribed: 0\n };\n }\n \n /**\n * Helper method to validate required fields\n */\n protected validateRequiredFields(data: any, fields: string[]): void {\n const missing = fields.filter(field => !data[field]);\n if (missing.length > 0) {\n throw new BroadcastProviderError(\n `Missing required fields: ${missing.join(', ')}`,\n BroadcastErrorCode.VALIDATION_ERROR,\n this.name\n );\n }\n }\n \n /**\n * Helper method to check if a status transition is allowed\n */\n protected canEditInStatus(status: BroadcastStatus): boolean {\n const capabilities = this.getCapabilities();\n return capabilities.editableStatuses.includes(status);\n }\n \n /**\n * Helper to build pagination response\n */\n protected buildListResponse<T>(\n items: T[],\n total: number,\n options: ListBroadcastOptions = {}\n ): ListBroadcastResponse<T> {\n const limit = options.limit || 20;\n const offset = options.offset || 0;\n \n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + items.length < total\n };\n }\n}\n\n/**\n * Base abstract class for newsletter providers\n * @deprecated Use BaseBroadcastProvider instead\n */\nexport abstract class BaseNewsletterProvider implements NewsletterProvider {\n abstract readonly name: string;\n \n constructor(protected config: any) {}\n \n abstract list(options?: ListNewsletterOptions): Promise<ListNewsletterResponse<Newsletter>>;\n abstract get(id: string): Promise<Newsletter>;\n abstract create(data: CreateNewsletterInput): Promise<Newsletter>;\n abstract update(id: string, data: UpdateNewsletterInput): Promise<Newsletter>;\n abstract delete(id: string): Promise<void>;\n abstract send(id: string, options?: SendNewsletterOptions): Promise<Newsletter>;\n abstract getCapabilities(): NewsletterProviderCapabilities;\n abstract validateConfiguration(): Promise<boolean>;\n \n /**\n * Schedule a newsletter - default implementation throws not supported\n */\n async schedule(id: string, scheduledAt: Date): Promise<Newsletter> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsScheduling) {\n throw new NewsletterProviderError(\n 'Scheduling is not supported by this provider',\n NewsletterErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n throw new Error('Method not implemented');\n }\n \n /**\n * Cancel scheduled newsletter - default implementation throws not supported\n */\n async cancelSchedule(id: string): Promise<Newsletter> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsScheduling) {\n throw new NewsletterProviderError(\n 'Scheduling is not supported by this provider',\n NewsletterErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n throw new Error('Method not implemented');\n }\n \n /**\n * Get analytics - default implementation returns zeros\n */\n async getAnalytics(id: string): Promise<NewsletterAnalytics> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsAnalytics) {\n throw new NewsletterProviderError(\n 'Analytics are not supported by this provider',\n NewsletterErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n \n return {\n sent: 0,\n delivered: 0,\n opened: 0,\n clicked: 0,\n bounced: 0,\n complained: 0,\n unsubscribed: 0\n };\n }\n \n /**\n * Helper method to validate required fields\n */\n protected validateRequiredFields(data: any, fields: string[]): void {\n const missing = fields.filter(field => !data[field]);\n if (missing.length > 0) {\n throw new NewsletterProviderError(\n `Missing required fields: ${missing.join(', ')}`,\n NewsletterErrorCode.VALIDATION_ERROR,\n this.name\n );\n }\n }\n \n /**\n * Helper method to check if a status transition is allowed\n */\n protected canEditInStatus(status: NewsletterStatus): boolean {\n const capabilities = this.getCapabilities();\n return capabilities.editableStatuses.includes(status);\n }\n \n /**\n * Helper to build pagination response\n */\n protected buildListResponse<T>(\n items: T[],\n total: number,\n options: ListNewsletterOptions = {}\n ): ListNewsletterResponse<T> {\n const limit = options.limit || 20;\n const offset = options.offset || 0;\n \n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + items.length < total\n };\n }\n}"],"mappings":"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","names":["NewsletterStatus","NewsletterErrorCode","BroadcastStatus","BroadcastErrorCode"]}
|
package/dist/utils.cjs
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/exports/utils.ts
|
|
31
|
+
var utils_exports = {};
|
|
32
|
+
__export(utils_exports, {
|
|
33
|
+
EMAIL_SAFE_CONFIG: () => EMAIL_SAFE_CONFIG,
|
|
34
|
+
convertToEmailSafeHtml: () => convertToEmailSafeHtml,
|
|
35
|
+
validateEmailHtml: () => validateEmailHtml
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(utils_exports);
|
|
38
|
+
|
|
39
|
+
// src/utils/emailSafeHtml.ts
|
|
40
|
+
var import_isomorphic_dompurify = __toESM(require("isomorphic-dompurify"), 1);
|
|
41
|
+
var EMAIL_SAFE_CONFIG = {
|
|
42
|
+
ALLOWED_TAGS: [
|
|
43
|
+
"p",
|
|
44
|
+
"br",
|
|
45
|
+
"strong",
|
|
46
|
+
"b",
|
|
47
|
+
"em",
|
|
48
|
+
"i",
|
|
49
|
+
"u",
|
|
50
|
+
"strike",
|
|
51
|
+
"s",
|
|
52
|
+
"span",
|
|
53
|
+
"a",
|
|
54
|
+
"h1",
|
|
55
|
+
"h2",
|
|
56
|
+
"h3",
|
|
57
|
+
"ul",
|
|
58
|
+
"ol",
|
|
59
|
+
"li",
|
|
60
|
+
"blockquote",
|
|
61
|
+
"hr"
|
|
62
|
+
],
|
|
63
|
+
ALLOWED_ATTR: ["href", "style", "target", "rel", "align"],
|
|
64
|
+
ALLOWED_STYLES: {
|
|
65
|
+
"*": [
|
|
66
|
+
"color",
|
|
67
|
+
"background-color",
|
|
68
|
+
"font-size",
|
|
69
|
+
"font-weight",
|
|
70
|
+
"font-style",
|
|
71
|
+
"text-decoration",
|
|
72
|
+
"text-align",
|
|
73
|
+
"margin",
|
|
74
|
+
"margin-top",
|
|
75
|
+
"margin-right",
|
|
76
|
+
"margin-bottom",
|
|
77
|
+
"margin-left",
|
|
78
|
+
"padding",
|
|
79
|
+
"padding-top",
|
|
80
|
+
"padding-right",
|
|
81
|
+
"padding-bottom",
|
|
82
|
+
"padding-left",
|
|
83
|
+
"line-height",
|
|
84
|
+
"border-left",
|
|
85
|
+
"border-left-width",
|
|
86
|
+
"border-left-style",
|
|
87
|
+
"border-left-color"
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
FORBID_TAGS: ["script", "style", "iframe", "object", "embed", "form", "input"],
|
|
91
|
+
FORBID_ATTR: ["class", "id", "onclick", "onload", "onerror"]
|
|
92
|
+
};
|
|
93
|
+
async function convertToEmailSafeHtml(editorState, options) {
|
|
94
|
+
const rawHtml = await lexicalToEmailHtml(editorState);
|
|
95
|
+
const sanitizedHtml = import_isomorphic_dompurify.default.sanitize(rawHtml, EMAIL_SAFE_CONFIG);
|
|
96
|
+
if (options?.wrapInTemplate) {
|
|
97
|
+
return wrapInEmailTemplate(sanitizedHtml, options.preheader);
|
|
98
|
+
}
|
|
99
|
+
return sanitizedHtml;
|
|
100
|
+
}
|
|
101
|
+
async function lexicalToEmailHtml(editorState) {
|
|
102
|
+
const { root } = editorState;
|
|
103
|
+
if (!root || !root.children) {
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
const html = root.children.map((node) => convertNode(node)).join("");
|
|
107
|
+
return html;
|
|
108
|
+
}
|
|
109
|
+
function convertNode(node) {
|
|
110
|
+
switch (node.type) {
|
|
111
|
+
case "paragraph":
|
|
112
|
+
return convertParagraph(node);
|
|
113
|
+
case "heading":
|
|
114
|
+
return convertHeading(node);
|
|
115
|
+
case "list":
|
|
116
|
+
return convertList(node);
|
|
117
|
+
case "listitem":
|
|
118
|
+
return convertListItem(node);
|
|
119
|
+
case "blockquote":
|
|
120
|
+
return convertBlockquote(node);
|
|
121
|
+
case "text":
|
|
122
|
+
return convertText(node);
|
|
123
|
+
case "link":
|
|
124
|
+
return convertLink(node);
|
|
125
|
+
case "linebreak":
|
|
126
|
+
return "<br>";
|
|
127
|
+
default:
|
|
128
|
+
if (node.children) {
|
|
129
|
+
return node.children.map(convertNode).join("");
|
|
130
|
+
}
|
|
131
|
+
return "";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function convertParagraph(node) {
|
|
135
|
+
const align = getAlignment(node.format);
|
|
136
|
+
const children = node.children?.map(convertNode).join("") || "";
|
|
137
|
+
if (!children.trim()) {
|
|
138
|
+
return '<p style="margin: 0 0 16px 0; min-height: 1em;"> </p>';
|
|
139
|
+
}
|
|
140
|
+
return `<p style="margin: 0 0 16px 0; text-align: ${align};">${children}</p>`;
|
|
141
|
+
}
|
|
142
|
+
function convertHeading(node) {
|
|
143
|
+
const tag = node.tag || "h1";
|
|
144
|
+
const align = getAlignment(node.format);
|
|
145
|
+
const children = node.children?.map(convertNode).join("") || "";
|
|
146
|
+
const styles = {
|
|
147
|
+
h1: "font-size: 32px; font-weight: 700; margin: 0 0 24px 0; line-height: 1.2;",
|
|
148
|
+
h2: "font-size: 24px; font-weight: 600; margin: 0 0 16px 0; line-height: 1.3;",
|
|
149
|
+
h3: "font-size: 20px; font-weight: 600; margin: 0 0 12px 0; line-height: 1.4;"
|
|
150
|
+
};
|
|
151
|
+
const style = `${styles[tag] || styles.h3} text-align: ${align};`;
|
|
152
|
+
return `<${tag} style="${style}">${children}</${tag}>`;
|
|
153
|
+
}
|
|
154
|
+
function convertList(node) {
|
|
155
|
+
const tag = node.listType === "number" ? "ol" : "ul";
|
|
156
|
+
const children = node.children?.map(convertNode).join("") || "";
|
|
157
|
+
const style = tag === "ul" ? "margin: 0 0 16px 0; padding-left: 24px; list-style-type: disc;" : "margin: 0 0 16px 0; padding-left: 24px; list-style-type: decimal;";
|
|
158
|
+
return `<${tag} style="${style}">${children}</${tag}>`;
|
|
159
|
+
}
|
|
160
|
+
function convertListItem(node) {
|
|
161
|
+
const children = node.children?.map(convertNode).join("") || "";
|
|
162
|
+
return `<li style="margin: 0 0 8px 0;">${children}</li>`;
|
|
163
|
+
}
|
|
164
|
+
function convertBlockquote(node) {
|
|
165
|
+
const children = node.children?.map(convertNode).join("") || "";
|
|
166
|
+
const style = "margin: 0 0 16px 0; padding-left: 16px; border-left: 4px solid #e5e7eb; color: #6b7280;";
|
|
167
|
+
return `<blockquote style="${style}">${children}</blockquote>`;
|
|
168
|
+
}
|
|
169
|
+
function convertText(node) {
|
|
170
|
+
let text = escapeHtml(node.text || "");
|
|
171
|
+
if (node.format & 1) {
|
|
172
|
+
text = `<strong>${text}</strong>`;
|
|
173
|
+
}
|
|
174
|
+
if (node.format & 2) {
|
|
175
|
+
text = `<em>${text}</em>`;
|
|
176
|
+
}
|
|
177
|
+
if (node.format & 8) {
|
|
178
|
+
text = `<u>${text}</u>`;
|
|
179
|
+
}
|
|
180
|
+
if (node.format & 4) {
|
|
181
|
+
text = `<strike>${text}</strike>`;
|
|
182
|
+
}
|
|
183
|
+
return text;
|
|
184
|
+
}
|
|
185
|
+
function convertLink(node) {
|
|
186
|
+
const children = node.children?.map(convertNode).join("") || "";
|
|
187
|
+
const url = node.fields?.url || "#";
|
|
188
|
+
return `<a href="${escapeHtml(url)}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${children}</a>`;
|
|
189
|
+
}
|
|
190
|
+
function getAlignment(format) {
|
|
191
|
+
if (!format) return "left";
|
|
192
|
+
if (format & 2) return "center";
|
|
193
|
+
if (format & 3) return "right";
|
|
194
|
+
if (format & 4) return "justify";
|
|
195
|
+
return "left";
|
|
196
|
+
}
|
|
197
|
+
function escapeHtml(text) {
|
|
198
|
+
const map = {
|
|
199
|
+
"&": "&",
|
|
200
|
+
"<": "<",
|
|
201
|
+
">": ">",
|
|
202
|
+
'"': """,
|
|
203
|
+
"'": "'"
|
|
204
|
+
};
|
|
205
|
+
return text.replace(/[&<>"']/g, (m) => map[m]);
|
|
206
|
+
}
|
|
207
|
+
function wrapInEmailTemplate(content, preheader) {
|
|
208
|
+
return `<!DOCTYPE html>
|
|
209
|
+
<html lang="en">
|
|
210
|
+
<head>
|
|
211
|
+
<meta charset="UTF-8">
|
|
212
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
213
|
+
<title>Email</title>
|
|
214
|
+
<!--[if mso]>
|
|
215
|
+
<noscript>
|
|
216
|
+
<xml>
|
|
217
|
+
<o:OfficeDocumentSettings>
|
|
218
|
+
<o:PixelsPerInch>96</o:PixelsPerInch>
|
|
219
|
+
</o:OfficeDocumentSettings>
|
|
220
|
+
</xml>
|
|
221
|
+
</noscript>
|
|
222
|
+
<![endif]-->
|
|
223
|
+
</head>
|
|
224
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 16px; line-height: 1.5; color: #333333; background-color: #f3f4f6;">
|
|
225
|
+
${preheader ? `<div style="display: none; max-height: 0; overflow: hidden;">${escapeHtml(preheader)}</div>` : ""}
|
|
226
|
+
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin: 0; padding: 0;">
|
|
227
|
+
<tr>
|
|
228
|
+
<td align="center" style="padding: 20px 0;">
|
|
229
|
+
<table role="presentation" cellpadding="0" cellspacing="0" width="600" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; overflow: hidden;">
|
|
230
|
+
<tr>
|
|
231
|
+
<td style="padding: 40px 30px;">
|
|
232
|
+
${content}
|
|
233
|
+
</td>
|
|
234
|
+
</tr>
|
|
235
|
+
</table>
|
|
236
|
+
</td>
|
|
237
|
+
</tr>
|
|
238
|
+
</table>
|
|
239
|
+
</body>
|
|
240
|
+
</html>`;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/utils/validateEmailHtml.ts
|
|
244
|
+
function validateEmailHtml(html) {
|
|
245
|
+
const warnings = [];
|
|
246
|
+
const errors = [];
|
|
247
|
+
const sizeInBytes = new Blob([html]).size;
|
|
248
|
+
if (sizeInBytes > 102400) {
|
|
249
|
+
warnings.push(`Email size (${Math.round(sizeInBytes / 1024)}KB) exceeds Gmail's 102KB limit - email may be clipped`);
|
|
250
|
+
}
|
|
251
|
+
if (html.includes("position:") && (html.includes("position: absolute") || html.includes("position: fixed"))) {
|
|
252
|
+
errors.push("Absolute/fixed positioning is not supported in most email clients");
|
|
253
|
+
}
|
|
254
|
+
if (html.includes("display: flex") || html.includes("display: grid")) {
|
|
255
|
+
errors.push("Flexbox and Grid layouts are not supported in many email clients");
|
|
256
|
+
}
|
|
257
|
+
if (html.includes("@media")) {
|
|
258
|
+
warnings.push("Media queries may not work in all email clients");
|
|
259
|
+
}
|
|
260
|
+
const hasJavaScript = html.includes("<script") || html.includes("onclick") || html.includes("onload") || html.includes("javascript:");
|
|
261
|
+
if (hasJavaScript) {
|
|
262
|
+
errors.push("JavaScript is not supported in email and will be stripped by email clients");
|
|
263
|
+
}
|
|
264
|
+
const hasExternalStyles = html.includes("<link") && html.includes("stylesheet");
|
|
265
|
+
if (hasExternalStyles) {
|
|
266
|
+
errors.push("External stylesheets are not supported - use inline styles only");
|
|
267
|
+
}
|
|
268
|
+
if (html.includes("<form") || html.includes("<input") || html.includes("<button")) {
|
|
269
|
+
errors.push("Forms and form elements are not reliably supported in email");
|
|
270
|
+
}
|
|
271
|
+
const unsupportedTags = [
|
|
272
|
+
"video",
|
|
273
|
+
"audio",
|
|
274
|
+
"iframe",
|
|
275
|
+
"embed",
|
|
276
|
+
"object",
|
|
277
|
+
"canvas",
|
|
278
|
+
"svg"
|
|
279
|
+
];
|
|
280
|
+
for (const tag of unsupportedTags) {
|
|
281
|
+
if (html.includes(`<${tag}`)) {
|
|
282
|
+
errors.push(`<${tag}> tags are not supported in email`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const imageCount = (html.match(/<img/g) || []).length;
|
|
286
|
+
const linkCount = (html.match(/<a/g) || []).length;
|
|
287
|
+
if (imageCount > 20) {
|
|
288
|
+
warnings.push(`High number of images (${imageCount}) may affect email performance`);
|
|
289
|
+
}
|
|
290
|
+
const imagesWithoutAlt = (html.match(/<img(?![^>]*\balt\s*=)[^>]*>/g) || []).length;
|
|
291
|
+
if (imagesWithoutAlt > 0) {
|
|
292
|
+
warnings.push(`${imagesWithoutAlt} image(s) missing alt text - important for accessibility`);
|
|
293
|
+
}
|
|
294
|
+
const linksWithoutTarget = (html.match(/<a(?![^>]*\btarget\s*=)[^>]*>/g) || []).length;
|
|
295
|
+
if (linksWithoutTarget > 0) {
|
|
296
|
+
warnings.push(`${linksWithoutTarget} link(s) missing target="_blank" attribute`);
|
|
297
|
+
}
|
|
298
|
+
if (html.includes("margin: auto") || html.includes("margin:auto")) {
|
|
299
|
+
warnings.push('margin: auto is not supported in Outlook - use align="center" or tables for centering');
|
|
300
|
+
}
|
|
301
|
+
if (html.includes("background-image")) {
|
|
302
|
+
warnings.push("Background images are not reliably supported - consider using <img> tags instead");
|
|
303
|
+
}
|
|
304
|
+
if (html.match(/\d+\s*(rem|em)/)) {
|
|
305
|
+
warnings.push("rem/em units may render inconsistently - use px for reliable sizing");
|
|
306
|
+
}
|
|
307
|
+
if (html.match(/margin[^:]*:\s*-\d+/)) {
|
|
308
|
+
errors.push("Negative margins are not supported in many email clients");
|
|
309
|
+
}
|
|
310
|
+
const personalizationTags = html.match(/\{\{([^}]+)\}\}/g) || [];
|
|
311
|
+
const validTags = ["subscriber.name", "subscriber.email", "subscriber.firstName", "subscriber.lastName"];
|
|
312
|
+
for (const tag of personalizationTags) {
|
|
313
|
+
const tagContent = tag.replace(/[{}]/g, "").trim();
|
|
314
|
+
if (!validTags.includes(tagContent)) {
|
|
315
|
+
warnings.push(`Unknown personalization tag: ${tag}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
valid: errors.length === 0,
|
|
320
|
+
warnings,
|
|
321
|
+
errors,
|
|
322
|
+
stats: {
|
|
323
|
+
sizeInBytes,
|
|
324
|
+
imageCount,
|
|
325
|
+
linkCount,
|
|
326
|
+
hasExternalStyles,
|
|
327
|
+
hasJavaScript
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
332
|
+
0 && (module.exports = {
|
|
333
|
+
EMAIL_SAFE_CONFIG,
|
|
334
|
+
convertToEmailSafeHtml,
|
|
335
|
+
validateEmailHtml
|
|
336
|
+
});
|
|
337
|
+
//# sourceMappingURL=utils.cjs.map
|