@decocms/apps-resend 8.1.0-next.0 → 8.1.0-next.2

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.
@@ -0,0 +1,21 @@
1
+ import type { CreateEmailOptions, CreateEmailResponseSuccess } from "./types.ts";
2
+ export interface ResendClientConfig {
3
+ apiKey: string;
4
+ }
5
+ /** A Resend email; the API requires a sender. */
6
+ export type SendEmailOptions = CreateEmailOptions & {
7
+ from: string;
8
+ };
9
+ export declare class ResendError extends Error {
10
+ readonly operation: string;
11
+ readonly status: number;
12
+ constructor(operation: string, status: number);
13
+ }
14
+ export declare function createResendClient(config: ResendClientConfig, options?: {
15
+ fetch?: typeof globalThis.fetch;
16
+ }): {
17
+ /** Sends one email and returns its id. */
18
+ sendEmail(email: SendEmailOptions): Promise<CreateEmailResponseSuccess>;
19
+ };
20
+ export type ResendClient = ReturnType<typeof createResendClient>;
21
+ //# sourceMappingURL=emails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emails.d.ts","sourceRoot":"","sources":["../src/emails.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAEjF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,iDAAiD;AACjD,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,qBAAa,WAAY,SAAQ,KAAK;IAElC,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM;IAFzB,YACW,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EAGxB;CACF;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAAO;IAK/C,0CAA0C;IACpC,SAAS,QAAQ,gBAAgB,GAAG,OAAO,CAAC,0BAA0B,CAAC;EAchF;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
package/dist/emails.js ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The Resend client: a thin client for the Resend REST API.
3
+ * See /next/upstream-clients.
4
+ *
5
+ * Every request goes through `createInstrumentedFetch` (provider `resend`).
6
+ * No retries or circuit breaker: sending an email isn't safe to repeat.
7
+ * Default senders, recipients and subjects belong to the site.
8
+ */
9
+ import { createInstrumentedFetch } from "@decocms/blocks/fetch";
10
+ export class ResendError extends Error {
11
+ operation;
12
+ status;
13
+ constructor(operation, status) {
14
+ super(`resend ${operation} failed with HTTP ${status}`);
15
+ this.operation = operation;
16
+ this.status = status;
17
+ }
18
+ }
19
+ export function createResendClient(config, options = {}) {
20
+ const request = createInstrumentedFetch({ provider: "resend", fetch: options.fetch });
21
+ return {
22
+ /** Sends one email and returns its id. */
23
+ async sendEmail(email) {
24
+ const response = await request("https://api.resend.com/emails", {
25
+ operation: "sendEmail",
26
+ method: "POST",
27
+ headers: {
28
+ authorization: `Bearer ${config.apiKey}`,
29
+ "content-type": "application/json",
30
+ },
31
+ body: JSON.stringify(email),
32
+ });
33
+ if (!response.ok)
34
+ throw new ResendError("sendEmail", response.status);
35
+ return (await response.json());
36
+ },
37
+ };
38
+ }
39
+ //# sourceMappingURL=emails.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emails.js","sourceRoot":"","sources":["../src/emails.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAUhE,MAAM,OAAO,WAAY,SAAQ,KAAK;IAEzB,SAAS;IACT,MAAM;IAFjB,YACW,SAAiB,EACjB,MAAc;QAEvB,KAAK,CAAC,UAAU,SAAS,qBAAqB,MAAM,EAAE,CAAC,CAAC;yBAH/C,SAAS;sBACT,MAAM;IAGjB,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAChC,MAA0B,EAC1B,OAAO,GAAwC,EAAE;IAEjD,MAAM,OAAO,GAAG,uBAAuB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAEtF,OAAO;QACL,0CAA0C;QAC1C,KAAK,CAAC,SAAS,CAAC,KAAuB;YACrC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,+BAA+B,EAAE;gBAC9D,SAAS,EAAE,WAAW;gBACtB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;oBACxC,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;aAC5B,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,WAAW,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtE,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA+B,CAAC;QAC/D,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `@decocms/apps-resend`: the Resend client (`createResendClient`).
3
+ * See /next/upstream-clients.
4
+ */
5
+ export { createResendClient, type ResendClient, type ResendClientConfig, ResendError, type SendEmailOptions, } from "./emails.ts";
6
+ export type { CreateEmailOptions, CreateEmailResponseSuccess } from "./types.ts";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,WAAW,EACX,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `@decocms/apps-resend`: the Resend client (`createResendClient`).
3
+ * See /next/upstream-clients.
4
+ */
5
+ export { createResendClient, ResendError, } from "./emails.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,kBAAkB,EAGlB,WAAW,GAEZ,MAAM,aAAa,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface CreateEmailResponseSuccess {
2
+ /** The ID of the newly created email. */
3
+ id: string;
4
+ }
5
+ export interface CreateEmailOptions {
6
+ from?: string;
7
+ to: string | string[];
8
+ subject: string;
9
+ bcc?: string | string[];
10
+ cc?: string | string[];
11
+ reply_to?: string | string[];
12
+ html?: string;
13
+ text?: string;
14
+ headers?: Record<string, string>;
15
+ }
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,0BAA0B;IACzC,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ // The Resend API shapes the client sends and receives (https://resend.com/docs/api-reference/emails/send-email).
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,iHAAiH"}
package/package.json CHANGED
@@ -1,45 +1,36 @@
1
1
  {
2
2
  "name": "@decocms/apps-resend",
3
- "version": "8.1.0-next.0",
3
+ "version": "8.1.0-next.2",
4
4
  "type": "module",
5
- "description": "Deco app: Resend transactional email integration",
5
+ "description": "Thin Resend email API client for Deco sites",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/decocms/blocks.git",
9
9
  "directory": "packages/apps-resend"
10
10
  },
