email-bounce-parser 1.0.22 → 1.1.1
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 +17 -1
- package/lib/parser.js +6 -7
- package/lib/utils.js +51 -9
- package/package.json +17 -6
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Parses bounce emails and extracts details.
|
|
|
10
10
|
|
|
11
11
|
<table>
|
|
12
12
|
<tr>
|
|
13
|
-
<td align="center"><a href="https://crisp.chat/"><img src="https://crisp.chat/favicon-256x256.png" height="64" /></a></td>
|
|
13
|
+
<td align="center"><a href="https://crisp.chat/"><img src="https://crisp.chat/favicons/favicon-256x256.png" height="64" /></a></td>
|
|
14
14
|
</tr>
|
|
15
15
|
<tr>
|
|
16
16
|
<td align="center">Crisp</td>
|
|
@@ -24,6 +24,22 @@ _👋 You use this library and you want to be listed there? [Contact us](https:/
|
|
|
24
24
|
This library is used at [Crisp](https://crisp.chat/) everyday to parse bounce emails.
|
|
25
25
|
It supports most variations of bounce emails (Postfix, Dovecat, Gmail, Outlook).
|
|
26
26
|
|
|
27
|
+
## RE2 Support
|
|
28
|
+
|
|
29
|
+
This library uses the [RE2](https://github.com/uhop/node-re2) regex engine when available for better performance and [ReDoS](https://en.wikipedia.org/wiki/ReDoS) protection. It automatically falls back to native JavaScript `RegExp` if RE2 is unavailable or fails to load.
|
|
30
|
+
|
|
31
|
+
RE2 is an optional dependency. To use it, install it alongside this library:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install re2
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
After a Node.js upgrade, you may need to rebuild RE2:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm rebuild re2
|
|
41
|
+
```
|
|
42
|
+
|
|
27
43
|
## Usage
|
|
28
44
|
|
|
29
45
|
```js
|
package/lib/parser.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
var { loopRegexes, trimString } = require("./utils");
|
|
4
|
+
var { buildSafeRegExp, loopRegexes, trimString } = require("./utils");
|
|
6
5
|
|
|
7
6
|
var ERROR_CODES_TYPES = {
|
|
8
7
|
"421" : "service_not_available",
|
|
@@ -265,13 +264,13 @@ class Parser {
|
|
|
265
264
|
|
|
266
265
|
for (var _i = 0; _i < _entry.length; _i++) {
|
|
267
266
|
_regexes[_key].push(
|
|
268
|
-
|
|
267
|
+
buildSafeRegExp(_entry[_i])
|
|
269
268
|
);
|
|
270
269
|
}
|
|
271
270
|
} else if (typeof _entry === "object" && Object.keys(_entry).length > 0) {
|
|
272
271
|
_regexes[_key] = this.__initRegexes(_entry);
|
|
273
272
|
} else {
|
|
274
|
-
_regexes[_key] =
|
|
273
|
+
_regexes[_key] = buildSafeRegExp(_entry);
|
|
275
274
|
}
|
|
276
275
|
}
|
|
277
276
|
|
|
@@ -316,7 +315,7 @@ class Parser {
|
|
|
316
315
|
// In cases where the bounce is detected as permanent, or the \
|
|
317
316
|
// temporary / permanent status is still unclear, run additional detection
|
|
318
317
|
if (_permanent === true || (_temporary === false && _permanent === false)) {
|
|
319
|
-
var _match = this.__regexes.temporary_safe
|
|
318
|
+
var _match = str.match(this.__regexes.temporary_safe);
|
|
320
319
|
|
|
321
320
|
if (_match && _match.length > 1) {
|
|
322
321
|
_temporary = true;
|
|
@@ -415,14 +414,14 @@ class Parser {
|
|
|
415
414
|
}
|
|
416
415
|
|
|
417
416
|
// Check if blocked
|
|
418
|
-
var _blocked_match = this.__regexes.blocked
|
|
417
|
+
var _blocked_match = str.match(this.__regexes.blocked);
|
|
419
418
|
|
|
420
419
|
if (_blocked_match && _blocked_match.length > 1) {
|
|
421
420
|
_blocked = true;
|
|
422
421
|
}
|
|
423
422
|
|
|
424
423
|
// Check if spam
|
|
425
|
-
var _spam_match = this.__regexes.spam
|
|
424
|
+
var _spam_match = str.match(this.__regexes.spam);
|
|
426
425
|
|
|
427
426
|
if (_spam_match && _spam_match.length > 1) {
|
|
428
427
|
_spam = true;
|
package/lib/utils.js
CHANGED
|
@@ -4,12 +4,45 @@
|
|
|
4
4
|
* IMPORTS
|
|
5
5
|
***************************************************************************/
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/**************************************************************************
|
|
8
|
+
* INSTANCES
|
|
9
|
+
***************************************************************************/
|
|
10
|
+
|
|
11
|
+
var RE2Engine = detectRE2();
|
|
8
12
|
|
|
9
13
|
/**************************************************************************
|
|
10
14
|
* FUNCTIONS
|
|
11
15
|
***************************************************************************/
|
|
12
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Detects whether the RE2 engine is available
|
|
19
|
+
* @private
|
|
20
|
+
* @return {function|null} The RE2 engine, or null if unavailable
|
|
21
|
+
*/
|
|
22
|
+
function detectRE2() {
|
|
23
|
+
// Notice: RE2 can be explicitly disabled, to test the RegExp fallback
|
|
24
|
+
if (process.env.EMAIL_BOUNCE_PARSER_DISABLE_RE2) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
return require("re2");
|
|
30
|
+
} catch (error) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Builds a safe regular expression (RE2 when available, RegExp otherwise)
|
|
37
|
+
* @public
|
|
38
|
+
* @param {string|RegExp} source
|
|
39
|
+
* @return {RegExp} The regular expression
|
|
40
|
+
*/
|
|
41
|
+
var buildSafeRegExp = function(source) {
|
|
42
|
+
return (RE2Engine !== null) ? new RE2Engine(source) : new RegExp(source);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
13
46
|
/**
|
|
14
47
|
* Executes multiple regular expressions until one succeeds
|
|
15
48
|
* @public
|
|
@@ -31,12 +64,19 @@ var loopRegexes = function(
|
|
|
31
64
|
|
|
32
65
|
if (typeof regexes[_i] === "string") {
|
|
33
66
|
// Build the regex
|
|
34
|
-
_regex =
|
|
67
|
+
_regex = buildSafeRegExp(regexes[_i]);
|
|
35
68
|
}
|
|
36
69
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
70
|
+
// Notice: call String methods, as they support both RE2 and RegExp
|
|
71
|
+
var _currentMatch;
|
|
72
|
+
|
|
73
|
+
if (mode === "replace") {
|
|
74
|
+
_currentMatch = (str || "").replace(_regex, "");
|
|
75
|
+
} else if (mode === "split") {
|
|
76
|
+
_currentMatch = (str || "").split(_regex);
|
|
77
|
+
} else {
|
|
78
|
+
_currentMatch = (str || "").match(_regex);
|
|
79
|
+
}
|
|
40
80
|
|
|
41
81
|
if (mode === "replace") {
|
|
42
82
|
_match = _currentMatch;
|
|
@@ -98,10 +138,12 @@ var trimString = function(str) {
|
|
|
98
138
|
* EXPORTS
|
|
99
139
|
***************************************************************************/
|
|
100
140
|
|
|
101
|
-
exports.
|
|
102
|
-
exports.
|
|
141
|
+
exports.buildSafeRegExp = buildSafeRegExp;
|
|
142
|
+
exports.loopRegexes = loopRegexes;
|
|
143
|
+
exports.trimString = trimString;
|
|
103
144
|
|
|
104
145
|
exports.default = {
|
|
105
|
-
|
|
106
|
-
|
|
146
|
+
buildSafeRegExp : buildSafeRegExp,
|
|
147
|
+
loopRegexes : loopRegexes,
|
|
148
|
+
trimString : trimString
|
|
107
149
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "email-bounce-parser",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Parses bounce emails and extract errors",
|
|
5
5
|
"author": "Eliott Vincent <eliott@crisp.chat>",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -14,16 +14,27 @@
|
|
|
14
14
|
},
|
|
15
15
|
"engineStrict": true,
|
|
16
16
|
"engines": {
|
|
17
|
-
"node": ">=
|
|
17
|
+
"node": ">= 22.0.0"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
|
-
"
|
|
20
|
+
"unit": "nodeunit ./test/index.js",
|
|
21
|
+
"unit:regexp": "EMAIL_BOUNCE_PARSER_DISABLE_RE2=1 npm run unit",
|
|
22
|
+
"test": "npm run unit && npm run unit:regexp"
|
|
21
23
|
},
|
|
22
24
|
"devDependencies": {
|
|
23
|
-
"nodeunit-x": "0.15.0"
|
|
25
|
+
"nodeunit-x": "0.15.0",
|
|
26
|
+
"re2": "1.27.0"
|
|
24
27
|
},
|
|
25
|
-
"
|
|
26
|
-
"re2": "1.
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"re2": "1.x"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"re2": {
|
|
33
|
+
"optional": true
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"allowScripts": {
|
|
37
|
+
"re2@1.27.0": true
|
|
27
38
|
},
|
|
28
39
|
"keywords": [
|
|
29
40
|
"parser",
|