@storecraft/mailer-providers-http 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.
- package/README.md +105 -0
- package/index.js +4 -0
- package/mailchimp/README.md +27 -0
- package/mailchimp/adapter.js +78 -0
- package/mailchimp/adapter.utils.js +26 -0
- package/mailchimp/index.js +1 -0
- package/mailchimp/tests/test.js +25 -0
- package/mailchimp/tsconfig.json +14 -0
- package/mailchimp/types.private.d.ts +50 -0
- package/mailchimp/types.public.d.ts +10 -0
- package/mailgun/README.md +29 -0
- package/mailgun/adapter.js +81 -0
- package/mailgun/adapter.utils.js +34 -0
- package/mailgun/index.js +1 -0
- package/mailgun/tests/test.js +30 -0
- package/mailgun/tsconfig.json +14 -0
- package/mailgun/types.private.d.ts +0 -0
- package/mailgun/types.public.d.ts +12 -0
- package/package.json +56 -0
- package/resend/README.md +27 -0
- package/resend/adapter.js +76 -0
- package/resend/adapter.utils.js +35 -0
- package/resend/index.js +1 -0
- package/resend/tests/test.js +25 -0
- package/resend/tsconfig.json +14 -0
- package/resend/types.private.d.ts +41 -0
- package/resend/types.public.d.ts +9 -0
- package/sendgrid/README.md +27 -0
- package/sendgrid/adapter.js +91 -0
- package/sendgrid/adapter.utils.js +27 -0
- package/sendgrid/index.js +1 -0
- package/sendgrid/tests/test.js +25 -0
- package/sendgrid/tsconfig.json +14 -0
- package/sendgrid/types.private.d.ts +58 -0
- package/sendgrid/types.public.d.ts +10 -0
- package/tsconfig.json +14 -0
package/README.md
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# `storecraft` Official serverless http email providers
|
2
|
+
|
3
|
+
Supports wellknown http-based `serverless` friendly `email` providers,
|
4
|
+
|
5
|
+
- [Sendgrid](https://docs.sendgrid.com/api-reference/mail-send/mail-send)
|
6
|
+
- [Resend](https://resend.com/docs/api-reference/emails/send-email)
|
7
|
+
- [Mailchimp](https://mailchimp.com/developer/transactional/api/messages/send-new-message/)
|
8
|
+
- [Mailgun](https://documentation.mailgun.com/en/latest/api-sending.html#examples)
|
9
|
+
|
10
|
+
> TODO: confirm tests
|
11
|
+
|
12
|
+
## Howto
|
13
|
+
|
14
|
+
### Sendgrid
|
15
|
+
|
16
|
+
```js
|
17
|
+
import { MailerSendGrid } from '@storecraft/mailer-providers-http/sendgrid';
|
18
|
+
|
19
|
+
const mailer = new MailerSendGrid(
|
20
|
+
{
|
21
|
+
apikey: process.env.SEND_GRID_SECRET
|
22
|
+
}
|
23
|
+
);
|
24
|
+
|
25
|
+
|
26
|
+
let { success, native_response } = await mailer.email({
|
27
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
28
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
29
|
+
subject: 'subject test', // Subject line
|
30
|
+
text: 'plain text test', // plain text body
|
31
|
+
html: '<p>html test</p>', // html body
|
32
|
+
});
|
33
|
+
|
34
|
+
```
|
35
|
+
|
36
|
+
### Resend
|
37
|
+
```js
|
38
|
+
import { MailerResend } from '@storecraft/mailer-providers-http/resend';
|
39
|
+
|
40
|
+
const mailer = new MailerResend(
|
41
|
+
{
|
42
|
+
apikey: process.env.RESEND_API_KEY
|
43
|
+
}
|
44
|
+
);
|
45
|
+
|
46
|
+
let { success, native_response } = await mailer.email({
|
47
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
48
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
49
|
+
subject: 'subject test', // Subject line
|
50
|
+
text: 'plain text test', // plain text body
|
51
|
+
html: '<p>html test</p>', // html body
|
52
|
+
});
|
53
|
+
|
54
|
+
```
|
55
|
+
|
56
|
+
|
57
|
+
### Mailchimp
|
58
|
+
|
59
|
+
```js
|
60
|
+
import { MailerMailChimp } from '@storecraft/mailer-providers-http/mailchimp';
|
61
|
+
|
62
|
+
const mailer = new MailerMailChimp(
|
63
|
+
{
|
64
|
+
apikey: process.env.MAILCHIMP_API_KEY
|
65
|
+
}
|
66
|
+
);
|
67
|
+
|
68
|
+
let { success, native_response } = await mailer.email({
|
69
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
70
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
71
|
+
subject: 'subject test', // Subject line
|
72
|
+
text: 'plain text test', // plain text body
|
73
|
+
html: '<p>html test</p>', // html body
|
74
|
+
});
|
75
|
+
|
76
|
+
```
|
77
|
+
|
78
|
+
|
79
|
+
### Mailgun
|
80
|
+
|
81
|
+
```js
|
82
|
+
import { MailerMailgun } from '@storecraft/mailer-providers-http/mailgun';
|
83
|
+
|
84
|
+
const mailer = new MailerMailgun(
|
85
|
+
{
|
86
|
+
apikey: process.env.MAILGUN_API_KEY
|
87
|
+
}
|
88
|
+
);
|
89
|
+
|
90
|
+
let { success, native_response } = await mailer.email(
|
91
|
+
{
|
92
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
93
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
94
|
+
subject: 'subject test', // Subject line
|
95
|
+
text: 'plain text test', // plain text body
|
96
|
+
html: '<p>html test</p>', // html body
|
97
|
+
}
|
98
|
+
);
|
99
|
+
|
100
|
+
```
|
101
|
+
|
102
|
+
|
103
|
+
```text
|
104
|
+
Author: Tomer Shalev (tomer.shalev@gmail.com)
|
105
|
+
```
|
package/index.js
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# `mailchimp` mailer over http
|
2
|
+
[mailchimp.com](https://mailchimp.com/developer/transactional/api/messages/send-new-message/) client, that can work on eveywhere with `javascript`. We only rely on `fetch api`.
|
3
|
+
|
4
|
+
## Howto
|
5
|
+
|
6
|
+
```js
|
7
|
+
import { MailerMailChimp } from '@storecraft/mailer-mailchimp-http';
|
8
|
+
|
9
|
+
const mailer = new MailerMailChimp(
|
10
|
+
{
|
11
|
+
apikey: process.env.MAILCHIMP_API_KEY
|
12
|
+
}
|
13
|
+
);
|
14
|
+
|
15
|
+
let { success, native_response } = await mailer.email({
|
16
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
17
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
18
|
+
subject: 'subject test', // Subject line
|
19
|
+
text: 'plain text test', // plain text body
|
20
|
+
html: '<p>html test</p>', // html body
|
21
|
+
});
|
22
|
+
|
23
|
+
```
|
24
|
+
|
25
|
+
```text
|
26
|
+
Author: Tomer Shalev (tomer.shalev@gmail.com)
|
27
|
+
```
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import { convert_to_base64 } from "./adapter.utils.js";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @typedef {import("./types.public.js").Config} Config
|
5
|
+
* @typedef {import('@storecraft/core/v-mailer').mailer<Config>} mailer
|
6
|
+
* @implements {mailer}
|
7
|
+
*
|
8
|
+
* mailer with mail-chimp / mandrill http api
|
9
|
+
*/
|
10
|
+
export class MailerMailChimp {
|
11
|
+
|
12
|
+
/** @type {Config} */ #_config;
|
13
|
+
|
14
|
+
/**
|
15
|
+
*
|
16
|
+
* @param {Config} config
|
17
|
+
*/
|
18
|
+
constructor(config) {
|
19
|
+
this.#_config = config;
|
20
|
+
}
|
21
|
+
|
22
|
+
get config() { return this.#_config; }
|
23
|
+
|
24
|
+
/**
|
25
|
+
*
|
26
|
+
* @type {mailer["email"]}
|
27
|
+
*/
|
28
|
+
async email(o) {
|
29
|
+
|
30
|
+
/** @type {import("./types.private.js").Mailchimp_sendmail} */
|
31
|
+
const body = {
|
32
|
+
key: this.config.apikey,
|
33
|
+
message: {
|
34
|
+
from_email: o.from.address,
|
35
|
+
from_name: o.from.name,
|
36
|
+
to: o.to.map(t => ({ email:t.address, name: t.name, type: 'to'})),
|
37
|
+
subject: o.subject,
|
38
|
+
html: o.html,
|
39
|
+
text: o.text,
|
40
|
+
attachments: o.attachments && await Promise.all(
|
41
|
+
o.attachments.map(
|
42
|
+
/**
|
43
|
+
* @returns {Promise<import("./types.private.js").Mailchimp_sendmail_message_attachment>}
|
44
|
+
*/
|
45
|
+
async a => ({
|
46
|
+
type: a.content_type,
|
47
|
+
name: a.filename,
|
48
|
+
content: await convert_to_base64(a.content)
|
49
|
+
})
|
50
|
+
)
|
51
|
+
),
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
let r;
|
56
|
+
try {
|
57
|
+
r = await fetch(
|
58
|
+
'https://mandrillapp.com/api/1.0/messages/send',
|
59
|
+
{
|
60
|
+
body: JSON.stringify(body),
|
61
|
+
headers: {
|
62
|
+
'Content-Type': 'application/json',
|
63
|
+
},
|
64
|
+
method: 'POST',
|
65
|
+
}
|
66
|
+
);
|
67
|
+
} catch (e) {
|
68
|
+
console.log(e);
|
69
|
+
}
|
70
|
+
|
71
|
+
return {
|
72
|
+
success: r.ok,
|
73
|
+
native_response: await r.json()
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
}
|
78
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { base64 } from '@storecraft/core/v-crypto'
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @param {import('@storecraft/core/v-mailer').MailObject["attachments"][0]["content"]} c
|
5
|
+
*/
|
6
|
+
export const convert_to_base64 = async (c) => {
|
7
|
+
if(c instanceof ArrayBuffer)
|
8
|
+
return base64.fromUint8Array(new Uint8Array(c));
|
9
|
+
else if(c instanceof ReadableStream) {
|
10
|
+
const reader = c.getReader();
|
11
|
+
const buffer = []
|
12
|
+
while (true) {
|
13
|
+
// The `read()` method returns a promise that
|
14
|
+
// resolves when a value has been received.
|
15
|
+
const { done, value } = await reader.read();
|
16
|
+
// Result objects contain two properties:
|
17
|
+
// `done` - `true` if the stream has already given you all its data.
|
18
|
+
// `value` - Some data. Always `undefined` when `done` is `true`.
|
19
|
+
if (done) {
|
20
|
+
return base64.fromUint8Array(new Uint8Array(buffer));
|
21
|
+
}
|
22
|
+
buffer.push(value);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
else return base64.toBase64(c.toString());
|
26
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './adapter.js'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import 'dotenv/config';
|
2
|
+
import { test } from 'uvu';
|
3
|
+
import * as assert from 'uvu/assert';
|
4
|
+
import { MailerMailChimp } from '../index.js';
|
5
|
+
|
6
|
+
const mailer = new MailerMailChimp(
|
7
|
+
{
|
8
|
+
apikey: process.env.MAILCHIMP_API_KEY
|
9
|
+
}
|
10
|
+
);
|
11
|
+
|
12
|
+
test('send email', async () => {
|
13
|
+
|
14
|
+
let { success, native_response } = await mailer.email({
|
15
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
16
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
17
|
+
subject: 'subject test', // Subject line
|
18
|
+
text: 'plain text test', // plain text body
|
19
|
+
html: '<p>html test</p>', // html body
|
20
|
+
});
|
21
|
+
|
22
|
+
assert.ok(success, `failed with native_response ${JSON.stringify(native_response, null, 2)}`)
|
23
|
+
});
|
24
|
+
|
25
|
+
test.run();
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"compileOnSave": false,
|
3
|
+
"compilerOptions": {
|
4
|
+
"noEmit": true,
|
5
|
+
"allowJs": true,
|
6
|
+
"checkJs": true,
|
7
|
+
"target": "ESNext",
|
8
|
+
"resolveJsonModule": true,
|
9
|
+
"moduleResolution": "NodeNext",
|
10
|
+
"module": "NodeNext",
|
11
|
+
"composite": true,
|
12
|
+
},
|
13
|
+
"include": ["*", "*/*", "src/*"]
|
14
|
+
}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
export type Mailchimp_sendmail_message_to = {
|
3
|
+
/** the email address of the recipient */
|
4
|
+
email: string;
|
5
|
+
/** the optional display name to use for the recipient */
|
6
|
+
name?: string;
|
7
|
+
/** the header type to use for the recipient, defaults to "to" if not provided Possible values: "to", "cc", or "bcc". */
|
8
|
+
type?: 'to' | 'cc' | 'bcc';
|
9
|
+
}
|
10
|
+
|
11
|
+
export type Mailchimp_sendmail_message_attachment = {
|
12
|
+
/** the MIME type of the attachment */
|
13
|
+
type?: string;
|
14
|
+
/** the file name of the attachment */
|
15
|
+
name: string;
|
16
|
+
/** the content of the attachment as a base64-encoded string */
|
17
|
+
content: string;
|
18
|
+
}
|
19
|
+
|
20
|
+
export type Mailchimp_sendmail_message = {
|
21
|
+
/** the full HTML content to be sent */
|
22
|
+
html?: string;
|
23
|
+
/** optional full text content to be sent */
|
24
|
+
text?: string;
|
25
|
+
/** the message subject */
|
26
|
+
subject: string;
|
27
|
+
/** the sender email address */
|
28
|
+
from_email: string;
|
29
|
+
/** optional from name to be used */
|
30
|
+
from_name?: string;
|
31
|
+
/** an array of recipient information. */
|
32
|
+
to: Mailchimp_sendmail_message_to[];
|
33
|
+
/** optional extra headers to add to the message (most headers are allowed) */
|
34
|
+
headers?: Record<string, string>;
|
35
|
+
/** an array of supported attachments to add to the message */
|
36
|
+
attachments?: Mailchimp_sendmail_message_attachment[];
|
37
|
+
}
|
38
|
+
|
39
|
+
export type Mailchimp_sendmail = {
|
40
|
+
/** a valid api key */
|
41
|
+
key: string;
|
42
|
+
/** enable a background sending mode that is optimized for bulk sending. In async mode, messages/send will immediately return a status of "queued" for every recipient. To handle rejections when sending in async mode, set up a webhook for the 'reject' event. Defaults to false for messages with no more than 10 recipients; messages with more than 10 recipients are always sent asynchronously, regardless of the value of async. */
|
43
|
+
async?: boolean;
|
44
|
+
/** the name of the dedicated ip pool that should be used to send the message. If you do not have any dedicated IPs, this parameter has no effect. If you specify a pool that does not exist, your default pool will be used instead. */
|
45
|
+
ip_pool?: string;
|
46
|
+
/** when this message should be sent as a UTC timestamp in YYYY-MM-DD HH:MM:SS format. If you specify a time in the past, the message will be sent immediately; for future dates, you're limited to one year from the date of scheduling. */
|
47
|
+
send_at?: string;
|
48
|
+
/** the information on the message to send */
|
49
|
+
message: Mailchimp_sendmail_message;
|
50
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# `mailgun` mailer over http
|
2
|
+
[mailgun.com](https://documentation.mailgun.com/en/latest/api-sending.html#examples) client, that can work on eveywhere with `javascript`. We only rely on `fetch api`.
|
3
|
+
|
4
|
+
## Howto
|
5
|
+
|
6
|
+
```js
|
7
|
+
import { MailerMailChimp } from '@storecraft/mailer-mailgun-http';
|
8
|
+
|
9
|
+
const mailer = new MailerMailgun(
|
10
|
+
{
|
11
|
+
apikey: process.env.MAILGUN_API_KEY
|
12
|
+
}
|
13
|
+
);
|
14
|
+
|
15
|
+
let { success, native_response } = await mailer.email(
|
16
|
+
{
|
17
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
18
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
19
|
+
subject: 'subject test', // Subject line
|
20
|
+
text: 'plain text test', // plain text body
|
21
|
+
html: '<p>html test</p>', // html body
|
22
|
+
}
|
23
|
+
);
|
24
|
+
|
25
|
+
```
|
26
|
+
|
27
|
+
```text
|
28
|
+
Author: Tomer Shalev (tomer.shalev@gmail.com)
|
29
|
+
```
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import { base64 } from "@storecraft/core/v-crypto";
|
2
|
+
import { address_to_friendly_name, convert_attachment_to_blob } from "./adapter.utils.js";
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @typedef {import("./types.public.js").Config} Config
|
6
|
+
* @typedef {import('@storecraft/core/v-mailer').mailer<Config>} mailer
|
7
|
+
* @implements {mailer}
|
8
|
+
*
|
9
|
+
* mailer with mailgun http api
|
10
|
+
*/
|
11
|
+
export class MailerMailgun {
|
12
|
+
|
13
|
+
/** @type {Config} */ #_config;
|
14
|
+
|
15
|
+
/**
|
16
|
+
*
|
17
|
+
* @param {Config} config
|
18
|
+
*/
|
19
|
+
constructor(config) {
|
20
|
+
this.#_config = config;
|
21
|
+
}
|
22
|
+
|
23
|
+
get config() { return this.#_config; }
|
24
|
+
|
25
|
+
/**
|
26
|
+
*
|
27
|
+
* @type {mailer["email"]}
|
28
|
+
*/
|
29
|
+
async email(o) {
|
30
|
+
|
31
|
+
const form = new FormData();
|
32
|
+
form.append('from', address_to_friendly_name(o.from));
|
33
|
+
|
34
|
+
o.to.forEach(
|
35
|
+
t => {
|
36
|
+
form.append('to', address_to_friendly_name(t));
|
37
|
+
}
|
38
|
+
);
|
39
|
+
form.append('subject', o.subject);
|
40
|
+
form.append('text', o.text);
|
41
|
+
form.append('html', o.html);
|
42
|
+
|
43
|
+
if(Array.isArray(o.attachments)) {
|
44
|
+
o.attachments.forEach(
|
45
|
+
async a => {
|
46
|
+
form.append(
|
47
|
+
'attachment',
|
48
|
+
await convert_attachment_to_blob(a.content),
|
49
|
+
a.filename ?? ''
|
50
|
+
);
|
51
|
+
}
|
52
|
+
)
|
53
|
+
}
|
54
|
+
|
55
|
+
const auth = base64.encode(`api:${this.config.apikey}`, false);
|
56
|
+
|
57
|
+
let r;
|
58
|
+
try {
|
59
|
+
r = await fetch(
|
60
|
+
`https://api.mailgun.net/v3/${this.config.domain_name}/messages`,
|
61
|
+
{
|
62
|
+
body: form,
|
63
|
+
headers: {
|
64
|
+
'Content-Type': 'multipart/form-data',
|
65
|
+
'Authorization': `Basic ${auth}`
|
66
|
+
},
|
67
|
+
method: 'POST',
|
68
|
+
}
|
69
|
+
);
|
70
|
+
} catch (e) {
|
71
|
+
console.log(e);
|
72
|
+
}
|
73
|
+
|
74
|
+
return {
|
75
|
+
success: r.ok,
|
76
|
+
native_response: await r.text()
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
}
|
81
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
/**
|
3
|
+
*
|
4
|
+
* @param {import('@storecraft/core/v-mailer').MailObject["attachments"][0]["content"]} c
|
5
|
+
*/
|
6
|
+
export const convert_attachment_to_blob = async c => {
|
7
|
+
if(c instanceof ArrayBuffer)
|
8
|
+
return new Blob([new Uint8Array(c)]);
|
9
|
+
else if(c instanceof ReadableStream) {
|
10
|
+
const reader = c.getReader();
|
11
|
+
const buffer = []
|
12
|
+
while (true) {
|
13
|
+
// The `read()` method returns a promise that
|
14
|
+
// resolves when a value has been received.
|
15
|
+
const { done, value } = await reader.read();
|
16
|
+
// Result objects contain two properties:
|
17
|
+
// `done` - `true` if the stream has already given you all its data.
|
18
|
+
// `value` - Some data. Always `undefined` when `done` is `true`.
|
19
|
+
if (done) {
|
20
|
+
return new Blob([new Uint8Array(buffer)]);
|
21
|
+
}
|
22
|
+
buffer.push(value);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
else return new Blob([c.toString()]);
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
*
|
30
|
+
* @param {import('@storecraft/core/v-mailer').MailAddress} a
|
31
|
+
*/
|
32
|
+
export const address_to_friendly_name = a => {
|
33
|
+
return a.name ? `${a.name} <${a.address}>` : a.address;
|
34
|
+
}
|
package/mailgun/index.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export * from './adapter.js'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import 'dotenv/config';
|
2
|
+
import { test } from 'uvu';
|
3
|
+
import * as assert from 'uvu/assert';
|
4
|
+
import { MailerMailgun } from '../index.js';
|
5
|
+
|
6
|
+
const mailer = new MailerMailgun(
|
7
|
+
{
|
8
|
+
apikey: process.env.MAILGUN_API_KEY,
|
9
|
+
domain_name: process.env.YOUR_DOMAIN_NAME
|
10
|
+
}
|
11
|
+
);
|
12
|
+
|
13
|
+
test('send email', async () => {
|
14
|
+
|
15
|
+
let { success, native_response } = await mailer.email({
|
16
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
17
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
18
|
+
subject: 'subject test', // Subject line
|
19
|
+
text: 'plain text test', // plain text body
|
20
|
+
html: '<p>html test</p>', // html body
|
21
|
+
});
|
22
|
+
|
23
|
+
assert.ok(
|
24
|
+
success,
|
25
|
+
`failed with native_response ${JSON.stringify(native_response, null, 2)}`
|
26
|
+
);
|
27
|
+
|
28
|
+
});
|
29
|
+
|
30
|
+
test.run();
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"compileOnSave": false,
|
3
|
+
"compilerOptions": {
|
4
|
+
"noEmit": true,
|
5
|
+
"allowJs": true,
|
6
|
+
"checkJs": true,
|
7
|
+
"target": "ESNext",
|
8
|
+
"resolveJsonModule": true,
|
9
|
+
"moduleResolution": "NodeNext",
|
10
|
+
"module": "NodeNext",
|
11
|
+
"composite": true,
|
12
|
+
},
|
13
|
+
"include": ["*", "*/*", "src/*"]
|
14
|
+
}
|
File without changes
|
package/package.json
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
{
|
2
|
+
"name": "@storecraft/mailer-providers-http",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "Official Serverless Friendly e-mail adapters for storecraft",
|
5
|
+
"license": "MIT",
|
6
|
+
"author": "Tomer Shalev (https://github.com/store-craft)",
|
7
|
+
"homepage": "https://github.com/store-craft/storecraft",
|
8
|
+
"repository": {
|
9
|
+
"type": "git",
|
10
|
+
"url": "https://github.com/store-craft/storecraft.git",
|
11
|
+
"directory": "packages/mailer-providers-http"
|
12
|
+
},
|
13
|
+
"keywords": [
|
14
|
+
"commerce",
|
15
|
+
"dashboard",
|
16
|
+
"code",
|
17
|
+
"storecraft"
|
18
|
+
],
|
19
|
+
"type": "module",
|
20
|
+
"main": "index.js",
|
21
|
+
"types": "./types.public.d.ts",
|
22
|
+
"scripts": {
|
23
|
+
"mailer-providers-http:test": "uvu -c",
|
24
|
+
"mailer-providers-http:publish": "npm publish --access public"
|
25
|
+
},
|
26
|
+
"exports": {
|
27
|
+
".": {
|
28
|
+
"import": "./index.js"
|
29
|
+
},
|
30
|
+
"./package.json": "./package.json",
|
31
|
+
"./mailchimp": {
|
32
|
+
"import": "./mailchimp/index.js",
|
33
|
+
"types": "./mailchimp/types.public.d.ts"
|
34
|
+
},
|
35
|
+
"./mailgun": {
|
36
|
+
"import": "./mailgun/index.js",
|
37
|
+
"types": "./mailgun/types.public.d.ts"
|
38
|
+
},
|
39
|
+
"./resend": {
|
40
|
+
"import": "./resend/index.js",
|
41
|
+
"types": "./resend/types.public.d.ts"
|
42
|
+
},
|
43
|
+
"./sendgrid": {
|
44
|
+
"import": "./sendgrid/index.js",
|
45
|
+
"types": "./sendgrid/types.public.d.ts"
|
46
|
+
}
|
47
|
+
},
|
48
|
+
"dependencies": {
|
49
|
+
"@storecraft/core": "^1.0.0"
|
50
|
+
},
|
51
|
+
"devDependencies": {
|
52
|
+
"@types/node": "^20.11.0",
|
53
|
+
"uvu": "^0.5.6",
|
54
|
+
"dotenv": "^16.3.1"
|
55
|
+
}
|
56
|
+
}
|
package/resend/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Resend mailer over http
|
2
|
+
[Resend.com](https://resend.com/docs/api-reference/emails/send-email) client, that can work on eveywhere with `javascript`. We only rely on `fetch api`
|
3
|
+
|
4
|
+
## Howto
|
5
|
+
|
6
|
+
```js
|
7
|
+
import { MailerResend } from '@storecraft/mailer-resend-http';
|
8
|
+
|
9
|
+
const mailer = new MailerResend(
|
10
|
+
{
|
11
|
+
apikey: process.env.RESEND_API_KEY
|
12
|
+
}
|
13
|
+
);
|
14
|
+
|
15
|
+
let { success, native_response } = await mailer.email({
|
16
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
17
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
18
|
+
subject: 'subject test', // Subject line
|
19
|
+
text: 'plain text test', // plain text body
|
20
|
+
html: '<p>html test</p>', // html body
|
21
|
+
});
|
22
|
+
|
23
|
+
```
|
24
|
+
|
25
|
+
```text
|
26
|
+
Author: Tomer Shalev (tomer.shalev@gmail.com)
|
27
|
+
```
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import { address_to_friendly_name, convert_to_base64 } from "./adapter.utils.js";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @typedef {import("./types.public.js").Config} Config
|
5
|
+
* @typedef {import('@storecraft/core/v-mailer').mailer<Config>} mailer
|
6
|
+
* @implements {mailer}
|
7
|
+
*
|
8
|
+
* mailer with Resend rest api
|
9
|
+
*/
|
10
|
+
export class MailerResend {
|
11
|
+
|
12
|
+
/** @type {Config} */ #_config;
|
13
|
+
|
14
|
+
/**
|
15
|
+
*
|
16
|
+
* @param {Config} config
|
17
|
+
*/
|
18
|
+
constructor(config) {
|
19
|
+
this.#_config = config;
|
20
|
+
}
|
21
|
+
|
22
|
+
get config() { return this.#_config; }
|
23
|
+
|
24
|
+
/**
|
25
|
+
*
|
26
|
+
* @type {mailer["email"]}
|
27
|
+
*/
|
28
|
+
async email(o) {
|
29
|
+
|
30
|
+
/** @type {import("./types.private.js").Resend_sendmail} */
|
31
|
+
const body = {
|
32
|
+
from: address_to_friendly_name(o.from),
|
33
|
+
to: o.to.map(t => t.address),
|
34
|
+
subject: o.subject,
|
35
|
+
html: o.html,
|
36
|
+
text: o.text,
|
37
|
+
attachments: o.attachments && await Promise.all(
|
38
|
+
o.attachments.map(
|
39
|
+
/**
|
40
|
+
* @returns {Promise<import("./types.private.js").Resend_sendmail_attachment>}
|
41
|
+
*/
|
42
|
+
async a => (
|
43
|
+
{
|
44
|
+
filename: a.filename,
|
45
|
+
content: await convert_to_base64(a.content)
|
46
|
+
}
|
47
|
+
)
|
48
|
+
)
|
49
|
+
),
|
50
|
+
}
|
51
|
+
|
52
|
+
let r;
|
53
|
+
try {
|
54
|
+
r = await fetch(
|
55
|
+
'https://api.resend.com/emails',
|
56
|
+
{
|
57
|
+
body: JSON.stringify(body),
|
58
|
+
headers: {
|
59
|
+
'Authorization': `Bearer ${this.config.apikey}`,
|
60
|
+
'Content-Type': 'application/json',
|
61
|
+
},
|
62
|
+
method: 'POST',
|
63
|
+
}
|
64
|
+
);
|
65
|
+
} catch (e) {
|
66
|
+
console.log(e);
|
67
|
+
}
|
68
|
+
|
69
|
+
return {
|
70
|
+
success: r.ok,
|
71
|
+
native_response: await r.json()
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
}
|
76
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import { base64 } from '@storecraft/core/v-crypto'
|
2
|
+
|
3
|
+
/**
|
4
|
+
*
|
5
|
+
* @param {import('@storecraft/core/v-mailer').MailObject["attachments"][0]["content"]} c
|
6
|
+
*/
|
7
|
+
export const convert_to_base64 = async (c) => {
|
8
|
+
if(c instanceof ArrayBuffer)
|
9
|
+
return base64.fromUint8Array(new Uint8Array(c));
|
10
|
+
else if(c instanceof ReadableStream) {
|
11
|
+
const reader = c.getReader();
|
12
|
+
const buffer = []
|
13
|
+
while (true) {
|
14
|
+
// The `read()` method returns a promise that
|
15
|
+
// resolves when a value has been received.
|
16
|
+
const { done, value } = await reader.read();
|
17
|
+
// Result objects contain two properties:
|
18
|
+
// `done` - `true` if the stream has already given you all its data.
|
19
|
+
// `value` - Some data. Always `undefined` when `done` is `true`.
|
20
|
+
if (done) {
|
21
|
+
return base64.fromUint8Array(new Uint8Array(buffer));
|
22
|
+
}
|
23
|
+
buffer.push(value);
|
24
|
+
}
|
25
|
+
}
|
26
|
+
else return base64.toBase64(c.toString());
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
*
|
31
|
+
* @param {import('@storecraft/core/v-mailer').MailAddress} a
|
32
|
+
*/
|
33
|
+
export const address_to_friendly_name = a => {
|
34
|
+
return a.name ? `${a.name} <${a.address}>` : a.address;
|
35
|
+
}
|
package/resend/index.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export * from './adapter.js'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import 'dotenv/config';
|
2
|
+
import { test } from 'uvu';
|
3
|
+
import * as assert from 'uvu/assert';
|
4
|
+
import { MailerResend } from '../index.js';
|
5
|
+
|
6
|
+
const mailer = new MailerResend(
|
7
|
+
{
|
8
|
+
apikey: process.env.RESEND_API_KEY
|
9
|
+
}
|
10
|
+
);
|
11
|
+
|
12
|
+
test('send email', async () => {
|
13
|
+
|
14
|
+
let { success, native_response } = await mailer.email({
|
15
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
16
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
17
|
+
subject: 'subject test', // Subject line
|
18
|
+
text: 'plain text test', // plain text body
|
19
|
+
html: '<p>html test</p>', // html body
|
20
|
+
});
|
21
|
+
|
22
|
+
assert.ok(success, `failed with native_response ${JSON.stringify(native_response, null, 2)}`)
|
23
|
+
});
|
24
|
+
|
25
|
+
test.run();
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"compileOnSave": false,
|
3
|
+
"compilerOptions": {
|
4
|
+
"noEmit": true,
|
5
|
+
"allowJs": true,
|
6
|
+
"checkJs": true,
|
7
|
+
"target": "ESNext",
|
8
|
+
"resolveJsonModule": true,
|
9
|
+
"moduleResolution": "NodeNext",
|
10
|
+
"module": "NodeNext",
|
11
|
+
"composite": true,
|
12
|
+
},
|
13
|
+
"include": ["*", "*/*", "src/*"]
|
14
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
export type Resend_sendmail_tag = {
|
3
|
+
/** 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. */
|
4
|
+
name: string;
|
5
|
+
/** 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. */
|
6
|
+
value?: string;
|
7
|
+
}
|
8
|
+
|
9
|
+
export type Resend_sendmail_attachment = {
|
10
|
+
/** Content of an attached file, base 64 encoded. */
|
11
|
+
content?: string;
|
12
|
+
/** Name of attached file. */
|
13
|
+
filename?: string;
|
14
|
+
/** Path where the attachment file is hosted */
|
15
|
+
path?: string;
|
16
|
+
}
|
17
|
+
|
18
|
+
export type Resend_sendmail = {
|
19
|
+
/** Sender email address. To include a friendly name, use the format "Your Name <sender@domain.com>". */
|
20
|
+
from: string,
|
21
|
+
/** Recipient email address. For multiple addresses, send as an array of strings. Max 50. */
|
22
|
+
to: string | string[]
|
23
|
+
/** Email subject. */
|
24
|
+
subject: string,
|
25
|
+
/** Bcc recipient email address. For multiple addresses, send as an array of strings. */
|
26
|
+
bcc?: string | string[],
|
27
|
+
/** Cc recipient email address. For multiple addresses, send as an array of strings. */
|
28
|
+
cc?: string | string[],
|
29
|
+
/** Reply-to email address. For multiple addresses, send as an array of strings. */
|
30
|
+
reply_to?: string | string[],
|
31
|
+
/** The HTML version of the message. */
|
32
|
+
html?: string;
|
33
|
+
/** The plain text version of the message. */
|
34
|
+
text?: string;
|
35
|
+
/** Custom headers to add to the email. */
|
36
|
+
headers?: Record<string, string>,
|
37
|
+
/** Email tags */
|
38
|
+
tags?: Resend_sendmail_tag[],
|
39
|
+
/** Filename and content of attachments (max 40mb per email) */
|
40
|
+
attachments?: Resend_sendmail_attachment[];
|
41
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# SendGrid mailer over http
|
2
|
+
[sendgrid.com](https://docs.sendgrid.com/api-reference/mail-send/mail-send) client, that can work on eveywhere with `javascript`. We only rely on `fetch api`.
|
3
|
+
|
4
|
+
## Howto
|
5
|
+
|
6
|
+
```js
|
7
|
+
import { MailerSendGrid } from '@storecraft/mailer-sendgrid-http';
|
8
|
+
|
9
|
+
const mailer = new MailerSendGrid(
|
10
|
+
{
|
11
|
+
apikey: process.env.SEND_GRID_SECRET
|
12
|
+
}
|
13
|
+
);
|
14
|
+
|
15
|
+
let { success, native_response } = await mailer.email({
|
16
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
17
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
18
|
+
subject: 'subject test', // Subject line
|
19
|
+
text: 'plain text test', // plain text body
|
20
|
+
html: '<p>html test</p>', // html body
|
21
|
+
});
|
22
|
+
|
23
|
+
```
|
24
|
+
|
25
|
+
```text
|
26
|
+
Author: Tomer Shalev (tomer.shalev@gmail.com)
|
27
|
+
```
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import { convert_to_base64 } from "./adapter.utils.js";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @typedef {import("./types.public.js").Config} Config
|
5
|
+
* @typedef {import('@storecraft/core/v-mailer').mailer<Config>} mailer
|
6
|
+
* @implements {mailer}
|
7
|
+
*
|
8
|
+
* mailer with sendgrid http api
|
9
|
+
*/
|
10
|
+
export class MailerSendGrid {
|
11
|
+
|
12
|
+
/** @type {Config} */ #_config;
|
13
|
+
|
14
|
+
/**
|
15
|
+
*
|
16
|
+
* @param {Config} config
|
17
|
+
*/
|
18
|
+
constructor(config) {
|
19
|
+
this.#_config = config;
|
20
|
+
}
|
21
|
+
|
22
|
+
get config() { return this.#_config; }
|
23
|
+
|
24
|
+
/**
|
25
|
+
*
|
26
|
+
* @type {mailer["email"]}
|
27
|
+
*/
|
28
|
+
async email(o) {
|
29
|
+
|
30
|
+
/** @type {import("./types.private.js").SendgridV3_sendmail} */
|
31
|
+
const body = {
|
32
|
+
content: [
|
33
|
+
{
|
34
|
+
type: 'text/html', value: o.html
|
35
|
+
},
|
36
|
+
{
|
37
|
+
type: 'text/plain', value: o.text
|
38
|
+
}
|
39
|
+
].filter(c => Boolean(c.value)),
|
40
|
+
from: { email: o.from.address, name: o.from.name ?? ''},
|
41
|
+
subject: o.subject,
|
42
|
+
attachments: o.attachments && await Promise.all(
|
43
|
+
o.attachments.map(
|
44
|
+
async a => (
|
45
|
+
{
|
46
|
+
content_id: a.content_id,
|
47
|
+
disposition: a.disposition,
|
48
|
+
filename: a.filename,
|
49
|
+
type: a.content_type,
|
50
|
+
content: await convert_to_base64(a.content)
|
51
|
+
}
|
52
|
+
)
|
53
|
+
)
|
54
|
+
),
|
55
|
+
personalizations: o.to.map(
|
56
|
+
t => (
|
57
|
+
{
|
58
|
+
to: {
|
59
|
+
email: t.address,
|
60
|
+
name: t.name ?? ''
|
61
|
+
}
|
62
|
+
}
|
63
|
+
)
|
64
|
+
)
|
65
|
+
}
|
66
|
+
|
67
|
+
let r;
|
68
|
+
try {
|
69
|
+
r = await fetch(
|
70
|
+
'https://api.sendgrid.com/v3/mail/send',
|
71
|
+
{
|
72
|
+
body: JSON.stringify(body),
|
73
|
+
headers: {
|
74
|
+
'Authorization': `Bearer ${this.config.apikey}`,
|
75
|
+
'Content-Type': 'application/json',
|
76
|
+
},
|
77
|
+
method: 'POST',
|
78
|
+
}
|
79
|
+
);
|
80
|
+
} catch (e) {
|
81
|
+
console.log(e);
|
82
|
+
}
|
83
|
+
|
84
|
+
return {
|
85
|
+
success: r.ok,
|
86
|
+
native_response: await r.json()
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
}
|
91
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { base64 } from '@storecraft/core/v-crypto'
|
2
|
+
|
3
|
+
/**
|
4
|
+
*
|
5
|
+
* @param {import('@storecraft/core/v-mailer').MailObject["attachments"][0]["content"]} c
|
6
|
+
*/
|
7
|
+
export const convert_to_base64 = async (c) => {
|
8
|
+
if(c instanceof ArrayBuffer)
|
9
|
+
return base64.fromUint8Array(new Uint8Array(c));
|
10
|
+
else if(c instanceof ReadableStream) {
|
11
|
+
const reader = c.getReader();
|
12
|
+
const buffer = []
|
13
|
+
while (true) {
|
14
|
+
// The `read()` method returns a promise that
|
15
|
+
// resolves when a value has been received.
|
16
|
+
const { done, value } = await reader.read();
|
17
|
+
// Result objects contain two properties:
|
18
|
+
// `done` - `true` if the stream has already given you all its data.
|
19
|
+
// `value` - Some data. Always `undefined` when `done` is `true`.
|
20
|
+
if (done) {
|
21
|
+
return base64.fromUint8Array(new Uint8Array(buffer));
|
22
|
+
}
|
23
|
+
buffer.push(value);
|
24
|
+
}
|
25
|
+
}
|
26
|
+
else return base64.toBase64(c.toString());
|
27
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './adapter.js'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import 'dotenv/config';
|
2
|
+
import { test } from 'uvu';
|
3
|
+
import * as assert from 'uvu/assert';
|
4
|
+
import { MailerSendGrid } from '../index.js';
|
5
|
+
|
6
|
+
const mailer = new MailerSendGrid(
|
7
|
+
{
|
8
|
+
apikey: process.env.SEND_GRID_SECRET
|
9
|
+
}
|
10
|
+
);
|
11
|
+
|
12
|
+
test('send email', async () => {
|
13
|
+
|
14
|
+
let { success, native_response } = await mailer.email({
|
15
|
+
from: {name: 'bob đź‘»', address: process.env.FROM_EMAIL }, // sender address
|
16
|
+
to: [ { address: process.env.TO_EMAIL } ], // list of receivers
|
17
|
+
subject: 'subject test', // Subject line
|
18
|
+
text: 'plain text test', // plain text body
|
19
|
+
html: '<p>html test</p>', // html body
|
20
|
+
});
|
21
|
+
|
22
|
+
assert.ok(success, `failed with native_response ${JSON.stringify(native_response, null, 2)}`)
|
23
|
+
});
|
24
|
+
|
25
|
+
test.run();
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"compileOnSave": false,
|
3
|
+
"compilerOptions": {
|
4
|
+
"noEmit": true,
|
5
|
+
"allowJs": true,
|
6
|
+
"checkJs": true,
|
7
|
+
"target": "ESNext",
|
8
|
+
"resolveJsonModule": true,
|
9
|
+
"moduleResolution": "NodeNext",
|
10
|
+
"module": "NodeNext",
|
11
|
+
"composite": true,
|
12
|
+
},
|
13
|
+
"include": ["*", "*/*", "src/*"]
|
14
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
export type SendgridV3_sendmail_content = {
|
3
|
+
/** The MIME type of the content you are including in your email (e.g., “text/plain” or “text/html”). */
|
4
|
+
type: string,
|
5
|
+
/** The actual content of the specified MIME type that you are including in your email. */
|
6
|
+
value: string,
|
7
|
+
}
|
8
|
+
|
9
|
+
export type SendgridV3_sendmail_attachment = {
|
10
|
+
/** The Base64 encoded content of the attachment. */
|
11
|
+
content: string;
|
12
|
+
/** The MIME type of the content you are attaching (e.g., “text/plain” or “text/html”). */
|
13
|
+
type?: string;
|
14
|
+
/** The attachment's filename. */
|
15
|
+
filename: string;
|
16
|
+
/**
|
17
|
+
* The attachment's content-disposition, specifying how you would like the attachment
|
18
|
+
* to be displayed. For example, “inline” results in the attached file are displayed
|
19
|
+
* automatically within the message while “attachment” results in the attached file
|
20
|
+
* require some action to be taken before it is displayed, such as opening or downloading the file.
|
21
|
+
* default: attachment
|
22
|
+
* Allowed Values: inline, attachment
|
23
|
+
*/
|
24
|
+
disposition: 'inline' | 'attachment';
|
25
|
+
/** The attachment's content ID. This is used when the disposition is set to “inline” and the attachment is an image, allowing the file to be displayed within the body of your email. */
|
26
|
+
content_id: string;
|
27
|
+
}
|
28
|
+
|
29
|
+
export type SendgridV3_sendmail_address = {
|
30
|
+
/** The 'From' email address used to deliver the message. This address should be a verified sender in your Twilio SendGrid account. format: email */
|
31
|
+
email: string,
|
32
|
+
/** A name or title associated with the sending email address. */
|
33
|
+
name?: string
|
34
|
+
}
|
35
|
+
|
36
|
+
export type SendgridV3_sendmail_personalization = {
|
37
|
+
from?: SendgridV3_sendmail_address,
|
38
|
+
to: SendgridV3_sendmail_address,
|
39
|
+
/** An array of recipients who will receive a copy of your email. Each object in this array must contain the recipient's email address. Each object in the array may optionally contain the recipient's name. maxItems: 1000 */
|
40
|
+
cc?: SendgridV3_sendmail_address[],
|
41
|
+
/** An array of recipients who will receive a copy of your email. Each object in this array must contain the recipient's email address. Each object in the array may optionally contain the recipient's name. maxItems: 1000 */
|
42
|
+
bcc?: SendgridV3_sendmail_address[],
|
43
|
+
/** The subject of your email. See character length requirements according to RFC 2822. */
|
44
|
+
subject?: string
|
45
|
+
|
46
|
+
}
|
47
|
+
|
48
|
+
export type SendgridV3_sendmail = {
|
49
|
+
from: SendgridV3_sendmail_address,
|
50
|
+
/** The global or 'message level' subject of your email. This may be overridden by subject lines set in personalizations. minLength: 1 */
|
51
|
+
subject: string,
|
52
|
+
/** An array where you can specify the content of your email. You can include multiple MIME types of content, but you must specify at least one MIME type. To include more than one MIME type, add another object to the array containing the type and value parameters. */
|
53
|
+
content: SendgridV3_sendmail_content[],
|
54
|
+
/** An array of objects where you can specify any attachments you want to include. */
|
55
|
+
attachments?: SendgridV3_sendmail_attachment[],
|
56
|
+
/** An array of messages and their metadata. Each object within personalizations can be thought of as an envelope - it defines who should receive an individual message and how that message should be handled. See our Personalizations documentation for examples. */
|
57
|
+
personalizations: SendgridV3_sendmail_personalization[]
|
58
|
+
}
|
package/tsconfig.json
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"compileOnSave": false,
|
3
|
+
"compilerOptions": {
|
4
|
+
"noEmit": true,
|
5
|
+
"allowJs": true,
|
6
|
+
"checkJs": true,
|
7
|
+
"target": "ESNext",
|
8
|
+
"resolveJsonModule": true,
|
9
|
+
"moduleResolution": "NodeNext",
|
10
|
+
"module": "NodeNext",
|
11
|
+
"composite": true,
|
12
|
+
},
|
13
|
+
"include": ["*", "*/*"]
|
14
|
+
}
|