11
11
  "files": [
12
+ "dist",
12
13
  "src",
13
- "tsconfig.json"
14
+ "!src/**/*.test.ts",
15
+ "!src/**/__tests__"
14
16
  ],
15
- "main": "./src/index.ts",
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
16
19
  "exports": {
17
- ".": "./src/index.ts",
18
- "./mod": "./src/mod.ts",
19
- "./client": "./src/client.ts",
20
- "./types": "./src/types.ts",
21
- "./actions/send": "./src/actions/send.ts",
22
- "./registry": "./src/registry.ts"
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "source": "./src/index.ts",
23
+ "default": "./dist/index.js"
24
+ }
23
25
  },
24
26
  "scripts": {
25
- "build": "tsc",
27
+ "build": "node ../../scripts/tsc.mjs --clean dist -p tsconfig.build.json",
28
+ "prepack": "npm run build",
26
29
  "test": "vitest run --root ../.. packages/apps-resend/",
27
- "typecheck": "tsc --noEmit",
28
- "lint:unused": "knip"
30
+ "typecheck": "node ../../scripts/tsc.mjs -p tsconfig.json"
29
31
  },
30
32
  "dependencies": {
31
- "@decocms/blocks": "8.1.0-next.0",
32
- "@decocms/apps-commerce": "8.1.0-next.0"
33
- },
34
- "peerDependencies": {
35
- "react": "^19.0.0",
36
- "react-dom": "^19.0.0"
37
- },
38
- "devDependencies": {
39
- "@types/react": "^19.0.0",
40
- "@types/react-dom": "^19.0.0",
41
- "knip": "^5.86.0",
42
- "typescript": "^5.9.0"
33
+ "@decocms/blocks": "8.1.0-next.2"
43
34
  },
