@coastweb/emdash-plugin-cloudflare-email 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 immber (Jessie Rushing) & CoastWeb, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # @coastweb/emdash-plugin-cloudflare-email
2
+
3
+ An [EmDash CMS](https://docs.emdashcms.com/introduction/) plugin that adds email sending through [Cloudflare Email Service](https://developers.cloudflare.com/email-service/) as an email provider.
4
+
5
+ ## Requirements
6
+
7
+ **Cloudflare Workers only.** This plugin uses Cloudflare's `cloudflare:email` runtime API, which is only available on Cloudflare Workers. This plugin will not work on Node.js deployments.
8
+
9
+ You must have [Cloudflare Email Service](https://developers.cloudflare.com/email-service/) (available in public beta) enabled on the same Cloudflare account where you are deploying your emdash worker. You will need to configure a sending domain in Cloudflare before you can use this plugin.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @coastweb/@coastweb/emdash-plugin-cloudflare-email
15
+ ```
16
+
17
+ ### 1. Add the `SEND_EMAIL` binding in `wrangler.jsonc` on your emdashcms worker
18
+
19
+ ```jsonc
20
+ {
21
+ "send_email": [
22
+ {
23
+ "name": "SEND_EMAIL"
24
+ }
25
+ ]
26
+ }
27
+ ```
28
+
29
+ ### 2. Register the plugin in `astro.config.mjs`
30
+
31
+ ```js
32
+ import { cloudflareEmailPlugin } from "@coastweb/emdash-plugin-cloudflare-email";
33
+
34
+ export default defineConfig({
35
+ integrations: [
36
+ emdash({
37
+ plugins: [cloudflareEmailPlugin()],
38
+ // ...
39
+ }),
40
+ ],
41
+ });
42
+ ```
43
+
44
+ The plugin must be in `plugins: []` (trusted mode), not `sandboxed: []`, because it requires access to the `SEND_EMAIL` Cloudflare binding.
45
+
46
+ ### 3. Set the from address
47
+
48
+ After deploying, go to your EmDash admin and navigate to **Plugins → Cloudflare Email → Settings**. Enter a sender from address — this must be an address for a domain you have already configured in Cloudflare Email Service.
49
+
50
+ ## How it works
51
+
52
+ This plugin registers as the `email:deliver` transport for EmDash's email pipeline. When another plugin (such as a forms plugin) calls `ctx.email.send()`, this plugin handles delivery by:
53
+
54
+ 1. Reading the configured `from` address from plugin settings
55
+ 2. Building a MIME message (plain text, or multipart `text/plain` + `text/html` if HTML is provided)
56
+ 3. Sending via the Cloudflare `SEND_EMAIL` Workers binding
57
+
58
+ ## License
59
+
60
+ MIT
@@ -0,0 +1,5 @@
1
+ import { PluginDescriptor } from 'emdash';
2
+
3
+ declare function cloudflareEmailPlugin(): PluginDescriptor;
4
+
5
+ export { cloudflareEmailPlugin };
package/dist/index.mjs ADDED
@@ -0,0 +1,15 @@
1
+ // src/index.ts
2
+ function cloudflareEmailPlugin() {
3
+ return {
4
+ id: "cloudflare-email",
5
+ version: "0.1.0",
6
+ format: "standard",
7
+ entrypoint: "emdash-plugin-cloudflare-email/sandbox",
8
+ options: {},
9
+ capabilities: ["email:provide"],
10
+ adminPages: [{ path: "/settings", label: "Cloudflare Email Settings", icon: "settings" }]
11
+ };
12
+ }
13
+ export {
14
+ cloudflareEmailPlugin
15
+ };
@@ -0,0 +1,105 @@
1
+ // src/sandbox-entry.ts
2
+ import { definePlugin } from "emdash";
3
+ function buildMimeMessage(from, to, subject, text, html) {
4
+ const boundary = `----=_Part_${Date.now()}`;
5
+ const lines = [
6
+ `From: ${from}`,
7
+ `To: ${to}`,
8
+ `Subject: ${subject}`,
9
+ `MIME-Version: 1.0`
10
+ ];
11
+ if (html) {
12
+ lines.push(`Content-Type: multipart/alternative; boundary="${boundary}"`, ``);
13
+ lines.push(`--${boundary}`);
14
+ lines.push(`Content-Type: text/plain; charset=UTF-8`, ``, text, ``);
15
+ lines.push(`--${boundary}`);
16
+ lines.push(`Content-Type: text/html; charset=UTF-8`, ``, html, ``);
17
+ lines.push(`--${boundary}--`);
18
+ } else {
19
+ lines.push(`Content-Type: text/plain; charset=UTF-8`, ``, text);
20
+ }
21
+ return lines.join("\r\n");
22
+ }
23
+ var sandbox_entry_default = definePlugin({
24
+ hooks: {
25
+ "email:deliver": {
26
+ exclusive: true,
27
+ handler: async (event, ctx) => {
28
+ const from = await ctx.kv.get("settings:from");
29
+ if (!from) {
30
+ throw new Error(
31
+ "From address not configured. Set it in the Cloudflare Email plugin settings."
32
+ );
33
+ }
34
+ const { env } = await import("cloudflare:workers");
35
+ const binding = env.SEND_EMAIL;
36
+ if (!binding) {
37
+ throw new Error(
38
+ "SEND_EMAIL binding not found. Ensure send_email is configured in wrangler.jsonc."
39
+ );
40
+ }
41
+ const { message } = event;
42
+ const { EmailMessage } = await import("cloudflare:email");
43
+ const raw = buildMimeMessage(from, message.to, message.subject, message.text, message.html);
44
+ const emailMessage = new EmailMessage(from, message.to, raw);
45
+ await binding.send(emailMessage);
46
+ }
47
+ }
48
+ },
49
+ routes: {
50
+ admin: {
51
+ handler: async (routeCtx, ctx) => {
52
+ const interaction = routeCtx.input;
53
+ if (interaction.type === "form_submit" && interaction.action_id === "save_settings") {
54
+ const from2 = interaction.values?.from ?? "";
55
+ await ctx.kv.set("settings:from", from2);
56
+ return {
57
+ blocks: [
58
+ { type: "header", text: "Cloudflare Email Settings" },
59
+ {
60
+ type: "form",
61
+ block_id: "settings",
62
+ fields: [
63
+ {
64
+ type: "text_input",
65
+ action_id: "from",
66
+ label: "From address",
67
+ initial_value: from2
68
+ }
69
+ ],
70
+ submit: { label: "Save", action_id: "save_settings" }
71
+ }
72
+ ],
73
+ toast: { message: "Settings saved", type: "success" }
74
+ };
75
+ }
76
+ const from = await ctx.kv.get("settings:from") ?? "";
77
+ return {
78
+ blocks: [
79
+ { type: "header", text: "Cloudflare Email Settings" },
80
+ {
81
+ type: "context",
82
+ text: "Must be an address for a domain you have already configured in Cloudflare Email Service"
83
+ },
84
+ {
85
+ type: "form",
86
+ block_id: "settings",
87
+ fields: [
88
+ {
89
+ type: "text_input",
90
+ action_id: "from",
91
+ label: "From address",
92
+ initial_value: from
93
+ }
94
+ ],
95
+ submit: { label: "Save", action_id: "save_settings" }
96
+ }
97
+ ]
98
+ };
99
+ }
100
+ }
101
+ }
102
+ });
103
+ export {
104
+ sandbox_entry_default as default
105
+ };
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@coastweb/emdash-plugin-cloudflare-email",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": { "import": "./dist/index.mjs", "types": "./dist/index.d.ts" },
7
+ "./sandbox": { "import": "./dist/sandbox-entry.mjs" }
8
+ },
9
+ "files": ["dist"],
10
+ "scripts": {
11
+ "build": "tsup",
12
+ "dev": "tsup --watch"
13
+ },
14
+ "peerDependencies": {
15
+ "emdash": "*"
16
+ },
17
+ "devDependencies": {
18
+ "@cloudflare/workers-types": "^4.20260426.1",
19
+ "tsup": "^8.5.1",
20
+ "typescript": "^5.9.3"
21
+ }
22
+ }