@hogsend/plugin-resend 0.12.1 → 0.12.2
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/package.json +3 -3
- package/src/__tests__/send.test.ts +32 -0
- package/src/send.ts +20 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hogsend/plugin-resend",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"resend": "^6.12.3",
|
|
26
26
|
"svix": "^1.94.0",
|
|
27
|
-
"@hogsend/core": "^0.12.
|
|
28
|
-
"@hogsend/email": "^0.12.
|
|
27
|
+
"@hogsend/core": "^0.12.2",
|
|
28
|
+
"@hogsend/email": "^0.12.2"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^22.0.0",
|
|
@@ -118,6 +118,38 @@ describe("sendEmail", () => {
|
|
|
118
118
|
expect(arg.tags).toEqual(tags);
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
+
it("sanitizes tag names/values to Resend's allowed charset", async () => {
|
|
122
|
+
const sendFn = vi.fn().mockResolvedValue({
|
|
123
|
+
data: { id: "resend_123" },
|
|
124
|
+
error: null,
|
|
125
|
+
});
|
|
126
|
+
const client = mockResendClient({ sendFn });
|
|
127
|
+
|
|
128
|
+
await sendEmail({
|
|
129
|
+
client,
|
|
130
|
+
options: {
|
|
131
|
+
from: "test@hogsend.com",
|
|
132
|
+
to: "user@example.com",
|
|
133
|
+
subject: "Test",
|
|
134
|
+
html: HTML,
|
|
135
|
+
// The engine's neutral tags carry journey names and slashed template
|
|
136
|
+
// keys — Resend only allows [A-Za-z0-9_-].
|
|
137
|
+
tags: [
|
|
138
|
+
{ name: "journeyId", value: "Docs Subscriber" },
|
|
139
|
+
{ name: "templateKey", value: "docs/welcome" },
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const arg = sendFn.mock.calls[0]?.[0] as {
|
|
145
|
+
tags?: Array<{ name: string; value: string }>;
|
|
146
|
+
};
|
|
147
|
+
expect(arg.tags).toEqual([
|
|
148
|
+
{ name: "journeyId", value: "Docs-Subscriber" },
|
|
149
|
+
{ name: "templateKey", value: "docs-welcome" },
|
|
150
|
+
]);
|
|
151
|
+
});
|
|
152
|
+
|
|
121
153
|
it("omits Resend tags when none are set", async () => {
|
|
122
154
|
const sendFn = vi.fn().mockResolvedValue({
|
|
123
155
|
data: { id: "resend_123" },
|
package/src/send.ts
CHANGED
|
@@ -9,6 +9,21 @@ import type { BatchEmailItem, SendEmailOptions, SendResult } from "./types.js";
|
|
|
9
9
|
|
|
10
10
|
const BATCH_CHUNK_SIZE = 100;
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Resend rejects tag names/values outside ASCII letters, numbers, underscores
|
|
14
|
+
* and dashes. The engine's neutral tags carry journey names ("Docs Subscriber")
|
|
15
|
+
* and template keys ("docs/welcome"), so map anything else to "-" rather than
|
|
16
|
+
* failing the whole send.
|
|
17
|
+
*/
|
|
18
|
+
function sanitizeTags(
|
|
19
|
+
tags: SendEmailOptions["tags"],
|
|
20
|
+
): SendEmailOptions["tags"] {
|
|
21
|
+
return tags?.map((tag) => ({
|
|
22
|
+
name: tag.name.replace(/[^a-zA-Z0-9_-]/g, "-"),
|
|
23
|
+
value: tag.value.replace(/[^a-zA-Z0-9_-]/g, "-"),
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
|
|
12
27
|
function isRetryableStatusCode(statusCode: number): boolean {
|
|
13
28
|
return statusCode === 429 || statusCode >= 500;
|
|
14
29
|
}
|
|
@@ -109,8 +124,9 @@ export async function sendEmail(args: {
|
|
|
109
124
|
cc: options.cc,
|
|
110
125
|
bcc: options.bcc,
|
|
111
126
|
scheduledAt: options.scheduledAt,
|
|
112
|
-
// Resend accepts neutral `{name,value}[]` tags natively —
|
|
113
|
-
|
|
127
|
+
// Resend accepts neutral `{name,value}[]` tags natively — sanitized to
|
|
128
|
+
// its allowed charset (see sanitizeTags).
|
|
129
|
+
tags: sanitizeTags(options.tags),
|
|
114
130
|
headers: options.headers,
|
|
115
131
|
});
|
|
116
132
|
|
|
@@ -182,8 +198,8 @@ async function sendBatchChunk(
|
|
|
182
198
|
replyTo: email.replyTo,
|
|
183
199
|
cc: email.cc,
|
|
184
200
|
bcc: email.bcc,
|
|
185
|
-
// Resend accepts neutral `{name,value}[]` tags
|
|
186
|
-
tags: email.tags,
|
|
201
|
+
// Resend accepts neutral `{name,value}[]` tags — sanitized to its charset.
|
|
202
|
+
tags: sanitizeTags(email.tags),
|
|
187
203
|
headers: email.headers,
|
|
188
204
|
})),
|
|
189
205
|
);
|