@mailerport/mime 0.1.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/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/mime.js +47 -0
- package/package.json +1 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createMimeMessage } from "./mime.js";
|
package/dist/mime.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import { MailPortError, ERROR_CODES } from "@mailerport/core";
|
|
4
|
+
|
|
5
|
+
function header(value) {
|
|
6
|
+
const text = String(value || "");
|
|
7
|
+
if (/\r|\n/.test(text)) throw new MailPortError(ERROR_CODES.MAIL_INVALID_HEADER, "Mail header contains a newline");
|
|
8
|
+
return text;
|
|
9
|
+
}
|
|
10
|
+
const addresses = (value) => (Array.isArray(value) ? value : [value]).filter(Boolean).map(header).join(", ");
|
|
11
|
+
const encode = (value) => Buffer.from(value).toString("base64").replace(/(.{76})/g, "$1\r\n");
|
|
12
|
+
|
|
13
|
+
export function createMimeMessage(message) {
|
|
14
|
+
const boundary = `mailport_${randomUUID().replaceAll("-", "")}`;
|
|
15
|
+
const alternative = `${boundary}_alternative`;
|
|
16
|
+
const lines = [
|
|
17
|
+
`From: ${header(message.from)}`, `To: ${addresses(message.to)}`,
|
|
18
|
+
...(message.cc?.length ? [`Cc: ${addresses(message.cc)}`] : []),
|
|
19
|
+
...(message.reply_to?.length ? [`Reply-To: ${addresses(message.reply_to)}`] : []),
|
|
20
|
+
`Subject: ${header(message.subject)}`, `Date: ${new Date().toUTCString()}`,
|
|
21
|
+
`Message-ID: <${header(message.message_id)}@mailport>`, "MIME-Version: 1.0",
|
|
22
|
+
];
|
|
23
|
+
const attachments = message.attachments || [];
|
|
24
|
+
const bodyBoundary = attachments.length ? alternative : boundary;
|
|
25
|
+
lines.push(`Content-Type: multipart/${attachments.length ? "mixed" : "alternative"}; boundary="${boundary}"`, "");
|
|
26
|
+
if (attachments.length) lines.push(`--${boundary}`, `Content-Type: multipart/alternative; boundary="${alternative}"`, "");
|
|
27
|
+
lines.push(`--${bodyBoundary}`, "Content-Type: text/plain; charset=utf-8", "Content-Transfer-Encoding: 8bit", "", message.text || "",
|
|
28
|
+
`--${bodyBoundary}`, "Content-Type: text/html; charset=utf-8", "Content-Transfer-Encoding: 8bit", "", message.html || "",
|
|
29
|
+
`--${bodyBoundary}--`);
|
|
30
|
+
for (const item of attachments) {
|
|
31
|
+
const filename = header(item.filename || "attachment");
|
|
32
|
+
lines.push(`--${boundary}`, `Content-Type: ${header(item.contentType || "application/octet-stream")}; name="${filename}"`,
|
|
33
|
+
"Content-Transfer-Encoding: base64", `Content-Disposition: attachment; filename="${filename}"`, "",
|
|
34
|
+
encode(Buffer.isBuffer(item.content) ? item.content : Buffer.from(String(item.content || ""))));
|
|
35
|
+
}
|
|
36
|
+
if (attachments.length) lines.push(`--${boundary}--`);
|
|
37
|
+
const mime = lines.join("\r\n");
|
|
38
|
+
if (!message.dkim?.privateKey) return mime;
|
|
39
|
+
const split = mime.indexOf("\r\n\r\n");
|
|
40
|
+
const body = mime.slice(split + 4).replace(/\r\n*$/, "\r\n");
|
|
41
|
+
const bodyHash = crypto.createHash("sha256").update(body).digest("base64");
|
|
42
|
+
const fields = lines.slice(0, lines.indexOf("")).filter((line) => /^(from|to|subject|date|message-id):/i.test(line));
|
|
43
|
+
const unsigned = `v=1; a=rsa-sha256; c=simple/simple; d=${message.dkim.domain}; s=${message.dkim.selector}; h=from:to:subject:date:message-id; bh=${bodyHash}; b=`;
|
|
44
|
+
const signingData = `${fields.join("\r\n")}\r\ndkim-signature:${unsigned}`;
|
|
45
|
+
const signature = crypto.sign("RSA-SHA256", Buffer.from(signingData), message.dkim.privateKey).toString("base64");
|
|
46
|
+
return `DKIM-Signature: ${unsigned}${signature}\r\n${mime}`;
|
|
47
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"@mailerport/mime","version":"0.1.0","description":"MIME and DKIM primitives for MailPort","type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"types":"./dist/index.d.ts","files":["dist"],"scripts":{"build":"node ../../scripts/build-package.js mime","prepack":"npm run build"},"dependencies":{"@mailerport/core":"0.1.0"},"engines":{"node":">=20"},"license":"MIT","publishConfig":{"access":"public"}}
|