@zone-eu/mailsplit 5.4.26 → 5.4.28
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 +43 -54
- package/lib/message-splitter.js +39 -27
- package/lib/mime-node.d.ts +1 -1
- package/lib/mime-node.js +36 -7
- package/lib/types.d.ts +1 -1
- package/package.json +20 -15
package/README.md
CHANGED
|
@@ -29,9 +29,9 @@ let splitter = new Splitter(options);
|
|
|
29
29
|
|
|
30
30
|
Where
|
|
31
31
|
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
32
|
+
- **options** is an optional options object
|
|
33
|
+
- **options.ignoreEmbedded** (boolean, defaults to false) if true then treat message/rfc822 node as normal leaf node and do not try to parse it
|
|
34
|
+
- **options.maxHeadSize** (number, defaults to 1 MiB) limits the header size of a MIME node in bytes, every header line counting for at least 64 bytes
|
|
35
35
|
|
|
36
36
|
#### Events
|
|
37
37
|
|
|
@@ -39,13 +39,13 @@ Where
|
|
|
39
39
|
|
|
40
40
|
#### Data objects
|
|
41
41
|
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
42
|
+
- **type**
|
|
43
|
+
- `'node'` means that we reached the next mime node and the previous one is completely processed
|
|
44
|
+
- `'data'` provides us multipart body parts, including boundaries. This data is not directly related to any specific multipart node, basically it includes everything between the end of one normal node and the header of next node
|
|
45
|
+
- `'body'` provides us next chunk for the last seen `'node'` element
|
|
46
|
+
- **value** is a buffer value for `'body'` and `'data'` parts
|
|
47
|
+
- **getDecoder()** is a function that returns a stream object you can use to decode node contents. Write data from 'body' to decoder and read decoded Buffer value out from it
|
|
48
|
+
- **getEncoder()** is a function that returns a stream object you can use to encode node contents. Write buffer data to encoder and read encoded object value out that you can pass to a Joiner
|
|
49
49
|
|
|
50
50
|
Element with type `'node'` has a bunch of header related methods and properties, see [below](#manipulating-headers).
|
|
51
51
|
|
|
@@ -82,31 +82,31 @@ someMessagStream.pipe(splitter);
|
|
|
82
82
|
|
|
83
83
|
If the data object has `type='node'` then you can modify headers for that node (headers can be modified until the data object is passed over to a `Joiner`)
|
|
84
84
|
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
85
|
+
- **node.getHeaders()** returns a Buffer value with generated headers. If you have not modified the headers object in any way then you should get the exact copy of the original. In case you have done something (for example removed a key, or added a new header key), then all linebreaks are forced to <CR><LF> even if the original headers used just <LF>
|
|
86
|
+
- **node.setContentType(contentType)** sets or updates mime type for the node
|
|
87
|
+
- **node.setCharset(charset)** sets or updates character set in the Content-Type header
|
|
88
|
+
- **node.setFilename(filename)** sets or updates filename in the Content-Disposition header (unicode allowed)
|
|
89
89
|
|
|
90
90
|
You can manipulate specific header keys as well using the `headers` object
|
|
91
91
|
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
99
|
-
-
|
|
92
|
+
- **node.headers.get(key)** returns an array of strings with all header rows for the selected key (these are full header lines, so key name is part of the row string, eg `["Subject: This is subject line"]`)
|
|
93
|
+
- **node.headers.getFirst(key)** returns string value of the specified header key (eg `"This is subject line"`)
|
|
94
|
+
- **node.headers.hasHeader(key)** returns boolean value if the specified header key exists
|
|
95
|
+
- **node.headers.add(key, value [,index])** adds a new header value to the specified index or to the top of the header block if index is not specified
|
|
96
|
+
- **node.headers.update(key, value, [,relativeKeyIndex])** replaces a header value to the specified relative key index (note that relative key index means relative to the same header key, eg if multiple exist and you specify `1` as the value, then it will update the second) or if no relative key index is specified, then it will remove all header value matches found for the key and append one at the last matching key index found with the specified value. If a relative key index is specified and it does not exist then it will be replaced (eg if there are two headers of `X-Foo-Bar` and you pass `2`, meaning it will update the third, no updates will be made since the third did not exist)
|
|
97
|
+
- **node.headers.remove(key)** remove header value
|
|
98
|
+
- **node.headers.mbox** If this is a MBOX formatted message then this value holds the prefix line (eg. "From MAILER-DAEMON Fri Jul 8 12:08:34 2011")
|
|
99
|
+
- **node.headers.mbox** If this is a POST form-data then this value holds the HTTP prefix line (eg. "POST /upload.php HTTP/1.1")
|
|
100
100
|
|
|
101
101
|
Additionally you can check the details of the node with the following properties automatically parsed from the headers:
|
|
102
102
|
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
103
|
+
- **node.root** if true then it means this is the message root, so this node should contain Subject, From, To etc. headers
|
|
104
|
+
- **node.contentType** returns the mime type of the node (eg. 'text/html')
|
|
105
|
+
- **node.disposition** either `'attachment'`, `'inline'` or `false` if not set
|
|
106
|
+
- **node.charset** returns the charset of the node as defined in 'Content-Type' header (eg. 'UTF-8') or false if not defined
|
|
107
|
+
- **node.encoding** returns the Transfer-Encoding value (eg. 'base64' or 'quoted-printable') or false if not defined
|
|
108
|
+
- **node.multipart** if has value, then this is a multipart node (does not have 'body' parts)
|
|
109
|
+
- **node.filename** is set if the headers contain a filename value. This is decoded to unicode, so it is a normal string or false if not found
|
|
110
110
|
|
|
111
111
|
### Join parsed message stream
|
|
112
112
|
|
|
@@ -118,10 +118,7 @@ let Joiner = require('mailsplit').Joiner;
|
|
|
118
118
|
let splitter = new Splitter();
|
|
119
119
|
let joiner = new Joiner();
|
|
120
120
|
// pipe a message source to splitter, then joiner and finally to stdout
|
|
121
|
-
someMessagStream
|
|
122
|
-
.pipe(splitter)
|
|
123
|
-
.pipe(joiner)
|
|
124
|
-
.pipe(process.stdout);
|
|
121
|
+
someMessagStream.pipe(splitter).pipe(joiner).pipe(process.stdout);
|
|
125
122
|
```
|
|
126
123
|
|
|
127
124
|
### Rewrite specific nodes
|
|
@@ -130,14 +127,14 @@ someMessagStream
|
|
|
130
127
|
|
|
131
128
|
Rewriter takes the following argument:
|
|
132
129
|
|
|
133
|
-
-
|
|
130
|
+
- **filterFunc** gets the current node as argument and starts processing it if `filterFunc` returns true
|
|
134
131
|
|
|
135
132
|
Once Rewriter finds a matching node, it emits the following event:
|
|
136
133
|
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
140
|
-
-
|
|
134
|
+
- _'node'_ with an object argument `data`
|
|
135
|
+
- `data.node` includes the current node with headers
|
|
136
|
+
- `data.decoder` is the decoder stream that you can read data from
|
|
137
|
+
- `data.encoder` is the encoder stream that you can write data to. Whatever you write into that stream will be encoded properly and inserted as the content of the current node
|
|
141
138
|
|
|
142
139
|
```javascript
|
|
143
140
|
let Splitter = require('mailsplit').Splitter;
|
|
@@ -153,11 +150,7 @@ rewriter.on('node', data => {
|
|
|
153
150
|
data.decoder.pipe(data.encoder);
|
|
154
151
|
});
|
|
155
152
|
// pipe a message source to splitter, then rewriter, then joiner and finally to stdout
|
|
156
|
-
someMessagStream
|
|
157
|
-
.pipe(splitter)
|
|
158
|
-
.pipe(rewriter)
|
|
159
|
-
.pipe(joiner)
|
|
160
|
-
.pipe(process.stdout);
|
|
153
|
+
someMessagStream.pipe(splitter).pipe(rewriter).pipe(joiner).pipe(process.stdout);
|
|
161
154
|
```
|
|
162
155
|
|
|
163
156
|
### Stream specific nodes
|
|
@@ -166,14 +159,14 @@ someMessagStream
|
|
|
166
159
|
|
|
167
160
|
Streamer takes the following argument:
|
|
168
161
|
|
|
169
|
-
-
|
|
162
|
+
- **filterFunc** gets the current node as argument and starts processing it if `filterFunc` returns true
|
|
170
163
|
|
|
171
164
|
Once Streamer finds a matching node, it emits the following event:
|
|
172
165
|
|
|
173
|
-
-
|
|
174
|
-
-
|
|
175
|
-
-
|
|
176
|
-
-
|
|
166
|
+
- _'node'_ with an object argument `data`
|
|
167
|
+
- `data.node` includes the current node with headers (informational only, you can't modify it)
|
|
168
|
+
- `data.decoder` is the decoder stream that you can read data from
|
|
169
|
+
- `data.done` is a function you must call once you have processed the stream
|
|
177
170
|
|
|
178
171
|
```javascript
|
|
179
172
|
let Splitter = require('mailsplit').Splitter;
|
|
@@ -189,11 +182,7 @@ streamer.on('node', data => {
|
|
|
189
182
|
data.done();
|
|
190
183
|
});
|
|
191
184
|
// pipe a message source to splitter, then streamer, then joiner and finally to stdout
|
|
192
|
-
someMessagStream
|
|
193
|
-
.pipe(splitter)
|
|
194
|
-
.pipe(streamer)
|
|
195
|
-
.pipe(joiner)
|
|
196
|
-
.pipe(process.stdout);
|
|
185
|
+
someMessagStream.pipe(splitter).pipe(streamer).pipe(joiner).pipe(process.stdout);
|
|
197
186
|
```
|
|
198
187
|
|
|
199
188
|
### Benchmark
|
|
@@ -211,4 +200,4 @@ Done. 20000 messages [1244 MB] processed in 55.882 s. with average of 358 messag
|
|
|
211
200
|
|
|
212
201
|
Dual licensed under **MIT** or **EUPLv1.1+**
|
|
213
202
|
|
|
214
|
-
> `mailsplit` is part of the Zone Mail Suite (ZMS). Suite of programs and modules for an efficient, fast and modern email server.
|
|
203
|
+
> `mailsplit` is part of the Zone Mail Suite (ZMS). Suite of programs and modules for an efficient, fast and modern email server.
|
package/lib/message-splitter.js
CHANGED
|
@@ -20,9 +20,17 @@ const MAX_PENDING_LINE_SIZE = 64 * 1024;
|
|
|
20
20
|
// how many separate writes the pending line may be kept in before it is compacted
|
|
21
21
|
const MAX_PENDING_LINE_CHUNKS = 1024;
|
|
22
22
|
|
|
23
|
+
// The most SP and HTAB a delimiter line may carry between the boundary value, or its closing
|
|
24
|
+
// "--", and the line ending. RFC 2046 5.1.1 allows the whitespace, and Python and postal-mime
|
|
25
|
+
// accept any amount of it, so a line that ends in a few spaces delimits the same parts here
|
|
26
|
+
// as there. The line still has to fit a header line (RFC 5322 2.1.1), so the pending line
|
|
27
|
+
// kept as a possible delimiter stays bounded
|
|
28
|
+
const BOUNDARY_LINE_TRAILING_WSP = 998;
|
|
29
|
+
|
|
23
30
|
// what a delimiter line may carry after the boundary value: the "--" prefix, an optional
|
|
24
|
-
// "--" suffix and the line terminator. This is the bound
|
|
25
|
-
|
|
31
|
+
// "--" suffix, the trailing whitespace and the line terminator. This is the bound
|
|
32
|
+
// compareBoundary() accepts.
|
|
33
|
+
const BOUNDARY_LINE_SUFFIX = 2 /* "--" prefix */ + 2 /* "--" suffix */ + BOUNDARY_LINE_TRAILING_WSP + 2; /* trailing <CR><LF> */
|
|
26
34
|
|
|
27
35
|
// checkBoundary() additionally allows a line ending in front of the delimiter, so this is
|
|
28
36
|
// the longest a delimiter line can ever be once the boundary value is subtracted
|
|
@@ -389,7 +397,8 @@ class MessageSplitter extends Transform {
|
|
|
389
397
|
* @returns {1 | 2 | false}
|
|
390
398
|
*/
|
|
391
399
|
compareBoundary(line, startpos, boundary) {
|
|
392
|
-
// --{boundary}\r\n or --{boundary}--\r\n
|
|
400
|
+
// --{boundary}\r\n or --{boundary}--\r\n, with SP and HTAB allowed in front of the
|
|
401
|
+
// line ending (RFC 2046 5.1.1), where Python and postal-mime accept them as well
|
|
393
402
|
if (line.length < boundary.length + 3 + startpos || line.length > boundary.length + BOUNDARY_LINE_SUFFIX + startpos) {
|
|
394
403
|
return false;
|
|
395
404
|
}
|
|
@@ -399,34 +408,37 @@ class MessageSplitter extends Transform {
|
|
|
399
408
|
}
|
|
400
409
|
}
|
|
401
410
|
|
|
402
|
-
let
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
}
|
|
409
|
-
if (pos === 0 && c !== 0x2d) {
|
|
410
|
-
// expecting "-"
|
|
411
|
-
return false;
|
|
412
|
-
}
|
|
413
|
-
if (pos === 1 && c !== 0x2d) {
|
|
414
|
-
// expecting "-"
|
|
415
|
-
return false;
|
|
416
|
-
}
|
|
417
|
-
if (pos === 2 && c !== 0x0d && c !== 0x0a) {
|
|
418
|
-
// expecting line terminator, either <CR> or <LF>
|
|
411
|
+
let i = boundary.length + 2 + startpos;
|
|
412
|
+
// 1: next node, 2: multipart end
|
|
413
|
+
let result = /** @type {1 | 2} */ (1);
|
|
414
|
+
if (line[i] === 0x2d) {
|
|
415
|
+
if (line[i + 1] !== 0x2d) {
|
|
416
|
+
// expecting "--"
|
|
419
417
|
return false;
|
|
420
418
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
419
|
+
result = 2;
|
|
420
|
+
i += 2;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
while (i < line.length && (line[i] === 0x20 || line[i] === 0x09)) {
|
|
424
|
+
i++;
|
|
426
425
|
}
|
|
427
426
|
|
|
428
|
-
|
|
429
|
-
|
|
427
|
+
if (i === line.length) {
|
|
428
|
+
// only the closing delimiter may end the input without a line ending
|
|
429
|
+
return result === 2 ? 2 : false;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// expecting the line terminator and nothing after it: <CR><LF>, <CR> or <LF>, where
|
|
433
|
+
// the <CR> may be a run of them, the artifact of a second CR to CRLF conversion,
|
|
434
|
+
// which postal-mime reads as the line ending as well
|
|
435
|
+
while (i < line.length && line[i] === 0x0d) {
|
|
436
|
+
i++;
|
|
437
|
+
}
|
|
438
|
+
if (i < line.length && line[i] === 0x0a) {
|
|
439
|
+
i++;
|
|
440
|
+
}
|
|
441
|
+
return i === line.length ? result : false;
|
|
430
442
|
}
|
|
431
443
|
|
|
432
444
|
/**
|
package/lib/mime-node.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ declare class MimeNode implements MimeNodeShape {
|
|
|
46
46
|
/** Decoded filename from Content-Disposition or Content-Type parameters, or `false` when absent. */
|
|
47
47
|
filename: string | false;
|
|
48
48
|
|
|
49
|
-
/** Whether this node is `text
|
|
49
|
+
/** Whether this node is `text/plain` with `format=flowed`. */
|
|
50
50
|
flowed: boolean;
|
|
51
51
|
|
|
52
52
|
/** Whether flowed text uses `delsp=yes`. */
|
package/lib/mime-node.js
CHANGED
|
@@ -27,6 +27,9 @@ const HEADER_LINE_MIN_COST = 64;
|
|
|
27
27
|
* `value.replace(/\(.*\)/g, '')`, but in linear time. The regex backtracks quadratically
|
|
28
28
|
* on a value with many `(` characters and no closing `)`.
|
|
29
29
|
*
|
|
30
|
+
* Used for the Content-Transfer-Encoding only. The MIME headers with parameters go through
|
|
31
|
+
* libmime's parseHeaderValue with stripComments, which knows where a comment may stand.
|
|
32
|
+
*
|
|
30
33
|
* @param {string} value
|
|
31
34
|
* @returns {string}
|
|
32
35
|
*/
|
|
@@ -73,7 +76,11 @@ function rfc2231ParamNames(value) {
|
|
|
73
76
|
if (eq < 0) {
|
|
74
77
|
return;
|
|
75
78
|
}
|
|
76
|
-
let match = segment
|
|
79
|
+
let match = segment
|
|
80
|
+
.substring(0, eq)
|
|
81
|
+
.trim()
|
|
82
|
+
.toLowerCase()
|
|
83
|
+
.match(/^(.+?)\*(\d+\*?)?$/);
|
|
77
84
|
if (match) {
|
|
78
85
|
names.add(match[1]);
|
|
79
86
|
}
|
|
@@ -212,8 +219,14 @@ class MimeNode {
|
|
|
212
219
|
this._headersLines = [];
|
|
213
220
|
let headers = this.headers;
|
|
214
221
|
|
|
222
|
+
// The comments of the MIME headers are dropped, so that `boundary=b (comment)` names
|
|
223
|
+
// the boundary `b` that postal-mime and Python's email.policy.default split by. Read as
|
|
224
|
+
// content, it matched no delimiter line and the parts both of them find stayed hidden
|
|
225
|
+
// here. Python's legacy compat32 policy, the default of message_from_bytes() without a
|
|
226
|
+
// policy, keeps the comment and splits by `--b (comment)` lines instead: a split
|
|
227
|
+
// against it is the price of agreeing with the two readers a renderer is built on
|
|
215
228
|
let dispositionHeader = headers.getFirst('Content-Disposition');
|
|
216
|
-
this._parsedContentDisposition = this.libmime.parseHeaderValue(dispositionHeader);
|
|
229
|
+
this._parsedContentDisposition = this.libmime.parseHeaderValue(dispositionHeader, { stripComments: true });
|
|
217
230
|
let parsedContentDisposition = /** @type {StructuredHeader} */ (this._parsedContentDisposition);
|
|
218
231
|
|
|
219
232
|
// if content-type is missing default to plaintext
|
|
@@ -236,9 +249,12 @@ class MimeNode {
|
|
|
236
249
|
}
|
|
237
250
|
}
|
|
238
251
|
|
|
239
|
-
this._parsedContentType = this.libmime.parseHeaderValue(contentHeader);
|
|
252
|
+
this._parsedContentType = this.libmime.parseHeaderValue(contentHeader, { stripComments: true });
|
|
240
253
|
let parsedContentType = /** @type {StructuredHeader} */ (this._parsedContentType);
|
|
241
254
|
|
|
255
|
+
// The encoding is a bare token, and one written in quotes stays quoted, so that
|
|
256
|
+
// "base64" is not decoded, as Python and postal-mime do not decode it. parseHeaderValue
|
|
257
|
+
// would unquote it, so the transfer encoding keeps its own comment stripper
|
|
242
258
|
this.encoding = stripComments(headers.getFirst('Content-Transfer-Encoding')).toLowerCase().trim();
|
|
243
259
|
this.contentType = (parsedContentType.value || '').toLowerCase().trim() || false;
|
|
244
260
|
this.charset = parsedContentType.params.charset || false;
|
|
@@ -248,14 +264,18 @@ class MimeNode {
|
|
|
248
264
|
if (this.disposition) {
|
|
249
265
|
try {
|
|
250
266
|
this.disposition = this.libmime.decodeWords(this.disposition);
|
|
251
|
-
} catch (
|
|
267
|
+
} catch (_E) {
|
|
252
268
|
// failed to parse disposition, keep as is (most probably an unknown charset is used)
|
|
253
269
|
}
|
|
254
270
|
}
|
|
255
271
|
|
|
256
272
|
this.filename = parsedContentDisposition.params.filename || parsedContentType.params.name || false;
|
|
257
273
|
|
|
258
|
-
|
|
274
|
+
// format=flowed is defined for text/plain (RFC 3676), and a browser never unfolds an
|
|
275
|
+
// html part. Flagged on any text part, a text/html part with delsp=yes had its
|
|
276
|
+
// `<scri \r\npt>` joined into a script tag by the consumer that unfolded it, a tag
|
|
277
|
+
// neither Python nor a browser given the raw bytes sees
|
|
278
|
+
if (this.contentType === 'text/plain' && parsedContentType.params.format && parsedContentType.params.format.toLowerCase().trim() === 'flowed') {
|
|
259
279
|
this.flowed = true;
|
|
260
280
|
if (parsedContentType.params.delsp && parsedContentType.params.delsp.toLowerCase().trim() === 'yes') {
|
|
261
281
|
this.delSp = true;
|
|
@@ -270,7 +290,7 @@ class MimeNode {
|
|
|
270
290
|
if (!decoded) {
|
|
271
291
|
try {
|
|
272
292
|
this.filename = this.libmime.decodeWords(this.filename);
|
|
273
|
-
} catch (
|
|
293
|
+
} catch (_E) {
|
|
274
294
|
// failed to parse filename, keep as is (most probably an unknown charset is used)
|
|
275
295
|
}
|
|
276
296
|
}
|
|
@@ -281,7 +301,16 @@ class MimeNode {
|
|
|
281
301
|
this.contentType.substr(0, this.contentType.indexOf('/')) === 'multipart' &&
|
|
282
302
|
this.contentType.substr(this.contentType.indexOf('/') + 1)) ||
|
|
283
303
|
false;
|
|
284
|
-
|
|
304
|
+
// A boundary is ASCII and does not end in whitespace (RFC 2046 5.1.1, bchars), and
|
|
305
|
+
// Python matches no line against one that is not. One holding a non-ASCII character
|
|
306
|
+
// came out of a header read as latin1 or as UTF-8, and matched under `--ab\xC3\xA9cd`
|
|
307
|
+
// lines parts that a scanner built on Python never saw. Such a boundary counts as
|
|
308
|
+
// none, as a boundary parameter that is missing does. Trailing whitespace is dropped
|
|
309
|
+
// as Python drops it, a delimiter line may end in whitespace anyway
|
|
310
|
+
let boundary = (parsedContentType.params.boundary || '').trimEnd();
|
|
311
|
+
// a non-ASCII character takes more than one byte in UTF-8
|
|
312
|
+
let ascii = Buffer.byteLength(boundary) === boundary.length;
|
|
313
|
+
this._boundary = boundary && ascii ? Buffer.from(boundary) : false;
|
|
285
314
|
|
|
286
315
|
this.rfc822 = this.contentType === 'message/rfc822';
|
|
287
316
|
|
package/lib/types.d.ts
CHANGED
|
@@ -107,7 +107,7 @@ export interface MimeNode {
|
|
|
107
107
|
/** Decoded filename from Content-Disposition or Content-Type parameters, or `false` when absent. */
|
|
108
108
|
filename: string | false;
|
|
109
109
|
|
|
110
|
-
/** Whether this node is `text
|
|
110
|
+
/** Whether this node is `text/plain` with `format=flowed`. */
|
|
111
111
|
flowed: boolean;
|
|
112
112
|
|
|
113
113
|
/** Whether flowed text uses `delsp=yes`. */
|
package/package.json
CHANGED
|
@@ -1,38 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zone-eu/mailsplit",
|
|
3
|
-
"version": "5.4.
|
|
3
|
+
"version": "5.4.28",
|
|
4
4
|
"description": "Split email messages into an object stream",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20.0.0"
|
|
9
|
+
},
|
|
7
10
|
"directories": {
|
|
8
11
|
"test": "test"
|
|
9
12
|
},
|
|
10
13
|
"scripts": {
|
|
11
|
-
"test": "
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
+
"test": "node --test test/*-test.js",
|
|
15
|
+
"test:cjs": "NODE_OPTIONS=--no-experimental-require-module npm test",
|
|
16
|
+
"test:coverage": "c8 npm test",
|
|
17
|
+
"lint": "npm run typecheck && eslint .",
|
|
18
|
+
"lint:fix": "eslint . --fix",
|
|
19
|
+
"format": "prettier --write .",
|
|
20
|
+
"format:check": "prettier --check .",
|
|
21
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
22
|
+
"update": "rm -rf node_modules package-lock.json && ncu -u && npm install"
|
|
14
23
|
},
|
|
15
24
|
"author": "Andris Reinman",
|
|
16
25
|
"license": "(MIT OR EUPL-1.1+)",
|
|
17
26
|
"dependencies": {
|
|
18
27
|
"libbase64": "1.4.1",
|
|
19
|
-
"libmime": "5.
|
|
28
|
+
"libmime": "5.6.3",
|
|
20
29
|
"libqp": "3.0.1"
|
|
21
30
|
},
|
|
22
31
|
"devDependencies": {
|
|
23
|
-
"@
|
|
24
|
-
"@types/eslint-config-prettier": "6.11.3",
|
|
25
|
-
"@types/grunt": "0.4.32",
|
|
32
|
+
"@eslint/js": "10.0.1",
|
|
26
33
|
"@types/libmime": "5.3.0",
|
|
27
34
|
"@types/libqp": "1.1.3",
|
|
28
35
|
"@types/node": "26.6.5",
|
|
29
|
-
"
|
|
30
|
-
"eslint
|
|
31
|
-
"eslint-config-prettier": "
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"grunt-contrib-nodeunit": "5.0.0",
|
|
35
|
-
"grunt-eslint": "24.0.1",
|
|
36
|
+
"c8": "12.0.0",
|
|
37
|
+
"eslint": "10.13.0",
|
|
38
|
+
"eslint-config-prettier": "10.1.8",
|
|
39
|
+
"globals": "17.13.0",
|
|
40
|
+
"prettier": "3.9.10",
|
|
36
41
|
"random-message": "1.1.0",
|
|
37
42
|
"typescript": "7.0.2"
|
|
38
43
|
},
|