@numoteq/emdash-plugin-jetemail 0.3.0 → 0.4.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/README.md CHANGED
@@ -11,21 +11,22 @@ pnpm add @numoteq/emdash-plugin-jetemail
11
11
  Register the package in the site's sandboxed plugin configuration, activate it, then select **JetEmail** under **Settings > Email**. Open the plugin settings and enter:
12
12
 
13
13
  - A JetEmail transactional API key (`transactional_…`) allowed to send from the sender's domain.
14
- - The sender name and email. The sender domain must be verified in JetEmail.
14
+ - The sender email. The sender domain must be verified in JetEmail.
15
+ - Optionally the sender name (defaults to the site title) and a Reply-To address.
15
16
 
16
17
  If the domain publishes JetEmail Domain Security (`_jm` TXT), add the API key's UUID to it, otherwise JetEmail rejects the message.
17
18
 
18
- The key is stored in the plugin-scoped KV store and is sent only to `https://api.jetemail.com/email`. The plugin requests only the fixed-host network and email transport registration capabilities. Recipients, CC and reply-to are passed through; EmDash email has no attachments.
19
+ The key is stored in the plugin-scoped KV store and is sent only to `https://api.jetemail.com/email`. The plugin requests only the fixed-host network and email transport registration capabilities. Recipients, CC and reply-to are passed through; a message's own reply-to wins over the configured one. EmDash email has no attachments.
19
20
 
20
21
  ## Native use (Cloudflare Workers sites)
21
22
 
22
- Sites that configure EmDash in code can register the transport in `astro.config.mjs` instead; the API key then lives in a Worker secret and nothing is set in the admin:
23
+ Sites that configure EmDash in code register the transport in `astro.config.mjs` instead; the API key lives in a Worker secret and nothing is set in the admin:
23
24
 
24
25
  ```js
25
26
  import { jetEmail } from "@numoteq/emdash-plugin-jetemail/native";
26
27
 
27
28
  emdash({
28
- plugins: [jetEmail({ from: { email: "noreply@example.com", name: "Example" } })],
29
+ plugins: [jetEmail({ from: { email: "noreply@example.com" }, replyTo: "info@example.com" })],
29
30
  });
30
31
  ```
31
32
 
