resend 0.18.0-canary.0 → 1.0.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.
@@ -1,19 +1,22 @@
1
1
  {
2
2
  "name": "resend",
3
- "version": "0.18.0-canary.0",
3
+ "version": "1.0.0",
4
4
  "description": "Node.js library for the Resend API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/src/index.d.ts",
7
7
  "files": [
8
8
  "build/"
9
9
  ],
10
+ "engines": {
11
+ "node": ">=16"
12
+ },
10
13
  "scripts": {
11
14
  "build": "tsc -p .",
12
- "lint": "tslint -p tsconfig.json -c tslint.json",
13
15
  "test": "jest",
14
16
  "test:watch": "jest --watch",
15
- "prettier": "prettier \"src/**/*.{js,ts,tsx}\" --check",
16
- "format": "prettier \"src/**/*.{js,ts,tsx}\" --write",
17
+ "lint": "eslint --fix --ext .ts ./src",
18
+ "format:check": "prettier --check \"**/*.{ts,tsx}\"",
19
+ "format": "prettier --write .",
17
20
  "prepublishOnly": "yarn run build"
18
21
  },
19
22
  "repository": {
@@ -28,22 +31,24 @@
28
31
  "homepage": "https://github.com/resend-inc/resend-node#readme",
29
32
  "dependencies": {
30
33
  "@react-email/render": "0.0.7",
31
- "node-fetch": "2.6.6",
34
+ "node-fetch": "2.6.12",
32
35
  "type-fest": "3.13.0"
33
36
  },
34
37
  "devDependencies": {
35
38
  "@types/jest": "29.5.3",
36
- "@types/node": "18.16.2",
39
+ "@types/node": "18.17.5",
37
40
  "@types/node-fetch": "2.6.4",
38
- "@types/react": "18.2.0",
41
+ "@types/react": "18.2.20",
42
+ "@typescript-eslint/eslint-plugin": "6.4.0",
43
+ "@typescript-eslint/parser": "6.4.0",
44
+ "eslint": "8.47.0",
39
45
  "fetch-mock": "9.11.0",
40
- "jest": "29.6.1",
46
+ "jest": "29.6.2",
41
47
  "jest-fetch-mock": "3.0.3",
42
- "prettier": "3.0.0",
48
+ "prettier": "3.0.2",
43
49
  "prettier-2": "npm:prettier@2",
44
50
  "supertest": "6.3.3",
45
51
  "ts-jest": "29.1.1",
46
- "tslint": "6.1.3",
47
52
  "typescript": "5.1.6"
48
53
  }
49
54
  }
@@ -115,7 +115,7 @@ describe('API Keys', () => {
115
115
  yield expect(resend.apiKeys.create({
116
116
  name: 'Test',
117
117
  permission: 'wrong_access',
118
- })).resolves.toThrowErrorMatchingInlineSnapshot(`"Access must be "full_access" | "sending_access""`);
118
+ })).resolves.toThrowErrorMatchingInlineSnapshot('"Access must be "full_access" | "sending_access""');
119
119
  }));
120
120
  });
121
121
  describe('restricted by domain', () => {
@@ -159,7 +159,7 @@ describe('API Keys', () => {
159
159
  name: 'Test',
160
160
  permission: 'sending_access',
161
161
  domain_id: '1234',
162
- })).resolves.toThrowErrorMatchingInlineSnapshot(`"Something went wrong"`);
162
+ })).resolves.toThrowErrorMatchingInlineSnapshot('"Something went wrong"');
163
163
  }));
164
164
  });
165
165
  });
@@ -214,7 +214,7 @@ describe('API Keys', () => {
214
214
  },
215
215
  });
216
216
  const resend = new resend_1.Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');
217
- yield expect(resend.apiKeys.remove('5262504e-8ed7-4fac-bd16-0d4be94bc9f2')).resolves.toMatchInlineSnapshot(`undefined`);
217
+ yield expect(resend.apiKeys.remove('5262504e-8ed7-4fac-bd16-0d4be94bc9f2')).resolves.toMatchInlineSnapshot('undefined');
218
218
  }));
