@headless-lms/adapter-email-resend 0.1.0 → 0.1.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/README.md +3 -3
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ const container = await createContainer(config, {
|
|
|
15
15
|
|
|
16
16
|
## Environment variables (reference installation)
|
|
17
17
|
|
|
18
|
-
| Variable | Required | Maps to | Notes
|
|
19
|
-
| ---------------- | -------- | -------- |
|
|
20
|
-
| `RESEND_API_KEY` | yes | `apiKey` | Unset → no adapter injected; email sends fail loudly.
|
|
18
|
+
| Variable | Required | Maps to | Notes |
|
|
19
|
+
| ---------------- | -------- | -------- | ----------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| `RESEND_API_KEY` | yes | `apiKey` | Unset → no adapter injected; email sends fail loudly. |
|
|
21
21
|
| `EMAIL_FROM` | no | `from` | e.g. `Acme LMS <noreply@acme.com>`, on a verified Resend domain. Defaults to `onboarding@resend.dev`. |
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { EmailRejectedError } from "@headless-lms/core/shared/mailer";
|
|
1
2
|
//#region src/index.ts
|
|
2
3
|
const RESEND_ENDPOINT = "https://api.resend.com/emails";
|
|
4
|
+
function permanent(status) {
|
|
5
|
+
return status >= 400 && status < 500 && status !== 408 && status !== 429;
|
|
6
|
+
}
|
|
3
7
|
var ResendEmailAdapter = class {
|
|
4
8
|
config;
|
|
5
9
|
logger;
|
|
@@ -27,11 +31,12 @@ var ResendEmailAdapter = class {
|
|
|
27
31
|
if (!response.ok) {
|
|
28
32
|
const detail = await response.json().then((body) => body && typeof body === "object" && "message" in body ? String(body.message) : "").catch(() => "");
|
|
29
33
|
this.logger?.error("email send failed", {
|
|
30
|
-
to: message.to,
|
|
31
34
|
status: response.status,
|
|
32
35
|
detail
|
|
33
36
|
});
|
|
34
|
-
|
|
37
|
+
const message = `resend responded ${response.status}${detail ? `: ${detail}` : ""}`;
|
|
38
|
+
if (permanent(response.status)) throw new EmailRejectedError(message, response.status);
|
|
39
|
+
throw new Error(message);
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// Resend implementation of the EmailSender port.\nimport type { EmailSender, EmailMessage, Logger } from \"@headless-lms/core/types\";\n\nconst RESEND_ENDPOINT = \"https://api.resend.com/emails\";\n\nexport interface ResendEmailConfig {\n apiKey: string;\n /** Sender, e.g. \"Acme LMS <noreply@acme.com>\". Must be on a verified Resend domain. */\n from: string;\n}\n\nexport class ResendEmailAdapter implements EmailSender {\n constructor(\n private readonly config: ResendEmailConfig,\n private readonly logger?: Logger,\n private readonly fetchFn: typeof fetch = fetch,\n ) {}\n\n async send(message: EmailMessage): Promise<void> {\n const response = await this.fetchFn(RESEND_ENDPOINT, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${this.config.apiKey}`,\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n from: this.config.from,\n to: message.to,\n subject: message.subject,\n text: message.text,\n ...(message.html !== undefined && { html: message.html }),\n }),\n });\n if (!response.ok) {\n const detail = await response\n .json()\n .then((body: unknown) =>\n body && typeof body === \"object\" && \"message\" in body ? String(body.message) : \"\",\n )\n .catch(() => \"\");\n this.logger?.error(\"email send failed\", {
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// Resend implementation of the EmailSender port.\nimport type { EmailSender, EmailMessage, Logger } from \"@headless-lms/core/types\";\nimport { EmailRejectedError } from \"@headless-lms/core/shared/mailer\";\n\nconst RESEND_ENDPOINT = \"https://api.resend.com/emails\";\n\nexport interface ResendEmailConfig {\n apiKey: string;\n /** Sender, e.g. \"Acme LMS <noreply@acme.com>\". Must be on a verified Resend domain. */\n from: string;\n}\n\nfunction permanent(status: number): boolean {\n return status >= 400 && status < 500 && status !== 408 && status !== 429;\n}\n\nexport class ResendEmailAdapter implements EmailSender {\n constructor(\n private readonly config: ResendEmailConfig,\n private readonly logger?: Logger,\n private readonly fetchFn: typeof fetch = fetch,\n ) {}\n\n async send(message: EmailMessage): Promise<void> {\n const response = await this.fetchFn(RESEND_ENDPOINT, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${this.config.apiKey}`,\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n from: this.config.from,\n to: message.to,\n subject: message.subject,\n text: message.text,\n ...(message.html !== undefined && { html: message.html }),\n }),\n });\n if (!response.ok) {\n const detail = await response\n .json()\n .then((body: unknown) =>\n body && typeof body === \"object\" && \"message\" in body ? String(body.message) : \"\",\n )\n .catch(() => \"\");\n this.logger?.error(\"email send failed\", { status: response.status, detail });\n const message = `resend responded ${response.status}${detail ? `: ${detail}` : \"\"}`;\n if (permanent(response.status)) {\n throw new EmailRejectedError(message, response.status);\n }\n throw new Error(message);\n }\n }\n}\n"],"mappings":";;AAIA,MAAM,kBAAkB;AAQxB,SAAS,UAAU,QAAyB;CAC1C,OAAO,UAAU,OAAO,SAAS,OAAO,WAAW,OAAO,WAAW;AACvE;AAEA,IAAa,qBAAb,MAAuD;CAElC;CACA;CACA;CAHnB,YACE,QACA,QACA,UAAyC,OACzC;EAHiB,KAAA,SAAA;EACA,KAAA,SAAA;EACA,KAAA,UAAA;CAChB;CAEH,MAAM,KAAK,SAAsC;EAC/C,MAAM,WAAW,MAAM,KAAK,QAAQ,iBAAiB;GACnD,QAAQ;GACR,SAAS;IACP,eAAe,UAAU,KAAK,OAAO;IACrC,gBAAgB;GAClB;GACA,MAAM,KAAK,UAAU;IACnB,MAAM,KAAK,OAAO;IAClB,IAAI,QAAQ;IACZ,SAAS,QAAQ;IACjB,MAAM,QAAQ;IACd,GAAI,QAAQ,SAAS,KAAA,KAAa,EAAE,MAAM,QAAQ,KAAK;GACzD,CAAC;EACH,CAAC;EACD,IAAI,CAAC,SAAS,IAAI;GAChB,MAAM,SAAS,MAAM,SAClB,KAAK,CAAC,CACN,MAAM,SACL,QAAQ,OAAO,SAAS,YAAY,aAAa,OAAO,OAAO,KAAK,OAAO,IAAI,EACjF,CAAC,CACA,YAAY,EAAE;GACjB,KAAK,QAAQ,MAAM,qBAAqB;IAAE,QAAQ,SAAS;IAAQ;GAAO,CAAC;GAC3E,MAAM,UAAU,oBAAoB,SAAS,SAAS,SAAS,KAAK,WAAW;GAC/E,IAAI,UAAU,SAAS,MAAM,GAC3B,MAAM,IAAI,mBAAmB,SAAS,SAAS,MAAM;GAEvD,MAAM,IAAI,MAAM,OAAO;EACzB;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@headless-lms/adapter-email-resend",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@headless-lms/core": "0.
|
|
14
|
+
"@headless-lms/core": "0.5.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@types/node": "^22.10.2",
|