44
35
  "publishConfig": {
45
36
  "registry": "https://registry.npmjs.org",
package/src/emails.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * Default senders, recipients and subjects belong to the site.
8
8
  */
9
9
  import { createInstrumentedFetch } from "@decocms/blocks/fetch";
10
- import type { CreateEmailOptions, CreateEmailResponseSuccess } from "./types";
10
+ import type { CreateEmailOptions, CreateEmailResponseSuccess } from "./types.ts";
11
11
 
12
12
  export interface ResendClientConfig {
13
13
  apiKey: string;
package/src/index.ts CHANGED
@@ -1,24 +1,12 @@
1
1
  /**
2
2
  * `@decocms/apps-resend`: the Resend client (`createResendClient`).
3
3
  * See /next/upstream-clients.
4
- *
5
- * `sendEmail`, `configureResend` and `getResendConfig` are the v7 surface,
6
- * kept for v7 sites.
7
4
  */
8
- export { sendEmail } from "./actions/send";
9
- export { configureResend, getResendConfig } from "./client";
10
5
  export {
11
6
  createResendClient,
12
7
  type ResendClient,
13
8
  type ResendClientConfig,
14
9
  ResendError,
15
10
  type SendEmailOptions,
16
- } from "./emails";
17
- export type {
18
- CreateEmailOptions,
19
- CreateEmailResponse,
20
- CreateEmailResponseSuccess,
21
- ErrorResponse,
22
- ResendConfig,
23
- ResendErrorCodeKey,
24
- } from "./types";
11
+ } from "./emails.ts";
12
+ export type { CreateEmailOptions, CreateEmailResponseSuccess } from "./types.ts";
package/src/types.ts CHANGED
@@ -1,54 +1,18 @@
1
- export const RESEND_ERROR_CODES_BY_KEY = {
2
- missing_required_field: 422,
3
- invalid_access: 422,
4
- invalid_parameter: 422,
5
- invalid_region: 422,
6
- rate_limit_exceeded: 429,
7
- missing_api_key: 401,
8
- invalid_api_Key: 403,
9
- invalid_from_address: 403,
10
- validation_error: 403,
11
- not_found: 404,
12
- method_not_allowed: 405,
13
- application_error: 500,
14
- internal_server_error: 500,
15
- } as const;
16
-
17
- export type ResendErrorCodeKey = keyof typeof RESEND_ERROR_CODES_BY_KEY;
18
-
19
- export interface ErrorResponse {
20
- message: string;
21
- name: ResendErrorCodeKey;
22
- }
1
+ // The Resend API shapes the client sends and receives (https://resend.com/docs/api-reference/emails/send-email).
23
2
 
24
3
  export interface CreateEmailResponseSuccess {
25
- /** The ID of the newly created email. */
26
- id: string;
27
- }
28
-
29
- export interface CreateEmailResponse {
30
- data: CreateEmailResponseSuccess | null;
31
- error: ErrorResponse | null;
4
+ /** The ID of the newly created email. */
5
+ id: string;
32
6
  }
33
7
 
34
8
  export interface CreateEmailOptions {
35
- from?: string;
36
- to: string | string[];
37
- subject: string;
38
- bcc?: string | string[];
39
- cc?: string | string[];
40
- reply_to?: string | string[];
41
- html?: string;
42
- text?: string;
43
- headers?: Record<string, string>;
44
- }
45
-
46
- export interface ResendConfig {
47
- apiKey: string;
48
- /** Default sender — e.g. "Contact <onboarding@resend.dev>" */
49
- emailFrom?: string;
50
- /** Default recipients */
51
- emailTo?: string[];
52
- /** Default subject */
53
- subject?: string;
9
+ from?: string;
10
+ to: string | string[];
11
+ subject: string;
12
+ bcc?: string | string[];
13
+ cc?: string | string[];
14
+ reply_to?: string | string[];
15
+ html?: string;
16
+ text?: string;
17
+ headers?: Record<string, string>;
54
18
  }
@@ -1,54 +0,0 @@
1
- /**
2
- * The v8 Resend client (/next/upstream-clients): one typed call through the
3
- * instrumented fetch, errors without bodies or keys, no retries.
4
- */
5
- import { describe, expect, it, vi } from "vitest";
6
- import { createResendClient, ResendError } from "../index";
7
-
8
- function fakeFetch(status: number, body: unknown) {
9
- return vi.fn(
10
- async (_input: string | URL | Request, _init?: RequestInit) =>
11
- new Response(JSON.stringify(body), { status }),
12
- );
13
- }
14
-
15
- const email = {
16
- from: "Store <hello@example.com>",
17
- to: "shopper@example.com",
18
- subject: "Your order",
19
- html: "<p>Thanks</p>",
20
- };
21
-
22
- describe("createResendClient", () => {
23
- it("sendEmail posts the email with the API key and returns its id", async () => {
24
- const fetch = fakeFetch(200, { id: "email_1" });
25
- const resend = createResendClient({ apiKey: "re_test" }, { fetch });
26
-
27
- await expect(resend.sendEmail(email)).resolves.toEqual({ id: "email_1" });
28
-
29
- const [url, init] = fetch.mock.calls[0];
30
- expect(String(url)).toBe("https://api.resend.com/emails");
31
- expect(init?.method).toBe("POST");
32
- expect(init?.headers).toMatchObject({ authorization: "Bearer re_test" });
33
- expect(JSON.parse(String(init?.body))).toEqual(email);
34
- });
35
-
36
- it("throws the operation and status, never the body or the key", async () => {
37
- const fetch = fakeFetch(422, { name: "validation_error", message: "re_test is bad" });
38
- const resend = createResendClient({ apiKey: "re_test" }, { fetch });
39
-
40
- const error = await resend.sendEmail(email).catch((e: unknown) => e);
41
-
42
- expect(error).toBeInstanceOf(ResendError);
43
- expect(error).toMatchObject({ operation: "sendEmail", status: 422 });
44
- expect((error as Error).message).not.toContain("re_test");
45
- });
46
-
47
- it("does not retry", async () => {
48
- const fetch = fakeFetch(503, {});
49
- const resend = createResendClient({ apiKey: "re_test" }, { fetch });
50
-
51
- await expect(resend.sendEmail(email)).rejects.toThrow(ResendError);
52
- expect(fetch).toHaveBeenCalledTimes(1);
53
- });
54
- });
@@ -1,105 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
- import { sendEmail } from "../actions/send";
3
- import { configureResend } from "../client";
4
-
5
- describe("sendEmail", () => {
6
- let fetchSpy: ReturnType<typeof vi.fn>;
7
-
8
- beforeEach(() => {
9
- fetchSpy = vi.fn();
10
- vi.stubGlobal("fetch", fetchSpy);
11
- configureResend({
12
- apiKey: "re_test_123",
13
- emailFrom: "Test <test@example.com>",
14
- emailTo: ["default@example.com"],
15
- subject: "Default Subject",
16
- });
17
- });
18
-
19
- afterEach(() => {
20
- vi.restoreAllMocks();
21
- });
22
-
23
- it("sends email with provided fields", async () => {
24
- fetchSpy.mockResolvedValueOnce({
25
- ok: true,
26
- json: () => Promise.resolve({ id: "email_abc123" }),
27
- });
28
-
29
- const result = await sendEmail({
30
- to: "user@example.com",
31
- subject: "Hello",
32
- html: "<p>World</p>",
33
- });
34
-
35
- expect(result.data).toEqual({ id: "email_abc123" });
36
- expect(result.error).toBeNull();
37
-
38
- expect(fetchSpy).toHaveBeenCalledWith(
39
- "https://api.resend.com/emails",
40
- expect.objectContaining({
41
- method: "POST",
42
- headers: {
43
- Authorization: "Bearer re_test_123",
44
- "Content-Type": "application/json",
45
- },
46
- body: JSON.stringify({
47
- from: "Test <test@example.com>",
48
- to: "user@example.com",
49
- subject: "Hello",
50
- html: "<p>World</p>",
51
- }),
52
- signal: expect.any(AbortSignal),
53
- }),
54
- );
55
- });
56
-
57
- it("falls back to defaults when fields are omitted", async () => {
58
- fetchSpy.mockResolvedValueOnce({
59
- ok: true,
60
- json: () => Promise.resolve({ id: "email_def456" }),
61
- });
62
-
63
- await sendEmail({
64
- html: "<p>Contact form</p>",
65
- });
66
-
67
- const body = JSON.parse(fetchSpy.mock.calls[0][1].body);
68
- expect(body.from).toBe("Test <test@example.com>");
69
- expect(body.to).toEqual(["default@example.com"]);
70
- expect(body.subject).toBe("Default Subject");
71
- expect(body.html).toBe("<p>Contact form</p>");
72
- });
73
-
74
- it("returns error on API failure", async () => {
75
- fetchSpy.mockResolvedValueOnce({
76
- ok: false,
77
- json: () =>
78
- Promise.resolve({
79
- message: "Invalid API key",
80
- name: "invalid_api_Key",
81
- }),
82
- });
83
-
84
- const result = await sendEmail({
85
- to: "user@example.com",
86
- subject: "Test",
87
- html: "<p>Test</p>",
88
- });
89
-
90
- expect(result.data).toBeNull();
91
- expect(result.error).toEqual({
92
- message: "Invalid API key",
93
- name: "invalid_api_Key",
94
- });
95
- });
96
-
97
- it("throws when configureResend was not called", async () => {
98
- // Reset the config by importing a fresh module
99
- // We can't easily reset the singleton, so we test the error path
100
- // by calling getResendConfig directly
101
- const { getResendConfig } = await import("../client");
102
- // Config was set in beforeEach, so this should work
103
- expect(() => getResendConfig()).not.toThrow();
104
- });
105
- });
@@ -1,60 +0,0 @@
1
- import { withFetchTimeout } from "@decocms/blocks/sdk/fetchTimeout";
2
- import { getResendConfig } from "../client";
3
- import type { CreateEmailOptions, CreateEmailResponse } from "../types";
4
-
5
- const timeoutFetch = withFetchTimeout();
6
-
7
- /**
8
- * Send an email via Resend API.
9
- *
10
- * ```ts
11
- * import { sendEmail } from "@decocms/apps/resend/actions/send";
12
- *
13
- * const result = await sendEmail({
14
- * subject: "Hello",
15
- * html: "<p>World</p>",
16
- * });
17
- * ```
18
- *
19
- * Fields not provided fall back to the defaults set in `configureResend()`.
20
- */
21
- export async function sendEmail(
22
- payload: Partial<CreateEmailOptions> & { subject?: string; html?: string },
23
- ): Promise<CreateEmailResponse> {
24
- const config = getResendConfig();
25
-
26
- const body: CreateEmailOptions = {
27
- from: payload.from ?? config.emailFrom ?? "Contact <onboarding@resend.dev>",
28
- to: payload.to ?? config.emailTo ?? [],
29
- subject: payload.subject ?? config.subject ?? "No subject",
30
- ...(payload.bcc && { bcc: payload.bcc }),
31
- ...(payload.cc && { cc: payload.cc }),
32
- ...(payload.reply_to && { reply_to: payload.reply_to }),
33
- ...(payload.html && { html: payload.html }),
34
- ...(payload.text && { text: payload.text }),
35
- ...(payload.headers && { headers: payload.headers }),
36
- };
37
-
38
- const response = await timeoutFetch("https://api.resend.com/emails", {
39
- method: "POST",
40
- headers: {
41
- Authorization: `Bearer ${config.apiKey}`,
42
- "Content-Type": "application/json",
43
- },
44
- body: JSON.stringify(body),
45
- });
46
-
47
- const data = await response.json();
48
-
49
- if (!response.ok) {
50
- return {
51
- data: null,
52
- error: data,
53
- };
54
- }
55
-
56
- return {
57
- data,
58
- error: null,
59
- };
60
- }
package/src/client.ts DELETED
@@ -1,39 +0,0 @@
1
- import type { ResendConfig } from "./types";
2
-
3
- let _config: ResendConfig | null = null;
4
-
5
- /**
6
- * Configure the Resend client. Call once in your site's setup.ts.
7
- *
8
- * ```ts
9
- * import { configureResend } from "@decocms/apps/resend/client";
10
- *
11
- * configureResend({
12
- * apiKey: process.env.RESEND_API_KEY!,
13
- * emailFrom: "Contact <hello@example.com>",
14
- * emailTo: ["team@example.com"],
15
- * subject: "Contact form submission",
16
- * });
17
- * ```
18
- *
19
- * TODO(secrets-decrypt): Add an `initResendFromBlocks(blocks, blockKey?)`
20
- * helper that mirrors magento/algolia/vtex. The Deco CMS Resend block
21
- * stores `apiKey` as an encrypted Secret reference (`{ encrypted, name }`)
22
- * — sites currently have to call `configureResend()` with a manually
23
- * resolved env var, missing the AES-CBC decrypt path via
24
- * `@decocms/start/sdk/crypto#resolveSecret`. Until that ships, sites
25
- * keep passing a string they obtain from `process.env` or a custom
26
- * resolver.
27
- */
28
- export function configureResend(config: ResendConfig) {
29
- _config = config;
30
- }
31
-
32
- export function getResendConfig(): ResendConfig {
33
- if (!_config) {
34
- throw new Error(
35
- "Resend not configured. Call configureResend() in setup.ts before using Resend actions.",
36
- );
37
- }
38
- return _config;
39
- }
@@ -1,15 +0,0 @@
1
- // AUTO-GENERATED by scripts/generate-manifests.ts — DO NOT EDIT
2
- // This file is checked into source control and updated via: npm run generate:manifests
3
- import * as actions_send from "./actions/send";
4
-
5
- const manifest = {
6
- name: "resend",
7
- loaders: {},
8
- actions: {
9
- "resend/actions/send": actions_send,
10
- },
11
- sections: {},
12
- } as const;
13
-
14
- export type Manifest = typeof manifest;
15
- export default manifest;
package/src/mod.ts DELETED
@@ -1,67 +0,0 @@
1
- /**
2
- * Resend app module — standard autoconfig contract.
3
- *
4
- * Exports `configure` and `handlers` following the AppModContract pattern.
5
- * The framework's `autoconfigApps()` calls these generically — no hardcoded
6
- * app knowledge needed in the framework.
7
- */
8
-
9
- import type { AppDefinition, AppHandler, ResolveSecretFn } from "@decocms/apps-commerce/app-types";
10
- import { sendEmail } from "./actions/send";
11
- import { configureResend } from "./client";
12
- import manifest from "./manifest.gen";
13
- import type { ResendConfig } from "./types";
14
-
15
- // -------------------------------------------------------------------------
16
- // State
17
- // -------------------------------------------------------------------------
18
-
19
- export interface ResendState {
20
- config: ResendConfig;
21
- }
22
-
23
- // -------------------------------------------------------------------------
24
- // Configure
25
- // -------------------------------------------------------------------------
26
-
27
- /**
28
- * Configure Resend from CMS block data.
29
- * Returns an AppDefinition or null if missing credentials.
30
- */
31
- export async function configure(
32
- block: any,
33
- resolveSecret: ResolveSecretFn,
34
- ): Promise<AppDefinition<ResendState> | null> {
35
- const apiKey = await resolveSecret(block.apiKey, "RESEND_API_KEY");
36
- if (!apiKey) return null;
37
-
38
- const config: ResendConfig = {
39
- apiKey,
40
- emailFrom: block.emailFrom
41
- ? `${block.emailFrom.name || "Contact"} <${block.emailFrom.domain || "onboarding@resend.dev"}>`
42
- : undefined,
43
- emailTo: block.emailTo,
44
- subject: block.subject,
45
- };
46
-
47
- // Bridge: maintain global singleton for backward compat
48
- configureResend(config);
49
-
50
- return {
51
- name: "resend",
52
- manifest,
53
- state: { config },
54
- };
55
- }
56
-
57
- /**
58
- * Invoke handlers registered under /deco/invoke/{key}.
59
- * Both with and without .ts suffix for compatibility.
60
- */
61
- export const handlers: Record<string, AppHandler> = {
62
- "resend/actions/emails/send": (props) => sendEmail(props),
63
- "resend/actions/emails/send.ts": (props) => sendEmail(props),
64
- };
65
-
66
- /** Placeholder preview for CMS editor — evolves when admin supports it. */
67
- export const preview = undefined;
package/src/registry.ts DELETED
@@ -1,9 +0,0 @@
1
- import type { AppRegistryEntry } from "@decocms/apps-commerce/registry";
2
-
3
- export const RESEND_REGISTRY_ENTRY: AppRegistryEntry = {
4
- blockKey: "deco-resend",
5
- module: () => import("./mod"),
6
- displayName: "Resend",
7
- category: "email",
8
- description: "Transactional email via Resend",
9
- };
package/tsconfig.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "outDir": "dist"
5
- },
6
- "include": ["src/**/*"]
7
- }