219
219
  it('throws error when missing id', () => __awaiter(void 0, void 0, void 0, function* () {
220
220
  fetchMock.mockOnce(JSON.stringify({
@@ -229,7 +229,7 @@ describe('API Keys', () => {
229
229
  },
230
230
  });
231
231
  const resend = new resend_1.Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');
232
- yield expect(resend.apiKeys.remove('')).resolves.toMatchInlineSnapshot(`undefined`);
232
+ yield expect(resend.apiKeys.remove('')).resolves.toMatchInlineSnapshot('undefined');
233
233
  }));
234
234
  it('throws error when wrong id', () => __awaiter(void 0, void 0, void 0, function* () {
235
235
  fetchMock.mockOnce(JSON.stringify({
@@ -244,7 +244,7 @@ describe('API Keys', () => {
244
244
  },
245
245
  });
246
246
  const resend = new resend_1.Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');
247
- yield expect(resend.apiKeys.remove('34bd250e-615a-400c-be11-5912572ee15b')).resolves.toMatchInlineSnapshot(`undefined`);
247
+ yield expect(resend.apiKeys.remove('34bd250e-615a-400c-be11-5912572ee15b')).resolves.toMatchInlineSnapshot('undefined');
248
248
  }));
249
249
  });
250
250
  });
@@ -28,7 +28,7 @@ describe('Domains', () => {
28
28
  type: 'MX',
29
29
  ttl: 'Auto',
30
30
  status: 'not_started',
31
- value: `feedback-smtp.us-east-1.com`,
31
+ value: 'feedback-smtp.us-east-1.com',
32
32
  priority: 10,
33
33
  },
34
34
  {
@@ -163,7 +163,7 @@ describe('Domains', () => {
163
163
  type: 'MX',
164
164
  ttl: 'Auto',
165
165
  status: 'not_started',
166
- value: `feedback-smtp.eu-west-1.com`,
166
+ value: 'feedback-smtp.eu-west-1.com',
167
167
  priority: 10,
168
168
  },
169
169
  {
@@ -448,7 +448,7 @@ describe('Domains', () => {
448
448
  },
449
449
  });
450
450
  const resend = new resend_1.Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');
451
- yield expect(resend.domains.verify('5262504e-8ed7-4fac-bd16-0d4be94bc9f2')).resolves.toMatchInlineSnapshot(`undefined`);
451
+ yield expect(resend.domains.verify('5262504e-8ed7-4fac-bd16-0d4be94bc9f2')).resolves.toMatchInlineSnapshot('undefined');
452
452
  }));
453
453
  });
454
454
  describe('remove', () => {
@@ -461,7 +461,7 @@ describe('Domains', () => {
461
461
  },
462
462
  });
463
463
  const resend = new resend_1.Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');
464
- yield expect(resend.domains.remove('5262504e-8ed7-4fac-bd16-0d4be94bc9f2')).resolves.toMatchInlineSnapshot(`undefined`);
464
+ yield expect(resend.domains.remove('5262504e-8ed7-4fac-bd16-0d4be94bc9f2')).resolves.toMatchInlineSnapshot('undefined');
465
465
  }));
466
466
  });
467
467
  });
@@ -1,6 +1,5 @@
1
1
  import { Resend } from '../resend';
