@dotpress/mailer 0.0.0-stage → 2.0.0-alpha.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 +491 -2
- package/dist/aws-ses.d.ts +30 -0
- package/dist/aws-ses.js +108 -0
- package/dist/aws-ses.js.map +1 -0
- package/dist/brevo.d.ts +23 -0
- package/dist/brevo.js +115 -0
- package/dist/brevo.js.map +1 -0
- package/dist/chunk-2ZABFOZQ.js +15 -0
- package/dist/chunk-2ZABFOZQ.js.map +1 -0
- package/dist/chunk-FRMKUAF4.js +88 -0
- package/dist/chunk-FRMKUAF4.js.map +1 -0
- package/dist/chunk-GOAXQ2GE.js +28 -0
- package/dist/chunk-GOAXQ2GE.js.map +1 -0
- package/dist/chunk-QYVN5N3B.js +221 -0
- package/dist/chunk-QYVN5N3B.js.map +1 -0
- package/dist/http-B35l9W1d.d.ts +9 -0
- package/dist/index.d.ts +97 -0
- package/dist/index.js +649 -0
- package/dist/index.js.map +1 -0
- package/dist/mailer-CmQCJ0qc.d.ts +339 -0
- package/dist/mailgun.d.ts +31 -0
- package/dist/mailgun.js +100 -0
- package/dist/mailgun.js.map +1 -0
- package/dist/mailjet.d.ts +28 -0
- package/dist/mailjet.js +104 -0
- package/dist/mailjet.js.map +1 -0
- package/dist/plugin.d.ts +33 -0
- package/dist/plugin.js +21 -0
- package/dist/plugin.js.map +1 -0
- package/dist/postmark.d.ts +27 -0
- package/dist/postmark.js +149 -0
- package/dist/postmark.js.map +1 -0
- package/dist/resend.d.ts +31 -0
- package/dist/resend.js +92 -0
- package/dist/resend.js.map +1 -0
- package/dist/scaleway.d.ts +31 -0
- package/dist/scaleway.js +113 -0
- package/dist/scaleway.js.map +1 -0
- package/dist/sendgrid.d.ts +26 -0
- package/dist/sendgrid.js +121 -0
- package/dist/sendgrid.js.map +1 -0
- package/dist/smtp.d.ts +45 -0
- package/dist/smtp.js +61 -0
- package/dist/smtp.js.map +1 -0
- package/package.json +139 -3
package/dist/brevo.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { MailProvider } from './index.js';
|
|
2
|
+
import { HttpProviderOptions } from './index.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
|
|
5
|
+
/** Options of `createBrevoProvider()`. */
|
|
6
|
+
interface BrevoProviderOptions extends HttpProviderOptions {
|
|
7
|
+
/** API key (v3). */
|
|
8
|
+
apiKey: string;
|
|
9
|
+
/** Base URL of the API. Defaults to `https://api.brevo.com`. */
|
|
10
|
+
baseUrl?: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Creates a provider that sends transactional messages with the API of
|
|
14
|
+
* [Brevo](https://www.brevo.com) (v3), through `fetch`: no dependency.
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* const provider = createBrevoProvider({ apiKey: process.env.BREVO_API_KEY! })
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare const createBrevoProvider: (options: BrevoProviderOptions) => MailProvider;
|
|
21
|
+
|
|
22
|
+
export { createBrevoProvider };
|
|
23
|
+
export type { BrevoProviderOptions };
|
package/dist/brevo.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
checkHttpOptions,
|
|
3
|
+
checkNoInline,
|
|
4
|
+
checkSingleReplyTo,
|
|
5
|
+
readJson,
|
|
6
|
+
responseError,
|
|
7
|
+
stringOf
|
|
8
|
+
} from "./chunk-FRMKUAF4.js";
|
|
9
|
+
import {
|
|
10
|
+
base64
|
|
11
|
+
} from "./chunk-2ZABFOZQ.js";
|
|
12
|
+
import {
|
|
13
|
+
MailMessageError
|
|
14
|
+
} from "./chunk-QYVN5N3B.js";
|
|
15
|
+
|
|
16
|
+
// src/providers/brevo.ts
|
|
17
|
+
var DEFAULT_BASE_URL = "https://api.brevo.com";
|
|
18
|
+
var MAX_NAME_LENGTH = 70;
|
|
19
|
+
var EXTENSIONS = new Set(
|
|
20
|
+
"xlsx xls ods docx docm doc csv pdf txt gif jpg jpeg png tif tiff rtf bmp cgm css shtml html htm zip xml ppt pptx tar ez ics mobi msg pub eps odt mp3 m4a m4v wma ogg flac wav aif aifc aiff mp4 mov avi mkv mpeg mpg wmv pkpass xlsm".split(
|
|
21
|
+
" "
|
|
22
|
+
)
|
|
23
|
+
);
|
|
24
|
+
var fail = (message) => {
|
|
25
|
+
throw new MailMessageError(`Brevo ${message}`);
|
|
26
|
+
};
|
|
27
|
+
var checkLimits = (message) => {
|
|
28
|
+
checkSingleReplyTo("Brevo", message.replyTo.length);
|
|
29
|
+
checkNoInline("Brevo", message.attachments);
|
|
30
|
+
const addresses = [
|
|
31
|
+
message.from,
|
|
32
|
+
...message.to,
|
|
33
|
+
...message.cc,
|
|
34
|
+
...message.bcc,
|
|
35
|
+
...message.replyTo
|
|
36
|
+
];
|
|
37
|
+
const named = addresses.find(
|
|
38
|
+
({ name }) => name !== void 0 && name.length > MAX_NAME_LENGTH
|
|
39
|
+
);
|
|
40
|
+
if (named) {
|
|
41
|
+
fail(
|
|
42
|
+
`accepts names of at most ${MAX_NAME_LENGTH} characters: "${named.name?.slice(0, 20)}\u2026" (${named.address}).`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
for (const { filename } of message.attachments) {
|
|
46
|
+
const extension = /\.([^.]+)$/.exec(filename)?.[1]?.toLowerCase();
|
|
47
|
+
if (!EXTENSIONS.has(extension)) {
|
|
48
|
+
fail(
|
|
49
|
+
`does not accept this attachment type: ${filename} (see the extensions accepted by Brevo).`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var toAddress = ({ name, address }) => name ? { email: address, name } : { email: address };
|
|
55
|
+
var toBody = (message) => {
|
|
56
|
+
checkLimits(message);
|
|
57
|
+
const { cc, bcc, replyTo, html, text, attachments, headers, tags } = message;
|
|
58
|
+
return {
|
|
59
|
+
sender: toAddress(message.from),
|
|
60
|
+
to: message.to.map(toAddress),
|
|
61
|
+
...cc.length > 0 ? { cc: cc.map(toAddress) } : {},
|
|
62
|
+
...bcc.length > 0 ? { bcc: bcc.map(toAddress) } : {},
|
|
63
|
+
...replyTo.length > 0 ? { replyTo: toAddress(replyTo[0]) } : {},
|
|
64
|
+
subject: message.subject,
|
|
65
|
+
...html === void 0 || html === "" ? {} : { htmlContent: html },
|
|
66
|
+
...text === "" ? {} : { textContent: text },
|
|
67
|
+
...attachments.length > 0 ? {
|
|
68
|
+
attachment: attachments.map(({ filename, content }) => ({
|
|
69
|
+
name: filename,
|
|
70
|
+
content: base64(content)
|
|
71
|
+
}))
|
|
72
|
+
} : {},
|
|
73
|
+
...Object.keys(headers).length > 0 ? { headers: { ...headers } } : {},
|
|
74
|
+
...tags.length > 0 ? { tags: [...tags] } : {}
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
var createBrevoProvider = (options) => {
|
|
78
|
+
const { values, baseUrl, fetch } = checkHttpOptions(
|
|
79
|
+
"createBrevoProvider()",
|
|
80
|
+
options,
|
|
81
|
+
["apiKey"],
|
|
82
|
+
DEFAULT_BASE_URL
|
|
83
|
+
);
|
|
84
|
+
const url = `${baseUrl}/v3/smtp/email`;
|
|
85
|
+
return {
|
|
86
|
+
name: "brevo",
|
|
87
|
+
send: async (message) => {
|
|
88
|
+
const body = JSON.stringify(toBody(message));
|
|
89
|
+
const response = await fetch(url, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: {
|
|
92
|
+
accept: "application/json",
|
|
93
|
+
"content-type": "application/json",
|
|
94
|
+
"api-key": values.apiKey
|
|
95
|
+
},
|
|
96
|
+
body
|
|
97
|
+
});
|
|
98
|
+
const result = await readJson(response);
|
|
99
|
+
if (!response.ok) {
|
|
100
|
+
const detail = stringOf(result, "message");
|
|
101
|
+
const code = stringOf(result, "code");
|
|
102
|
+
throw responseError(
|
|
103
|
+
"brevo",
|
|
104
|
+
response,
|
|
105
|
+
detail && code ? `${code}: ${detail}` : detail
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return { messageId: stringOf(result, "messageId") };
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
export {
|
|
113
|
+
createBrevoProvider
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=brevo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/brevo.ts"],"sourcesContent":["import { base64 } from '../encoding.js'\nimport { MailMessageError } from '../errors.js'\nimport type { MailProvider } from '../mailer.js'\nimport type { Address, MailMessage } from '../message.js'\nimport {\n checkHttpOptions,\n checkNoInline,\n checkSingleReplyTo,\n readJson,\n responseError,\n stringOf,\n type HttpProviderOptions,\n} from './http.js'\n\n/** Options of `createBrevoProvider()`. */\nexport interface BrevoProviderOptions extends HttpProviderOptions {\n /** API key (v3). */\n apiKey: string\n /** Base URL of the API. Defaults to `https://api.brevo.com`. */\n baseUrl?: string | undefined\n}\n\nconst DEFAULT_BASE_URL = 'https://api.brevo.com'\nconst MAX_NAME_LENGTH = 70\n\n// Attachment extensions accepted by the API.\nconst EXTENSIONS = new Set(\n 'xlsx xls ods docx docm doc csv pdf txt gif jpg jpeg png tif tiff rtf bmp cgm css shtml html htm zip xml ppt pptx tar ez ics mobi msg pub eps odt mp3 m4a m4v wma ogg flac wav aif aifc aiff mp4 mov avi mkv mpeg mpg wmv pkpass xlsm'.split(\n ' '\n )\n)\n\nconst fail = (message: string): never => {\n throw new MailMessageError(`Brevo ${message}`)\n}\n\n/** Checks the limits of the API. */\nconst checkLimits = (message: MailMessage) => {\n checkSingleReplyTo('Brevo', message.replyTo.length)\n checkNoInline('Brevo', message.attachments)\n const addresses = [\n message.from,\n ...message.to,\n ...message.cc,\n ...message.bcc,\n ...message.replyTo,\n ]\n const named = addresses.find(\n ({ name }) => name !== undefined && name.length > MAX_NAME_LENGTH\n )\n if (named) {\n fail(\n `accepts names of at most ${MAX_NAME_LENGTH} characters: \"${named.name?.slice(0, 20)}…\" (${named.address}).`\n )\n }\n for (const { filename } of message.attachments) {\n const extension = /\\.([^.]+)$/.exec(filename)?.[1]?.toLowerCase()\n if (!EXTENSIONS.has(extension as string)) {\n fail(\n `does not accept this attachment type: ${filename} (see the extensions accepted by Brevo).`\n )\n }\n }\n}\n\nconst toAddress = ({ name, address }: Address) =>\n name ? { email: address, name } : { email: address }\n\n/** Body of the API, see https://developers.brevo.com/reference/sendtransacemail */\nconst toBody = (message: MailMessage) => {\n checkLimits(message)\n const { cc, bcc, replyTo, html, text, attachments, headers, tags } = message\n return {\n sender: toAddress(message.from),\n to: message.to.map(toAddress),\n ...(cc.length > 0 ? { cc: cc.map(toAddress) } : {}),\n ...(bcc.length > 0 ? { bcc: bcc.map(toAddress) } : {}),\n ...(replyTo.length > 0 ? { replyTo: toAddress(replyTo[0]!) } : {}),\n subject: message.subject,\n ...(html === undefined || html === '' ? {} : { htmlContent: html }),\n ...(text === '' ? {} : { textContent: text }),\n ...(attachments.length > 0\n ? {\n attachment: attachments.map(({ filename, content }) => ({\n name: filename,\n content: base64(content),\n })),\n }\n : {}),\n ...(Object.keys(headers).length > 0 ? { headers: { ...headers } } : {}),\n ...(tags.length > 0 ? { tags: [...tags] } : {}),\n }\n}\n\n/**\n * Creates a provider that sends transactional messages with the API of\n * [Brevo](https://www.brevo.com) (v3), through `fetch`: no dependency.\n *\n * ```ts\n * const provider = createBrevoProvider({ apiKey: process.env.BREVO_API_KEY! })\n * ```\n */\nexport const createBrevoProvider = (\n options: BrevoProviderOptions\n): MailProvider => {\n const { values, baseUrl, fetch } = checkHttpOptions(\n 'createBrevoProvider()',\n options,\n ['apiKey'],\n DEFAULT_BASE_URL\n )\n const url = `${baseUrl}/v3/smtp/email`\n\n return {\n name: 'brevo',\n send: async (message) => {\n const body = JSON.stringify(toBody(message))\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n 'api-key': values.apiKey,\n },\n body,\n })\n const result = await readJson(response)\n if (!response.ok) {\n const detail = stringOf(result, 'message')\n const code = stringOf(result, 'code')\n throw responseError(\n 'brevo',\n response,\n detail && code ? `${code}: ${detail}` : detail\n )\n }\n return { messageId: stringOf(result, 'messageId') }\n },\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/encoding.ts
|
|
2
|
+
var encoder = new TextEncoder();
|
|
3
|
+
var utf8 = (text) => encoder.encode(text);
|
|
4
|
+
var base64 = (content) => {
|
|
5
|
+
const bytes = typeof content === "string" ? utf8(content) : content;
|
|
6
|
+
let binary = "";
|
|
7
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
8
|
+
return btoa(binary);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
utf8,
|
|
13
|
+
base64
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=chunk-2ZABFOZQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/encoding.ts"],"sourcesContent":["const encoder = new TextEncoder()\n\n/** UTF-8 bytes of a string. */\nexport const utf8 = (text: string) => encoder.encode(text)\n\n/** Base64 of bytes, or of the UTF-8 bytes of a string (Web APIs only). */\nexport const base64 = (content: Uint8Array | string) => {\n const bytes = typeof content === 'string' ? utf8(content) : content\n let binary = ''\n for (const byte of bytes) binary += String.fromCharCode(byte)\n return btoa(binary)\n}\n"],"mappings":";AAAA,IAAM,UAAU,IAAI,YAAY;AAGzB,IAAM,OAAO,CAAC,SAAiB,QAAQ,OAAO,IAAI;AAGlD,IAAM,SAAS,CAAC,YAAiC;AACtD,QAAM,QAAQ,OAAO,YAAY,WAAW,KAAK,OAAO,IAAI;AAC5D,MAAI,SAAS;AACb,aAAW,QAAQ,MAAO,WAAU,OAAO,aAAa,IAAI;AAC5D,SAAO,KAAK,MAAM;AACpB;","names":[]}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {
|
|
2
|
+
utf8
|
|
3
|
+
} from "./chunk-2ZABFOZQ.js";
|
|
4
|
+
import {
|
|
5
|
+
MailMessageError,
|
|
6
|
+
MailSendError,
|
|
7
|
+
checkKeys
|
|
8
|
+
} from "./chunk-QYVN5N3B.js";
|
|
9
|
+
|
|
10
|
+
// src/providers/http.ts
|
|
11
|
+
var checkHttpOptions = (label, options, required, defaultBaseUrl, optional = []) => {
|
|
12
|
+
const values = checkKeys(
|
|
13
|
+
label,
|
|
14
|
+
options,
|
|
15
|
+
/* @__PURE__ */ new Set([...required, ...optional, "baseUrl", "fetch"])
|
|
16
|
+
);
|
|
17
|
+
for (const key of required) {
|
|
18
|
+
const value = values[key];
|
|
19
|
+
if (typeof value !== "string" || value === "") {
|
|
20
|
+
throw new TypeError(`${label}: ${key} must be a non-empty string.`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const { baseUrl = defaultBaseUrl, fetch: fetchFn = globalThis.fetch } = values;
|
|
24
|
+
if (typeof baseUrl !== "string" || !/^https?:\/\/[^/]/.test(baseUrl)) {
|
|
25
|
+
throw new TypeError(`${label}: baseUrl must be an http(s) URL.`);
|
|
26
|
+
}
|
|
27
|
+
if (typeof fetchFn !== "function") {
|
|
28
|
+
throw new TypeError(`${label}: fetch must be a function.`);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
values,
|
|
32
|
+
baseUrl: baseUrl.replace(/\/+$/, ""),
|
|
33
|
+
fetch: fetchFn
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
var readJson = async (response) => {
|
|
37
|
+
const text = await response.text().catch(() => "");
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(text);
|
|
40
|
+
} catch {
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var responseError = (provider, response, detail) => new MailSendError(
|
|
45
|
+
provider,
|
|
46
|
+
`${response.status} ${detail ?? response.statusText}`.trim(),
|
|
47
|
+
{ status: response.status }
|
|
48
|
+
);
|
|
49
|
+
var stringOf = (value, key) => {
|
|
50
|
+
const property = value?.[key];
|
|
51
|
+
return typeof property === "string" && property !== "" ? property : void 0;
|
|
52
|
+
};
|
|
53
|
+
var jsonBody = (provider, body, maxBytes) => {
|
|
54
|
+
const json = JSON.stringify(body);
|
|
55
|
+
const size = utf8(json).byteLength;
|
|
56
|
+
if (size > maxBytes) {
|
|
57
|
+
throw new MailMessageError(
|
|
58
|
+
`${provider} accepts requests of at most ${maxBytes / 1e6} MB, got ${(size / 1e6).toFixed(1)} MB (attachments are base64-encoded).`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return json;
|
|
62
|
+
};
|
|
63
|
+
var checkSingleReplyTo = (provider, count) => {
|
|
64
|
+
if (count > 1) {
|
|
65
|
+
throw new MailMessageError(
|
|
66
|
+
`${provider} accepts a single reply-to address, got ${count}.`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var checkNoInline = (provider, attachments) => {
|
|
71
|
+
const inline = attachments.find(({ contentId }) => contentId !== void 0);
|
|
72
|
+
if (inline) {
|
|
73
|
+
throw new MailMessageError(
|
|
74
|
+
`${provider} does not support inline attachments (${inline.filename} has a contentId).`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export {
|
|
80
|
+
checkHttpOptions,
|
|
81
|
+
readJson,
|
|
82
|
+
responseError,
|
|
83
|
+
stringOf,
|
|
84
|
+
jsonBody,
|
|
85
|
+
checkSingleReplyTo,
|
|
86
|
+
checkNoInline
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=chunk-FRMKUAF4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/http.ts"],"sourcesContent":["import { utf8 } from '../encoding.js'\nimport { MailMessageError, MailSendError } from '../errors.js'\nimport { checkKeys } from '../message.js'\n\n/** Options shared by the providers based on an HTTP API. */\nexport interface HttpProviderOptions {\n /** Base URL of the API (region, proxy, tests). */\n baseUrl?: string | undefined\n /** `fetch` implementation. Defaults to the global `fetch`. */\n fetch?: typeof fetch | undefined\n}\n\n/**\n * Checks the options of an HTTP provider: unknown options, required\n * strings, `baseUrl` and `fetch`. Throws a `TypeError` prefixed with\n * `label`, like `createPostmarkProvider()`.\n */\nexport const checkHttpOptions = <K extends string>(\n label: string,\n options: unknown,\n required: readonly K[],\n defaultBaseUrl: string,\n optional: readonly string[] = []\n) => {\n const values = checkKeys(\n label,\n options,\n new Set<string>([...required, ...optional, 'baseUrl', 'fetch'])\n )\n for (const key of required) {\n const value = values[key]\n if (typeof value !== 'string' || value === '') {\n throw new TypeError(`${label}: ${key} must be a non-empty string.`)\n }\n }\n const { baseUrl = defaultBaseUrl, fetch: fetchFn = globalThis.fetch } = values\n if (typeof baseUrl !== 'string' || !/^https?:\\/\\/[^/]/.test(baseUrl)) {\n throw new TypeError(`${label}: baseUrl must be an http(s) URL.`)\n }\n if (typeof fetchFn !== 'function') {\n throw new TypeError(`${label}: fetch must be a function.`)\n }\n return {\n values: values as Record<K, string> & Record<string, unknown>,\n baseUrl: baseUrl.replace(/\\/+$/, ''),\n fetch: fetchFn as typeof fetch,\n }\n}\n\n/** JSON body of a response, or undefined (no body, not JSON). */\nexport const readJson = async (response: Response): Promise<unknown> => {\n const text = await response.text().catch(() => '')\n try {\n return JSON.parse(text) as unknown\n } catch {\n return undefined\n }\n}\n\n/**\n * Send error of an API response: its status, and the message of the API\n * (`detail`) or the status text.\n */\nexport const responseError = (\n provider: string,\n response: Response,\n detail: string | undefined\n) =>\n new MailSendError(\n provider,\n `${response.status} ${detail ?? response.statusText}`.trim(),\n { status: response.status }\n )\n\n/** String property of a JSON value, if any. */\nexport const stringOf = (value: unknown, key: string) => {\n const property = (value as Record<string, unknown> | null | undefined)?.[key]\n return typeof property === 'string' && property !== '' ? property : undefined\n}\n\n/** Request body as JSON, checked against the size limit of the API. */\nexport const jsonBody = (provider: string, body: unknown, maxBytes: number) => {\n const json = JSON.stringify(body)\n const size = utf8(json).byteLength\n if (size > maxBytes) {\n throw new MailMessageError(\n `${provider} accepts requests of at most ${maxBytes / 1_000_000} MB, got ${(size / 1_000_000).toFixed(1)} MB (attachments are base64-encoded).`\n )\n }\n return json\n}\n\n/** Fails when a message has more reply-to addresses than the API accepts. */\nexport const checkSingleReplyTo = (provider: string, count: number) => {\n if (count > 1) {\n throw new MailMessageError(\n `${provider} accepts a single reply-to address, got ${count}.`\n )\n }\n}\n\n/** Fails when a message has inline attachments the API cannot send. */\nexport const checkNoInline = (\n provider: string,\n attachments: readonly { contentId?: string; filename: string }[]\n) => {\n const inline = attachments.find(({ contentId }) => contentId !== undefined)\n if (inline) {\n throw new MailMessageError(\n `${provider} does not support inline attachments (${inline.filename} has a contentId).`\n )\n }\n}\n"],"mappings":";;;;;;;;;;AAiBO,IAAM,mBAAmB,CAC9B,OACA,SACA,UACA,gBACA,WAA8B,CAAC,MAC5B;AACH,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA,oBAAI,IAAY,CAAC,GAAG,UAAU,GAAG,UAAU,WAAW,OAAO,CAAC;AAAA,EAChE;AACA,aAAW,OAAO,UAAU;AAC1B,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,OAAO,UAAU,YAAY,UAAU,IAAI;AAC7C,YAAM,IAAI,UAAU,GAAG,KAAK,KAAK,GAAG,8BAA8B;AAAA,IACpE;AAAA,EACF;AACA,QAAM,EAAE,UAAU,gBAAgB,OAAO,UAAU,WAAW,MAAM,IAAI;AACxE,MAAI,OAAO,YAAY,YAAY,CAAC,mBAAmB,KAAK,OAAO,GAAG;AACpE,UAAM,IAAI,UAAU,GAAG,KAAK,mCAAmC;AAAA,EACjE;AACA,MAAI,OAAO,YAAY,YAAY;AACjC,UAAM,IAAI,UAAU,GAAG,KAAK,6BAA6B;AAAA,EAC3D;AACA,SAAO;AAAA,IACL;AAAA,IACA,SAAS,QAAQ,QAAQ,QAAQ,EAAE;AAAA,IACnC,OAAO;AAAA,EACT;AACF;AAGO,IAAM,WAAW,OAAO,aAAyC;AACtE,QAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,IAAM,gBAAgB,CAC3B,UACA,UACA,WAEA,IAAI;AAAA,EACF;AAAA,EACA,GAAG,SAAS,MAAM,IAAI,UAAU,SAAS,UAAU,GAAG,KAAK;AAAA,EAC3D,EAAE,QAAQ,SAAS,OAAO;AAC5B;AAGK,IAAM,WAAW,CAAC,OAAgB,QAAgB;AACvD,QAAM,WAAY,QAAuD,GAAG;AAC5E,SAAO,OAAO,aAAa,YAAY,aAAa,KAAK,WAAW;AACtE;AAGO,IAAM,WAAW,CAAC,UAAkB,MAAe,aAAqB;AAC7E,QAAM,OAAO,KAAK,UAAU,IAAI;AAChC,QAAM,OAAO,KAAK,IAAI,EAAE;AACxB,MAAI,OAAO,UAAU;AACnB,UAAM,IAAI;AAAA,MACR,GAAG,QAAQ,gCAAgC,WAAW,GAAS,aAAa,OAAO,KAAW,QAAQ,CAAC,CAAC;AAAA,IAC1G;AAAA,EACF;AACA,SAAO;AACT;AAGO,IAAM,qBAAqB,CAAC,UAAkB,UAAkB;AACrE,MAAI,QAAQ,GAAG;AACb,UAAM,IAAI;AAAA,MACR,GAAG,QAAQ,2CAA2C,KAAK;AAAA,IAC7D;AAAA,EACF;AACF;AAGO,IAAM,gBAAgB,CAC3B,UACA,gBACG;AACH,QAAM,SAAS,YAAY,KAAK,CAAC,EAAE,UAAU,MAAM,cAAc,MAAS;AAC1E,MAAI,QAAQ;AACV,UAAM,IAAI;AAAA,MACR,GAAG,QAAQ,yCAAyC,OAAO,QAAQ;AAAA,IACrE;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MailMessageError
|
|
3
|
+
} from "./chunk-QYVN5N3B.js";
|
|
4
|
+
|
|
5
|
+
// src/providers/key-value-tags.ts
|
|
6
|
+
var VALUE = /^[A-Za-z0-9_-]{1,256}$/;
|
|
7
|
+
var keyValueTags = (message, provider) => {
|
|
8
|
+
const pairs = message.tags.map((tag) => ({ name: tag, value: "true" }));
|
|
9
|
+
for (const [name, value] of Object.entries(message.metadata)) {
|
|
10
|
+
if (!VALUE.test(value)) {
|
|
11
|
+
throw new MailMessageError(
|
|
12
|
+
`${provider} only accepts metadata values of 1 to 256 letters, digits, _ or -: metadata.${name} is ${JSON.stringify(value)}.`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
if (message.tags.includes(name)) {
|
|
16
|
+
throw new MailMessageError(
|
|
17
|
+
`${provider} stores tags and metadata together: "${name}" cannot be both a tag and a metadata key.`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
pairs.push({ name, value });
|
|
21
|
+
}
|
|
22
|
+
return pairs;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
keyValueTags
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=chunk-GOAXQ2GE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/key-value-tags.ts"],"sourcesContent":["import { MailMessageError } from '../errors.js'\nimport type { MailMessage } from '../message.js'\n\n// SES and Resend: names and values of 1 to 256 letters, digits, _ or -.\nconst VALUE = /^[A-Za-z0-9_-]{1,256}$/\n\n/**\n * Tags and metadata as name/value pairs, for providers with a single list\n * (SES, Resend): a tag becomes `{ name: tag, value: 'true' }`.\n */\nexport const keyValueTags = (message: MailMessage, provider: string) => {\n const pairs = message.tags.map((tag) => ({ name: tag, value: 'true' }))\n for (const [name, value] of Object.entries(message.metadata)) {\n if (!VALUE.test(value)) {\n throw new MailMessageError(\n `${provider} only accepts metadata values of 1 to 256 letters, digits, _ or -: metadata.${name} is ${JSON.stringify(value)}.`\n )\n }\n if (message.tags.includes(name)) {\n throw new MailMessageError(\n `${provider} stores tags and metadata together: \"${name}\" cannot be both a tag and a metadata key.`\n )\n }\n pairs.push({ name, value })\n }\n return pairs\n}\n"],"mappings":";;;;;AAIA,IAAM,QAAQ;AAMP,IAAM,eAAe,CAAC,SAAsB,aAAqB;AACtE,QAAM,QAAQ,QAAQ,KAAK,IAAI,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,OAAO,EAAE;AACtE,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,QAAQ,QAAQ,GAAG;AAC5D,QAAI,CAAC,MAAM,KAAK,KAAK,GAAG;AACtB,YAAM,IAAI;AAAA,QACR,GAAG,QAAQ,+EAA+E,IAAI,OAAO,KAAK,UAAU,KAAK,CAAC;AAAA,MAC5H;AAAA,IACF;AACA,QAAI,QAAQ,KAAK,SAAS,IAAI,GAAG;AAC/B,YAAM,IAAI;AAAA,QACR,GAAG,QAAQ,wCAAwC,IAAI;AAAA,MACzD;AAAA,IACF;AACA,UAAM,KAAK,EAAE,MAAM,MAAM,CAAC;AAAA,EAC5B;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var MailerError = class extends Error {
|
|
3
|
+
name = "MailerError";
|
|
4
|
+
};
|
|
5
|
+
var MailDataError = class extends MailerError {
|
|
6
|
+
constructor(template, issues) {
|
|
7
|
+
const details = issues.map(
|
|
8
|
+
({ path, message }) => path.length > 0 ? `${path.join(".")}: ${message}` : message
|
|
9
|
+
).join("; ");
|
|
10
|
+
super(`Invalid data for mail template "${template}": ${details}`);
|
|
11
|
+
this.template = template;
|
|
12
|
+
this.issues = issues;
|
|
13
|
+
}
|
|
14
|
+
template;
|
|
15
|
+
issues;
|
|
16
|
+
name = "MailDataError";
|
|
17
|
+
};
|
|
18
|
+
var MailRenderError = class extends MailerError {
|
|
19
|
+
constructor(template, message, options) {
|
|
20
|
+
super(`Cannot render mail template "${template}": ${message}`, options);
|
|
21
|
+
this.template = template;
|
|
22
|
+
}
|
|
23
|
+
template;
|
|
24
|
+
name = "MailRenderError";
|
|
25
|
+
};
|
|
26
|
+
var MailMessageError = class extends MailerError {
|
|
27
|
+
name = "MailMessageError";
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(`Invalid mail message: ${message}`);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var MailSendError = class extends MailerError {
|
|
33
|
+
constructor(provider, message, options = {}) {
|
|
34
|
+
const { status, ...errorOptions } = options;
|
|
35
|
+
super(`Mail not sent by the ${provider} provider: ${message}`, errorOptions);
|
|
36
|
+
this.provider = provider;
|
|
37
|
+
this.status = status;
|
|
38
|
+
}
|
|
39
|
+
provider;
|
|
40
|
+
name = "MailSendError";
|
|
41
|
+
/** HTTP status of the provider API, if any. */
|
|
42
|
+
status;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/message.ts
|
|
46
|
+
var fail = (message) => {
|
|
47
|
+
throw new MailMessageError(message);
|
|
48
|
+
};
|
|
49
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
50
|
+
var EMAIL = /^[^\s@<>()[\]\\,;:"]+@[^\s@<>()[\]\\,;:"]+$/;
|
|
51
|
+
var NAMED = /^([\s\S]*?)\s*<([^<>]*)>$/;
|
|
52
|
+
var LINE_BREAK = /[\r\n]/;
|
|
53
|
+
var TOKEN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
54
|
+
var TAG = /^[A-Za-z0-9_-]{1,256}$/;
|
|
55
|
+
var RESERVED_HEADERS = /* @__PURE__ */ new Set([
|
|
56
|
+
"from",
|
|
57
|
+
"to",
|
|
58
|
+
"cc",
|
|
59
|
+
"bcc",
|
|
60
|
+
"reply-to",
|
|
61
|
+
"subject",
|
|
62
|
+
"date",
|
|
63
|
+
"message-id",
|
|
64
|
+
"mime-version",
|
|
65
|
+
"content-type",
|
|
66
|
+
"content-transfer-encoding"
|
|
67
|
+
]);
|
|
68
|
+
var parseAddress = (value, label) => {
|
|
69
|
+
let name;
|
|
70
|
+
let address;
|
|
71
|
+
if (typeof value === "string") {
|
|
72
|
+
const named = NAMED.exec(value.trim());
|
|
73
|
+
if (named) {
|
|
74
|
+
name = named[1].replace(/^"(.*)"$/, "$1").trim();
|
|
75
|
+
address = named[2].trim();
|
|
76
|
+
} else {
|
|
77
|
+
address = value.trim();
|
|
78
|
+
}
|
|
79
|
+
} else if (isRecord(value)) {
|
|
80
|
+
;
|
|
81
|
+
({ name, address } = value);
|
|
82
|
+
if (name !== void 0 && typeof name !== "string") {
|
|
83
|
+
fail(`${label}.name must be a string.`);
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
fail(`${label} must be an email address.`);
|
|
87
|
+
}
|
|
88
|
+
if (typeof address !== "string" || !EMAIL.test(address)) {
|
|
89
|
+
fail(`${label} is not an email address: ${JSON.stringify(address)}.`);
|
|
90
|
+
}
|
|
91
|
+
if (typeof name === "string" && LINE_BREAK.test(name)) {
|
|
92
|
+
fail(`${label} has a line break in its name.`);
|
|
93
|
+
}
|
|
94
|
+
return name ? { name, address } : { address };
|
|
95
|
+
};
|
|
96
|
+
var parseRecipients = (value, label) => {
|
|
97
|
+
if (value === void 0) return [];
|
|
98
|
+
const list = Array.isArray(value) ? value : [value];
|
|
99
|
+
return list.map(
|
|
100
|
+
(item, index) => parseAddress(item, Array.isArray(value) ? `${label}[${index}]` : label)
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
var parseAttachments = (value) => {
|
|
104
|
+
if (value === void 0) return [];
|
|
105
|
+
if (!Array.isArray(value)) fail("attachments must be a list.");
|
|
106
|
+
return value.map((attachment, index) => {
|
|
107
|
+
const label = `attachments[${index}]`;
|
|
108
|
+
if (!isRecord(attachment)) fail(`${label} must be an object.`);
|
|
109
|
+
const { filename, content, contentType, contentId } = attachment;
|
|
110
|
+
if (typeof filename !== "string" || filename === "") {
|
|
111
|
+
fail(`${label}.filename must be a non-empty string.`);
|
|
112
|
+
}
|
|
113
|
+
if (!(content instanceof Uint8Array) && typeof content !== "string") {
|
|
114
|
+
fail(`${label}.content must be a Uint8Array or a string.`);
|
|
115
|
+
}
|
|
116
|
+
for (const [key, field] of [
|
|
117
|
+
["contentType", contentType],
|
|
118
|
+
["contentId", contentId]
|
|
119
|
+
]) {
|
|
120
|
+
if (field !== void 0 && (typeof field !== "string" || field === "" || LINE_BREAK.test(field))) {
|
|
121
|
+
fail(`${label}.${key} must be a non-empty string on one line.`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
filename,
|
|
126
|
+
content,
|
|
127
|
+
...contentType === void 0 ? {} : { contentType },
|
|
128
|
+
...contentId === void 0 ? {} : { contentId }
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
var parseHeaders = (value) => {
|
|
133
|
+
if (value === void 0) return {};
|
|
134
|
+
if (!isRecord(value)) fail("headers must be an object.");
|
|
135
|
+
const headers = {};
|
|
136
|
+
for (const [name, header] of Object.entries(value)) {
|
|
137
|
+
if (!TOKEN.test(name)) fail(`"${name}" is not a header name.`);
|
|
138
|
+
if (RESERVED_HEADERS.has(name.toLowerCase())) {
|
|
139
|
+
fail(`the ${name} header is set by the mailer.`);
|
|
140
|
+
}
|
|
141
|
+
if (typeof header !== "string" || LINE_BREAK.test(header)) {
|
|
142
|
+
fail(`the ${name} header must be a string on one line.`);
|
|
143
|
+
}
|
|
144
|
+
headers[name] = header;
|
|
145
|
+
}
|
|
146
|
+
return headers;
|
|
147
|
+
};
|
|
148
|
+
var parseTags = (value) => {
|
|
149
|
+
if (value === void 0) return [];
|
|
150
|
+
if (!Array.isArray(value)) fail("tags must be a list.");
|
|
151
|
+
const tags = /* @__PURE__ */ new Set();
|
|
152
|
+
for (const [index, tag] of value.entries()) {
|
|
153
|
+
if (typeof tag !== "string" || !TAG.test(tag)) {
|
|
154
|
+
fail(
|
|
155
|
+
`tags[${index}] must have 1 to 256 letters, digits, _ or -: ${JSON.stringify(tag)}.`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
tags.add(tag);
|
|
159
|
+
}
|
|
160
|
+
return [...tags];
|
|
161
|
+
};
|
|
162
|
+
var parseMetadata = (value) => {
|
|
163
|
+
if (value === void 0) return {};
|
|
164
|
+
if (!isRecord(value)) fail("metadata must be an object.");
|
|
165
|
+
const metadata = {};
|
|
166
|
+
for (const [key, item] of Object.entries(value)) {
|
|
167
|
+
if (!TAG.test(key)) {
|
|
168
|
+
fail(
|
|
169
|
+
`metadata keys must have 1 to 256 letters, digits, _ or -: ${JSON.stringify(key)}.`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
if (typeof item !== "string" || LINE_BREAK.test(item)) {
|
|
173
|
+
fail(`metadata.${key} must be a string on one line.`);
|
|
174
|
+
}
|
|
175
|
+
metadata[key] = item;
|
|
176
|
+
}
|
|
177
|
+
return metadata;
|
|
178
|
+
};
|
|
179
|
+
var toSubject = (subject) => subject.replace(/\s*[\r\n]+\s*/g, " ").trim();
|
|
180
|
+
var parseEnvelope = (options, defaults) => {
|
|
181
|
+
const to = parseRecipients(options.to, "to");
|
|
182
|
+
if (to.length === 0) fail("to needs at least one address.");
|
|
183
|
+
return {
|
|
184
|
+
from: options.from === void 0 ? defaults.from : parseAddress(options.from, "from"),
|
|
185
|
+
to,
|
|
186
|
+
cc: parseRecipients(options.cc, "cc"),
|
|
187
|
+
bcc: parseRecipients(options.bcc, "bcc"),
|
|
188
|
+
replyTo: options.replyTo === void 0 ? defaults.replyTo : parseRecipients(options.replyTo, "replyTo"),
|
|
189
|
+
attachments: parseAttachments(options.attachments),
|
|
190
|
+
headers: parseHeaders(options.headers),
|
|
191
|
+
tags: parseTags(options.tags),
|
|
192
|
+
metadata: parseMetadata(options.metadata)
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
var checkKeys = (label, options, allowed) => {
|
|
196
|
+
if (!isRecord(options)) {
|
|
197
|
+
throw new TypeError(`${label}: expected an object of options.`);
|
|
198
|
+
}
|
|
199
|
+
for (const key of Object.keys(options)) {
|
|
200
|
+
if (!allowed.has(key)) {
|
|
201
|
+
throw new TypeError(`${label}: unknown option "${key}".`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return options;
|
|
205
|
+
};
|
|
206
|
+
var formatAddress = ({ name, address }) => name ? `"${name.replace(/(["\\])/g, "\\$1")}" <${address}>` : address;
|
|
207
|
+
|
|
208
|
+
export {
|
|
209
|
+
MailerError,
|
|
210
|
+
MailDataError,
|
|
211
|
+
MailRenderError,
|
|
212
|
+
MailMessageError,
|
|
213
|
+
MailSendError,
|
|
214
|
+
parseAddress,
|
|
215
|
+
parseRecipients,
|
|
216
|
+
toSubject,
|
|
217
|
+
parseEnvelope,
|
|
218
|
+
checkKeys,
|
|
219
|
+
formatAddress
|
|
220
|
+
};
|
|
221
|
+
//# sourceMappingURL=chunk-QYVN5N3B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/message.ts"],"sourcesContent":["/** Base class of the errors thrown by `@dotpress/mailer`. */\nexport class MailerError extends Error {\n override name = 'MailerError'\n}\n\n/** Problem found in the data passed to a template. */\nexport interface MailDataIssue {\n /** Path of the invalid value, empty for the data itself. */\n readonly path: readonly (string | number)[]\n readonly message: string\n}\n\n/** The data passed to a template does not match its schema. */\nexport class MailDataError extends MailerError {\n override name = 'MailDataError'\n\n constructor(\n /** Name of the template. */\n readonly template: string,\n readonly issues: readonly MailDataIssue[]\n ) {\n const details = issues\n .map(({ path, message }) =>\n path.length > 0 ? `${path.join('.')}: ${message}` : message\n )\n .join('; ')\n super(`Invalid data for mail template \"${template}\": ${details}`)\n }\n}\n\n/** A template could not be rendered (MJML, translations, Mustache). */\nexport class MailRenderError extends MailerError {\n override name = 'MailRenderError'\n\n constructor(\n /** Name of the template. */\n readonly template: string,\n message: string,\n options?: ErrorOptions\n ) {\n super(`Cannot render mail template \"${template}\": ${message}`, options)\n }\n}\n\n/** Invalid message: address, attachment, header or option. */\nexport class MailMessageError extends MailerError {\n override name = 'MailMessageError'\n\n constructor(message: string) {\n super(`Invalid mail message: ${message}`)\n }\n}\n\n/** Options of `MailSendError`. */\nexport interface MailSendErrorOptions extends ErrorOptions {\n /** HTTP status of the provider API, if any. */\n status?: number | undefined\n}\n\n/** The provider failed to send a message. */\nexport class MailSendError extends MailerError {\n override name = 'MailSendError'\n /** HTTP status of the provider API, if any. */\n readonly status: number | undefined\n\n constructor(\n /** Name of the provider. */\n readonly provider: string,\n message: string,\n options: MailSendErrorOptions = {}\n ) {\n const { status, ...errorOptions } = options\n super(`Mail not sent by the ${provider} provider: ${message}`, errorOptions)\n this.status = status\n }\n}\n","import { MailMessageError } from './errors.js'\n\n/** An email address, normalized. */\nexport interface Address {\n /** Display name, like `Ada Lovelace`. */\n readonly name?: string\n /** Email address, like `ada@example.com`. */\n readonly address: string\n}\n\n/**\n * An email address: `'ada@example.com'`, `'Ada Lovelace <ada@example.com>'`\n * or `{ name: 'Ada Lovelace', address: 'ada@example.com' }`.\n */\nexport type MailAddress = string | Address\n\n/** One or more email addresses. */\nexport type MailRecipients = MailAddress | readonly MailAddress[]\n\n/** A file attached to a message. */\nexport interface MailAttachment {\n /** File name shown by email clients. */\n readonly filename: string\n /** Binary content, or text content (UTF-8). */\n readonly content: Uint8Array | string\n /** MIME type, like `application/pdf`. Guessed by the provider if missing. */\n readonly contentType?: string\n /**\n * Makes the attachment inline: the HTML version references it with\n * `cid:` (`<img src=\"cid:logo\">`).\n */\n readonly contentId?: string\n}\n\n/** Message given to providers. */\nexport interface MailMessage {\n readonly from: Address\n /** At least one address. */\n readonly to: readonly Address[]\n readonly cc: readonly Address[]\n readonly bcc: readonly Address[]\n readonly replyTo: readonly Address[]\n /** Single line. */\n readonly subject: string\n /** HTML version; undefined for a text-only message. */\n readonly html: string | undefined\n readonly text: string\n readonly attachments: readonly MailAttachment[]\n /** Additional headers, like `List-Unsubscribe`. */\n readonly headers: Readonly<Record<string, string>>\n /** Labels to group messages in the statistics of the provider. */\n readonly tags: readonly string[]\n /** Key/value pairs returned by the webhooks of the provider. */\n readonly metadata: Readonly<Record<string, string>>\n /** Name of the template, if the message was rendered from one. */\n readonly template: string | undefined\n /** Rendered locale, if any. */\n readonly locale: string | undefined\n}\n\n/** Recipients and options shared by every message. */\nexport interface EnvelopeOptions {\n to: MailRecipients\n cc?: MailRecipients | undefined\n bcc?: MailRecipients | undefined\n /** Defaults to the `replyTo` option of the mailer. */\n replyTo?: MailRecipients | undefined\n /** Defaults to the `from` option of the mailer. */\n from?: MailAddress | undefined\n attachments?: readonly MailAttachment[] | undefined\n /** Additional headers, like `List-Unsubscribe`. */\n headers?: Readonly<Record<string, string>> | undefined\n /**\n * Labels to group messages in the statistics of the provider: letters,\n * digits, `_` and `-`. Ignored by providers without tags.\n */\n tags?: readonly string[] | undefined\n /**\n * Key/value pairs returned by the webhooks of the provider, to link an\n * event to the app. Keys: letters, digits, `_` and `-`. Ignored by\n * providers without metadata.\n */\n metadata?: Readonly<Record<string, string>> | undefined\n}\n\n/** Envelope of a message, validated. */\nexport interface Envelope {\n readonly from: Address\n readonly to: readonly Address[]\n readonly cc: readonly Address[]\n readonly bcc: readonly Address[]\n readonly replyTo: readonly Address[]\n readonly attachments: readonly MailAttachment[]\n readonly headers: Readonly<Record<string, string>>\n readonly tags: readonly string[]\n readonly metadata: Readonly<Record<string, string>>\n}\n\nconst fail = (message: string): never => {\n throw new MailMessageError(message)\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n// Pragmatic check: one @, no spaces, nor characters that break headers.\nconst EMAIL = /^[^\\s@<>()[\\]\\\\,;:\"]+@[^\\s@<>()[\\]\\\\,;:\"]+$/\nconst NAMED = /^([\\s\\S]*?)\\s*<([^<>]*)>$/\nconst LINE_BREAK = /[\\r\\n]/\nconst TOKEN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/\n// The strictest rule of the providers (SES, Resend): valid everywhere.\nconst TAG = /^[A-Za-z0-9_-]{1,256}$/\n\n// Set by the mailer or the provider.\nconst RESERVED_HEADERS = new Set([\n 'from',\n 'to',\n 'cc',\n 'bcc',\n 'reply-to',\n 'subject',\n 'date',\n 'message-id',\n 'mime-version',\n 'content-type',\n 'content-transfer-encoding',\n])\n\n/** Normalizes and checks an address. */\nexport const parseAddress = (value: unknown, label: string): Address => {\n let name: unknown\n let address: unknown\n if (typeof value === 'string') {\n const named = NAMED.exec(value.trim())\n if (named) {\n // Optional quotes around the name.\n name = (named[1] as string).replace(/^\"(.*)\"$/, '$1').trim()\n address = (named[2] as string).trim()\n } else {\n address = value.trim()\n }\n } else if (isRecord(value)) {\n ;({ name, address } = value)\n if (name !== undefined && typeof name !== 'string') {\n fail(`${label}.name must be a string.`)\n }\n } else {\n fail(`${label} must be an email address.`)\n }\n\n if (typeof address !== 'string' || !EMAIL.test(address)) {\n fail(`${label} is not an email address: ${JSON.stringify(address)}.`)\n }\n if (typeof name === 'string' && LINE_BREAK.test(name)) {\n fail(`${label} has a line break in its name.`)\n }\n return name\n ? { name: name as string, address: address as string }\n : { address: address as string }\n}\n\n/** Normalizes and checks a list of addresses. */\nexport const parseRecipients = (value: unknown, label: string): Address[] => {\n if (value === undefined) return []\n const list: unknown[] = Array.isArray(value) ? value : [value]\n return list.map((item, index) =>\n parseAddress(item, Array.isArray(value) ? `${label}[${index}]` : label)\n )\n}\n\nconst parseAttachments = (value: unknown): MailAttachment[] => {\n if (value === undefined) return []\n if (!Array.isArray(value)) fail('attachments must be a list.')\n\n return (value as unknown[]).map((attachment, index) => {\n const label = `attachments[${index}]`\n if (!isRecord(attachment)) fail(`${label} must be an object.`)\n const { filename, content, contentType, contentId } = attachment as Record<\n string,\n unknown\n >\n if (typeof filename !== 'string' || filename === '') {\n fail(`${label}.filename must be a non-empty string.`)\n }\n if (!(content instanceof Uint8Array) && typeof content !== 'string') {\n fail(`${label}.content must be a Uint8Array or a string.`)\n }\n for (const [key, field] of [\n ['contentType', contentType],\n ['contentId', contentId],\n ] as const) {\n if (\n field !== undefined &&\n (typeof field !== 'string' || field === '' || LINE_BREAK.test(field))\n ) {\n fail(`${label}.${key} must be a non-empty string on one line.`)\n }\n }\n return {\n filename: filename as string,\n content: content as Uint8Array | string,\n ...(contentType === undefined\n ? {}\n : { contentType: contentType as string }),\n ...(contentId === undefined ? {} : { contentId: contentId as string }),\n }\n })\n}\n\nconst parseHeaders = (value: unknown): Record<string, string> => {\n if (value === undefined) return {}\n if (!isRecord(value)) fail('headers must be an object.')\n\n const headers: Record<string, string> = {}\n for (const [name, header] of Object.entries(value as object)) {\n if (!TOKEN.test(name)) fail(`\"${name}\" is not a header name.`)\n if (RESERVED_HEADERS.has(name.toLowerCase())) {\n fail(`the ${name} header is set by the mailer.`)\n }\n if (typeof header !== 'string' || LINE_BREAK.test(header)) {\n fail(`the ${name} header must be a string on one line.`)\n }\n headers[name] = header as string\n }\n return headers\n}\n\nconst parseTags = (value: unknown): string[] => {\n if (value === undefined) return []\n if (!Array.isArray(value)) fail('tags must be a list.')\n const tags = new Set<string>()\n for (const [index, tag] of (value as unknown[]).entries()) {\n if (typeof tag !== 'string' || !TAG.test(tag)) {\n fail(\n `tags[${index}] must have 1 to 256 letters, digits, _ or -: ${JSON.stringify(tag)}.`\n )\n }\n tags.add(tag as string)\n }\n return [...tags]\n}\n\nconst parseMetadata = (value: unknown): Record<string, string> => {\n if (value === undefined) return {}\n if (!isRecord(value)) fail('metadata must be an object.')\n\n const metadata: Record<string, string> = {}\n for (const [key, item] of Object.entries(value as object)) {\n if (!TAG.test(key)) {\n fail(\n `metadata keys must have 1 to 256 letters, digits, _ or -: ${JSON.stringify(key)}.`\n )\n }\n if (typeof item !== 'string' || LINE_BREAK.test(item)) {\n fail(`metadata.${key} must be a string on one line.`)\n }\n metadata[key] = item as string\n }\n return metadata\n}\n\n/** Single-line subject. */\nexport const toSubject = (subject: string) =>\n subject.replace(/\\s*[\\r\\n]+\\s*/g, ' ').trim()\n\n/**\n * Validates the envelope options of a message. `defaults` provides the\n * sender and the reply-to addresses of the mailer.\n */\nexport const parseEnvelope = (\n options: EnvelopeOptions,\n defaults: { from: Address; replyTo: readonly Address[] }\n): Envelope => {\n const to = parseRecipients(options.to, 'to')\n if (to.length === 0) fail('to needs at least one address.')\n\n return {\n from:\n options.from === undefined\n ? defaults.from\n : parseAddress(options.from, 'from'),\n to,\n cc: parseRecipients(options.cc, 'cc'),\n bcc: parseRecipients(options.bcc, 'bcc'),\n replyTo:\n options.replyTo === undefined\n ? defaults.replyTo\n : parseRecipients(options.replyTo, 'replyTo'),\n attachments: parseAttachments(options.attachments),\n headers: parseHeaders(options.headers),\n tags: parseTags(options.tags),\n metadata: parseMetadata(options.metadata),\n }\n}\n\n/** Rejects unknown options. */\nexport const checkKeys = (\n label: string,\n options: unknown,\n allowed: ReadonlySet<string>\n): Record<string, unknown> => {\n if (!isRecord(options)) {\n throw new TypeError(`${label}: expected an object of options.`)\n }\n for (const key of Object.keys(options)) {\n if (!allowed.has(key)) {\n throw new TypeError(`${label}: unknown option \"${key}\".`)\n }\n }\n return options\n}\n\n/**\n * Address as a header value: `\"Ada Lovelace\" <ada@example.com>`, or the\n * address alone. For providers that take strings.\n */\nexport const formatAddress = ({ name, address }: Address) =>\n name ? `\"${name.replace(/([\"\\\\])/g, '\\\\$1')}\" <${address}>` : address\n"],"mappings":";AACO,IAAM,cAAN,cAA0B,MAAM;AAAA,EAC5B,OAAO;AAClB;AAUO,IAAM,gBAAN,cAA4B,YAAY;AAAA,EAG7C,YAEW,UACA,QACT;AACA,UAAM,UAAU,OACb;AAAA,MAAI,CAAC,EAAE,MAAM,QAAQ,MACpB,KAAK,SAAS,IAAI,GAAG,KAAK,KAAK,GAAG,CAAC,KAAK,OAAO,KAAK;AAAA,IACtD,EACC,KAAK,IAAI;AACZ,UAAM,mCAAmC,QAAQ,MAAM,OAAO,EAAE;AARvD;AACA;AAAA,EAQX;AAAA,EATW;AAAA,EACA;AAAA,EALF,OAAO;AAclB;AAGO,IAAM,kBAAN,cAA8B,YAAY;AAAA,EAG/C,YAEW,UACT,SACA,SACA;AACA,UAAM,gCAAgC,QAAQ,MAAM,OAAO,IAAI,OAAO;AAJ7D;AAAA,EAKX;AAAA,EALW;AAAA,EAJF,OAAO;AAUlB;AAGO,IAAM,mBAAN,cAA+B,YAAY;AAAA,EACvC,OAAO;AAAA,EAEhB,YAAY,SAAiB;AAC3B,UAAM,yBAAyB,OAAO,EAAE;AAAA,EAC1C;AACF;AASO,IAAM,gBAAN,cAA4B,YAAY;AAAA,EAK7C,YAEW,UACT,SACA,UAAgC,CAAC,GACjC;AACA,UAAM,EAAE,QAAQ,GAAG,aAAa,IAAI;AACpC,UAAM,wBAAwB,QAAQ,cAAc,OAAO,IAAI,YAAY;AALlE;AAMT,SAAK,SAAS;AAAA,EAChB;AAAA,EAPW;AAAA,EANF,OAAO;AAAA;AAAA,EAEP;AAYX;;;ACuBA,IAAM,OAAO,CAAC,YAA2B;AACvC,QAAM,IAAI,iBAAiB,OAAO;AACpC;AAEA,IAAM,WAAW,CAAC,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAGrE,IAAM,QAAQ;AACd,IAAM,QAAQ;AACd,IAAM,aAAa;AACnB,IAAM,QAAQ;AAEd,IAAM,MAAM;AAGZ,IAAM,mBAAmB,oBAAI,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,eAAe,CAAC,OAAgB,UAA2B;AACtE,MAAI;AACJ,MAAI;AACJ,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,QAAQ,MAAM,KAAK,MAAM,KAAK,CAAC;AACrC,QAAI,OAAO;AAET,aAAQ,MAAM,CAAC,EAAa,QAAQ,YAAY,IAAI,EAAE,KAAK;AAC3D,gBAAW,MAAM,CAAC,EAAa,KAAK;AAAA,IACtC,OAAO;AACL,gBAAU,MAAM,KAAK;AAAA,IACvB;AAAA,EACF,WAAW,SAAS,KAAK,GAAG;AAC1B;AAAC,KAAC,EAAE,MAAM,QAAQ,IAAI;AACtB,QAAI,SAAS,UAAa,OAAO,SAAS,UAAU;AAClD,WAAK,GAAG,KAAK,yBAAyB;AAAA,IACxC;AAAA,EACF,OAAO;AACL,SAAK,GAAG,KAAK,4BAA4B;AAAA,EAC3C;AAEA,MAAI,OAAO,YAAY,YAAY,CAAC,MAAM,KAAK,OAAO,GAAG;AACvD,SAAK,GAAG,KAAK,6BAA6B,KAAK,UAAU,OAAO,CAAC,GAAG;AAAA,EACtE;AACA,MAAI,OAAO,SAAS,YAAY,WAAW,KAAK,IAAI,GAAG;AACrD,SAAK,GAAG,KAAK,gCAAgC;AAAA,EAC/C;AACA,SAAO,OACH,EAAE,MAAsB,QAA2B,IACnD,EAAE,QAA2B;AACnC;AAGO,IAAM,kBAAkB,CAAC,OAAgB,UAA6B;AAC3E,MAAI,UAAU,OAAW,QAAO,CAAC;AACjC,QAAM,OAAkB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAC7D,SAAO,KAAK;AAAA,IAAI,CAAC,MAAM,UACrB,aAAa,MAAM,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,KAAK;AAAA,EACxE;AACF;AAEA,IAAM,mBAAmB,CAAC,UAAqC;AAC7D,MAAI,UAAU,OAAW,QAAO,CAAC;AACjC,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,MAAK,6BAA6B;AAE7D,SAAQ,MAAoB,IAAI,CAAC,YAAY,UAAU;AACrD,UAAM,QAAQ,eAAe,KAAK;AAClC,QAAI,CAAC,SAAS,UAAU,EAAG,MAAK,GAAG,KAAK,qBAAqB;AAC7D,UAAM,EAAE,UAAU,SAAS,aAAa,UAAU,IAAI;AAItD,QAAI,OAAO,aAAa,YAAY,aAAa,IAAI;AACnD,WAAK,GAAG,KAAK,uCAAuC;AAAA,IACtD;AACA,QAAI,EAAE,mBAAmB,eAAe,OAAO,YAAY,UAAU;AACnE,WAAK,GAAG,KAAK,4CAA4C;AAAA,IAC3D;AACA,eAAW,CAAC,KAAK,KAAK,KAAK;AAAA,MACzB,CAAC,eAAe,WAAW;AAAA,MAC3B,CAAC,aAAa,SAAS;AAAA,IACzB,GAAY;AACV,UACE,UAAU,WACT,OAAO,UAAU,YAAY,UAAU,MAAM,WAAW,KAAK,KAAK,IACnE;AACA,aAAK,GAAG,KAAK,IAAI,GAAG,0CAA0C;AAAA,MAChE;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,GAAI,gBAAgB,SAChB,CAAC,IACD,EAAE,YAAmC;AAAA,MACzC,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,UAA+B;AAAA,IACtE;AAAA,EACF,CAAC;AACH;AAEA,IAAM,eAAe,CAAC,UAA2C;AAC/D,MAAI,UAAU,OAAW,QAAO,CAAC;AACjC,MAAI,CAAC,SAAS,KAAK,EAAG,MAAK,4BAA4B;AAEvD,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAe,GAAG;AAC5D,QAAI,CAAC,MAAM,KAAK,IAAI,EAAG,MAAK,IAAI,IAAI,yBAAyB;AAC7D,QAAI,iBAAiB,IAAI,KAAK,YAAY,CAAC,GAAG;AAC5C,WAAK,OAAO,IAAI,+BAA+B;AAAA,IACjD;AACA,QAAI,OAAO,WAAW,YAAY,WAAW,KAAK,MAAM,GAAG;AACzD,WAAK,OAAO,IAAI,uCAAuC;AAAA,IACzD;AACA,YAAQ,IAAI,IAAI;AAAA,EAClB;AACA,SAAO;AACT;AAEA,IAAM,YAAY,CAAC,UAA6B;AAC9C,MAAI,UAAU,OAAW,QAAO,CAAC;AACjC,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,MAAK,sBAAsB;AACtD,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,CAAC,OAAO,GAAG,KAAM,MAAoB,QAAQ,GAAG;AACzD,QAAI,OAAO,QAAQ,YAAY,CAAC,IAAI,KAAK,GAAG,GAAG;AAC7C;AAAA,QACE,QAAQ,KAAK,iDAAiD,KAAK,UAAU,GAAG,CAAC;AAAA,MACnF;AAAA,IACF;AACA,SAAK,IAAI,GAAa;AAAA,EACxB;AACA,SAAO,CAAC,GAAG,IAAI;AACjB;AAEA,IAAM,gBAAgB,CAAC,UAA2C;AAChE,MAAI,UAAU,OAAW,QAAO,CAAC;AACjC,MAAI,CAAC,SAAS,KAAK,EAAG,MAAK,6BAA6B;AAExD,QAAM,WAAmC,CAAC;AAC1C,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,KAAe,GAAG;AACzD,QAAI,CAAC,IAAI,KAAK,GAAG,GAAG;AAClB;AAAA,QACE,6DAA6D,KAAK,UAAU,GAAG,CAAC;AAAA,MAClF;AAAA,IACF;AACA,QAAI,OAAO,SAAS,YAAY,WAAW,KAAK,IAAI,GAAG;AACrD,WAAK,YAAY,GAAG,gCAAgC;AAAA,IACtD;AACA,aAAS,GAAG,IAAI;AAAA,EAClB;AACA,SAAO;AACT;AAGO,IAAM,YAAY,CAAC,YACxB,QAAQ,QAAQ,kBAAkB,GAAG,EAAE,KAAK;AAMvC,IAAM,gBAAgB,CAC3B,SACA,aACa;AACb,QAAM,KAAK,gBAAgB,QAAQ,IAAI,IAAI;AAC3C,MAAI,GAAG,WAAW,EAAG,MAAK,gCAAgC;AAE1D,SAAO;AAAA,IACL,MACE,QAAQ,SAAS,SACb,SAAS,OACT,aAAa,QAAQ,MAAM,MAAM;AAAA,IACvC;AAAA,IACA,IAAI,gBAAgB,QAAQ,IAAI,IAAI;AAAA,IACpC,KAAK,gBAAgB,QAAQ,KAAK,KAAK;AAAA,IACvC,SACE,QAAQ,YAAY,SAChB,SAAS,UACT,gBAAgB,QAAQ,SAAS,SAAS;AAAA,IAChD,aAAa,iBAAiB,QAAQ,WAAW;AAAA,IACjD,SAAS,aAAa,QAAQ,OAAO;AAAA,IACrC,MAAM,UAAU,QAAQ,IAAI;AAAA,IAC5B,UAAU,cAAc,QAAQ,QAAQ;AAAA,EAC1C;AACF;AAGO,IAAM,YAAY,CACvB,OACA,SACA,YAC4B;AAC5B,MAAI,CAAC,SAAS,OAAO,GAAG;AACtB,UAAM,IAAI,UAAU,GAAG,KAAK,kCAAkC;AAAA,EAChE;AACA,aAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,QAAI,CAAC,QAAQ,IAAI,GAAG,GAAG;AACrB,YAAM,IAAI,UAAU,GAAG,KAAK,qBAAqB,GAAG,IAAI;AAAA,IAC1D;AAAA,EACF;AACA,SAAO;AACT;AAMO,IAAM,gBAAgB,CAAC,EAAE,MAAM,QAAQ,MAC5C,OAAO,IAAI,KAAK,QAAQ,YAAY,MAAM,CAAC,MAAM,OAAO,MAAM;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Options shared by the providers based on an HTTP API. */
|
|
2
|
+
interface HttpProviderOptions {
|
|
3
|
+
/** Base URL of the API (region, proxy, tests). */
|
|
4
|
+
baseUrl?: string | undefined;
|
|
5
|
+
/** `fetch` implementation. Defaults to the global `fetch`. */
|
|
6
|
+
fetch?: typeof fetch | undefined;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type { HttpProviderOptions as H };
|