Skip to content

Commit 1c9eebd

Browse files
jdevalkclaude
andcommitted
Switch from raw HTTP to official Lettermint SDK
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ea4d6fd commit 1c9eebd

3 files changed

Lines changed: 59 additions & 73 deletions

File tree

package-lock.json

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@jdevalk/emdash-plugin-lettermint",
3-
"version": "0.2.3",
3+
"version": "0.3.0",
44
"description": "Lettermint email provider plugin for EmDash CMS",
55
"type": "module",
66
"exports": {
@@ -18,6 +18,9 @@
1818
"type": "git",
1919
"url": "https://github.com/jdevalk/emdash-plugin-lettermint"
2020
},
21+
"dependencies": {
22+
"lettermint": "^1.0.0"
23+
},
2124
"peerDependencies": {
2225
"emdash": "*"
2326
},

src/index.ts

Lines changed: 44 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { definePlugin } from "emdash";
22
import type { PluginContext, PluginDescriptor, RouteContext } from "emdash";
3+
import { Lettermint } from "lettermint";
34

45
interface EmailDeliverEvent {
56
message: {
@@ -12,19 +13,24 @@ interface EmailDeliverEvent {
1213
}
1314

1415
/**
15-
* Delivers an email via the Lettermint API.
16+
* Helper to read plugin settings from KV.
1617
*/
17-
async function deliverHandler(event: EmailDeliverEvent, ctx: PluginContext) {
18-
const entries = await ctx.kv.list("settings:");
19-
const settings = new Map<string, string>();
18+
async function getSettings(kv: PluginContext["kv"]) {
19+
const entries = await kv.list("settings:");
20+
const settings: Record<string, string> = {};
2021
for (const { key, value } of entries) {
21-
if (typeof value === "string") {
22-
settings.set(key.replace("settings:", ""), value);
23-
}
22+
const k = key.replace("settings:", "");
23+
settings[k] = typeof value === "string" ? value : String(value);
2424
}
25+
return settings;
26+
}
2527

26-
const apiToken = settings.get("apiToken");
27-
const fromAddress = settings.get("fromAddress");
28+
/**
29+
* Delivers an email via the Lettermint SDK.
30+
*/
31+
async function deliverHandler(event: EmailDeliverEvent, ctx: PluginContext) {
32+
const settings = await getSettings(ctx.kv);
33+
const { apiToken, fromAddress } = settings;
2834

2935
if (!apiToken) {
3036
ctx.log.error("Lettermint API token not configured");
@@ -36,39 +42,26 @@ async function deliverHandler(event: EmailDeliverEvent, ctx: PluginContext) {
3642
return;
3743
}
3844

39-
const body: Record<string, string> = {
40-
from: fromAddress,
41-
to: event.message.to,
42-
subject: event.message.subject,
43-
text: event.message.text,
44-
};
45-
46-
if (event.message.html) {
47-
body.html = event.message.html;
48-
}
45+
const client = new Lettermint({ apiToken });
4946

50-
const response = await ctx.http?.fetch("https://api.lettermint.co/v1/email/send", {
51-
method: "POST",
52-
headers: {
53-
"Authorization": `Bearer ${apiToken}`,
54-
"Content-Type": "application/json",
55-
},
56-
body: JSON.stringify(body),
57-
});
47+
let email = client.email
48+
.from(fromAddress)
49+
.to(event.message.to)
50+
.subject(event.message.subject)
51+
.text(event.message.text);
5852

59-
if (!response || !response.ok) {
60-
ctx.log.error(`Lettermint delivery failed: ${response?.status ?? "no response"}`);
61-
return;
53+
if (event.message.html) {
54+
email = email.html(event.message.html);
6255
}
6356

64-
const result = await response.json();
65-
ctx.log.info(`Email delivered via Lettermint: ${result.message_id}`);
57+
const response = await email.send();
58+
ctx.log.info(`Email delivered via Lettermint: ${response.message_id}`);
6659
}
6760

6861
export function lettermintPlugin(): PluginDescriptor {
6962
return {
7063
id: "lettermint",
71-
version: "0.2.1",
64+
version: "0.3.0",
7265
format: "native",
7366
entrypoint: new URL("./index.ts", import.meta.url).pathname,
7467
adminEntry: new URL("./admin.tsx", import.meta.url).pathname,
@@ -82,9 +75,8 @@ export function lettermintPlugin(): PluginDescriptor {
8275
export function createPlugin() {
8376
return definePlugin({
8477
id: "lettermint",
85-
version: "0.2.1",
86-
capabilities: ["email:provide", "network:fetch"],
87-
allowedHosts: ["api.lettermint.co"],
78+
version: "0.3.0",
79+
capabilities: ["email:provide"],
8880

8981
hooks: {
9082
"email:deliver": {
@@ -96,12 +88,7 @@ export function createPlugin() {
9688
routes: {
9789
"settings": {
9890
handler: async (ctx: RouteContext) => {
99-
const entries = await ctx.kv.list("settings:");
100-
const settings: Record<string, string> = {};
101-
for (const { key, value } of entries) {
102-
const k = key.replace("settings:", "");
103-
settings[k] = typeof value === "string" ? value : String(value);
104-
}
91+
const settings = await getSettings(ctx.kv);
10592
return { settings };
10693
},
10794
},
@@ -116,40 +103,27 @@ export function createPlugin() {
116103
},
117104
"test": {
118105
handler: async (ctx: RouteContext) => {
119-
const entries = await ctx.kv.list("settings:");
120-
const settings: Record<string, string> = {};
121-
for (const { key, value } of entries) {
122-
const k = key.replace("settings:", "");
123-
settings[k] = typeof value === "string" ? value : String(value);
124-
}
125-
126-
const apiToken = settings.apiToken;
127-
const fromAddress = settings.fromAddress;
106+
const settings = await getSettings(ctx.kv);
107+
const { apiToken, fromAddress } = settings;
128108

129109
if (!apiToken || !fromAddress) {
130110
return { ok: false, error: "Please configure your API token and from address first." };
131111
}
132112

133-
const response = await ctx.http?.fetch("https://api.lettermint.co/v1/email/send", {
134-
method: "POST",
135-
headers: {
136-
"Authorization": `Bearer ${apiToken}`,
137-
"Content-Type": "application/json",
138-
},
139-
body: JSON.stringify({
140-
from: fromAddress,
141-
to: fromAddress,
142-
subject: "Lettermint test email from EmDash",
143-
text: "If you're reading this, your Lettermint email integration is working!",
144-
}),
145-
});
146-
147-
if (!response || !response.ok) {
148-
const status = response?.status ?? "no response";
149-
return { ok: false, error: `Lettermint API returned ${status}.` };
113+
try {
114+
const client = new Lettermint({ apiToken });
115+
await client.email
116+
.from(fromAddress)
117+
.to(fromAddress)
118+
.subject("Lettermint test email from EmDash")
119+
.text("If you're reading this, your Lettermint email integration is working!")
120+
.send();
121+
122+
return { ok: true };
123+
} catch (err) {
124+
const message = err instanceof Error ? err.message : String(err);
125+
return { ok: false, error: `Lettermint error: ${message}` };
150126
}
151-
152-
return { ok: true };
153127
},
154128
},
155129
},

0 commit comments

Comments
 (0)