@@ -33,18 +34,30 @@ emdash({
33
34
  wrangler secret put JETEMAIL_API_KEY
34
35
  ```
35
36
 
36
- Activate it under **Admin > Extensions** and select it under **Settings > Email**. Site code (contact forms) can send through the same client:
37
+ - `from.name` is optional and defaults to the site title (Settings > General).
38
+ - `replyTo` applies when a message sets none.
39
+ - EmDash selects the provider automatically when it is the only one.
37
40
 
38
- ```ts
39
- import { deliverWithJetEmail } from "@numoteq/emdash-plugin-jetemail/client";
41
+ Site code (forms, Workflows) sends through the same sender with `sendEmail`, which also supports attachments. It throws on failure; a `JetEmailError` carries the HTTP status.
40
42
 
41
- await deliverWithJetEmail(
42
- fetch,
43
- { apiToken, fromName: "Example", fromEmail: "noreply@example.com" },
44
- message,
45
- );
43
+ ```ts
44
+ import { sendEmail } from "@numoteq/emdash-plugin-jetemail/native";
45
+
46
+ await sendEmail(mailConfig, {
47
+ to: "team@example.com",
48
+ replyTo: visitorEmail,
49
+ subject: "New enquiry",
50
+ text,
51
+ attachments: [{ filename: "offer.pdf", content: pdfBytes }],
52
+ });
46
53
  ```
47
54
 
55
+ Pass the same config object to `jetEmail()` and `sendEmail()`, for example from a shared `src/mail-config.ts`.
56
+
57
+ ### E2E and local runs without a key
58
+
59
+ With the Worker variable `JETEMAIL_DRY_RUN` set to exactly `true`, nothing is sent: each message is logged as a warning (from, to, reply-to, subject) and reported as sent. Set it only in the E2E Wrangler config, never in production. Without it, a missing `JETEMAIL_API_KEY` fails the send.
60
+
48
61
  ## Development
49
62
 
50
63
  ```sh
package/dist/client.d.mts CHANGED
@@ -19,9 +19,14 @@ interface JetEmailMessage {
19
19
  attachments?: JetEmailAttachment[];
20
20
  }
21
21
  type FetchFn = (url: string, init?: RequestInit) => Promise<Response>;
22
+ /** JetEmail answered with a non-2xx status. HTTP 429 means the message was not accepted. */
23
+ declare class JetEmailError extends Error {
24
+ readonly status: number;
25
+ constructor(message: string, status: number);
26
+ }
22
27
  declare function formatSender(name: string, email: string): string;
23
28
  declare function deliverWithJetEmail(fetchFn: FetchFn, sender: JetEmailSender, message: JetEmailMessage): Promise<{
24
29
  id?: string;
25
30
  }>;
26
31
  //#endregion
27
- export { FetchFn, JetEmailAttachment, JetEmailMessage, JetEmailSender, deliverWithJetEmail, formatSender };
32
+ export { FetchFn, JetEmailAttachment, JetEmailError, JetEmailMessage, JetEmailSender, deliverWithJetEmail, formatSender };
package/dist/client.mjs CHANGED
@@ -1,5 +1,13 @@
1
1
  //#region src/client.ts
2
2
  const API_URL = "https://api.jetemail.com/email";
3
+ /** JetEmail answered with a non-2xx status. HTTP 429 means the message was not accepted. */
4
+ var JetEmailError = class extends Error {
5
+ constructor(message, status) {
6
+ super(message);
7
+ this.status = status;
8
+ this.name = "JetEmailError";
9
+ }
10
+ };
3
11
  function formatSender(name, email) {
4
12
  return `${name.replace(/[<>"\r\n]/g, "").trim()} <${email}>`;
5
13
  }
@@ -33,11 +41,11 @@ async function deliverWithJetEmail(fetchFn, sender, message) {
33
41
  });
34
42
  if (!response.ok) {
35
43
  const detail = (await response.text()).slice(0, 500);
36
- throw new Error(`JetEmail rejected the message with HTTP ${response.status}${detail ? `: ${detail}` : ""}`);
44
+ throw new JetEmailError(`JetEmail rejected the message with HTTP ${response.status}${detail ? `: ${detail}` : ""}`, response.status);
37
45
  }
38
46
  const result = await response.json().catch(() => ({}));
39
47
  return typeof result.id === "string" ? { id: result.id } : {};
40
48
  }
41
49
 
42
50
  //#endregion
43
- export { deliverWithJetEmail, formatSender };
51
+ export { JetEmailError, deliverWithJetEmail, formatSender };
@@ -1,9 +1,12 @@
1
1
  //#region src/native/config.d.ts
2
2
  interface JetEmailConfig {
3
- /** Sender. The domain must be verified in JetEmail and allowed for the API key. */
3
+ /**
4
+ * Sender. The domain must be verified in JetEmail and allowed for the API key.
5
+ * The name defaults to the site title from EmDash settings.
6
+ */
4
7
  from: {
5
8
  email: string;
6
- name: string;
9
+ name?: string;
7
10
  };
8
11
  /** Reply-To used when the message sets none. */
9
12
  replyTo?: string;
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@
7
7
  /** @type {import("emdash").PluginDescriptor} */
8
8
  const descriptor = Object.freeze({
9
9
  "id": "jetemail",
10
- "version": "0.3.0",
10
+ "version": "0.4.0",
11
11
  "format": "standard",
12
12
  "entrypoint": "@numoteq/emdash-plugin-jetemail/sandbox",
13
13
  "capabilities": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "jetemail",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "declaredAccess": {
5
5
  "network": {
6
6
  "request": {
@@ -1,4 +1,5 @@
1
- import { t as JetEmailConfig } from "../config-DcMo9a49.mjs";
1
+ import { JetEmailAttachment, JetEmailError, JetEmailMessage } from "../client.mjs";
2
+ import { t as JetEmailConfig } from "../config-BaMQYoEl.mjs";
2
3
  import { PluginDescriptor } from "emdash";
3
4
 
4
5
  //#region src/native/index.d.ts
@@ -7,5 +8,13 @@ import { PluginDescriptor } from "emdash";
7
8
  * delivery time from a Worker secret (default `JETEMAIL_API_KEY`), so nothing is configured in the admin.
8
9
  */
9
10
  declare function jetEmail(config: JetEmailConfig): PluginDescriptor<JetEmailConfig>;
11
+ /**
12
+ * Sends a message from site code (forms, Workflows) with the same sender as {@link jetEmail}.
13
+ * Supports attachments, which EmDash email cannot carry. Throws on failure; a
14
+ * {@link JetEmailError} carries the HTTP status.
15
+ */
16
+ declare function sendEmail(config: JetEmailConfig, message: JetEmailMessage): Promise<{
17
+ id?: string;
18
+ }>;
10
19
  //#endregion
11
- export { type JetEmailConfig, jetEmail as default, jetEmail };
20
+ export { type JetEmailAttachment, type JetEmailConfig, JetEmailError, type JetEmailMessage, jetEmail as default, jetEmail, sendEmail };
@@ -1,4 +1,5 @@
1
- import { n as PLUGIN_VERSION, r as assertValidConfig, t as PLUGIN_ID } from "../config-Bdqx5Bs6.mjs";
1
+ import { JetEmailError } from "../client.mjs";
2
+ import { a as assertValidConfig, i as PLUGIN_VERSION, n as sendWithConfig, r as PLUGIN_ID, t as defaultDeps } from "../send-DoICK33S.mjs";
2
3
 
3
4
  //#region src/native/index.ts
4
5
  /**
@@ -16,6 +17,14 @@ function jetEmail(config) {
16
17
  capabilities: ["hooks.email-transport:register"]
17
18
  };
18
19
  }
20
+ /**
21
+ * Sends a message from site code (forms, Workflows) with the same sender as {@link jetEmail}.
22
+ * Supports attachments, which EmDash email cannot carry. Throws on failure; a
23
+ * {@link JetEmailError} carries the HTTP status.
24
+ */
25
+ function sendEmail(config, message) {
26
+ return sendWithConfig(config, message, defaultDeps);
27
+ }
19
28
 
20
29
  //#endregion
21
- export { jetEmail as default, jetEmail };
30
+ export { JetEmailError, jetEmail as default, jetEmail, sendEmail };
@@ -1,11 +1,19 @@
1
- import { t as JetEmailConfig } from "../config-DcMo9a49.mjs";
1
+ import { FetchFn } from "../client.mjs";
2
+ import { t as JetEmailConfig } from "../config-BaMQYoEl.mjs";
2
3
  import * as emdash from "emdash";
3
4
  import { EmailDeliverEvent, PluginContext } from "emdash/plugin";
4
5
 
6
+ //#region src/native/send.d.ts
7
+ interface SendDeps {
8
+ env: () => Promise<Record<string, unknown>>;
9
+ siteTitle: () => Promise<string | undefined>;
10
+ fetch: FetchFn;
11
+ warn: (message: string, data: Record<string, unknown>) => void;
12
+ }
13
+ //#endregion
5
14
  //#region src/native/plugin.d.ts
6
- type LoadEnv = () => Promise<Record<string, unknown>>;
7
15
  /** @internal Exported for tests. */
8
- declare function createJetEmailDeliver(config: JetEmailConfig, loadEnv?: LoadEnv): (event: EmailDeliverEvent, ctx: PluginContext) => Promise<void>;
16
+ declare function createJetEmailDeliver(config: JetEmailConfig, deps?: SendDeps): (event: EmailDeliverEvent, ctx: PluginContext) => Promise<void>;
9
17
  /** Called by EmDash with the options of the {@link jetEmail} descriptor. */
10
18
  declare function createPlugin(config: JetEmailConfig): emdash.ResolvedPlugin<emdash.PluginStorageConfig>;
11
19
  //#endregion
@@ -1,32 +1,16 @@
1
- import { deliverWithJetEmail } from "../client.mjs";
2
- import { n as PLUGIN_VERSION, r as assertValidConfig, t as PLUGIN_ID } from "../config-Bdqx5Bs6.mjs";
1
+ import { a as assertValidConfig, i as PLUGIN_VERSION, n as sendWithConfig, r as PLUGIN_ID, t as defaultDeps } from "../send-DoICK33S.mjs";
3
2
  import { definePlugin } from "emdash";
4
3
 
5
4
  //#region src/native/plugin.ts
6
- async function loadWorkerEnv() {
7
- try {
8
- const { env } = await import("../env-DnAfk4Vp.mjs");
9
- return env;
10
- } catch {
11
- throw new Error("[jetemail] cloudflare:workers is not available; this provider runs only on Cloudflare Workers.");
12
- }
13
- }
14
5
  /** @internal Exported for tests. */
15
- function createJetEmailDeliver(config, loadEnv = loadWorkerEnv) {
16
- const secret = config.secret ?? "JETEMAIL_API_KEY";
6
+ function createJetEmailDeliver(config, deps = defaultDeps) {
17
7
  return async (event, ctx) => {
18
- const apiToken = (await loadEnv())[secret];
19
- if (typeof apiToken !== "string" || !apiToken) throw new Error(`[jetemail] Worker secret "${secret}" is not set (wrangler secret put ${secret}).`);
20
8
  const { message } = event;
21
- const result = await deliverWithJetEmail(fetch, {
22
- apiToken,
23
- fromName: config.from.name,
24
- fromEmail: config.from.email
25
- }, {
26
- ...message,
27
- replyTo: message.replyTo ?? config.replyTo
9
+ const result = await sendWithConfig(config, message, {
10
+ ...deps,
11
+ siteTitle: async () => await deps.siteTitle() || ctx.site.name
28
12
  });
29
- ctx.log.info("Email delivered via JetEmail", {
13
+ if (result.id) ctx.log.info("Email delivered via JetEmail", {
30
14
  to: message.to,
31
15
  subject: message.subject,
32
16
  id: result.id
package/dist/plugin.mjs CHANGED
@@ -1 +1 @@
1
- function e(e,t){return`${e.replace(/[<>"\r\n]/g,``).trim()} <${t}>`}function t(e){if(typeof e==`string`)return e;let t=``;for(let n=0;n<e.length;n+=32768)t+=String.fromCharCode(...e.subarray(n,n+32768));return btoa(t)}async function n(n,r,i){let a={from:e(r.fromName,r.fromEmail),to:i.to,subject:i.subject};i.text&&(a.text=i.text),i.html&&(a.html=i.html),i.cc?.length&&(a.cc=i.cc),i.replyTo&&(a.reply_to=i.replyTo),i.attachments?.length&&(a.attachments=i.attachments.map(e=>({filename:e.filename,data:t(e.content)})));let o=await n(`https://api.jetemail.com/email`,{method:`POST`,headers:{Authorization:`Bearer ${r.apiToken}`,"Content-Type":`application/json`},body:JSON.stringify(a)});if(!o.ok){let e=(await o.text()).slice(0,500);throw Error(`JetEmail rejected the message with HTTP ${o.status}${e?`: ${e}`:``}`)}let s=await o.json().catch(()=>({}));return typeof s.id==`string`?{id:s.id}:{}}async function r(e){let t=await e.kv.get(`settings:apiToken`),n=await e.kv.get(`settings:fromName`),r=await e.kv.get(`settings:fromEmail`);if(!t||!n||!r)throw Error(`JetEmail is not configured. Set the API key, sender name and sender email.`);return{apiToken:t,fromName:n,fromEmail:r}}async function i(e){let t=await e.kv.get(`settings:fromName`)??``,n=await e.kv.get(`settings:fromEmail`)??``,r=!!await e.kv.get(`settings:apiToken`);return{blocks:[{type:`header`,text:`JetEmail`},{type:`section`,text:`Deliver EmDash transactional email through the JetEmail API. The sender domain must be verified in JetEmail and allowed for the API key.`},{type:`form`,block_id:`jetemail-settings`,fields:[{type:`text_input`,action_id:`fromName`,label:`Sender name`,initial_value:t},{type:`text_input`,action_id:`fromEmail`,label:`Sender email`,initial_value:n},{type:`secret_input`,action_id:`apiToken`,label:r?`Transactional API key (leave blank to keep current)`:`Transactional API key`}],submit:{label:`Save settings`,action_id:`save_settings`}}]}}function a(e){return{blocks:[{type:`banner`,style:`error`,text:e}],toast:{message:e,type:`error`}}}async function o(e,t){let n=typeof t.fromName==`string`?t.fromName.trim():``,r=typeof t.fromEmail==`string`?t.fromEmail.trim():``,o=typeof t.apiToken==`string`?t.apiToken.trim():``;return n.replace(/[<>"\r\n]/g,``).trim()?/^[^\s@<>]+@[^\s@<>]+$/.test(r)?o&&!o.startsWith(`transactional_`)?a(`Use a JetEmail transactional API key (transactional_…).`):(await e.kv.set(`settings:fromName`,n),await e.kv.set(`settings:fromEmail`,r),o&&await e.kv.set(`settings:apiToken`,o),{...await i(e),toast:{message:`JetEmail settings saved`,type:`success`}}):a(`Enter a valid sender email.`):a(`Enter a sender name.`)}const s={hooks:{"email:deliver":{exclusive:!0,timeout:3e4,handler:async(e,t)=>{if(!t.http)throw Error(`JetEmail requires network access.`);let i=await r(t);await n(t.http.fetch.bind(t.http),i,e.message),t.log.info(`Email delivered via JetEmail`,{to:e.message.to,subject:e.message.subject})}}},routes:{admin:{handler:async(e,t)=>{let n=e.input;return n.type===`page_load`&&n.page===`/settings`?i(t):n.type===`form_submit`&&n.action_id===`save_settings`?o(t,n.values??{}):{blocks:[]}}}}};export{s as default,n as deliverWithJetEmail,e as formatSender};
1
+ var e=class extends Error{constructor(e,t){super(e),this.status=t,this.name=`JetEmailError`}};function t(e,t){return`${e.replace(/[<>"\r\n]/g,``).trim()} <${t}>`}function n(e){if(typeof e==`string`)return e;let t=``;for(let n=0;n<e.length;n+=32768)t+=String.fromCharCode(...e.subarray(n,n+32768));return btoa(t)}async function r(r,i,a){let o={from:t(i.fromName,i.fromEmail),to:a.to,subject:a.subject};a.text&&(o.text=a.text),a.html&&(o.html=a.html),a.cc?.length&&(o.cc=a.cc),a.replyTo&&(o.reply_to=a.replyTo),a.attachments?.length&&(o.attachments=a.attachments.map(e=>({filename:e.filename,data:n(e.content)})));let s=await r(`https://api.jetemail.com/email`,{method:`POST`,headers:{Authorization:`Bearer ${i.apiToken}`,"Content-Type":`application/json`},body:JSON.stringify(o)});if(!s.ok){let t=(await s.text()).slice(0,500);throw new e(`JetEmail rejected the message with HTTP ${s.status}${t?`: ${t}`:``}`,s.status)}let c=await s.json().catch(()=>({}));return typeof c.id==`string`?{id:c.id}:{}}async function i(e){let t=await e.kv.get(`settings:apiToken`),n=await e.kv.get(`settings:fromName`)||e.site.name,r=await e.kv.get(`settings:fromEmail`),i=await e.kv.get(`settings:replyTo`)||void 0;if(!t||!n||!r)throw Error(`JetEmail is not configured. Set the API key and sender email.`);return{apiToken:t,fromName:n,fromEmail:r,replyTo:i}}async function a(e){let t=await e.kv.get(`settings:fromName`)??``,n=await e.kv.get(`settings:fromEmail`)??``,r=await e.kv.get(`settings:replyTo`)??``,i=!!await e.kv.get(`settings:apiToken`);return{blocks:[{type:`header`,text:`JetEmail`},{type:`section`,text:`Deliver EmDash transactional email through the JetEmail API. The sender domain must be verified in JetEmail and allowed for the API key.`},{type:`form`,block_id:`jetemail-settings`,fields:[{type:`text_input`,action_id:`fromName`,label:`Sender name (defaults to the site title)`,initial_value:t},{type:`text_input`,action_id:`fromEmail`,label:`Sender email`,initial_value:n},{type:`text_input`,action_id:`replyTo`,label:`Reply-To (optional)`,initial_value:r},{type:`secret_input`,action_id:`apiToken`,label:i?`Transactional API key (leave blank to keep current)`:`Transactional API key`}],submit:{label:`Save settings`,action_id:`save_settings`}}]}}const o=/^[^\s@<>]+@[^\s@<>]+$/;function s(e){return{blocks:[{type:`banner`,style:`error`,text:e}],toast:{message:e,type:`error`}}}async function c(e,t){let n=typeof t.fromName==`string`?t.fromName.trim():``,r=typeof t.fromEmail==`string`?t.fromEmail.trim():``,i=typeof t.replyTo==`string`?t.replyTo.trim():``,c=typeof t.apiToken==`string`?t.apiToken.trim():``;return o.test(r)?i&&!o.test(i)?s(`Enter a valid Reply-To email.`):c&&!c.startsWith(`transactional_`)?s(`Use a JetEmail transactional API key (transactional_…).`):(await e.kv.set(`settings:fromName`,n),await e.kv.set(`settings:fromEmail`,r),await e.kv.set(`settings:replyTo`,i),c&&await e.kv.set(`settings:apiToken`,c),{...await a(e),toast:{message:`JetEmail settings saved`,type:`success`}}):s(`Enter a valid sender email.`)}const l={hooks:{"email:deliver":{exclusive:!0,timeout:3e4,handler:async(e,t)=>{if(!t.http)throw Error(`JetEmail requires network access.`);let{replyTo:n,...a}=await i(t);await r(t.http.fetch.bind(t.http),a,{...e.message,replyTo:e.message.replyTo??n}),t.log.info(`Email delivered via JetEmail`,{to:e.message.to,subject:e.message.subject})}}},routes:{admin:{handler:async(e,t)=>{let n=e.input;return n.type===`page_load`&&n.page===`/settings`?a(t):n.type===`form_submit`&&n.action_id===`save_settings`?c(t,n.values??{}):{blocks:[]}}}}};export{l as default,r as deliverWithJetEmail,t as formatSender};
@@ -0,0 +1,70 @@
1
+ import { deliverWithJetEmail, formatSender } from "./client.mjs";
2
+
3
+ //#region package.json
4
+ var version = "0.4.0";
5
+
6
+ //#endregion
7
+ //#region src/native/config.ts
8
+ const PLUGIN_ID = "jetemail";
9
+ const PLUGIN_VERSION = version;
10
+ function assertValidConfig(config) {
11
+ if (!config.from?.email?.includes("@")) throw new Error("[jetemail] config.from needs an email, e.g. { from: { email: \"noreply@example.com\" } }");
12
+ }
13
+
14
+ //#endregion
15
+ //#region src/native/send.ts
16
+ async function loadWorkerEnv() {
17
+ try {
18
+ const { env } = await import("./env-DnAfk4Vp.mjs");
19
+ return env;
20
+ } catch {
21
+ throw new Error("[jetemail] cloudflare:workers is not available; this provider runs only on Cloudflare Workers.");
22
+ }
23
+ }
24
+ async function loadSiteTitle() {
25
+ try {
26
+ const { getSiteSetting } = await import("emdash");
27
+ return await getSiteSetting("title");
28
+ } catch {
29
+ return;
30
+ }
31
+ }
32
+ const defaultDeps = {
33
+ env: loadWorkerEnv,
34
+ siteTitle: loadSiteTitle,
35
+ fetch: (url, init) => fetch(url, init),
36
+ warn: (message, data) => console.warn(message, data)
37
+ };
38
+ /**
39
+ * Sends one message with the configured sender. With the Worker variable `JETEMAIL_DRY_RUN=true`
40
+ * the message is only logged; E2E runs use it because they have no API key.
41
+ */
42
+ async function sendWithConfig(config, message, deps) {
43
+ const fromName = config.from.name?.trim() || (await deps.siteTitle())?.trim();
44
+ if (!fromName) throw new Error("[jetemail] No sender name: set config.from.name or the site title.");
45
+ const replyTo = message.replyTo ?? config.replyTo;
46
+ const env = await deps.env();
47
+ if (env.JETEMAIL_DRY_RUN === "true") {
48
+ deps.warn("[jetemail] JETEMAIL_DRY_RUN=true, email not sent", {
49
+ from: formatSender(fromName, config.from.email),
50
+ to: message.to,
51
+ replyTo,
52
+ subject: message.subject
53
+ });
54
+ return {};
55
+ }
56
+ const secret = config.secret ?? "JETEMAIL_API_KEY";
57
+ const apiToken = env[secret];
58
+ if (typeof apiToken !== "string" || !apiToken) throw new Error(`[jetemail] Worker secret "${secret}" is not set (wrangler secret put ${secret}).`);
59
+ return deliverWithJetEmail(deps.fetch, {
60
+ apiToken,
61
+ fromName,
62
+ fromEmail: config.from.email
63
+ }, {
64
+ ...message,
65
+ replyTo
66
+ });
67
+ }
68
+
69
+ //#endregion
70
+ export { assertValidConfig as a, PLUGIN_VERSION as i, sendWithConfig as n, PLUGIN_ID as r, defaultDeps as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@numoteq/emdash-plugin-jetemail",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "JetEmail transport provider for EmDash",
5
5
  "keywords": [
6
6
  "email",
@@ -1,9 +0,0 @@
1
- //#region src/native/config.ts
2
- const PLUGIN_ID = "jetemail";
3
- const PLUGIN_VERSION = "1.0.0";
4
- function assertValidConfig(config) {
5
- if (!config.from?.email?.includes("@") || !config.from.name?.trim()) throw new Error("[jetemail] config.from needs an email and a name, e.g. { from: { email: \"noreply@example.com\", name: \"Example\" } }");
6
- }
7
-
8
- //#endregion
9
- export { PLUGIN_VERSION as n, assertValidConfig as r, PLUGIN_ID as t };