2
- import { CreateEmailOptions, CreateEmailRequestOptions, CreateEmailResponse } from './interfaces';
3
- import { GetEmailResponse } from './interfaces/get-email-options.interface';
2
+ import { CreateEmailOptions, CreateEmailRequestOptions, CreateEmailResponse, GetEmailResponse } from './interfaces';
4
3
  export declare class Emails {
5
4
  private readonly resend;
6
5
  constructor(resend: Resend);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const jest_fetch_mock_1 = require("jest-fetch-mock");
13
+ const resend_1 = require("../resend");
14
+ (0, jest_fetch_mock_1.enableFetchMocks)();
15
+ const resend = new resend_1.Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');
16
+ describe('Emails', () => {
17
+ afterEach(() => fetchMock.resetMocks());
18
+ describe('create', () => {
19
+ it('sends email', () => __awaiter(void 0, void 0, void 0, function* () {
20
+ const payload = {
21
+ from: 'bu@resend.com',
22
+ to: 'zeno@resend.com',
23
+ subject: 'Hello World',
24
+ html: '<h1>Hello world</h1>',
25
+ };
26
+ fetchMock.mockOnce(JSON.stringify({
27
+ id: '1234',
28
+ }), {
29
+ status: 200,
30
+ headers: {
31
+ 'content-type': 'application/json',
32
+ Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
33
+ },
34
+ });
35
+ const data = yield resend.emails.create(payload);
36
+ expect(data).toMatchInlineSnapshot(`
37
+ {
38
+ "id": "1234",
39
+ }
40
+ `);
41
+ }));
42
+ });
43
+ describe('send', () => {
44
+ it('sends email', () => __awaiter(void 0, void 0, void 0, function* () {
45
+ const payload = {
46
+ from: 'bu@resend.com',
47
+ to: 'zeno@resend.com',
48
+ subject: 'Hello World',
49
+ html: '<h1>Hello world</h1>',
50
+ };
51
+ fetchMock.mockOnce(JSON.stringify({
52
+ id: '1234',
53
+ }), {
54
+ status: 200,
55
+ headers: {
56
+ 'content-type': 'application/json',
57
+ Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
58
+ },
59
+ });
60
+ const data = yield resend.emails.send(payload);
61
+ expect(data).toMatchInlineSnapshot(`
62
+ {
63
+ "id": "1234",
64
+ }
65
+ `);
66
+ }));
67
+ });
68
+ describe('get', () => {
69
+ describe('when email not found', () => {
70
+ it('returns error', () => __awaiter(void 0, void 0, void 0, function* () {
71
+ fetchMock.mockOnce(JSON.stringify({
72
+ name: 'not_found',
73
+ message: 'Email not found',
74
+ statusCode: 404,
75
+ }), {
76
+ status: 404,
77
+ headers: {
78
+ 'content-type': 'application/json',
79
+ Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
80
+ },
81
+ });
82
+ yield expect(resend.emails.get('1234')).resolves.toMatchInlineSnapshot(`
83
+ {
84
+ "message": "Email not found",
85
+ "name": "not_found",
86
+ "statusCode": 404,
87
+ }
88
+ `);
89
+ }));
90
+ });
91
+ describe('when email found', () => {
92
+ it('returns emails with only to', () => __awaiter(void 0, void 0, void 0, function* () {
93
+ const response = {
94
+ object: 'email',
95
+ id: '123',
96
+ to: ['zeno@resend.com'],
97
+ from: 'bu@resend.com',
98
+ created_at: '321',
99
+ subject: 'Test email',
100
+ html: '<p>hello hello</p>',
101
+ text: null,
102
+ bcc: null,
103
+ cc: null,
104
+ reply_to: null,
105
+ last_event: 'sent',
106
+ };
107
+ fetchMock.mockOnce(JSON.stringify(response), {
108
+ status: 200,
109
+ headers: {
110
+ 'content-type': 'application/json',
111
+ Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
112
+ },
113
+ });
114
+ yield expect(resend.emails.get('1234')).resolves.toMatchInlineSnapshot(`
115
+ {
116
+ "bcc": null,
117
+ "cc": null,
118
+ "created_at": "321",
119
+ "from": "bu@resend.com",
120
+ "html": "<p>hello hello</p>",
121
+ "id": "123",
122
+ "last_event": "sent",
123
+ "object": "email",
124
+ "reply_to": null,
125
+ "subject": "Test email",
126
+ "text": null,
127
+ "to": [
128
+ "zeno@resend.com",
129
+ ],
130
+ }
131
+ `);
132
+ }));
133
+ it('returns emails with to and multiple cc', () => __awaiter(void 0, void 0, void 0, function* () {
134
+ const response = {
135
+ object: 'email',
136
+ id: '123',
137
+ to: ['zeno@resend.com'],
138
+ from: 'bu@resend.com',
139
+ created_at: '321',
140
+ subject: 'Test email',
141
+ html: '<p>hello hello</p>',
142
+ text: null,
143
+ bcc: null,
144
+ cc: ['zeno@resend.com', 'bu@resend.com'],
145
+ reply_to: null,
146
+ last_event: 'sent',
147
+ };
148
+ fetchMock.mockOnce(JSON.stringify(response), {
149
+ status: 200,
150
+ headers: {
151
+ 'content-type': 'application/json',
152
+ Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
153
+ },
154
+ });
155
+ yield expect(resend.emails.get('1234')).resolves.toMatchInlineSnapshot(`
156
+ {
157
+ "bcc": null,
158
+ "cc": [
159
+ "zeno@resend.com",
160
+ "bu@resend.com",
161
+ ],
162
+ "created_at": "321",
163
+ "from": "bu@resend.com",
164
+ "html": "<p>hello hello</p>",
165
+ "id": "123",
166
+ "last_event": "sent",
167
+ "object": "email",
168
+ "reply_to": null,
169
+ "subject": "Test email",
170
+ "text": null,
171
+ "to": [
172
+ "zeno@resend.com",
173
+ ],
174
+ }
175
+ `);
176
+ }));
177
+ });
178
+ });
179
+ });
@@ -3,32 +3,102 @@ import * as React from 'react';
3
3
  import { PostOptions } from '../../common/interfaces';
4
4
  import { RequireAtLeastOne } from 'type-fest';
5
5
  interface CreateEmailBaseOptions {
6
+ /**
7
+ * Filename and content of attachments (max 40mb per email)
8
+ *
9
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
10
+ */
6
11
  attachments?: Attachment[];
12
+ /**
13
+ * Blind carbon copy recipient email address. For multiple addresses, send as an array of strings.
14
+ *
15
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
16
+ */
7
17
  bcc?: string | string[];
18
+ /**
19
+ * Carbon copy recipient email address. For multiple addresses, send as an array of strings.
20
+ *
21
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
22
+ */
8
23
  cc?: string | string[];
24
+ /**
25
+ * Sender email address. To include a friendly name, use the format `"Your Name <sender@domain.com>"`
26
+ *
27
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
28
+ */
9
29
  from: string;
30
+ /**
31
+ * Custom headers to add to the email.
32
+ *
33
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
34
+ */
10
35
  headers?: Record<string, string>;
36
+ /**
37
+ * The React component used to write the message.
38
+ *
39
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
40
+ */
11
41
  react?: React.ReactElement | React.ReactNode | null;
42
+ /**
43
+ * The HTML version of the message.
44
+ *
45
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
46
+ */
12
47
  html?: string;
48
+ /**
49
+ * The plain text version of the message.
50
+ *
51
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
52
+ */
13
53
  text?: string;
54
+ /**
55
+ * Reply-to email address. For multiple addresses, send as an array of strings.
56
+ *
57
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
58
+ */
14
59
  reply_to?: string | string[];
60
+ /**
61
+ * Email subject.
62
+ *
63
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
64
+ */
15
65
  subject: string;
66
+ /**
67
+ * Email tags
68
+ *
69
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
70
+ */
16
71
  tags?: Tag[];
72
+ /**
73
+ * Recipient email address. For multiple addresses, send as an array of strings. Max 50.
74
+ *
75
+ * @link https://resend.com/api-reference/emails/send-email#body-parameters
76
+ */
17
77
  to: string | string[];
18
78
  }
19
79
  export type CreateEmailOptions = RequireAtLeastOne<CreateEmailBaseOptions, 'react' | 'html' | 'text'>;
20
80
  export interface CreateEmailRequestOptions extends PostOptions {
21
81
  }
22
82
  export interface CreateEmailResponse {
83
+ /** The ID of the newly created email. */
23
84
  id: string;
24
85
  }
25
86
  interface Attachment {
87
+ /** Content of an attached file. */
26
88
  content?: string | Buffer;
89
+ /** Name of attached file. */
27
90
  filename?: string | false | undefined;
91
+ /** Path where the attachment file is hosted */
28
92
  path?: string;
29
93
  }
30
94
  export type Tag = {
95
+ /**
96
+ * The name of the email tag. It can only contain ASCII letters (a–z, A–Z), numbers (0–9), underscores (_), or dashes (-). It can contain no more than 256 characters.
97
+ */
31
98
  name: string;
99
+ /**
100
+ * The value of the email tag. It can only contain ASCII letters (a–z, A–Z), numbers (0–9), underscores (_), or dashes (-). It can contain no more than 256 characters.
101
+ */
32
102
  value: string;
33
103
  };
34
104
  export {};
@@ -1,11 +1,14 @@
1
1
  export interface GetEmailResponse {
2
- bcc?: string[];
3
- cc?: string[];
2
+ bcc: string[] | null;
3
+ cc: string[] | null;
4
+ created_at: string;
4
5
  from: string;
5
- html?: string;
6
+ html: string | null;
6
7
  id: string;
8
+ last_event: string;
9
+ reply_to: string[] | null;
7
10
  subject: string;
8
- text?: string;
11
+ text: string | null;
9
12
  to: string[];
10
13
  object: 'email';
11
14
  }
@@ -1 +1,2 @@
1
1
  export * from './create-email-options.interface';
2
+ export * from './get-email-options.interface';
@@ -15,3 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./create-email-options.interface"), exports);
18
+ __exportStar(require("./get-email-options.interface"), exports);
@@ -69,7 +69,7 @@ class Resend {
69
69
  return yield response.json();
70
70
  });
