sently 0.2.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/LICENSE +21 -0
- package/README.md +367 -0
- package/dist/chunk-794hc3m4.js +105 -0
- package/dist/chunk-794hc3m4.js.map +10 -0
- package/dist/chunk-hdqpvsm8.js +116 -0
- package/dist/chunk-hdqpvsm8.js.map +11 -0
- package/dist/chunk-tch6s785.js +943 -0
- package/dist/chunk-tch6s785.js.map +14 -0
- package/dist/chunk-v0bahtg2.js +6 -0
- package/dist/chunk-v0bahtg2.js.map +9 -0
- package/dist/chunk-vm70w4e3.js +68 -0
- package/dist/chunk-vm70w4e3.js.map +10 -0
- package/dist/src/adapters/bun.js +175 -0
- package/dist/src/adapters/bun.js.map +10 -0
- package/dist/src/adapters/cf.js +78 -0
- package/dist/src/adapters/cf.js.map +10 -0
- package/dist/src/adapters/deno.js +73 -0
- package/dist/src/adapters/deno.js.map +10 -0
- package/dist/src/adapters/node.js +172 -0
- package/dist/src/adapters/node.js.map +10 -0
- package/dist/src/auth/oauth2.js +14 -0
- package/dist/src/auth/oauth2.js.map +9 -0
- package/dist/src/index.js +12 -0
- package/dist/src/index.js.map +9 -0
- package/dist/src/pool/pool.js +263 -0
- package/dist/src/pool/pool.js.map +11 -0
- package/dist/src/transports/postmark.js +85 -0
- package/dist/src/transports/postmark.js.map +10 -0
- package/dist/src/transports/resend.js +86 -0
- package/dist/src/transports/resend.js.map +10 -0
- package/dist/src/transports/sendgrid.js +104 -0
- package/dist/src/transports/sendgrid.js.map +10 -0
- package/dist/src/transports/smtp.js +24 -0
- package/dist/src/transports/smtp.js.map +9 -0
- package/package.json +105 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/transports/postmark.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @module\n * Postmark HTTP API transport for sending email via api.postmarkapp.com.\n *\n * @example\n * ```ts\n * import { PostmarkTransport } from \"sently/transports/postmark\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new PostmarkTransport({ serverToken: process.env.POSTMARK_TOKEN! }),\n * });\n *\n * await mailer.send({\n * from: \"sender@example.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * text: \"Plain text body\",\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport type { MailOptions, SendResult, Transport } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Postmark API configuration. */\nexport interface PostmarkConfig {\n serverToken: string;\n}\n\n/** Error thrown when the Postmark API returns a non-success response. */\nexport class PostmarkError extends Error {\n /** Creates a Postmark API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message);\n this.name = \"PostmarkError\";\n }\n}\n\n/**\n * Postmark HTTP API transport.\n */\nexport class PostmarkTransport implements Transport {\n private readonly serverToken: string;\n\n /** Creates a Postmark transport with the given server token. */\n constructor(config: PostmarkConfig) {\n this.serverToken = config.serverToken;\n }\n\n /** Sends an email via the Postmark HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const body = {\n From: from ? toMIMEHeader(from) : \"\",\n To: parseAddresses(options.to).map(toMIMEHeader).join(\", \"),\n Subject: options.subject,\n ...(options.cc ? { Cc: parseAddresses(options.cc).map(toMIMEHeader).join(\", \") } : {}),\n ...(options.bcc ? { Bcc: parseAddresses(options.bcc).map(toMIMEHeader).join(\", \") } : {}),\n ...(options.replyTo\n ? { ReplyTo: parseAddresses(options.replyTo).map(toMIMEHeader).join(\", \") }\n : {}),\n ...(options.text ? { TextBody: options.text } : {}),\n ...(options.html ? { HtmlBody: options.html } : {}),\n ...(options.headers\n ? { Headers: Object.entries(options.headers).map(([Name, Value]) => ({ Name, Value })) }\n : {}),\n ...(attachments.length > 0\n ? {\n Attachments: attachments.map((att) => ({\n Name: att.filename,\n Content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ContentType: att.contentType ?? \"application/octet-stream\",\n ...(att.contentId ? { ContentID: att.contentId } : {}),\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.postmarkapp.com/email\", {\n method: \"POST\",\n headers: {\n \"X-Postmark-Server-Token\": this.serverToken,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n MessageID?: string;\n Message?: string;\n ErrorCode?: number;\n };\n\n if (!response.ok) {\n throw new PostmarkError(payload.Message ?? \"Postmark API error\", response.status, payload);\n }\n\n return {\n messageId: payload.MessageID ?? options.messageId ?? \"\",\n accepted: extractEmails(options.to),\n rejected: [],\n response: payload.Message ?? \"OK\",\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;;;;;;;AAgCO,MAAM,sBAAsB,MAAM;AAAA,EAIrB;AAAA,EACA;AAAA,EAHlB,WAAW,CACT,SACgB,YACA,UAChB;AAAA,IACA,MAAM,OAAO;AAAA,IAHG;AAAA,IACA;AAAA,IAGhB,KAAK,OAAO;AAAA;AAEhB;AAAA;AAKO,MAAM,kBAAuC;AAAA,EACjC;AAAA,EAGjB,WAAW,CAAC,QAAwB;AAAA,IAClC,KAAK,cAAc,OAAO;AAAA;AAAA,OAItB,KAAI,CAAC,SAA2C;AAAA,IACpD,MAAM,cAAc,MAAM,mBAAmB,QAAQ,WAAW;AAAA,IAChE,MAAM,OAAO,eAAe,QAAQ,IAAI,EAAE;AAAA,IAE1C,MAAM,OAAO;AAAA,MACX,MAAM,OAAO,aAAa,IAAI,IAAI;AAAA,MAClC,IAAI,eAAe,QAAQ,EAAE,EAAE,IAAI,YAAY,EAAE,KAAK,IAAI;AAAA,MAC1D,SAAS,QAAQ;AAAA,SACb,QAAQ,KAAK,EAAE,IAAI,eAAe,QAAQ,EAAE,EAAE,IAAI,YAAY,EAAE,KAAK,IAAI,EAAE,IAAI,CAAC;AAAA,SAChF,QAAQ,MAAM,EAAE,KAAK,eAAe,QAAQ,GAAG,EAAE,IAAI,YAAY,EAAE,KAAK,IAAI,EAAE,IAAI,CAAC;AAAA,SACnF,QAAQ,UACR,EAAE,SAAS,eAAe,QAAQ,OAAO,EAAE,IAAI,YAAY,EAAE,KAAK,IAAI,EAAE,IACxE,CAAC;AAAA,SACD,QAAQ,OAAO,EAAE,UAAU,QAAQ,KAAK,IAAI,CAAC;AAAA,SAC7C,QAAQ,OAAO,EAAE,UAAU,QAAQ,KAAK,IAAI,CAAC;AAAA,SAC7C,QAAQ,UACR,EAAE,SAAS,OAAO,QAAQ,QAAQ,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,EAAE,MAAM,MAAM,EAAE,EAAE,IACrF,CAAC;AAAA,SACD,YAAY,SAAS,IACrB;AAAA,QACE,aAAa,YAAY,IAAI,CAAC,SAAS;AAAA,UACrC,MAAM,IAAI;AAAA,UACV,SACE,IAAI,mBAAmB,aACnB,aAAa,IAAI,OAAO,EAAE,QAAQ,SAAS,EAAE,IAC7C,IAAI;AAAA,UACV,aAAa,IAAI,eAAe;AAAA,aAC5B,IAAI,YAAY,EAAE,WAAW,IAAI,UAAU,IAAI,CAAC;AAAA,QACtD,EAAE;AAAA,MACJ,IACA,CAAC;AAAA,IACP;AAAA,IAEA,MAAM,WAAW,MAAM,MAAM,qCAAqC;AAAA,MAChE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,2BAA2B,KAAK;AAAA,QAChC,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MACV;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAAA,IAED,MAAM,UAAW,MAAM,SAAS,KAAK;AAAA,IAMrC,IAAI,CAAC,SAAS,IAAI;AAAA,MAChB,MAAM,IAAI,cAAc,QAAQ,WAAW,sBAAsB,SAAS,QAAQ,OAAO;AAAA,IAC3F;AAAA,IAEA,OAAO;AAAA,MACL,WAAW,QAAQ,aAAa,QAAQ,aAAa;AAAA,MACrD,UAAU,cAAc,QAAQ,EAAE;AAAA,MAClC,UAAU,CAAC;AAAA,MACX,UAAU,QAAQ,WAAW;AAAA,MAC7B,UAAU;AAAA,QACR,MAAM,MAAM,WAAW;AAAA,QACvB,IAAI;AAAA,UACF,GAAG,cAAc,QAAQ,EAAE;AAAA,UAC3B,GAAI,QAAQ,KAAK,cAAc,QAAQ,EAAE,IAAI,CAAC;AAAA,UAC9C,GAAI,QAAQ,MAAM,cAAc,QAAQ,GAAG,IAAI,CAAC;AAAA,QAClD;AAAA,MACF;AAAA,IACF;AAAA;AAEJ;",
|
|
8
|
+
"debugId": "882FB10A12306FDB64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractEmails,
|
|
3
|
+
parseAddresses,
|
|
4
|
+
resolveAttachments,
|
|
5
|
+
toMIMEHeader
|
|
6
|
+
} from "../../chunk-hdqpvsm8.js";
|
|
7
|
+
import {
|
|
8
|
+
encodeBase64
|
|
9
|
+
} from "../../chunk-794hc3m4.js";
|
|
10
|
+
import"../../chunk-v0bahtg2.js";
|
|
11
|
+
|
|
12
|
+
// src/transports/resend.ts
|
|
13
|
+
class ResendError extends Error {
|
|
14
|
+
statusCode;
|
|
15
|
+
apiError;
|
|
16
|
+
constructor(message, statusCode, apiError) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.statusCode = statusCode;
|
|
19
|
+
this.apiError = apiError;
|
|
20
|
+
this.name = "ResendError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class ResendTransport {
|
|
25
|
+
apiKey;
|
|
26
|
+
baseUrl;
|
|
27
|
+
constructor(config) {
|
|
28
|
+
this.apiKey = config.apiKey;
|
|
29
|
+
this.baseUrl = config.baseUrl ?? "https://api.resend.com";
|
|
30
|
+
}
|
|
31
|
+
async send(options) {
|
|
32
|
+
const attachments = await resolveAttachments(options.attachments);
|
|
33
|
+
const from = parseAddresses(options.from)[0];
|
|
34
|
+
const body = {
|
|
35
|
+
from: from ? toMIMEHeader(from) : "",
|
|
36
|
+
to: extractEmails(options.to),
|
|
37
|
+
subject: options.subject,
|
|
38
|
+
...options.cc ? { cc: extractEmails(options.cc) } : {},
|
|
39
|
+
...options.bcc ? { bcc: extractEmails(options.bcc) } : {},
|
|
40
|
+
...options.replyTo ? { reply_to: extractEmails(options.replyTo) } : {},
|
|
41
|
+
...options.text ? { text: options.text } : {},
|
|
42
|
+
...options.html ? { html: options.html } : {},
|
|
43
|
+
...options.headers ? { headers: options.headers } : {},
|
|
44
|
+
...attachments.length > 0 ? {
|
|
45
|
+
attachments: attachments.map((att) => ({
|
|
46
|
+
filename: att.filename,
|
|
47
|
+
content: att.content instanceof Uint8Array ? encodeBase64(att.content).replace(/\r\n/g, "") : att.content,
|
|
48
|
+
...att.contentType ? { content_type: att.contentType } : {}
|
|
49
|
+
}))
|
|
50
|
+
} : {}
|
|
51
|
+
};
|
|
52
|
+
const response = await fetch(`${this.baseUrl}/emails`, {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: {
|
|
55
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
56
|
+
"Content-Type": "application/json"
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify(body)
|
|
59
|
+
});
|
|
60
|
+
const payload = await response.json();
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
throw new ResendError(payload.message ?? "Resend API error", response.status, payload);
|
|
63
|
+
}
|
|
64
|
+
const accepted = extractEmails(options.to);
|
|
65
|
+
return {
|
|
66
|
+
messageId: payload.id ?? options.messageId ?? "",
|
|
67
|
+
accepted,
|
|
68
|
+
rejected: [],
|
|
69
|
+
response: payload.message ?? "Email sent",
|
|
70
|
+
envelope: {
|
|
71
|
+
from: from?.address ?? "",
|
|
72
|
+
to: [
|
|
73
|
+
...extractEmails(options.to),
|
|
74
|
+
...options.cc ? extractEmails(options.cc) : [],
|
|
75
|
+
...options.bcc ? extractEmails(options.bcc) : []
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export {
|
|
82
|
+
ResendTransport,
|
|
83
|
+
ResendError
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
//# debugId=98FD7F3A23B2FBC264756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/transports/resend.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @module\n * Resend HTTP API transport for sending email via api.resend.com.\n *\n * @example\n * ```ts\n * import { ResendTransport } from \"sently/transports/resend\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new ResendTransport({ apiKey: process.env.RESEND_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"onboarding@yourdomain.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * html: \"<p>Sent via Resend</p>\",\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport type { MailOptions, SendResult, Transport } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Resend API configuration. */\nexport interface ResendConfig {\n apiKey: string;\n baseUrl?: string;\n}\n\n/** Error thrown when the Resend API returns a non-success response. */\nexport class ResendError extends Error {\n /** Creates a Resend API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message);\n this.name = \"ResendError\";\n }\n}\n\n/**\n * Resend HTTP API transport.\n */\nexport class ResendTransport implements Transport {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n\n /** Creates a Resend transport with the given API key. */\n constructor(config: ResendConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? \"https://api.resend.com\";\n }\n\n /** Sends an email via the Resend HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n const body = {\n from: from ? toMIMEHeader(from) : \"\",\n to: extractEmails(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: extractEmails(options.cc) } : {}),\n ...(options.bcc ? { bcc: extractEmails(options.bcc) } : {}),\n ...(options.replyTo ? { reply_to: extractEmails(options.replyTo) } : {}),\n ...(options.text ? { text: options.text } : {}),\n ...(options.html ? { html: options.html } : {}),\n ...(options.headers ? { headers: options.headers } : {}),\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n filename: att.filename,\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ...(att.contentType ? { content_type: att.contentType } : {}),\n })),\n }\n : {}),\n };\n\n const response = await fetch(`${this.baseUrl}/emails`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as { id?: string; message?: string };\n\n if (!response.ok) {\n throw new ResendError(payload.message ?? \"Resend API error\", response.status, payload);\n }\n\n const accepted = extractEmails(options.to);\n return {\n messageId: payload.id ?? options.messageId ?? \"\",\n accepted,\n rejected: [],\n response: payload.message ?? \"Email sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;;;;;;;AAiCO,MAAM,oBAAoB,MAAM;AAAA,EAInB;AAAA,EACA;AAAA,EAHlB,WAAW,CACT,SACgB,YACA,UAChB;AAAA,IACA,MAAM,OAAO;AAAA,IAHG;AAAA,IACA;AAAA,IAGhB,KAAK,OAAO;AAAA;AAEhB;AAAA;AAKO,MAAM,gBAAqC;AAAA,EAC/B;AAAA,EACA;AAAA,EAGjB,WAAW,CAAC,QAAsB;AAAA,IAChC,KAAK,SAAS,OAAO;AAAA,IACrB,KAAK,UAAU,OAAO,WAAW;AAAA;AAAA,OAI7B,KAAI,CAAC,SAA2C;AAAA,IACpD,MAAM,cAAc,MAAM,mBAAmB,QAAQ,WAAW;AAAA,IAChE,MAAM,OAAO,eAAe,QAAQ,IAAI,EAAE;AAAA,IAC1C,MAAM,OAAO;AAAA,MACX,MAAM,OAAO,aAAa,IAAI,IAAI;AAAA,MAClC,IAAI,cAAc,QAAQ,EAAE;AAAA,MAC5B,SAAS,QAAQ;AAAA,SACb,QAAQ,KAAK,EAAE,IAAI,cAAc,QAAQ,EAAE,EAAE,IAAI,CAAC;AAAA,SAClD,QAAQ,MAAM,EAAE,KAAK,cAAc,QAAQ,GAAG,EAAE,IAAI,CAAC;AAAA,SACrD,QAAQ,UAAU,EAAE,UAAU,cAAc,QAAQ,OAAO,EAAE,IAAI,CAAC;AAAA,SAClE,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,SACzC,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,SACzC,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,SAClD,YAAY,SAAS,IACrB;AAAA,QACE,aAAa,YAAY,IAAI,CAAC,SAAS;AAAA,UACrC,UAAU,IAAI;AAAA,UACd,SACE,IAAI,mBAAmB,aACnB,aAAa,IAAI,OAAO,EAAE,QAAQ,SAAS,EAAE,IAC7C,IAAI;AAAA,aACN,IAAI,cAAc,EAAE,cAAc,IAAI,YAAY,IAAI,CAAC;AAAA,QAC7D,EAAE;AAAA,MACJ,IACA,CAAC;AAAA,IACP;AAAA,IAEA,MAAM,WAAW,MAAM,MAAM,GAAG,KAAK,kBAAkB;AAAA,MACrD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,KAAK;AAAA,QAC9B,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAAA,IAED,MAAM,UAAW,MAAM,SAAS,KAAK;AAAA,IAErC,IAAI,CAAC,SAAS,IAAI;AAAA,MAChB,MAAM,IAAI,YAAY,QAAQ,WAAW,oBAAoB,SAAS,QAAQ,OAAO;AAAA,IACvF;AAAA,IAEA,MAAM,WAAW,cAAc,QAAQ,EAAE;AAAA,IACzC,OAAO;AAAA,MACL,WAAW,QAAQ,MAAM,QAAQ,aAAa;AAAA,MAC9C;AAAA,MACA,UAAU,CAAC;AAAA,MACX,UAAU,QAAQ,WAAW;AAAA,MAC7B,UAAU;AAAA,QACR,MAAM,MAAM,WAAW;AAAA,QACvB,IAAI;AAAA,UACF,GAAG,cAAc,QAAQ,EAAE;AAAA,UAC3B,GAAI,QAAQ,KAAK,cAAc,QAAQ,EAAE,IAAI,CAAC;AAAA,UAC9C,GAAI,QAAQ,MAAM,cAAc,QAAQ,GAAG,IAAI,CAAC;AAAA,QAClD;AAAA,MACF;AAAA,IACF;AAAA;AAEJ;",
|
|
8
|
+
"debugId": "98FD7F3A23B2FBC264756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractEmails,
|
|
3
|
+
parseAddresses,
|
|
4
|
+
resolveAttachments
|
|
5
|
+
} from "../../chunk-hdqpvsm8.js";
|
|
6
|
+
import {
|
|
7
|
+
encodeBase64
|
|
8
|
+
} from "../../chunk-794hc3m4.js";
|
|
9
|
+
import"../../chunk-v0bahtg2.js";
|
|
10
|
+
|
|
11
|
+
// src/transports/sendgrid.ts
|
|
12
|
+
class SendGridError extends Error {
|
|
13
|
+
statusCode;
|
|
14
|
+
apiError;
|
|
15
|
+
constructor(message, statusCode, apiError) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.statusCode = statusCode;
|
|
18
|
+
this.apiError = apiError;
|
|
19
|
+
this.name = "SendGridError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class SendGridTransport {
|
|
24
|
+
apiKey;
|
|
25
|
+
constructor(config) {
|
|
26
|
+
this.apiKey = config.apiKey;
|
|
27
|
+
}
|
|
28
|
+
async send(options) {
|
|
29
|
+
const attachments = await resolveAttachments(options.attachments);
|
|
30
|
+
const from = parseAddresses(options.from)[0];
|
|
31
|
+
const personalization = {
|
|
32
|
+
to: parseAddresses(options.to).map((addr) => ({ email: addr.address, name: addr.name })),
|
|
33
|
+
...options.cc ? {
|
|
34
|
+
cc: parseAddresses(options.cc).map((addr) => ({
|
|
35
|
+
email: addr.address,
|
|
36
|
+
name: addr.name
|
|
37
|
+
}))
|
|
38
|
+
} : {},
|
|
39
|
+
...options.bcc ? {
|
|
40
|
+
bcc: parseAddresses(options.bcc).map((addr) => ({
|
|
41
|
+
email: addr.address,
|
|
42
|
+
name: addr.name
|
|
43
|
+
}))
|
|
44
|
+
} : {}
|
|
45
|
+
};
|
|
46
|
+
const body = {
|
|
47
|
+
personalizations: [personalization],
|
|
48
|
+
from: from ? { email: from.address, ...from.name ? { name: from.name } : {} } : { email: "" },
|
|
49
|
+
subject: options.subject,
|
|
50
|
+
...options.replyTo ? {
|
|
51
|
+
reply_to: parseAddresses(options.replyTo).map((addr) => ({
|
|
52
|
+
email: addr.address,
|
|
53
|
+
name: addr.name
|
|
54
|
+
}))[0]
|
|
55
|
+
} : {},
|
|
56
|
+
content: [
|
|
57
|
+
...options.text ? [{ type: "text/plain", value: options.text }] : [],
|
|
58
|
+
...options.html ? [{ type: "text/html", value: options.html }] : []
|
|
59
|
+
],
|
|
60
|
+
...attachments.length > 0 ? {
|
|
61
|
+
attachments: attachments.map((att) => ({
|
|
62
|
+
filename: att.filename,
|
|
63
|
+
type: att.contentType ?? "application/octet-stream",
|
|
64
|
+
content: att.content instanceof Uint8Array ? encodeBase64(att.content).replace(/\r\n/g, "") : att.content,
|
|
65
|
+
...att.contentId ? { content_id: att.contentId } : {},
|
|
66
|
+
disposition: att.inline ? "inline" : "attachment"
|
|
67
|
+
}))
|
|
68
|
+
} : {}
|
|
69
|
+
};
|
|
70
|
+
const response = await fetch("https://api.sendgrid.com/v3/mail/send", {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: {
|
|
73
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
74
|
+
"Content-Type": "application/json"
|
|
75
|
+
},
|
|
76
|
+
body: JSON.stringify(body)
|
|
77
|
+
});
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
const apiError = await response.text();
|
|
80
|
+
throw new SendGridError("SendGrid API error", response.status, apiError);
|
|
81
|
+
}
|
|
82
|
+
const messageId = response.headers.get("x-message-id") ?? options.messageId ?? "";
|
|
83
|
+
return {
|
|
84
|
+
messageId,
|
|
85
|
+
accepted: extractEmails(options.to),
|
|
86
|
+
rejected: [],
|
|
87
|
+
response: "Accepted",
|
|
88
|
+
envelope: {
|
|
89
|
+
from: from?.address ?? "",
|
|
90
|
+
to: [
|
|
91
|
+
...extractEmails(options.to),
|
|
92
|
+
...options.cc ? extractEmails(options.cc) : [],
|
|
93
|
+
...options.bcc ? extractEmails(options.bcc) : []
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export {
|
|
100
|
+
SendGridTransport,
|
|
101
|
+
SendGridError
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
//# debugId=ECC2C430859A7F7264756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/transports/sendgrid.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @module\n * SendGrid v3 HTTP API transport for sending email via api.sendgrid.com.\n *\n * @example\n * ```ts\n * import { SendGridTransport } from \"sently/transports/sendgrid\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new SendGridTransport({ apiKey: process.env.SENDGRID_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"sender@example.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * text: \"Plain text body\",\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport type { MailOptions, SendResult, Transport } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** SendGrid API configuration. */\nexport interface SendGridConfig {\n apiKey: string;\n}\n\n/** Error thrown when the SendGrid API returns a non-success response. */\nexport class SendGridError extends Error {\n /** Creates a SendGrid API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message);\n this.name = \"SendGridError\";\n }\n}\n\n/**\n * SendGrid v3 HTTP API transport.\n */\nexport class SendGridTransport implements Transport {\n private readonly apiKey: string;\n\n /** Creates a SendGrid transport with the given API key. */\n constructor(config: SendGridConfig) {\n this.apiKey = config.apiKey;\n }\n\n /** Sends an email via the SendGrid v3 HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const personalization = {\n to: parseAddresses(options.to).map((addr) => ({ email: addr.address, name: addr.name })),\n ...(options.cc\n ? {\n cc: parseAddresses(options.cc).map((addr) => ({\n email: addr.address,\n name: addr.name,\n })),\n }\n : {}),\n ...(options.bcc\n ? {\n bcc: parseAddresses(options.bcc).map((addr) => ({\n email: addr.address,\n name: addr.name,\n })),\n }\n : {}),\n };\n\n const body = {\n personalizations: [personalization],\n from: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n subject: options.subject,\n ...(options.replyTo\n ? {\n reply_to: parseAddresses(options.replyTo).map((addr) => ({\n email: addr.address,\n name: addr.name,\n }))[0],\n }\n : {}),\n content: [\n ...(options.text ? [{ type: \"text/plain\", value: options.text }] : []),\n ...(options.html ? [{ type: \"text/html\", value: options.html }] : []),\n ],\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n filename: att.filename,\n type: att.contentType ?? \"application/octet-stream\",\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ...(att.contentId ? { content_id: att.contentId } : {}),\n disposition: att.inline ? \"inline\" : \"attachment\",\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const apiError = await response.text();\n throw new SendGridError(\"SendGrid API error\", response.status, apiError);\n }\n\n const messageId = response.headers.get(\"x-message-id\") ?? options.messageId ?? \"\";\n\n return {\n messageId,\n accepted: extractEmails(options.to),\n rejected: [],\n response: \"Accepted\",\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "ECC2C430859A7F7264756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SMTPTransport,
|
|
3
|
+
closeSMTPSession,
|
|
4
|
+
deliverSMTPMessage,
|
|
5
|
+
encodeLine,
|
|
6
|
+
openSMTPSession,
|
|
7
|
+
readSMTPResponse,
|
|
8
|
+
resolveSMTPConfig
|
|
9
|
+
} from "../../chunk-tch6s785.js";
|
|
10
|
+
import"../../chunk-vm70w4e3.js";
|
|
11
|
+
import"../../chunk-hdqpvsm8.js";
|
|
12
|
+
import"../../chunk-794hc3m4.js";
|
|
13
|
+
import"../../chunk-v0bahtg2.js";
|
|
14
|
+
export {
|
|
15
|
+
resolveSMTPConfig,
|
|
16
|
+
readSMTPResponse,
|
|
17
|
+
openSMTPSession,
|
|
18
|
+
encodeLine,
|
|
19
|
+
deliverSMTPMessage,
|
|
20
|
+
closeSMTPSession,
|
|
21
|
+
SMTPTransport
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
//# debugId=C4DAC5428DA5225264756E2164756E21
|
package/package.json
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sently",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Runtime-agnostic email library for Node.js, Bun, Deno, and Cloudflare Workers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": ["dist", "README.md"],
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public",
|
|
12
|
+
"registry": "https://registry.npmjs.org"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "bun run build.ts",
|
|
16
|
+
"test": "bun test",
|
|
17
|
+
"test:watch": "bun test --watch",
|
|
18
|
+
"lint": "biome check src",
|
|
19
|
+
"format": "biome format src --write",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"mcp": "bun tools/mcp/index.ts",
|
|
22
|
+
"verify": "bun run lint && bun run typecheck && bun run build && bun test",
|
|
23
|
+
"prepack": "bun run verify",
|
|
24
|
+
"pack:npm": "npm pack --dry-run --cache ./.npm-cache",
|
|
25
|
+
"publish:npm-d": "npm publish --dry-run --cache ./.npm-cache",
|
|
26
|
+
"publish:npm": "npm publish --cache ./.npm-cache",
|
|
27
|
+
"publish:jsr-d": "npx jsr publish --dry-run",
|
|
28
|
+
"publish:jsr": "npx jsr publish",
|
|
29
|
+
"publish:all-d": "bun run publish:npm-d && bun run publish:jsr-d",
|
|
30
|
+
"publish:all": "bun run publish:npm && bun run publish:jsr",
|
|
31
|
+
"verify:publish": "bun run pack:npm"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@biomejs/biome": "latest",
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
36
|
+
"@types/node": "^25.9.1",
|
|
37
|
+
"typescript": "latest"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"email",
|
|
41
|
+
"smtp",
|
|
42
|
+
"mailer",
|
|
43
|
+
"nodemailer",
|
|
44
|
+
"bun",
|
|
45
|
+
"deno",
|
|
46
|
+
"cloudflare-workers",
|
|
47
|
+
"edge",
|
|
48
|
+
"runtime-agnostic"
|
|
49
|
+
],
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/alialnaghmoush/sently.git"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/alialnaghmoush/sendx#readme",
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/alialnaghmoush/sendx/issues"
|
|
58
|
+
},
|
|
59
|
+
"exports": {
|
|
60
|
+
".": {
|
|
61
|
+
"import": "./dist/index.js",
|
|
62
|
+
"types": "./dist/index.d.ts"
|
|
63
|
+
},
|
|
64
|
+
"./adapters/node": {
|
|
65
|
+
"import": "./dist/adapters/node.js",
|
|
66
|
+
"types": "./dist/adapters/node.d.ts"
|
|
67
|
+
},
|
|
68
|
+
"./adapters/bun": {
|
|
69
|
+
"import": "./dist/adapters/bun.js",
|
|
70
|
+
"types": "./dist/adapters/bun.d.ts"
|
|
71
|
+
},
|
|
72
|
+
"./adapters/deno": {
|
|
73
|
+
"import": "./dist/adapters/deno.js",
|
|
74
|
+
"types": "./dist/adapters/deno.d.ts"
|
|
75
|
+
},
|
|
76
|
+
"./adapters/cf": {
|
|
77
|
+
"import": "./dist/adapters/cf.js",
|
|
78
|
+
"types": "./dist/adapters/cf.d.ts"
|
|
79
|
+
},
|
|
80
|
+
"./transports/smtp": {
|
|
81
|
+
"import": "./dist/transports/smtp.js",
|
|
82
|
+
"types": "./dist/transports/smtp.d.ts"
|
|
83
|
+
},
|
|
84
|
+
"./transports/resend": {
|
|
85
|
+
"import": "./dist/transports/resend.js",
|
|
86
|
+
"types": "./dist/transports/resend.d.ts"
|
|
87
|
+
},
|
|
88
|
+
"./transports/sendgrid": {
|
|
89
|
+
"import": "./dist/transports/sendgrid.js",
|
|
90
|
+
"types": "./dist/transports/sendgrid.d.ts"
|
|
91
|
+
},
|
|
92
|
+
"./transports/postmark": {
|
|
93
|
+
"import": "./dist/transports/postmark.js",
|
|
94
|
+
"types": "./dist/transports/postmark.d.ts"
|
|
95
|
+
},
|
|
96
|
+
"./auth/oauth2": {
|
|
97
|
+
"import": "./dist/auth/oauth2.js",
|
|
98
|
+
"types": "./dist/auth/oauth2.d.ts"
|
|
99
|
+
},
|
|
100
|
+
"./pool": {
|
|
101
|
+
"import": "./dist/pool/pool.js",
|
|
102
|
+
"types": "./dist/pool/pool.d.ts"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|