sently 0.4.5 → 0.4.7
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 +44 -0
- package/README.md +94 -41
- package/dist/adapters/bun.d.ts +7 -0
- package/dist/adapters/bun.js +2 -183
- package/dist/adapters/bun.js.map +3 -3
- package/dist/adapters/cf.d.ts +6 -0
- package/dist/adapters/cf.js +2 -77
- package/dist/adapters/cf.js.map +3 -3
- package/dist/adapters/deno.d.ts +4 -0
- package/dist/adapters/deno.js +2 -72
- package/dist/adapters/deno.js.map +3 -3
- package/dist/adapters/node.d.ts +7 -0
- package/dist/adapters/node.js +2 -180
- package/dist/adapters/node.js.map +3 -3
- package/dist/auth/oauth2.d.ts +4 -0
- package/dist/auth/oauth2.js +2 -13
- package/dist/auth/oauth2.js.map +1 -1
- package/dist/chunk-2kcwa9gt.js +4 -0
- package/dist/chunk-2kcwa9gt.js.map +11 -0
- package/dist/chunk-2t6hjer3.js +5 -0
- package/dist/chunk-2t6hjer3.js.map +10 -0
- package/dist/chunk-6yggz45h.js +5 -0
- package/dist/{chunk-794hc3m4.js.map → chunk-6yggz45h.js.map} +2 -2
- package/dist/chunk-dgkh77yp.js +4 -0
- package/dist/chunk-dgkh77yp.js.map +10 -0
- package/dist/chunk-jfs80vhp.js +3 -0
- package/dist/chunk-jfs80vhp.js.map +10 -0
- package/dist/chunk-sqn04kae.js +4 -0
- package/dist/{chunk-v0bahtg2.js.map → chunk-sqn04kae.js.map} +1 -1
- package/dist/chunk-va2awz12.js +4 -0
- package/dist/{chunk-f4c9ttmr.js.map → chunk-va2awz12.js.map} +2 -2
- package/dist/chunk-wgtbr6ge.js +13 -0
- package/dist/chunk-wgtbr6ge.js.map +11 -0
- package/dist/core/mime.d.ts +4 -0
- package/dist/core/sigv4.d.ts +10 -0
- package/dist/core/smtp.d.ts +5 -0
- package/dist/core/smtp.js +2 -31
- package/dist/core/smtp.js.map +1 -1
- package/dist/core/types.d.ts +7 -0
- package/dist/detect.d.ts +2 -0
- package/dist/detect.js +2 -180
- package/dist/detect.js.map +4 -5
- package/dist/dkim.d.ts +21 -0
- package/dist/dkim.js +9 -0
- package/dist/dkim.js.map +10 -0
- package/dist/index.d.ts +74 -16
- package/dist/index.js +85 -14
- package/dist/mailer.d.ts +16 -0
- package/dist/mailer.js +3 -0
- package/dist/mailer.js.map +9 -0
- package/dist/plugins/template.js +2 -28
- package/dist/plugins/template.js.map +2 -2
- package/dist/pool/connection.d.ts +4 -0
- package/dist/pool/pool.d.ts +20 -0
- package/dist/pool/pool.js +2 -16
- package/dist/pool/pool.js.map +5 -3
- package/dist/transports/brevo.d.ts +1 -0
- package/dist/transports/brevo.js +2 -115
- package/dist/transports/brevo.js.map +3 -3
- package/dist/transports/mailgun.d.ts +3 -0
- package/dist/transports/mailgun.js +2 -119
- package/dist/transports/mailgun.js.map +3 -3
- package/dist/transports/postmark.d.ts +1 -0
- package/dist/transports/postmark.js +2 -113
- package/dist/transports/postmark.js.map +3 -3
- package/dist/transports/preview.d.ts +3 -0
- package/dist/transports/preview.js +2 -72
- package/dist/transports/preview.js.map +3 -3
- package/dist/transports/resend.d.ts +2 -0
- package/dist/transports/resend.js +2 -109
- package/dist/transports/resend.js.map +3 -3
- package/dist/transports/retry.d.ts +12 -1
- package/dist/transports/retry.js +2 -78
- package/dist/transports/retry.js.map +3 -3
- package/dist/transports/sendgrid.d.ts +1 -0
- package/dist/transports/sendgrid.js +2 -132
- package/dist/transports/sendgrid.js.map +3 -3
- package/dist/transports/ses.d.ts +5 -0
- package/dist/transports/ses.js +5 -251
- package/dist/transports/ses.js.map +4 -4
- package/dist/transports/smtp.d.ts +3 -0
- package/dist/transports/smtp.js +2 -26
- package/dist/transports/smtp.js.map +1 -1
- package/package.json +17 -6
- package/dist/chunk-794hc3m4.js +0 -105
- package/dist/chunk-7fqv71z1.js +0 -251
- package/dist/chunk-7fqv71z1.js.map +0 -10
- package/dist/chunk-f4c9ttmr.js +0 -154
- package/dist/chunk-mp5c9bfd.js +0 -270
- package/dist/chunk-mp5c9bfd.js.map +0 -11
- package/dist/chunk-tymfm441.js +0 -405
- package/dist/chunk-tymfm441.js.map +0 -11
- package/dist/chunk-v0bahtg2.js +0 -6
- package/dist/chunk-x3szga4k.js +0 -367
- package/dist/chunk-x3szga4k.js.map +0 -11
- package/dist/chunk-ym3zzv8b.js +0 -74
- package/dist/chunk-ym3zzv8b.js.map +0 -10
package/dist/chunk-x3szga4k.js
DELETED
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
assertSafeAddress,
|
|
3
|
-
extractEmails,
|
|
4
|
-
isValidEmail,
|
|
5
|
-
parseAddresses,
|
|
6
|
-
toMIMEHeader
|
|
7
|
-
} from "./chunk-f4c9ttmr.js";
|
|
8
|
-
import {
|
|
9
|
-
encodeBase64,
|
|
10
|
-
encodeHeader,
|
|
11
|
-
encodeUtf8
|
|
12
|
-
} from "./chunk-794hc3m4.js";
|
|
13
|
-
|
|
14
|
-
// src/core/dkim.ts
|
|
15
|
-
var CRLF = `\r
|
|
16
|
-
`;
|
|
17
|
-
var DEFAULT_HEADER_FIELDS = "from:to:subject:date:message-id:mime-version:content-type";
|
|
18
|
-
function canonicalizeHeadersRelaxed(headers, fieldNames) {
|
|
19
|
-
const parsed = parseHeaders(headers);
|
|
20
|
-
const lines = [];
|
|
21
|
-
for (const name of fieldNames) {
|
|
22
|
-
const key = name.toLowerCase().trim();
|
|
23
|
-
const values = parsed.get(key);
|
|
24
|
-
if (!values) {
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
for (const value of values) {
|
|
28
|
-
const unfolded = value.replace(/\r?\n/g, "").replace(/\s+/g, " ").trim();
|
|
29
|
-
lines.push(`${key}:${unfolded}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return lines.length > 0 ? `${lines.join(CRLF)}${CRLF}` : "";
|
|
33
|
-
}
|
|
34
|
-
function canonicalizeBodyRelaxed(body) {
|
|
35
|
-
const normalized = body.replace(/\r\n/g, `
|
|
36
|
-
`).replace(/\r/g, `
|
|
37
|
-
`);
|
|
38
|
-
const lines = normalized.split(`
|
|
39
|
-
`).map((line) => line.replace(/[ \t]+$/g, "").replace(/[ \t]+/g, " ")).filter((line) => line.length > 0);
|
|
40
|
-
if (lines.length === 0) {
|
|
41
|
-
return CRLF;
|
|
42
|
-
}
|
|
43
|
-
return `${lines.join(CRLF)}${CRLF}`;
|
|
44
|
-
}
|
|
45
|
-
async function importPrivateKey(pem, algorithm) {
|
|
46
|
-
if (algorithm === "ed25519-sha256" && /OPENSSH PRIVATE KEY/i.test(pem)) {
|
|
47
|
-
throw new Error("Ed25519 keys must be in PKCS#8 PEM format (-----BEGIN PRIVATE KEY-----). Convert with: openssl pkcs8 -topk8 -nocrypt -in key.pem -out key_pkcs8.pem");
|
|
48
|
-
}
|
|
49
|
-
const der = pemToDer(pem);
|
|
50
|
-
const derBuffer = toArrayBuffer(der);
|
|
51
|
-
try {
|
|
52
|
-
if (algorithm === "ed25519-sha256") {
|
|
53
|
-
return await crypto.subtle.importKey("pkcs8", derBuffer, { name: "Ed25519" }, false, [
|
|
54
|
-
"sign"
|
|
55
|
-
]);
|
|
56
|
-
}
|
|
57
|
-
return await crypto.subtle.importKey("pkcs8", derBuffer, { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, ["sign"]);
|
|
58
|
-
} catch (err) {
|
|
59
|
-
if (algorithm === "ed25519-sha256" && err instanceof DOMException) {
|
|
60
|
-
throw new Error("Ed25519 DKIM requires Node.js ≥ 18.4, Bun ≥ 1.0, or Cloudflare Workers", {
|
|
61
|
-
cause: err
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
throw err;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
async function signDKIM(rawMessage, config) {
|
|
68
|
-
const algorithm = config.algorithm ?? "rsa-sha256";
|
|
69
|
-
const fieldList = (config.headerFieldNames ?? DEFAULT_HEADER_FIELDS).split(":").map((f) => f.trim().toLowerCase()).filter(Boolean);
|
|
70
|
-
const skip = new Set((config.skipFields ?? "").split(":").map((f) => f.trim().toLowerCase()).filter(Boolean));
|
|
71
|
-
const signFields = fieldList.filter((f) => !skip.has(f));
|
|
72
|
-
const text = new TextDecoder().decode(rawMessage);
|
|
73
|
-
const sep = text.indexOf(`\r
|
|
74
|
-
\r
|
|
75
|
-
`);
|
|
76
|
-
const headerPart = sep >= 0 ? text.slice(0, sep) : text;
|
|
77
|
-
const bodyPart = sep >= 0 ? text.slice(sep + 4) : "";
|
|
78
|
-
const bodyCanonical = canonicalizeBodyRelaxed(bodyPart);
|
|
79
|
-
const bodyHash = await sha256Base64(encodeUtf8(bodyCanonical));
|
|
80
|
-
const dkimAlgo = algorithm === "ed25519-sha256" ? "ed25519-sha256" : "rsa-sha256";
|
|
81
|
-
const headerList = signFields.join(":");
|
|
82
|
-
const timestamp = Math.floor(Date.now() / 1000);
|
|
83
|
-
const dkimWithoutSig = [
|
|
84
|
-
`v=1`,
|
|
85
|
-
`a=${dkimAlgo}`,
|
|
86
|
-
`c=relaxed/relaxed`,
|
|
87
|
-
`d=${config.domainName}`,
|
|
88
|
-
`s=${config.keySelector}`,
|
|
89
|
-
`h=${headerList}`,
|
|
90
|
-
`bh=${bodyHash}`,
|
|
91
|
-
`b=`,
|
|
92
|
-
`t=${timestamp}`
|
|
93
|
-
].join("; ");
|
|
94
|
-
const dkimHeaderName = "dkim-signature";
|
|
95
|
-
const dkimHeaderValue = dkimWithoutSig;
|
|
96
|
-
const headersWithDkim = `${headerPart}${CRLF}${dkimHeaderName}:${dkimHeaderValue}`;
|
|
97
|
-
const canonical = canonicalizeHeadersRelaxed(headersWithDkim, [...signFields, dkimHeaderName]);
|
|
98
|
-
const key = await importPrivateKey(config.privateKey, algorithm);
|
|
99
|
-
const data = encodeUtf8(canonical);
|
|
100
|
-
const signature = await signData(key, data, algorithm);
|
|
101
|
-
const bValue = encodeBase64(signature).replace(/\r\n/g, "");
|
|
102
|
-
const header = `DKIM-Signature: v=1; a=${dkimAlgo}; c=relaxed/relaxed; d=${config.domainName}; s=${config.keySelector}; h=${headerList}; bh=${bodyHash}; b=${bValue}; t=${timestamp}`;
|
|
103
|
-
return { header };
|
|
104
|
-
}
|
|
105
|
-
function parseHeaders(headerBlock) {
|
|
106
|
-
const map = new Map;
|
|
107
|
-
const lines = headerBlock.split(/\r?\n/).filter((l) => l.length > 0);
|
|
108
|
-
let currentName = "";
|
|
109
|
-
let currentValue = "";
|
|
110
|
-
const flush = () => {
|
|
111
|
-
if (!currentName) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const key = currentName.toLowerCase();
|
|
115
|
-
const list = map.get(key) ?? [];
|
|
116
|
-
list.push(currentValue);
|
|
117
|
-
map.set(key, list);
|
|
118
|
-
currentName = "";
|
|
119
|
-
currentValue = "";
|
|
120
|
-
};
|
|
121
|
-
for (const line of lines) {
|
|
122
|
-
if (/^[ \t]/.test(line) && currentName) {
|
|
123
|
-
currentValue += ` ${line.trim()}`;
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
flush();
|
|
127
|
-
const colon = line.indexOf(":");
|
|
128
|
-
if (colon > 0) {
|
|
129
|
-
currentName = line.slice(0, colon).trim();
|
|
130
|
-
currentValue = line.slice(colon + 1).trim();
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
flush();
|
|
134
|
-
return map;
|
|
135
|
-
}
|
|
136
|
-
function pemToDer(pem) {
|
|
137
|
-
const lines = pem.replace(/-----BEGIN[^-]+-----/g, "").replace(/-----END[^-]+-----/g, "").replace(/\s/g, "");
|
|
138
|
-
const binary = atob(lines);
|
|
139
|
-
const der = new Uint8Array(binary.length);
|
|
140
|
-
for (let i = 0;i < binary.length; i++) {
|
|
141
|
-
der[i] = binary.charCodeAt(i);
|
|
142
|
-
}
|
|
143
|
-
return der;
|
|
144
|
-
}
|
|
145
|
-
function toArrayBuffer(bytes) {
|
|
146
|
-
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
147
|
-
}
|
|
148
|
-
async function sha256Base64(data) {
|
|
149
|
-
const hash = await crypto.subtle.digest("SHA-256", toArrayBuffer(data));
|
|
150
|
-
return encodeBase64(new Uint8Array(hash)).replace(/\r\n/g, "");
|
|
151
|
-
}
|
|
152
|
-
async function signData(key, data, algorithm) {
|
|
153
|
-
const buf = toArrayBuffer(data);
|
|
154
|
-
if (algorithm === "ed25519-sha256") {
|
|
155
|
-
const sig2 = await crypto.subtle.sign("Ed25519", key, buf);
|
|
156
|
-
return new Uint8Array(sig2);
|
|
157
|
-
}
|
|
158
|
-
const sig = await crypto.subtle.sign({ name: "RSASSA-PKCS1-v1_5" }, key, buf);
|
|
159
|
-
return new Uint8Array(sig);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// src/core/mime.ts
|
|
163
|
-
function sanitizeHeaderValue(value) {
|
|
164
|
-
return value.replace(/\r\n?|\n/g, " ").trim();
|
|
165
|
-
}
|
|
166
|
-
function sanitizeAddress(addr) {
|
|
167
|
-
assertSafeAddress(addr.address, "address");
|
|
168
|
-
if (addr.name !== undefined) {
|
|
169
|
-
assertSafeAddress(addr.name, "display name");
|
|
170
|
-
}
|
|
171
|
-
const address = addr.address.trim();
|
|
172
|
-
if (!isValidEmail(address)) {
|
|
173
|
-
throw new Error(`Invalid email address: ${JSON.stringify(addr.address)}`);
|
|
174
|
-
}
|
|
175
|
-
const sanitized = { address };
|
|
176
|
-
if (addr.name) {
|
|
177
|
-
sanitized.name = sanitizeHeaderValue(addr.name);
|
|
178
|
-
}
|
|
179
|
-
return sanitized;
|
|
180
|
-
}
|
|
181
|
-
var CRLF2 = `\r
|
|
182
|
-
`;
|
|
183
|
-
async function buildMIME(options, dkim) {
|
|
184
|
-
const messageId = options.messageId ?? generateMessageId();
|
|
185
|
-
const date = (options.date ?? new Date).toUTCString();
|
|
186
|
-
const fromAddrs = parseAddresses(options.from);
|
|
187
|
-
const toAddrs = parseAddresses(options.to);
|
|
188
|
-
const ccAddrs = options.cc ? parseAddresses(options.cc) : [];
|
|
189
|
-
if (fromAddrs.length === 0) {
|
|
190
|
-
throw new Error("Missing from address");
|
|
191
|
-
}
|
|
192
|
-
if (toAddrs.length === 0) {
|
|
193
|
-
throw new Error("Missing to address");
|
|
194
|
-
}
|
|
195
|
-
const envelope = {
|
|
196
|
-
from: fromAddrs[0]?.address ?? "",
|
|
197
|
-
to: [
|
|
198
|
-
...extractEmails(options.to),
|
|
199
|
-
...options.cc ? extractEmails(options.cc) : [],
|
|
200
|
-
...options.bcc ? extractEmails(options.bcc) : []
|
|
201
|
-
]
|
|
202
|
-
};
|
|
203
|
-
const attachments = options.attachments ?? [];
|
|
204
|
-
const inlineAttachments = attachments.filter((a) => a.inline || a.contentId);
|
|
205
|
-
const regularAttachments = attachments.filter((a) => !a.inline && !a.contentId);
|
|
206
|
-
let root = buildSimpleBody(options);
|
|
207
|
-
if (inlineAttachments.length > 0) {
|
|
208
|
-
const boundary = generateBoundary();
|
|
209
|
-
root = {
|
|
210
|
-
contentType: `multipart/related; boundary="${boundary}"`,
|
|
211
|
-
content: assembleMultipart(boundary, [
|
|
212
|
-
formatNestedPart(buildSimpleBody(options)),
|
|
213
|
-
...inlineAttachments.map(formatAttachmentPart)
|
|
214
|
-
])
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
if (regularAttachments.length > 0) {
|
|
218
|
-
const boundary = generateBoundary();
|
|
219
|
-
root = {
|
|
220
|
-
contentType: `multipart/mixed; boundary="${boundary}"`,
|
|
221
|
-
content: assembleMultipart(boundary, [
|
|
222
|
-
formatNestedPart(root),
|
|
223
|
-
...regularAttachments.map(formatAttachmentPart)
|
|
224
|
-
])
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
const headers = [
|
|
228
|
-
foldHeader("From", fromAddrs.map((a) => toMIMEHeader(sanitizeAddress(a))).join(", ")),
|
|
229
|
-
foldHeader("To", toAddrs.map((a) => toMIMEHeader(sanitizeAddress(a))).join(", "))
|
|
230
|
-
];
|
|
231
|
-
if (ccAddrs.length > 0) {
|
|
232
|
-
headers.push(foldHeader("Cc", ccAddrs.map((a) => toMIMEHeader(sanitizeAddress(a))).join(", ")));
|
|
233
|
-
}
|
|
234
|
-
if (options.replyTo) {
|
|
235
|
-
headers.push(foldHeader("Reply-To", parseAddresses(options.replyTo).map((a) => toMIMEHeader(sanitizeAddress(a))).join(", ")));
|
|
236
|
-
}
|
|
237
|
-
headers.push(foldHeader("Subject", encodeHeader(sanitizeHeaderValue(options.subject))), foldHeader("Date", date), foldHeader("Message-ID", messageId), "MIME-Version: 1.0");
|
|
238
|
-
if (options.priority === "high") {
|
|
239
|
-
headers.push("X-Priority: 1", "Importance: high");
|
|
240
|
-
} else if (options.priority === "low") {
|
|
241
|
-
headers.push("X-Priority: 5", "Importance: low");
|
|
242
|
-
}
|
|
243
|
-
if (options.headers) {
|
|
244
|
-
for (const [key, value] of Object.entries(options.headers)) {
|
|
245
|
-
headers.push(foldHeader(sanitizeHeaderValue(key), sanitizeHeaderValue(value)));
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
headers.push(`Content-Type: ${root.contentType}`);
|
|
249
|
-
if (root.contentTransferEncoding) {
|
|
250
|
-
headers.push(`Content-Transfer-Encoding: ${root.contentTransferEncoding}`);
|
|
251
|
-
}
|
|
252
|
-
const rawText = `${headers.join(CRLF2)}${CRLF2}${CRLF2}${root.content}`;
|
|
253
|
-
let raw = encodeUtf8(rawText);
|
|
254
|
-
if (dkim) {
|
|
255
|
-
const { header } = await signDKIM(raw, dkim);
|
|
256
|
-
const signedText = `${header}${CRLF2}${rawText}`;
|
|
257
|
-
raw = encodeUtf8(signedText);
|
|
258
|
-
}
|
|
259
|
-
return { raw, envelope, messageId, size: raw.length };
|
|
260
|
-
}
|
|
261
|
-
function buildSimpleBody(options) {
|
|
262
|
-
const hasText = Boolean(options.text);
|
|
263
|
-
const hasHtml = Boolean(options.html);
|
|
264
|
-
if (hasText && hasHtml) {
|
|
265
|
-
const boundary = generateBoundary();
|
|
266
|
-
return {
|
|
267
|
-
contentType: `multipart/alternative; boundary="${boundary}"`,
|
|
268
|
-
content: assembleMultipart(boundary, [
|
|
269
|
-
formatSimplePart({
|
|
270
|
-
contentType: "text/plain; charset=utf-8",
|
|
271
|
-
contentTransferEncoding: "8bit",
|
|
272
|
-
content: options.text ?? ""
|
|
273
|
-
}),
|
|
274
|
-
formatSimplePart({
|
|
275
|
-
contentType: "text/html; charset=utf-8",
|
|
276
|
-
contentTransferEncoding: "8bit",
|
|
277
|
-
content: options.html ?? ""
|
|
278
|
-
})
|
|
279
|
-
])
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
if (hasHtml) {
|
|
283
|
-
return {
|
|
284
|
-
contentType: "text/html; charset=utf-8",
|
|
285
|
-
contentTransferEncoding: "8bit",
|
|
286
|
-
content: options.html ?? ""
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
return {
|
|
290
|
-
contentType: "text/plain; charset=utf-8",
|
|
291
|
-
contentTransferEncoding: "8bit",
|
|
292
|
-
content: options.text ?? ""
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
function formatSimplePart(part) {
|
|
296
|
-
const headers = [`Content-Type: ${part.contentType}`];
|
|
297
|
-
if (part.contentTransferEncoding) {
|
|
298
|
-
headers.push(`Content-Transfer-Encoding: ${part.contentTransferEncoding}`);
|
|
299
|
-
}
|
|
300
|
-
return `${headers.join(CRLF2)}${CRLF2}${CRLF2}${part.content}`;
|
|
301
|
-
}
|
|
302
|
-
function formatNestedPart(part) {
|
|
303
|
-
const headers = [`Content-Type: ${part.contentType}`];
|
|
304
|
-
if (part.contentTransferEncoding) {
|
|
305
|
-
headers.push(`Content-Transfer-Encoding: ${part.contentTransferEncoding}`);
|
|
306
|
-
}
|
|
307
|
-
return `${headers.join(CRLF2)}${CRLF2}${CRLF2}${part.content}`;
|
|
308
|
-
}
|
|
309
|
-
function formatAttachmentPart(attachment) {
|
|
310
|
-
if (!attachment.content || typeof attachment.content === "string") {
|
|
311
|
-
throw new Error(`Attachment "${attachment.filename}" requires Uint8Array content`);
|
|
312
|
-
}
|
|
313
|
-
const safeFilename = sanitizeHeaderValue(attachment.filename ?? "");
|
|
314
|
-
const headers = [
|
|
315
|
-
`Content-Type: ${attachment.contentType ?? "application/octet-stream"}`,
|
|
316
|
-
"Content-Transfer-Encoding: base64",
|
|
317
|
-
`Content-Disposition: ${attachment.inline ? "inline" : "attachment"}; filename="${safeFilename}"`
|
|
318
|
-
];
|
|
319
|
-
if (attachment.contentId) {
|
|
320
|
-
headers.push(`Content-ID: <${attachment.contentId}>`);
|
|
321
|
-
}
|
|
322
|
-
if (attachment.headers) {
|
|
323
|
-
for (const [key, value] of Object.entries(attachment.headers)) {
|
|
324
|
-
const safeKey = sanitizeHeaderValue(key);
|
|
325
|
-
const safeValue = sanitizeHeaderValue(value);
|
|
326
|
-
headers.push(`${safeKey}: ${safeValue}`);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
return `${headers.join(CRLF2)}${CRLF2}${CRLF2}${encodeBase64(attachment.content)}`;
|
|
330
|
-
}
|
|
331
|
-
function assembleMultipart(boundary, parts) {
|
|
332
|
-
const segments = parts.map((part) => `--${boundary}${CRLF2}${part}`);
|
|
333
|
-
segments.push(`--${boundary}--`);
|
|
334
|
-
return segments.join(CRLF2);
|
|
335
|
-
}
|
|
336
|
-
function generateMessageId() {
|
|
337
|
-
const random = crypto.getRandomValues(new Uint8Array(8));
|
|
338
|
-
const hex = Array.from(random, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
339
|
-
return `<${Date.now()}.${hex}@sently>`;
|
|
340
|
-
}
|
|
341
|
-
function generateBoundary() {
|
|
342
|
-
const random = crypto.getRandomValues(new Uint8Array(12));
|
|
343
|
-
const hex = Array.from(random, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
344
|
-
return `----sently_${hex}`;
|
|
345
|
-
}
|
|
346
|
-
function foldHeader(name, value) {
|
|
347
|
-
const line = `${name}: ${value}`;
|
|
348
|
-
if (line.length <= 76) {
|
|
349
|
-
return line;
|
|
350
|
-
}
|
|
351
|
-
const chunks = [];
|
|
352
|
-
let remaining = line;
|
|
353
|
-
while (remaining.length > 76) {
|
|
354
|
-
let breakAt = remaining.lastIndexOf(" ", 76);
|
|
355
|
-
if (breakAt <= name.length + 1) {
|
|
356
|
-
breakAt = 76;
|
|
357
|
-
}
|
|
358
|
-
chunks.push(remaining.slice(0, breakAt));
|
|
359
|
-
remaining = ` ${remaining.slice(breakAt).trimStart()}`;
|
|
360
|
-
}
|
|
361
|
-
chunks.push(remaining);
|
|
362
|
-
return chunks.join(`${CRLF2} `);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
export { buildMIME };
|
|
366
|
-
|
|
367
|
-
//# debugId=AA495EEF0CB2364A64756E2164756E21
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/core/dkim.ts", "../src/core/mime.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"/**\n * @module\n * DKIM (DomainKeys Identified Mail) signing per RFC 6376.\n * Supports RSA-SHA256 and Ed25519-SHA256 using Web Crypto.\n * Ed25519 requires Node.js ≥ 18.4, Bun ≥ 1.0, or Cloudflare Workers.\n *\n * @example\n * ```ts\n * import { signDKIM } from \"sently/core/dkim\";\n * const signed = await signDKIM(rawMessage, {\n * domainName: \"example.com\",\n * keySelector: \"2024\",\n * privateKey: \"-----BEGIN PRIVATE KEY-----\\\\n...\",\n * });\n * ```\n */\nimport { encodeBase64, encodeUtf8 } from \"./base64.js\";\nimport type { DKIMConfig } from \"./types.js\";\n\nconst CRLF = \"\\r\\n\";\nconst DEFAULT_HEADER_FIELDS = \"from:to:subject:date:message-id:mime-version:content-type\";\n\n/** Result of DKIM signing — the header line to prepend. */\nexport interface DKIMSignResult {\n /** Complete DKIM-Signature header line (without trailing CRLF). */\n header: string;\n}\n\n/**\n * Canonicalize headers using the relaxed algorithm (RFC 6376 §3.4.2).\n */\nexport function canonicalizeHeadersRelaxed(headers: string, fieldNames: string[]): string {\n const parsed = parseHeaders(headers);\n const lines: string[] = [];\n\n for (const name of fieldNames) {\n const key = name.toLowerCase().trim();\n const values = parsed.get(key);\n if (!values) {\n continue;\n }\n for (const value of values) {\n const unfolded = value.replace(/\\r?\\n/g, \"\").replace(/\\s+/g, \" \").trim();\n lines.push(`${key}:${unfolded}`);\n }\n }\n\n return lines.length > 0 ? `${lines.join(CRLF)}${CRLF}` : \"\";\n}\n\n/**\n * Canonicalize body using the relaxed algorithm (RFC 6376 §3.4.4).\n */\nexport function canonicalizeBodyRelaxed(body: string): string {\n const normalized = body.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n const lines = normalized\n .split(\"\\n\")\n .map((line) => line.replace(/[ \\t]+$/g, \"\").replace(/[ \\t]+/g, \" \"))\n .filter((line) => line.length > 0);\n if (lines.length === 0) {\n return CRLF;\n }\n return `${lines.join(CRLF)}${CRLF}`;\n}\n\n/**\n * Import a PEM-encoded private key into a Web Crypto CryptoKey.\n */\nexport async function importPrivateKey(\n pem: string,\n algorithm: \"rsa-sha256\" | \"ed25519-sha256\",\n): Promise<CryptoKey> {\n if (algorithm === \"ed25519-sha256\" && /OPENSSH PRIVATE KEY/i.test(pem)) {\n throw new Error(\n \"Ed25519 keys must be in PKCS#8 PEM format (-----BEGIN PRIVATE KEY-----). Convert with: openssl pkcs8 -topk8 -nocrypt -in key.pem -out key_pkcs8.pem\",\n );\n }\n\n const der = pemToDer(pem);\n\n const derBuffer = toArrayBuffer(der);\n\n try {\n if (algorithm === \"ed25519-sha256\") {\n return await crypto.subtle.importKey(\"pkcs8\", derBuffer, { name: \"Ed25519\" }, false, [\n \"sign\",\n ]);\n }\n return await crypto.subtle.importKey(\n \"pkcs8\",\n derBuffer,\n { name: \"RSASSA-PKCS1-v1_5\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n } catch (err) {\n if (algorithm === \"ed25519-sha256\" && err instanceof DOMException) {\n throw new Error(\"Ed25519 DKIM requires Node.js ≥ 18.4, Bun ≥ 1.0, or Cloudflare Workers\", {\n cause: err,\n });\n }\n throw err;\n }\n}\n\n/**\n * Sign a raw MIME message with DKIM.\n */\nexport async function signDKIM(\n rawMessage: Uint8Array,\n config: DKIMConfig,\n): Promise<DKIMSignResult> {\n const algorithm = config.algorithm ?? \"rsa-sha256\";\n const fieldList = (config.headerFieldNames ?? DEFAULT_HEADER_FIELDS)\n .split(\":\")\n .map((f) => f.trim().toLowerCase())\n .filter(Boolean);\n const skip = new Set(\n (config.skipFields ?? \"\")\n .split(\":\")\n .map((f) => f.trim().toLowerCase())\n .filter(Boolean),\n );\n const signFields = fieldList.filter((f) => !skip.has(f));\n\n const text = new TextDecoder().decode(rawMessage);\n const sep = text.indexOf(\"\\r\\n\\r\\n\");\n const headerPart = sep >= 0 ? text.slice(0, sep) : text;\n const bodyPart = sep >= 0 ? text.slice(sep + 4) : \"\";\n\n const bodyCanonical = canonicalizeBodyRelaxed(bodyPart);\n const bodyHash = await sha256Base64(encodeUtf8(bodyCanonical));\n\n const dkimAlgo = algorithm === \"ed25519-sha256\" ? \"ed25519-sha256\" : \"rsa-sha256\";\n const headerList = signFields.join(\":\");\n const timestamp = Math.floor(Date.now() / 1000);\n\n const dkimWithoutSig = [\n `v=1`,\n `a=${dkimAlgo}`,\n `c=relaxed/relaxed`,\n `d=${config.domainName}`,\n `s=${config.keySelector}`,\n `h=${headerList}`,\n `bh=${bodyHash}`,\n `b=`,\n `t=${timestamp}`,\n ].join(\"; \");\n\n const dkimHeaderName = \"dkim-signature\";\n const dkimHeaderValue = dkimWithoutSig;\n const headersWithDkim = `${headerPart}${CRLF}${dkimHeaderName}:${dkimHeaderValue}`;\n const canonical = canonicalizeHeadersRelaxed(headersWithDkim, [...signFields, dkimHeaderName]);\n\n const key = await importPrivateKey(config.privateKey, algorithm);\n const data = encodeUtf8(canonical);\n const signature = await signData(key, data, algorithm);\n const bValue = encodeBase64(signature).replace(/\\r\\n/g, \"\");\n\n const header = `DKIM-Signature: v=1; a=${dkimAlgo}; c=relaxed/relaxed; d=${config.domainName}; s=${config.keySelector}; h=${headerList}; bh=${bodyHash}; b=${bValue}; t=${timestamp}`;\n\n return { header };\n}\n\nfunction parseHeaders(headerBlock: string): Map<string, string[]> {\n const map = new Map<string, string[]>();\n const lines = headerBlock.split(/\\r?\\n/).filter((l) => l.length > 0);\n let currentName = \"\";\n let currentValue = \"\";\n\n const flush = (): void => {\n if (!currentName) {\n return;\n }\n const key = currentName.toLowerCase();\n const list = map.get(key) ?? [];\n list.push(currentValue);\n map.set(key, list);\n currentName = \"\";\n currentValue = \"\";\n };\n\n for (const line of lines) {\n if (/^[ \\t]/.test(line) && currentName) {\n currentValue += ` ${line.trim()}`;\n continue;\n }\n flush();\n const colon = line.indexOf(\":\");\n if (colon > 0) {\n currentName = line.slice(0, colon).trim();\n currentValue = line.slice(colon + 1).trim();\n }\n }\n flush();\n return map;\n}\n\nfunction pemToDer(pem: string): Uint8Array {\n const lines = pem\n .replace(/-----BEGIN[^-]+-----/g, \"\")\n .replace(/-----END[^-]+-----/g, \"\")\n .replace(/\\s/g, \"\");\n const binary = atob(lines);\n const der = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n der[i] = binary.charCodeAt(i);\n }\n return der;\n}\n\nfunction toArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n}\n\nasync function sha256Base64(data: Uint8Array): Promise<string> {\n const hash = await crypto.subtle.digest(\"SHA-256\", toArrayBuffer(data));\n return encodeBase64(new Uint8Array(hash)).replace(/\\r\\n/g, \"\");\n}\n\nasync function signData(\n key: CryptoKey,\n data: Uint8Array,\n algorithm: \"rsa-sha256\" | \"ed25519-sha256\",\n): Promise<Uint8Array> {\n const buf = toArrayBuffer(data);\n if (algorithm === \"ed25519-sha256\") {\n const sig = await crypto.subtle.sign(\"Ed25519\", key, buf);\n return new Uint8Array(sig);\n }\n const sig = await crypto.subtle.sign({ name: \"RSASSA-PKCS1-v1_5\" }, key, buf);\n return new Uint8Array(sig);\n}\n",
|
|
6
|
-
"// src/core/mime.ts\nimport {\n assertSafeAddress,\n extractEmails,\n isValidEmail,\n parseAddresses,\n toMIMEHeader,\n} from \"./address.js\";\nimport { encodeBase64, encodeHeader, encodeUtf8 } from \"./base64.js\";\nimport { signDKIM } from \"./dkim.js\";\nimport type { Address, Attachment, DKIMConfig, Envelope, MailOptions } from \"./types.js\";\n\nfunction sanitizeHeaderValue(value: string): string {\n return value.replace(/\\r\\n?|\\n/g, \" \").trim();\n}\n\nfunction sanitizeAddress(addr: Address): Address {\n // Security check runs on the raw value FIRST and fails closed: any control\n // character (CR/LF/NUL/…) is rejected outright rather than stripped and\n // accepted. parseAddresses() already enforces this upstream; re-asserting\n // here keeps the function safe if it is ever called directly.\n assertSafeAddress(addr.address, \"address\");\n if (addr.name !== undefined) {\n assertSafeAddress(addr.name, \"display name\");\n }\n\n // Only ordinary surrounding whitespace is trimmed — the address identity is\n // never altered (no CR/LF-to-space repair).\n const address = addr.address.trim();\n if (!isValidEmail(address)) {\n throw new Error(`Invalid email address: ${JSON.stringify(addr.address)}`);\n }\n\n const sanitized: Address = { address };\n if (addr.name) {\n sanitized.name = sanitizeHeaderValue(addr.name);\n }\n return sanitized;\n}\n\n/** Result of building a complete MIME message. */\nexport interface MIMEBuildResult {\n raw: Uint8Array;\n envelope: Envelope;\n messageId: string;\n size: number;\n}\n\nconst CRLF = \"\\r\\n\";\n\n/**\n * Build a complete MIME message as a Uint8Array ready for SMTP DATA.\n * When `dkim` is provided, signs the message and prepends the DKIM-Signature header.\n */\nexport async function buildMIME(options: MailOptions, dkim?: DKIMConfig): Promise<MIMEBuildResult> {\n const messageId = options.messageId ?? generateMessageId();\n const date = (options.date ?? new Date()).toUTCString();\n const fromAddrs = parseAddresses(options.from);\n const toAddrs = parseAddresses(options.to);\n const ccAddrs = options.cc ? parseAddresses(options.cc) : [];\n\n if (fromAddrs.length === 0) {\n throw new Error(\"Missing from address\");\n }\n if (toAddrs.length === 0) {\n throw new Error(\"Missing to address\");\n }\n\n const envelope: Envelope = {\n from: fromAddrs[0]?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n };\n\n const attachments = options.attachments ?? [];\n const inlineAttachments = attachments.filter((a) => a.inline || a.contentId);\n const regularAttachments = attachments.filter((a) => !a.inline && !a.contentId);\n\n let root = buildSimpleBody(options);\n\n if (inlineAttachments.length > 0) {\n const boundary = generateBoundary();\n root = {\n contentType: `multipart/related; boundary=\"${boundary}\"`,\n content: assembleMultipart(boundary, [\n formatNestedPart(buildSimpleBody(options)),\n ...inlineAttachments.map(formatAttachmentPart),\n ]),\n };\n }\n\n if (regularAttachments.length > 0) {\n const boundary = generateBoundary();\n root = {\n contentType: `multipart/mixed; boundary=\"${boundary}\"`,\n content: assembleMultipart(boundary, [\n formatNestedPart(root),\n ...regularAttachments.map(formatAttachmentPart),\n ]),\n };\n }\n\n const headers: string[] = [\n foldHeader(\"From\", fromAddrs.map((a) => toMIMEHeader(sanitizeAddress(a))).join(\", \")),\n foldHeader(\"To\", toAddrs.map((a) => toMIMEHeader(sanitizeAddress(a))).join(\", \")),\n ];\n\n if (ccAddrs.length > 0) {\n headers.push(foldHeader(\"Cc\", ccAddrs.map((a) => toMIMEHeader(sanitizeAddress(a))).join(\", \")));\n }\n\n if (options.replyTo) {\n headers.push(\n foldHeader(\n \"Reply-To\",\n parseAddresses(options.replyTo)\n .map((a) => toMIMEHeader(sanitizeAddress(a)))\n .join(\", \"),\n ),\n );\n }\n\n headers.push(\n foldHeader(\"Subject\", encodeHeader(sanitizeHeaderValue(options.subject))),\n foldHeader(\"Date\", date),\n foldHeader(\"Message-ID\", messageId),\n \"MIME-Version: 1.0\",\n );\n\n if (options.priority === \"high\") {\n headers.push(\"X-Priority: 1\", \"Importance: high\");\n } else if (options.priority === \"low\") {\n headers.push(\"X-Priority: 5\", \"Importance: low\");\n }\n\n if (options.headers) {\n for (const [key, value] of Object.entries(options.headers)) {\n headers.push(foldHeader(sanitizeHeaderValue(key), sanitizeHeaderValue(value)));\n }\n }\n\n headers.push(`Content-Type: ${root.contentType}`);\n if (root.contentTransferEncoding) {\n headers.push(`Content-Transfer-Encoding: ${root.contentTransferEncoding}`);\n }\n\n const rawText = `${headers.join(CRLF)}${CRLF}${CRLF}${root.content}`;\n let raw = encodeUtf8(rawText);\n\n if (dkim) {\n const { header } = await signDKIM(raw, dkim);\n const signedText = `${header}${CRLF}${rawText}`;\n raw = encodeUtf8(signedText);\n }\n\n return { raw, envelope, messageId, size: raw.length };\n}\n\ninterface SimpleBody {\n contentType: string;\n contentTransferEncoding?: string;\n content: string;\n}\n\nfunction buildSimpleBody(options: MailOptions): SimpleBody {\n const hasText = Boolean(options.text);\n const hasHtml = Boolean(options.html);\n\n if (hasText && hasHtml) {\n const boundary = generateBoundary();\n return {\n contentType: `multipart/alternative; boundary=\"${boundary}\"`,\n content: assembleMultipart(boundary, [\n formatSimplePart({\n contentType: \"text/plain; charset=utf-8\",\n contentTransferEncoding: \"8bit\",\n content: options.text ?? \"\",\n }),\n formatSimplePart({\n contentType: \"text/html; charset=utf-8\",\n contentTransferEncoding: \"8bit\",\n content: options.html ?? \"\",\n }),\n ]),\n };\n }\n\n if (hasHtml) {\n return {\n contentType: \"text/html; charset=utf-8\",\n contentTransferEncoding: \"8bit\",\n content: options.html ?? \"\",\n };\n }\n\n return {\n contentType: \"text/plain; charset=utf-8\",\n contentTransferEncoding: \"8bit\",\n content: options.text ?? \"\",\n };\n}\n\nfunction formatSimplePart(part: SimpleBody): string {\n const headers = [`Content-Type: ${part.contentType}`];\n if (part.contentTransferEncoding) {\n headers.push(`Content-Transfer-Encoding: ${part.contentTransferEncoding}`);\n }\n return `${headers.join(CRLF)}${CRLF}${CRLF}${part.content}`;\n}\n\nfunction formatNestedPart(part: SimpleBody): string {\n const headers = [`Content-Type: ${part.contentType}`];\n if (part.contentTransferEncoding) {\n headers.push(`Content-Transfer-Encoding: ${part.contentTransferEncoding}`);\n }\n return `${headers.join(CRLF)}${CRLF}${CRLF}${part.content}`;\n}\n\nfunction formatAttachmentPart(attachment: Attachment): string {\n if (!attachment.content || typeof attachment.content === \"string\") {\n throw new Error(`Attachment \"${attachment.filename}\" requires Uint8Array content`);\n }\n\n const safeFilename = sanitizeHeaderValue(attachment.filename ?? \"\");\n const headers = [\n `Content-Type: ${attachment.contentType ?? \"application/octet-stream\"}`,\n \"Content-Transfer-Encoding: base64\",\n `Content-Disposition: ${attachment.inline ? \"inline\" : \"attachment\"}; filename=\"${safeFilename}\"`,\n ];\n\n if (attachment.contentId) {\n headers.push(`Content-ID: <${attachment.contentId}>`);\n }\n\n if (attachment.headers) {\n for (const [key, value] of Object.entries(attachment.headers)) {\n const safeKey = sanitizeHeaderValue(key);\n const safeValue = sanitizeHeaderValue(value);\n headers.push(`${safeKey}: ${safeValue}`);\n }\n }\n\n return `${headers.join(CRLF)}${CRLF}${CRLF}${encodeBase64(attachment.content)}`;\n}\n\nfunction assembleMultipart(boundary: string, parts: string[]): string {\n const segments = parts.map((part) => `--${boundary}${CRLF}${part}`);\n segments.push(`--${boundary}--`);\n return segments.join(CRLF);\n}\n\nfunction generateMessageId(): string {\n const random = crypto.getRandomValues(new Uint8Array(8));\n const hex = Array.from(random, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `<${Date.now()}.${hex}@sently>`;\n}\n\nfunction generateBoundary(): string {\n const random = crypto.getRandomValues(new Uint8Array(12));\n const hex = Array.from(random, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `----sently_${hex}`;\n}\n\nfunction foldHeader(name: string, value: string): string {\n const line = `${name}: ${value}`;\n if (line.length <= 76) {\n return line;\n }\n\n const chunks: string[] = [];\n let remaining = line;\n\n while (remaining.length > 76) {\n let breakAt = remaining.lastIndexOf(\" \", 76);\n if (breakAt <= name.length + 1) {\n breakAt = 76;\n }\n chunks.push(remaining.slice(0, breakAt));\n remaining = ` ${remaining.slice(breakAt).trimStart()}`;\n }\n chunks.push(remaining);\n\n return chunks.join(`${CRLF} `);\n}\n"
|
|
7
|
-
],
|
|
8
|
-
"mappings": "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",
|
|
9
|
-
"debugId": "AA495EEF0CB2364A64756E2164756E21",
|
|
10
|
-
"names": []
|
|
11
|
-
}
|
package/dist/chunk-ym3zzv8b.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
encodeBase64,
|
|
3
|
-
encodeUtf8
|
|
4
|
-
} from "./chunk-794hc3m4.js";
|
|
5
|
-
|
|
6
|
-
// src/auth/oauth2.ts
|
|
7
|
-
var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
8
|
-
var MICROSOFT_TOKEN_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
|
|
9
|
-
var EXPIRY_BUFFER_MS = 30000;
|
|
10
|
-
|
|
11
|
-
class OAuth2Client {
|
|
12
|
-
config;
|
|
13
|
-
cachedToken = null;
|
|
14
|
-
expiresAt = 0;
|
|
15
|
-
refreshPromise = null;
|
|
16
|
-
constructor(config) {
|
|
17
|
-
this.config = config;
|
|
18
|
-
if (config.accessToken) {
|
|
19
|
-
this.cachedToken = config.accessToken;
|
|
20
|
-
this.expiresAt = Date.now() + 3600000;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
async getAccessToken() {
|
|
24
|
-
if (this.config.getToken) {
|
|
25
|
-
return this.config.getToken();
|
|
26
|
-
}
|
|
27
|
-
if (this.cachedToken && Date.now() < this.expiresAt - EXPIRY_BUFFER_MS) {
|
|
28
|
-
return this.cachedToken;
|
|
29
|
-
}
|
|
30
|
-
if (!this.refreshPromise) {
|
|
31
|
-
this.refreshPromise = this.refreshAccessToken().finally(() => {
|
|
32
|
-
this.refreshPromise = null;
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
return this.refreshPromise;
|
|
36
|
-
}
|
|
37
|
-
async refreshAccessToken() {
|
|
38
|
-
if (this.config.getToken) {
|
|
39
|
-
const token = await this.config.getToken();
|
|
40
|
-
this.cachedToken = token;
|
|
41
|
-
this.expiresAt = Date.now() + 3600000;
|
|
42
|
-
return token;
|
|
43
|
-
}
|
|
44
|
-
const tokenUrl = this.config.tokenUrl ?? GOOGLE_TOKEN_URL;
|
|
45
|
-
const body = new URLSearchParams({
|
|
46
|
-
client_id: this.config.clientId,
|
|
47
|
-
client_secret: this.config.clientSecret,
|
|
48
|
-
refresh_token: this.config.refreshToken,
|
|
49
|
-
grant_type: "refresh_token"
|
|
50
|
-
});
|
|
51
|
-
const response = await fetch(tokenUrl, {
|
|
52
|
-
method: "POST",
|
|
53
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
54
|
-
body: body.toString()
|
|
55
|
-
});
|
|
56
|
-
if (!response.ok) {
|
|
57
|
-
const text = await response.text();
|
|
58
|
-
throw new Error(`OAuth2 token refresh failed (${response.status}): ${text}`);
|
|
59
|
-
}
|
|
60
|
-
const data = await response.json();
|
|
61
|
-
this.cachedToken = data.access_token;
|
|
62
|
-
this.expiresAt = Date.now() + data.expires_in * 1000;
|
|
63
|
-
return data.access_token;
|
|
64
|
-
}
|
|
65
|
-
async buildXOAUTH2() {
|
|
66
|
-
const token = await this.getAccessToken();
|
|
67
|
-
const raw = `user=${this.config.user}\x01auth=Bearer ${token}\x01\x01`;
|
|
68
|
-
return encodeBase64(encodeUtf8(raw)).replace(/\r\n/g, "");
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export { GOOGLE_TOKEN_URL, MICROSOFT_TOKEN_URL, OAuth2Client };
|
|
73
|
-
|
|
74
|
-
//# debugId=3609E75C0C54F13D64756E2164756E21
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/auth/oauth2.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"/**\n * @module\n * OAuth2 / XOAUTH2 authentication for SMTP.\n * Supports Gmail, Microsoft 365, and custom OAuth2 providers.\n *\n * @example\n * ```ts\n * import { OAuth2Client } from \"sently/auth/oauth2\";\n * const client = new OAuth2Client({\n * user: \"me@gmail.com\",\n * clientId: \"...\",\n * clientSecret: \"...\",\n * refreshToken: \"...\",\n * });\n * const token = await client.getAccessToken();\n * ```\n */\nimport { encodeBase64, encodeUtf8 } from \"../core/base64.js\";\nimport type { OAuth2Config } from \"../core/types.js\";\n\n/** Default Google OAuth2 token endpoint. */\nexport const GOOGLE_TOKEN_URL = \"https://oauth2.googleapis.com/token\";\n\n/** Microsoft OAuth2 token endpoint (common tenant). */\nexport const MICROSOFT_TOKEN_URL = \"https://login.microsoftonline.com/common/oauth2/v2.0/token\";\n\n/** OAuth2 token endpoint response shape. */\nexport interface TokenResponse {\n /** Bearer access token for API or SMTP XOAUTH2. */\n access_token: string;\n /** Token lifetime in seconds. */\n expires_in: number;\n /** Token type (typically `\"Bearer\"`). */\n token_type: string;\n}\n\nconst EXPIRY_BUFFER_MS = 30_000;\n\n/**\n * OAuth2 client with in-memory token cache and automatic refresh.\n */\nexport class OAuth2Client {\n private readonly config: OAuth2Config;\n private cachedToken: string | null = null;\n private expiresAt = 0;\n private refreshPromise: Promise<string> | null = null;\n\n /** Creates an OAuth2 client from configuration. */\n constructor(config: OAuth2Config) {\n this.config = config;\n if (config.accessToken) {\n this.cachedToken = config.accessToken;\n this.expiresAt = Date.now() + 3_600_000;\n }\n }\n\n /**\n * Get a valid access token (cached when still valid, refreshed otherwise).\n */\n async getAccessToken(): Promise<string> {\n if (this.config.getToken) {\n return this.config.getToken();\n }\n\n if (this.cachedToken && Date.now() < this.expiresAt - EXPIRY_BUFFER_MS) {\n return this.cachedToken;\n }\n\n if (!this.refreshPromise) {\n this.refreshPromise = this.refreshAccessToken().finally(() => {\n this.refreshPromise = null;\n });\n }\n return this.refreshPromise;\n }\n\n /**\n * Force-refresh the access token regardless of expiry.\n */\n async refreshAccessToken(): Promise<string> {\n if (this.config.getToken) {\n const token = await this.config.getToken();\n this.cachedToken = token;\n this.expiresAt = Date.now() + 3_600_000;\n return token;\n }\n\n const tokenUrl = this.config.tokenUrl ?? GOOGLE_TOKEN_URL;\n const body = new URLSearchParams({\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n refresh_token: this.config.refreshToken,\n grant_type: \"refresh_token\",\n });\n\n const response = await fetch(tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`OAuth2 token refresh failed (${response.status}): ${text}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n this.cachedToken = data.access_token;\n this.expiresAt = Date.now() + data.expires_in * 1000;\n return data.access_token;\n }\n\n /**\n * Build the XOAUTH2 SASL string for SMTP AUTH (base64-encoded).\n */\n async buildXOAUTH2(): Promise<string> {\n const token = await this.getAccessToken();\n const raw = `user=${this.config.user}\\x01auth=Bearer ${token}\\x01\\x01`;\n return encodeBase64(encodeUtf8(raw)).replace(/\\r\\n/g, \"\");\n }\n}\n"
|
|
6
|
-
],
|
|
7
|
-
"mappings": ";;;;;;AAqBO,IAAM,mBAAmB;AAGzB,IAAM,sBAAsB;AAYnC,IAAM,mBAAmB;AAAA;AAKlB,MAAM,aAAa;AAAA,EACP;AAAA,EACT,cAA6B;AAAA,EAC7B,YAAY;AAAA,EACZ,iBAAyC;AAAA,EAGjD,WAAW,CAAC,QAAsB;AAAA,IAChC,KAAK,SAAS;AAAA,IACd,IAAI,OAAO,aAAa;AAAA,MACtB,KAAK,cAAc,OAAO;AAAA,MAC1B,KAAK,YAAY,KAAK,IAAI,IAAI;AAAA,IAChC;AAAA;AAAA,OAMI,eAAc,GAAoB;AAAA,IACtC,IAAI,KAAK,OAAO,UAAU;AAAA,MACxB,OAAO,KAAK,OAAO,SAAS;AAAA,IAC9B;AAAA,IAEA,IAAI,KAAK,eAAe,KAAK,IAAI,IAAI,KAAK,YAAY,kBAAkB;AAAA,MACtE,OAAO,KAAK;AAAA,IACd;AAAA,IAEA,IAAI,CAAC,KAAK,gBAAgB;AAAA,MACxB,KAAK,iBAAiB,KAAK,mBAAmB,EAAE,QAAQ,MAAM;AAAA,QAC5D,KAAK,iBAAiB;AAAA,OACvB;AAAA,IACH;AAAA,IACA,OAAO,KAAK;AAAA;AAAA,OAMR,mBAAkB,GAAoB;AAAA,IAC1C,IAAI,KAAK,OAAO,UAAU;AAAA,MACxB,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS;AAAA,MACzC,KAAK,cAAc;AAAA,MACnB,KAAK,YAAY,KAAK,IAAI,IAAI;AAAA,MAC9B,OAAO;AAAA,IACT;AAAA,IAEA,MAAM,WAAW,KAAK,OAAO,YAAY;AAAA,IACzC,MAAM,OAAO,IAAI,gBAAgB;AAAA,MAC/B,WAAW,KAAK,OAAO;AAAA,MACvB,eAAe,KAAK,OAAO;AAAA,MAC3B,eAAe,KAAK,OAAO;AAAA,MAC3B,YAAY;AAAA,IACd,CAAC;AAAA,IAED,MAAM,WAAW,MAAM,MAAM,UAAU;AAAA,MACrC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,MAC/D,MAAM,KAAK,SAAS;AAAA,IACtB,CAAC;AAAA,IAED,IAAI,CAAC,SAAS,IAAI;AAAA,MAChB,MAAM,OAAO,MAAM,SAAS,KAAK;AAAA,MACjC,MAAM,IAAI,MAAM,gCAAgC,SAAS,YAAY,MAAM;AAAA,IAC7E;AAAA,IAEA,MAAM,OAAQ,MAAM,SAAS,KAAK;AAAA,IAClC,KAAK,cAAc,KAAK;AAAA,IACxB,KAAK,YAAY,KAAK,IAAI,IAAI,KAAK,aAAa;AAAA,IAChD,OAAO,KAAK;AAAA;AAAA,OAMR,aAAY,GAAoB;AAAA,IACpC,MAAM,QAAQ,MAAM,KAAK,eAAe;AAAA,IACxC,MAAM,MAAM,QAAQ,KAAK,OAAO,uBAAuB;AAAA,IACvD,OAAO,aAAa,WAAW,GAAG,CAAC,EAAE,QAAQ,SAAS,EAAE;AAAA;AAE5D;",
|
|
8
|
-
"debugId": "3609E75C0C54F13D64756E2164756E21",
|
|
9
|
-
"names": []
|
|
10
|
-
}
|