71
71
  }
72
- post(path, entity, options) {
72
+ post(path, entity, options = {}) {
73
73
  return __awaiter(this, void 0, void 0, function* () {
74
74
  const requestOptions = Object.assign({ method: 'POST', headers: this.headers, body: JSON.stringify(entity) }, options);
75
75
  return yield this.fetchRequest(path, requestOptions);
package/package.json CHANGED
@@ -1,19 +1,22 @@
1
1
  {
2
2
  "name": "resend",
3
- "version": "0.18.0-canary.0",
3
+ "version": "1.0.0",
4
4
  "description": "Node.js library for the Resend API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/src/index.d.ts",
7
7
  "files": [
8
8
  "build/"
9
9
  ],
10
+ "engines": {
11
+ "node": ">=16"
12
+ },
10
13
  "scripts": {
11
14
  "build": "tsc -p .",
12
- "lint": "tslint -p tsconfig.json -c tslint.json",
13
15
  "test": "jest",
14
16
  "test:watch": "jest --watch",
15
- "prettier": "prettier \"src/**/*.{js,ts,tsx}\" --check",
16
- "format": "prettier \"src/**/*.{js,ts,tsx}\" --write",
17
+ "lint": "eslint --fix --ext .ts ./src",
18
+ "format:check": "prettier --check \"**/*.{ts,tsx}\"",
19
+ "format": "prettier --write .",
17
20
  "prepublishOnly": "yarn run build"
18
21
  },
19
22
  "repository": {
@@ -28,22 +31,24 @@
28
31
  "homepage": "https://github.com/resend-inc/resend-node#readme",
29
32
  "dependencies": {
30
33
  "@react-email/render": "0.0.7",
31
- "node-fetch": "2.6.6",
34
+ "node-fetch": "2.6.12",
32
35
  "type-fest": "3.13.0"
33
36
  },
34
37
  "devDependencies": {
35
38
  "@types/jest": "29.5.3",
36
- "@types/node": "18.16.2",
39
+ "@types/node": "18.17.5",
37
40
  "@types/node-fetch": "2.6.4",
38
- "@types/react": "18.2.0",
41
+ "@types/react": "18.2.20",
42
+ "@typescript-eslint/eslint-plugin": "6.4.0",
43
+ "@typescript-eslint/parser": "6.4.0",
44
+ "eslint": "8.47.0",
39
45
  "fetch-mock": "9.11.0",
40
- "jest": "29.6.1",
46
+ "jest": "29.6.2",
41
47
  "jest-fetch-mock": "3.0.3",
42
- "prettier": "3.0.0",
48
+ "prettier": "3.0.2",
43
49
  "prettier-2": "npm:prettier@2",
44
50
  "supertest": "6.3.3",
45
51
  "ts-jest": "29.1.1",
46
- "tslint": "6.1.3",
47
52
  "typescript": "5.1.6"
48
53
  }
49
54
  }
package/readme.md CHANGED
@@ -14,10 +14,10 @@ yarn add resend
14
14
 
15
15
  Send email with:
16
16
 
17
- * [Node.js](https://github.com/resendlabs/resend-node-example)
18
- * [Next.js (App Router)](https://github.com/resendlabs/resend-nextjs-app-router-example)
19
- * [Next.js (Pages Router)](https://github.com/resendlabs/resend-nextjs-pages-router-example)
20
- * [Express](https://github.com/resendlabs/resend-express-example)
17
+ - [Node.js](https://github.com/resendlabs/resend-node-example)
18
+ - [Next.js (App Router)](https://github.com/resendlabs/resend-nextjs-app-router-example)
19
+ - [Next.js (Pages Router)](https://github.com/resendlabs/resend-nextjs-pages-router-example)
20
+ - [Express](https://github.com/resendlabs/resend-express-example)
21
21
 
22
22
  ## Setup
23
23
 
@@ -59,24 +59,24 @@ await resend.emails.send({
59
59
  Start by creating your email template as a React component.
60
60
 
61
61
  ```jsx
62
- import React from 'react'
62
+ import React from 'react';
63
63
 
64
64
  export default function EmailTemplate(props) {
65
- const { firstName, product } = props
65
+ const { firstName, product } = props;
66
66
 
67
67
  return (
68
68
  <div>
69
69
  <h1>Welcome, {firstName}!</h1>
70
70
  <p>Thanks for trying {product}. We’re thrilled to have you on board.</p>
71
71
  </div>
72
- )
72
+ );
73
73
  }
74
74
  ```
75
75
 
76
76
  Then import the template component and pass it to the `react` property.
77
77
 
78
78
  ```jsx
79
- import EmailTemplate from '../components/EmailTemplate'
79
+ import EmailTemplate from '../components/EmailTemplate';
80
80
 
81
81
  await resend.emails.send({
82
82
  from: 'you@example.com',