postal-mime 2.2.2 → 2.2.4
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/.ncurc.js +7 -0
- package/CHANGELOG.md +15 -0
- package/README.md +51 -0
- package/package.json +2 -2
- package/postal-mime.d.ts +17 -1
- package/src/mime-node.js +3 -0
- package/src/postal-mime.js +57 -10
package/.ncurc.js
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.2.4](https://github.com/postalsys/postal-mime/compare/v2.2.3...v2.2.4) (2024-04-11)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **exports:** Export addressParser and decodeWords functions ([43d3187](https://github.com/postalsys/postal-mime/commit/43d31873308d8eff61876f32614e5cc5143c90dd))
|
|
9
|
+
|
|
10
|
+
## [2.2.3](https://github.com/postalsys/postal-mime/compare/v2.2.2...v2.2.3) (2024-04-11)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **attachments:** Added description key from Content-Description attachment header ([6e29de9](https://github.com/postalsys/postal-mime/commit/6e29de97a4dc0043587a59870d52250602801e3c))
|
|
16
|
+
* **calendar-attachments:** treat text/calendar as an attachment ([2196b49](https://github.com/postalsys/postal-mime/commit/2196b497f289697e9dc72011708e4355ee7362cc))
|
|
17
|
+
|
|
3
18
|
## [2.2.2](https://github.com/postalsys/postal-mime/compare/v2.2.1...v2.2.2) (2024-04-10)
|
|
4
19
|
|
|
5
20
|
|
package/README.md
CHANGED
|
@@ -123,6 +123,57 @@ This method parses an email message into a structured object with the following
|
|
|
123
123
|
- **attachment[].contentId** is the ID from Content-ID header
|
|
124
124
|
- **attachment[].content** is an Uint8Array value that contains the attachment file
|
|
125
125
|
|
|
126
|
+
### Utility functions
|
|
127
|
+
|
|
128
|
+
#### addressParser
|
|
129
|
+
|
|
130
|
+
Parse email address strings
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
addressParser(addressStr, opts) -> Array
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
where
|
|
137
|
+
|
|
138
|
+
- **addressStr** is the header value for an address header
|
|
139
|
+
- **opts** is an optional options object
|
|
140
|
+
- **flattem** is a boolean value. If set to `true`, then ignores address groups and returns a flat array of addresses. By default (`flatten` is `false`) the result might include nested groups
|
|
141
|
+
|
|
142
|
+
The result is an array of objects
|
|
143
|
+
|
|
144
|
+
- **name** is the name string. An empty string is used if name value was not set.
|
|
145
|
+
- **address** is the email address value
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
import { addressParser } from 'postal-mime';
|
|
149
|
+
|
|
150
|
+
const addressStr = '=?utf-8?B?44Ko44Od44K544Kr44O844OJ?= <support@example.com>';
|
|
151
|
+
console.log(addressParser(addressStr));
|
|
152
|
+
// [ { name: 'エポスカード', address: 'support@example.com' } ]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### decodeWords
|
|
156
|
+
|
|
157
|
+
Decode MIME encoded-words
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
decodeWords(encodedStr) -> String
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
where
|
|
164
|
+
|
|
165
|
+
- **encodedStr** is a string value that _may_ include MIME encoded-words
|
|
166
|
+
|
|
167
|
+
The result is a unicode string
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
import { decodeWords } from 'postal-mime';
|
|
171
|
+
|
|
172
|
+
const encodedStr = 'Hello, =?utf-8?B?44Ko44Od44K544Kr44O844OJ?=';
|
|
173
|
+
console.log(decodeWords(encodedStr));
|
|
174
|
+
// Hello, エポスカード
|
|
175
|
+
```
|
|
176
|
+
|
|
126
177
|
## License
|
|
127
178
|
|
|
128
179
|
© 2021-2024 Andris Reinman
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postal-mime",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"description": "Email parser for browser environments",
|
|
5
5
|
"main": "./src/postal-mime.js",
|
|
6
6
|
"exports": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"author": "Andris Reinman",
|
|
28
28
|
"license": "MIT-0",
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@types/node": "20.
|
|
30
|
+
"@types/node": "20.12.7",
|
|
31
31
|
"cross-blob": "3.0.2",
|
|
32
32
|
"cross-env": "7.0.3",
|
|
33
33
|
"eslint": "8.57.0",
|
package/postal-mime.d.ts
CHANGED
|
@@ -8,11 +8,13 @@ export type Address = {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
export type Attachment = {
|
|
11
|
-
filename: string;
|
|
11
|
+
filename: string | null;
|
|
12
12
|
mimeType: string;
|
|
13
13
|
disposition: "attachment" | "inline" | null;
|
|
14
14
|
related?: boolean;
|
|
15
|
+
description?: string;
|
|
15
16
|
contentId?: string;
|
|
17
|
+
method?: string;
|
|
16
18
|
content: Uint8Array;
|
|
17
19
|
};
|
|
18
20
|
|
|
@@ -36,9 +38,23 @@ export type Email = {
|
|
|
36
38
|
attachments: Attachment[];
|
|
37
39
|
};
|
|
38
40
|
|
|
41
|
+
declare type AddressParserOptions = {
|
|
42
|
+
flatten?: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare function addressParser (
|
|
46
|
+
str: string,
|
|
47
|
+
opts?: AddressParserOptions
|
|
48
|
+
): Address[];
|
|
49
|
+
|
|
50
|
+
declare function decodeWords (
|
|
51
|
+
str: string
|
|
52
|
+
): string;
|
|
53
|
+
|
|
39
54
|
declare class PostalMime {
|
|
40
55
|
static parse(email: RawEmail): Promise<Email>;
|
|
41
56
|
parse(email: RawEmail): Promise<Email>;
|
|
42
57
|
}
|
|
43
58
|
|
|
59
|
+
export { addressParser, decodeWords };
|
|
44
60
|
export default PostalMime;
|
package/src/mime-node.js
CHANGED
package/src/postal-mime.js
CHANGED
|
@@ -3,6 +3,8 @@ import { textToHtml, htmlToText, formatTextHeader, formatHtmlHeader } from './te
|
|
|
3
3
|
import addressParser from './address-parser.js';
|
|
4
4
|
import { decodeWords, textEncoder, blobToArrayBuffer } from './decode-strings.js';
|
|
5
5
|
|
|
6
|
+
export { addressParser, decodeWords };
|
|
7
|
+
|
|
6
8
|
export default class PostalMime {
|
|
7
9
|
static parse(buf) {
|
|
8
10
|
const parser = new PostalMime();
|
|
@@ -162,10 +164,7 @@ export default class PostalMime {
|
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
// is it text?
|
|
165
|
-
else if (
|
|
166
|
-
(/^text\//i.test(node.contentType.parsed.value) || node.contentType.parsed.value === 'message/delivery-status') &&
|
|
167
|
-
node.contentDisposition.parsed.value !== 'attachment'
|
|
168
|
-
) {
|
|
167
|
+
else if (this.isInlineTextNode(node)) {
|
|
169
168
|
let textType = node.contentType.parsed.value.substr(node.contentType.parsed.value.indexOf('/') + 1);
|
|
170
169
|
if (node.contentType.parsed.value === 'message/delivery-status') {
|
|
171
170
|
textType = 'plain';
|
|
@@ -184,9 +183,9 @@ export default class PostalMime {
|
|
|
184
183
|
|
|
185
184
|
// is it an attachment
|
|
186
185
|
else if (node.content) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
filename: decodeWords(filename),
|
|
186
|
+
const filename = node.contentDisposition.parsed.params.filename || node.contentType.parsed.params.name || null;
|
|
187
|
+
const attachment = {
|
|
188
|
+
filename: filename ? decodeWords(filename) : null,
|
|
190
189
|
mimeType: node.contentType.parsed.value,
|
|
191
190
|
disposition: node.contentDisposition.parsed.value || null
|
|
192
191
|
};
|
|
@@ -195,11 +194,39 @@ export default class PostalMime {
|
|
|
195
194
|
attachment.related = true;
|
|
196
195
|
}
|
|
197
196
|
|
|
197
|
+
if (node.contentDescription) {
|
|
198
|
+
attachment.description = node.contentDescription;
|
|
199
|
+
}
|
|
200
|
+
|
|
198
201
|
if (node.contentId) {
|
|
199
202
|
attachment.contentId = node.contentId;
|
|
200
203
|
}
|
|
201
204
|
|
|
202
|
-
|
|
205
|
+
switch (node.contentType.parsed.value) {
|
|
206
|
+
// Special handling for calendar events
|
|
207
|
+
case 'text/calendar':
|
|
208
|
+
case 'application/ics': {
|
|
209
|
+
if (node.contentType.parsed.params.method) {
|
|
210
|
+
attachment.method = node.contentType.parsed.params.method.toString().toUpperCase().trim();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Enforce into unicode
|
|
214
|
+
const decodedText = node.getTextContent().replace(/\r?\n/g, '\n').replace(/\n*$/, '\n');
|
|
215
|
+
attachment.content = textEncoder.encode(decodedText);
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
case 'message/delivery-status': {
|
|
220
|
+
// Enforce into unicode
|
|
221
|
+
const decodedText = node.getTextContent().replace(/\r?\n/g, '\n').replace(/\n*$/, '\n');
|
|
222
|
+
attachment.content = textEncoder.encode(decodedText);
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Regular attachments
|
|
227
|
+
default:
|
|
228
|
+
attachment.content = node.content;
|
|
229
|
+
}
|
|
203
230
|
|
|
204
231
|
this.attachments.push(attachment);
|
|
205
232
|
}
|
|
@@ -292,6 +319,26 @@ export default class PostalMime {
|
|
|
292
319
|
this.textContent = textContent;
|
|
293
320
|
}
|
|
294
321
|
|
|
322
|
+
isInlineTextNode(node) {
|
|
323
|
+
if (node.contentDisposition.parsed.value === 'attachment') {
|
|
324
|
+
// no matter the type, this is an attachment
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
switch (node.contentType.parsed.value) {
|
|
329
|
+
case 'text/html':
|
|
330
|
+
case 'text/plain':
|
|
331
|
+
// message/delivery-status is cast into regular plaintext content
|
|
332
|
+
case 'message/delivery-status':
|
|
333
|
+
return true;
|
|
334
|
+
|
|
335
|
+
case 'text/calendar':
|
|
336
|
+
case 'text/csv':
|
|
337
|
+
default:
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
295
342
|
async resolveStream(stream) {
|
|
296
343
|
let chunkLen = 0;
|
|
297
344
|
let chunks = [];
|
|
@@ -418,11 +465,11 @@ export default class PostalMime {
|
|
|
418
465
|
message.date = date;
|
|
419
466
|
}
|
|
420
467
|
|
|
421
|
-
if (this.textContent
|
|
468
|
+
if (this.textContent?.html) {
|
|
422
469
|
message.html = this.textContent.html;
|
|
423
470
|
}
|
|
424
471
|
|
|
425
|
-
if (this.textContent
|
|
472
|
+
if (this.textContent?.plain) {
|
|
426
473
|
message.text = this.textContent.plain;
|
|
427
474
|
}
|
|
428
475
|
|