@8ms/helpers 2.0.46 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.yarn/install-state.gz +0 -0
- package/.yarn/releases/{yarn-4.9.2.cjs → yarn-4.9.4.cjs} +358 -358
- package/.yarnrc.yml +2 -1
- package/aws/ses/SimpleEmail.d.ts +25 -3
- package/aws/ses/SimpleEmail.js +172 -2
- package/package.json +2 -2
- package/util/promiseChunks.d.ts +1 -1
- package/util/promiseChunks.js +3 -1
package/.yarnrc.yml
CHANGED
package/aws/ses/SimpleEmail.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { BaseClass } from "../../_class";
|
|
2
|
+
type AttachmentSource = {
|
|
3
|
+
filename: string;
|
|
4
|
+
contentType?: string;
|
|
5
|
+
data64?: string;
|
|
6
|
+
url?: string;
|
|
7
|
+
};
|
|
2
8
|
type ConstructorProps = {
|
|
3
9
|
bcc?: string[];
|
|
4
10
|
cc?: string[];
|
|
@@ -6,6 +12,7 @@ type ConstructorProps = {
|
|
|
6
12
|
html?: string;
|
|
7
13
|
subject?: string;
|
|
8
14
|
to?: string | string[];
|
|
15
|
+
attachments?: AttachmentSource[];
|
|
9
16
|
};
|
|
10
17
|
/**
|
|
11
18
|
* Class to build and send an AWS SES email.
|
|
@@ -17,17 +24,29 @@ export declare class SimpleEmail extends BaseClass {
|
|
|
17
24
|
bcc: string[];
|
|
18
25
|
html?: string;
|
|
19
26
|
subject?: string;
|
|
20
|
-
|
|
27
|
+
attachments: AttachmentSource[];
|
|
28
|
+
constructor({ bcc, cc, from, html, subject, to, attachments }: ConstructorProps);
|
|
21
29
|
setFrom: (from: string) => this;
|
|
22
30
|
setHtml: (html: string) => this;
|
|
23
31
|
setSubject: (subject: string) => this;
|
|
32
|
+
setAttachments: (attachments: AttachmentSource[]) => this;
|
|
33
|
+
addAttachment: (attachment: AttachmentSource) => this;
|
|
34
|
+
addAttachmentFromData64: (filename: string, data64: string, contentType?: string) => this;
|
|
35
|
+
addAttachmentFromUrl: (filename: string, url: string, contentType?: string) => this;
|
|
36
|
+
clearAttachments: () => this;
|
|
24
37
|
pushBcc: (recipient: string | string[]) => this;
|
|
25
38
|
pushCc: (recipient: string | string[]) => this;
|
|
26
39
|
pushTo: (recipient: string | string[]) => this;
|
|
27
40
|
setBcc: (recipient: string | string[]) => this;
|
|
28
41
|
setCc: (recipient: string | string[]) => this;
|
|
29
42
|
setTo: (recipient: string | string[]) => this;
|
|
30
|
-
getSendParam: () => {
|
|
43
|
+
getSendParam: () => Promise<{
|
|
44
|
+
Destinations: string[];
|
|
45
|
+
RawMessage: {
|
|
46
|
+
Data: string;
|
|
47
|
+
};
|
|
48
|
+
Source: string;
|
|
49
|
+
} | {
|
|
31
50
|
Destination: {
|
|
32
51
|
BccAddresses: string[];
|
|
33
52
|
CcAddresses: string[];
|
|
@@ -50,7 +69,10 @@ export declare class SimpleEmail extends BaseClass {
|
|
|
50
69
|
};
|
|
51
70
|
};
|
|
52
71
|
Source: string;
|
|
53
|
-
}
|
|
72
|
+
}>;
|
|
73
|
+
private _getRawEmailParams;
|
|
74
|
+
private _getAttachmentData;
|
|
75
|
+
private _getContentTypeFromFilename;
|
|
54
76
|
private _filterRecipients;
|
|
55
77
|
}
|
|
56
78
|
export {};
|
package/aws/ses/SimpleEmail.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -10,11 +43,12 @@ const _class_1 = require("../../_class");
|
|
|
10
43
|
* Class to build and send an AWS SES email.
|
|
11
44
|
*/
|
|
12
45
|
class SimpleEmail extends _class_1.BaseClass {
|
|
13
|
-
constructor({ bcc, cc, from, html, subject, to }) {
|
|
46
|
+
constructor({ bcc, cc, from, html, subject, to, attachments }) {
|
|
14
47
|
super();
|
|
15
48
|
this.to = [];
|
|
16
49
|
this.cc = [];
|
|
17
50
|
this.bcc = [];
|
|
51
|
+
this.attachments = [];
|
|
18
52
|
this.setFrom = (from) => {
|
|
19
53
|
return this._setValue("from", from);
|
|
20
54
|
};
|
|
@@ -24,6 +58,31 @@ class SimpleEmail extends _class_1.BaseClass {
|
|
|
24
58
|
this.setSubject = (subject) => {
|
|
25
59
|
return this._setValue("subject", subject);
|
|
26
60
|
};
|
|
61
|
+
this.setAttachments = (attachments) => {
|
|
62
|
+
return this._setValue("attachments", attachments);
|
|
63
|
+
};
|
|
64
|
+
this.addAttachment = (attachment) => {
|
|
65
|
+
this.attachments.push(attachment);
|
|
66
|
+
return this;
|
|
67
|
+
};
|
|
68
|
+
this.addAttachmentFromData64 = (filename, data64, contentType) => {
|
|
69
|
+
return this.addAttachment({
|
|
70
|
+
filename,
|
|
71
|
+
data64,
|
|
72
|
+
contentType: contentType || this._getContentTypeFromFilename(filename)
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
this.addAttachmentFromUrl = (filename, url, contentType) => {
|
|
76
|
+
return this.addAttachment({
|
|
77
|
+
filename,
|
|
78
|
+
url,
|
|
79
|
+
contentType: contentType || this._getContentTypeFromFilename(filename)
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
this.clearAttachments = () => {
|
|
83
|
+
this.attachments = [];
|
|
84
|
+
return this;
|
|
85
|
+
};
|
|
27
86
|
this.pushBcc = (recipient) => {
|
|
28
87
|
return this._push("bcc", recipient);
|
|
29
88
|
};
|
|
@@ -42,7 +101,11 @@ class SimpleEmail extends _class_1.BaseClass {
|
|
|
42
101
|
this.setTo = (recipient) => {
|
|
43
102
|
return this._setArray("to", recipient);
|
|
44
103
|
};
|
|
45
|
-
this.getSendParam = () => {
|
|
104
|
+
this.getSendParam = async () => {
|
|
105
|
+
// For emails with attachments, we need to use SendRawEmail instead of SendEmail
|
|
106
|
+
if (this.attachments.length > 0) {
|
|
107
|
+
return await this._getRawEmailParams();
|
|
108
|
+
}
|
|
46
109
|
// https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/ses-examples-sending-email.html
|
|
47
110
|
const params = {
|
|
48
111
|
Destination: {
|
|
@@ -70,6 +133,110 @@ class SimpleEmail extends _class_1.BaseClass {
|
|
|
70
133
|
};
|
|
71
134
|
return params;
|
|
72
135
|
};
|
|
136
|
+
this._getRawEmailParams = async () => {
|
|
137
|
+
const boundary = `----=_NextPart_${Date.now()}_${Math.random()
|
|
138
|
+
.toString(36)
|
|
139
|
+
.substr(2, 9)}`;
|
|
140
|
+
// Build recipients list
|
|
141
|
+
const allRecipients = [
|
|
142
|
+
...this._filterRecipients("to"),
|
|
143
|
+
...this._filterRecipients("cc"),
|
|
144
|
+
...this._filterRecipients("bcc")
|
|
145
|
+
];
|
|
146
|
+
// Build email headers
|
|
147
|
+
let rawMessage = "";
|
|
148
|
+
rawMessage += `From: ${this.from}\r\n`;
|
|
149
|
+
if (this._filterRecipients("to").length > 0) {
|
|
150
|
+
rawMessage += `To: ${this._filterRecipients("to")
|
|
151
|
+
.join(", ")}\r\n`;
|
|
152
|
+
}
|
|
153
|
+
if (this._filterRecipients("cc").length > 0) {
|
|
154
|
+
rawMessage += `Cc: ${this._filterRecipients("cc")
|
|
155
|
+
.join(", ")}\r\n`;
|
|
156
|
+
}
|
|
157
|
+
rawMessage += `Subject: ${this.subject}\r\n`;
|
|
158
|
+
rawMessage += `MIME-Version: 1.0\r\n`;
|
|
159
|
+
rawMessage += `Content-Type: multipart/mixed; boundary="${boundary}"\r\n\r\n`;
|
|
160
|
+
// Add HTML body
|
|
161
|
+
rawMessage += `--${boundary}\r\n`;
|
|
162
|
+
rawMessage += `Content-Type: text/html; charset=UTF-8\r\n`;
|
|
163
|
+
rawMessage += `Content-Transfer-Encoding: 7bit\r\n\r\n`;
|
|
164
|
+
rawMessage += `${this.html}\r\n\r\n`;
|
|
165
|
+
// Add attachments
|
|
166
|
+
for (const attachment of this.attachments) {
|
|
167
|
+
const attachmentData = await this._getAttachmentData(attachment);
|
|
168
|
+
rawMessage += `--${boundary}\r\n`;
|
|
169
|
+
rawMessage += `Content-Type: ${attachment.contentType || "application/octet-stream"}\r\n`;
|
|
170
|
+
rawMessage += `Content-Disposition: attachment; filename="${attachment.filename}"\r\n`;
|
|
171
|
+
rawMessage += `Content-Transfer-Encoding: base64\r\n\r\n`;
|
|
172
|
+
rawMessage += `${attachmentData}\r\n\r\n`;
|
|
173
|
+
}
|
|
174
|
+
rawMessage += `--${boundary}--\r\n`;
|
|
175
|
+
return {
|
|
176
|
+
Destinations: allRecipients,
|
|
177
|
+
RawMessage: {
|
|
178
|
+
Data: rawMessage
|
|
179
|
+
},
|
|
180
|
+
Source: this.from
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
this._getAttachmentData = async (attachment) => {
|
|
184
|
+
// If data is already provided as base64
|
|
185
|
+
if (attachment.data64) {
|
|
186
|
+
// Remove data URL prefix if present (e.g., "data:image/png;base64,")
|
|
187
|
+
return attachment.data64.replace(/^data:[^;]+;base64,/, "");
|
|
188
|
+
}
|
|
189
|
+
// If URL is provided, fetch the data
|
|
190
|
+
if (attachment.url) {
|
|
191
|
+
const fetch = await Promise.resolve().then(() => __importStar(require("node-fetch"))).then(mod => mod.default);
|
|
192
|
+
const response = await fetch(attachment.url);
|
|
193
|
+
const buffer = await response.buffer();
|
|
194
|
+
return buffer.toString("base64");
|
|
195
|
+
}
|
|
196
|
+
throw new Error(`No valid data source provided for attachment: ${attachment.filename}`);
|
|
197
|
+
};
|
|
198
|
+
this._getContentTypeFromFilename = (filename) => {
|
|
199
|
+
const extension = filename.split(".")
|
|
200
|
+
.pop()
|
|
201
|
+
?.toLowerCase();
|
|
202
|
+
const mimeTypes = {
|
|
203
|
+
// Images
|
|
204
|
+
"jpg": "image/jpeg",
|
|
205
|
+
"jpeg": "image/jpeg",
|
|
206
|
+
"png": "image/png",
|
|
207
|
+
"gif": "image/gif",
|
|
208
|
+
"svg": "image/svg+xml",
|
|
209
|
+
"webp": "image/webp",
|
|
210
|
+
"bmp": "image/bmp",
|
|
211
|
+
"ico": "image/x-icon",
|
|
212
|
+
// Documents
|
|
213
|
+
"pdf": "application/pdf",
|
|
214
|
+
"doc": "application/msword",
|
|
215
|
+
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
216
|
+
"xls": "application/vnd.ms-excel",
|
|
217
|
+
"xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
218
|
+
"ppt": "application/vnd.ms-powerpoint",
|
|
219
|
+
"pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
220
|
+
// Data
|
|
221
|
+
"csv": "text/csv",
|
|
222
|
+
"json": "application/json",
|
|
223
|
+
"xml": "application/xml",
|
|
224
|
+
"txt": "text/plain",
|
|
225
|
+
// Archives
|
|
226
|
+
"zip": "application/zip",
|
|
227
|
+
"rar": "application/x-rar-compressed",
|
|
228
|
+
"7z": "application/x-7z-compressed",
|
|
229
|
+
"tar": "application/x-tar",
|
|
230
|
+
"gz": "application/gzip",
|
|
231
|
+
// Audio/Video
|
|
232
|
+
"mp3": "audio/mpeg",
|
|
233
|
+
"wav": "audio/wav",
|
|
234
|
+
"mp4": "video/mp4",
|
|
235
|
+
"avi": "video/x-msvideo",
|
|
236
|
+
"mov": "video/quicktime",
|
|
237
|
+
};
|
|
238
|
+
return mimeTypes[extension || ""] || "application/octet-stream";
|
|
239
|
+
};
|
|
73
240
|
this._filterRecipients = (field) => {
|
|
74
241
|
// Remove all undefined and null values
|
|
75
242
|
let response = this[field].filter(recipient => recipient && null !== recipient);
|
|
@@ -100,6 +267,9 @@ class SimpleEmail extends _class_1.BaseClass {
|
|
|
100
267
|
if (to) {
|
|
101
268
|
this.setTo(to);
|
|
102
269
|
}
|
|
270
|
+
if (attachments) {
|
|
271
|
+
this.setAttachments(attachments);
|
|
272
|
+
}
|
|
103
273
|
return this;
|
|
104
274
|
}
|
|
105
275
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@8ms/helpers",
|
|
3
3
|
"license": "UNLICENSED",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/8millionstories-organisation/8ms-helpers-ts.git"
|
|
@@ -183,5 +183,5 @@
|
|
|
183
183
|
"tslib": "2.8.1",
|
|
184
184
|
"typescript": "^5.0.0"
|
|
185
185
|
},
|
|
186
|
-
"packageManager": "yarn@4.9.
|
|
186
|
+
"packageManager": "yarn@4.9.4"
|
|
187
187
|
}
|
package/util/promiseChunks.d.ts
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* Chunk a queue of promises into controlled chunks to prevent overloading.
|
|
3
3
|
* https://stackoverflow.com/a/53964407
|
|
4
4
|
*/
|
|
5
|
-
export declare const promiseChunks: (promises: Promise<any>[], size: number) => Promise<any[]>;
|
|
5
|
+
export declare const promiseChunks: (promises: Promise<any>[], size: number, sleepSeconds?: number) => Promise<any[]>;
|
package/util/promiseChunks.js
CHANGED
|
@@ -2,17 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.promiseChunks = void 0;
|
|
4
4
|
const lodash_1 = require("lodash");
|
|
5
|
+
const sleep_1 = require("./sleep");
|
|
5
6
|
/**
|
|
6
7
|
* Chunk a queue of promises into controlled chunks to prevent overloading.
|
|
7
8
|
* https://stackoverflow.com/a/53964407
|
|
8
9
|
*/
|
|
9
|
-
const promiseChunks = async (promises, size) => {
|
|
10
|
+
const promiseChunks = async (promises, size, sleepSeconds = 0) => {
|
|
10
11
|
const batches = (0, lodash_1.chunk)(promises, size);
|
|
11
12
|
let results = [];
|
|
12
13
|
while (batches.length) {
|
|
13
14
|
const batch = batches.shift();
|
|
14
15
|
const result = await Promise.all(batch);
|
|
15
16
|
results.push(result);
|
|
17
|
+
await (0, sleep_1.sleep)(sleepSeconds);
|
|
16
18
|
}
|
|
17
19
|
return (0, lodash_1.flatten)(results);
|
|
18
20
|
};
|