@zone-eu/mailsplit 5.4.9 → 5.4.10
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/index.d.ts +44 -0
- package/index.js +4 -1
- package/lib/chunked-passthrough.d.ts +13 -0
- package/lib/chunked-passthrough.js +13 -0
- package/lib/flowed-decoder.d.ts +13 -0
- package/lib/flowed-decoder.js +21 -3
- package/lib/headers.d.ts +24 -0
- package/lib/headers.js +118 -23
- package/lib/message-joiner.d.ts +12 -0
- package/lib/message-joiner.js +12 -0
- package/lib/message-splitter.d.ts +13 -0
- package/lib/message-splitter.js +55 -9
- package/lib/mime-node.d.ts +38 -0
- package/lib/mime-node.js +126 -47
- package/lib/node-rewriter.d.ts +18 -0
- package/lib/node-rewriter.js +48 -17
- package/lib/node-streamer.d.ts +18 -0
- package/lib/node-streamer.js +42 -8
- package/lib/types.d.ts +105 -0
- package/package.json +15 -5
package/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
import Splitter = require('./lib/message-splitter');
|
|
4
|
+
import Joiner = require('./lib/message-joiner');
|
|
5
|
+
import Rewriter = require('./lib/node-rewriter');
|
|
6
|
+
import Streamer = require('./lib/node-streamer');
|
|
7
|
+
import ChunkedPassthrough = require('./lib/chunked-passthrough');
|
|
8
|
+
import Headers = require('./lib/headers');
|
|
9
|
+
import MimeNode = require('./lib/mime-node');
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
Splitter,
|
|
13
|
+
Joiner,
|
|
14
|
+
Rewriter,
|
|
15
|
+
Streamer,
|
|
16
|
+
ChunkedPassthrough,
|
|
17
|
+
Headers,
|
|
18
|
+
MimeNode
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type {
|
|
22
|
+
Maybe,
|
|
23
|
+
PartNumberItem,
|
|
24
|
+
PartNumber,
|
|
25
|
+
LibmimeOptions,
|
|
26
|
+
SplitterOptions,
|
|
27
|
+
ChunkedPassthroughOptions,
|
|
28
|
+
FlowedDecoderOptions,
|
|
29
|
+
HeaderLine,
|
|
30
|
+
DecodedHeader,
|
|
31
|
+
MimeNode,
|
|
32
|
+
MessageChunk,
|
|
33
|
+
EmptyChunk,
|
|
34
|
+
SplitterChunk,
|
|
35
|
+
RewriterInput,
|
|
36
|
+
FilterFunc,
|
|
37
|
+
ErrorWithCode,
|
|
38
|
+
ContinueCallback,
|
|
39
|
+
ContentStream,
|
|
40
|
+
DecoderStream,
|
|
41
|
+
SplitterGroup,
|
|
42
|
+
RewriterNode,
|
|
43
|
+
StreamerNode
|
|
44
|
+
} from './lib/types';
|
package/index.js
CHANGED
|
@@ -6,12 +6,15 @@ const NodeRewriter = require('./lib/node-rewriter');
|
|
|
6
6
|
const NodeStreamer = require('./lib/node-streamer');
|
|
7
7
|
const Headers = require('./lib/headers');
|
|
8
8
|
const ChunkedPassthrough = require('./lib/chunked-passthrough');
|
|
9
|
+
const MimeNode = require('./lib/mime-node');
|
|
9
10
|
|
|
11
|
+
/** @type {typeof import('.')} */
|
|
10
12
|
module.exports = {
|
|
11
13
|
Splitter: MessageSplitter,
|
|
12
14
|
Joiner: MessageJoiner,
|
|
13
15
|
Rewriter: NodeRewriter,
|
|
14
16
|
Streamer: NodeStreamer,
|
|
15
17
|
ChunkedPassthrough,
|
|
16
|
-
Headers
|
|
18
|
+
Headers,
|
|
19
|
+
MimeNode
|
|
17
20
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
import type { ChunkedPassthroughOptions } from './types';
|
|
3
|
+
|
|
4
|
+
declare class ChunkedPassthrough extends Transform {
|
|
5
|
+
constructor(options?: ChunkedPassthroughOptions);
|
|
6
|
+
on(event: 'data', listener: (data: Buffer) => void): this;
|
|
7
|
+
once(event: 'data', listener: (data: Buffer) => void): this;
|
|
8
|
+
addListener(event: 'data', listener: (data: Buffer) => void): this;
|
|
9
|
+
prependListener(event: 'data', listener: (data: Buffer) => void): this;
|
|
10
|
+
emit(event: 'data', data: Buffer): boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export = ChunkedPassthrough;
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
const { Transform } = require('stream');
|
|
4
4
|
|
|
5
5
|
class ChunkedPassthrough extends Transform {
|
|
6
|
+
/**
|
|
7
|
+
* @param {import('..').ChunkedPassthroughOptions} [options]
|
|
8
|
+
*/
|
|
6
9
|
constructor(options = {}) {
|
|
7
10
|
let config = {
|
|
8
11
|
readableObjectMode: true,
|
|
@@ -13,6 +16,12 @@ class ChunkedPassthrough extends Transform {
|
|
|
13
16
|
this.buffer = Buffer.alloc(0);
|
|
14
17
|
}
|
|
15
18
|
|
|
19
|
+
/**
|
|
20
|
+
* @param {Buffer} chunk
|
|
21
|
+
* @param {BufferEncoding} encoding
|
|
22
|
+
* @param {import('stream').TransformCallback} callback
|
|
23
|
+
* @returns {void}
|
|
24
|
+
*/
|
|
16
25
|
_transform(chunk, encoding, callback) {
|
|
17
26
|
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
18
27
|
|
|
@@ -24,6 +33,10 @@ class ChunkedPassthrough extends Transform {
|
|
|
24
33
|
callback();
|
|
25
34
|
}
|
|
26
35
|
|
|
36
|
+
/**
|
|
37
|
+
* @param {import('stream').TransformCallback} callback
|
|
38
|
+
* @returns {void}
|
|
39
|
+
*/
|
|
27
40
|
_flush(callback) {
|
|
28
41
|
// Send remaining data
|
|
29
42
|
if (this.buffer.length > 0) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
import type { FlowedDecoderOptions } from './types';
|
|
3
|
+
|
|
4
|
+
declare class FlowedDecoder extends Transform {
|
|
5
|
+
constructor(config?: FlowedDecoderOptions);
|
|
6
|
+
on(event: 'data', listener: (data: Buffer) => void): this;
|
|
7
|
+
once(event: 'data', listener: (data: Buffer) => void): this;
|
|
8
|
+
addListener(event: 'data', listener: (data: Buffer) => void): this;
|
|
9
|
+
prependListener(event: 'data', listener: (data: Buffer) => void): this;
|
|
10
|
+
emit(event: 'data', data: Buffer): boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export = FlowedDecoder;
|
package/lib/flowed-decoder.js
CHANGED
|
@@ -5,29 +5,43 @@
|
|
|
5
5
|
const Transform = require('stream').Transform;
|
|
6
6
|
const libmime = require('libmime');
|
|
7
7
|
|
|
8
|
+
/** @typedef {import('..').FlowedDecoderOptions} FlowedDecoderOptions */
|
|
9
|
+
|
|
10
|
+
const Libmime = /** @type {any} */ (libmime.Libmime);
|
|
11
|
+
|
|
8
12
|
/**
|
|
9
13
|
* Really bad "stream" transform to parse format=flowed content
|
|
10
14
|
*
|
|
11
15
|
* @constructor
|
|
12
|
-
* @param {
|
|
16
|
+
* @param {FlowedDecoderOptions} [config]
|
|
13
17
|
*/
|
|
14
18
|
class FlowedDecoder extends Transform {
|
|
19
|
+
/**
|
|
20
|
+
* @param {FlowedDecoderOptions} [config]
|
|
21
|
+
*/
|
|
15
22
|
constructor(config) {
|
|
16
23
|
super();
|
|
17
24
|
this.config = config || {};
|
|
18
25
|
|
|
26
|
+
/** @type {Buffer[]} */
|
|
19
27
|
this.chunks = [];
|
|
20
28
|
this.chunklen = 0;
|
|
21
29
|
|
|
22
|
-
this.libmime = new
|
|
30
|
+
this.libmime = new Libmime({ Iconv: this.config.Iconv });
|
|
23
31
|
}
|
|
24
32
|
|
|
33
|
+
/**
|
|
34
|
+
* @param {Buffer | string} chunk
|
|
35
|
+
* @param {BufferEncoding} encoding
|
|
36
|
+
* @param {import('stream').TransformCallback} callback
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
25
39
|
_transform(chunk, encoding, callback) {
|
|
26
40
|
if (!chunk || !chunk.length) {
|
|
27
41
|
return callback();
|
|
28
42
|
}
|
|
29
43
|
|
|
30
|
-
if (
|
|
44
|
+
if (typeof chunk === 'string') {
|
|
31
45
|
chunk = Buffer.from(chunk, encoding);
|
|
32
46
|
}
|
|
33
47
|
|
|
@@ -37,6 +51,10 @@ class FlowedDecoder extends Transform {
|
|
|
37
51
|
callback();
|
|
38
52
|
}
|
|
39
53
|
|
|
54
|
+
/**
|
|
55
|
+
* @param {import('stream').TransformCallback} callback
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
40
58
|
_flush(callback) {
|
|
41
59
|
if (this.chunklen) {
|
|
42
60
|
let currentBody = Buffer.concat(this.chunks, this.chunklen);
|
package/lib/headers.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DecodedHeader, HeaderLine, LibmimeOptions } from './types';
|
|
2
|
+
|
|
3
|
+
declare class Headers {
|
|
4
|
+
changed: boolean;
|
|
5
|
+
headers: string | Buffer | false;
|
|
6
|
+
parsed: boolean;
|
|
7
|
+
lines: HeaderLine[] | false;
|
|
8
|
+
mbox: string | false;
|
|
9
|
+
http: string | false;
|
|
10
|
+
|
|
11
|
+
constructor(headers?: string | Buffer | HeaderLine[] | false, config?: LibmimeOptions);
|
|
12
|
+
hasHeader(key: string): boolean;
|
|
13
|
+
get(key: string): string[];
|
|
14
|
+
getDecoded(key: string): DecodedHeader[];
|
|
15
|
+
getFirst(key: string): string;
|
|
16
|
+
getList(): HeaderLine[];
|
|
17
|
+
add(key: string, value?: string | number | Buffer, index?: number): void;
|
|
18
|
+
addFormatted(key: string, line?: string | Buffer | false, index?: number): void;
|
|
19
|
+
remove(key: string): void;
|
|
20
|
+
update(key: string, value?: string | number | Buffer, relativeIndex?: number): void;
|
|
21
|
+
build(lineEnd?: string | false): Buffer;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export = Headers;
|
package/lib/headers.js
CHANGED
|
@@ -2,76 +2,120 @@
|
|
|
2
2
|
|
|
3
3
|
const libmime = require('libmime');
|
|
4
4
|
|
|
5
|
+
/** @typedef {import('..').HeaderLine} HeaderLine */
|
|
6
|
+
/** @typedef {import('..').LibmimeOptions} LibmimeOptions */
|
|
7
|
+
/** @typedef {import('..').DecodedHeader} DecodedHeader */
|
|
8
|
+
|
|
9
|
+
const Libmime = /** @type {any} */ (libmime.Libmime);
|
|
10
|
+
|
|
5
11
|
/**
|
|
6
12
|
* Class Headers to parse and handle message headers. Headers instance allows to
|
|
7
13
|
* check existing, delete or add new headers
|
|
8
14
|
*/
|
|
9
15
|
class Headers {
|
|
16
|
+
/**
|
|
17
|
+
* @param {string | Buffer | HeaderLine[] | false} [headers]
|
|
18
|
+
* @param {LibmimeOptions} [config]
|
|
19
|
+
*/
|
|
10
20
|
constructor(headers, config) {
|
|
11
21
|
config = config || {};
|
|
12
22
|
|
|
13
23
|
if (Array.isArray(headers)) {
|
|
14
24
|
// already using parsed headers
|
|
15
25
|
this.changed = true;
|
|
26
|
+
/** @type {string | Buffer | false} */
|
|
16
27
|
this.headers = false;
|
|
17
28
|
this.parsed = true;
|
|
29
|
+
/** @type {HeaderLine[] | false} */
|
|
18
30
|
this.lines = headers;
|
|
19
31
|
} else {
|
|
20
32
|
// using original string/buffer headers
|
|
21
33
|
this.changed = false;
|
|
22
|
-
|
|
34
|
+
/** @type {string | Buffer | false} */
|
|
35
|
+
this.headers = headers || false;
|
|
23
36
|
this.parsed = false;
|
|
37
|
+
/** @type {HeaderLine[] | false} */
|
|
24
38
|
this.lines = false;
|
|
25
39
|
}
|
|
40
|
+
/** @type {string | false} */
|
|
26
41
|
this.mbox = false;
|
|
42
|
+
/** @type {string | false} */
|
|
27
43
|
this.http = false;
|
|
28
44
|
|
|
29
|
-
this.libmime = new
|
|
45
|
+
this.libmime = new Libmime({ Iconv: config.Iconv });
|
|
30
46
|
}
|
|
31
47
|
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} key
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
32
52
|
hasHeader(key) {
|
|
33
53
|
if (!this.parsed) {
|
|
34
54
|
this._parseHeaders();
|
|
35
55
|
}
|
|
56
|
+
let lines = this._getLines();
|
|
36
57
|
key = this._normalizeHeader(key);
|
|
37
|
-
return typeof
|
|
58
|
+
return typeof lines.find(line => line.key === key) === 'object';
|
|
38
59
|
}
|
|
39
60
|
|
|
61
|
+
/**
|
|
62
|
+
* @param {string} key
|
|
63
|
+
* @returns {string[]}
|
|
64
|
+
*/
|
|
40
65
|
get(key) {
|
|
41
66
|
if (!this.parsed) {
|
|
42
67
|
this._parseHeaders();
|
|
43
68
|
}
|
|
69
|
+
let headerLines = this._getLines();
|
|
44
70
|
key = this._normalizeHeader(key);
|
|
45
|
-
let lines =
|
|
71
|
+
let lines = headerLines.filter(line => line.key === key).map(line => this._decodeHeaderValue(line.line));
|
|
46
72
|
|
|
47
73
|
return lines;
|
|
48
74
|
}
|
|
49
75
|
|
|
76
|
+
/**
|
|
77
|
+
* @param {string} key
|
|
78
|
+
* @returns {DecodedHeader[]}
|
|
79
|
+
*/
|
|
50
80
|
getDecoded(key) {
|
|
51
81
|
return this.get(key)
|
|
52
82
|
.map(line => this.libmime.decodeHeader(line))
|
|
53
83
|
.filter(line => line && line.value);
|
|
54
84
|
}
|
|
55
85
|
|
|
86
|
+
/**
|
|
87
|
+
* @param {string} key
|
|
88
|
+
* @returns {string}
|
|
89
|
+
*/
|
|
56
90
|
getFirst(key) {
|
|
57
91
|
if (!this.parsed) {
|
|
58
92
|
this._parseHeaders();
|
|
59
93
|
}
|
|
94
|
+
let lines = this._getLines();
|
|
60
95
|
key = this._normalizeHeader(key);
|
|
61
|
-
let header =
|
|
96
|
+
let header = lines.find(line => line.key === key);
|
|
62
97
|
if (!header) {
|
|
63
98
|
return '';
|
|
64
99
|
}
|
|
65
100
|
return ((this.libmime.decodeHeader(this._decodeHeaderValue(header.line)) || {}).value || '').toString().trim();
|
|
66
101
|
}
|
|
67
102
|
|
|
103
|
+
/**
|
|
104
|
+
* @returns {HeaderLine[]}
|
|
105
|
+
*/
|
|
68
106
|
getList() {
|
|
69
107
|
if (!this.parsed) {
|
|
70
108
|
this._parseHeaders();
|
|
71
109
|
}
|
|
72
|
-
return this.
|
|
110
|
+
return this._getLines();
|
|
73
111
|
}
|
|
74
112
|
|
|
113
|
+
/**
|
|
114
|
+
* @param {string} key
|
|
115
|
+
* @param {string | number | Buffer} [value]
|
|
116
|
+
* @param {number} [index]
|
|
117
|
+
* @returns {void}
|
|
118
|
+
*/
|
|
75
119
|
add(key, value, index) {
|
|
76
120
|
if (typeof value === 'undefined') {
|
|
77
121
|
return;
|
|
@@ -89,10 +133,17 @@ class Headers {
|
|
|
89
133
|
this.addFormatted(key, this.libmime.foldLines(key + ': ' + value.replace(/\r?\n/g, ''), 76, false), index);
|
|
90
134
|
}
|
|
91
135
|
|
|
136
|
+
/**
|
|
137
|
+
* @param {string} key
|
|
138
|
+
* @param {string | Buffer | false} [line]
|
|
139
|
+
* @param {number} [index]
|
|
140
|
+
* @returns {void}
|
|
141
|
+
*/
|
|
92
142
|
addFormatted(key, line, index) {
|
|
93
143
|
if (!this.parsed) {
|
|
94
144
|
this._parseHeaders();
|
|
95
145
|
}
|
|
146
|
+
let lines = this._getLines();
|
|
96
147
|
index = index || 0;
|
|
97
148
|
this.changed = true;
|
|
98
149
|
|
|
@@ -110,45 +161,57 @@ class Headers {
|
|
|
110
161
|
};
|
|
111
162
|
|
|
112
163
|
if (index < 1) {
|
|
113
|
-
|
|
114
|
-
} else if (index >=
|
|
115
|
-
|
|
164
|
+
lines.unshift(header);
|
|
165
|
+
} else if (index >= lines.length) {
|
|
166
|
+
lines.push(header);
|
|
116
167
|
} else {
|
|
117
|
-
|
|
168
|
+
lines.splice(index, 0, header);
|
|
118
169
|
}
|
|
119
170
|
}
|
|
120
171
|
|
|
172
|
+
/**
|
|
173
|
+
* @param {string} key
|
|
174
|
+
* @returns {void}
|
|
175
|
+
*/
|
|
121
176
|
remove(key) {
|
|
122
177
|
if (!this.parsed) {
|
|
123
178
|
this._parseHeaders();
|
|
124
179
|
}
|
|
180
|
+
let lines = this._getLines();
|
|
125
181
|
key = this._normalizeHeader(key);
|
|
126
|
-
for (let i =
|
|
127
|
-
if (
|
|
182
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
183
|
+
if (lines[i].key === key) {
|
|
128
184
|
this.changed = true;
|
|
129
|
-
|
|
185
|
+
lines.splice(i, 1);
|
|
130
186
|
}
|
|
131
187
|
}
|
|
132
188
|
}
|
|
133
189
|
|
|
190
|
+
/**
|
|
191
|
+
* @param {string} key
|
|
192
|
+
* @param {string | number | Buffer} [value]
|
|
193
|
+
* @param {number} [relativeIndex]
|
|
194
|
+
* @returns {void}
|
|
195
|
+
*/
|
|
134
196
|
update(key, value, relativeIndex) {
|
|
135
197
|
if (!this.parsed) {
|
|
136
198
|
this._parseHeaders();
|
|
137
199
|
}
|
|
200
|
+
let lines = this._getLines();
|
|
138
201
|
let keyName = key;
|
|
139
202
|
let index = 0;
|
|
140
203
|
key = this._normalizeHeader(key);
|
|
141
204
|
let relativeIndexCount = 0;
|
|
142
205
|
let relativeMatchFound = false;
|
|
143
|
-
for (let i =
|
|
144
|
-
if (
|
|
206
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
207
|
+
if (lines[i].key === key) {
|
|
145
208
|
if (relativeIndex && relativeIndex !== relativeIndexCount) {
|
|
146
209
|
relativeIndexCount++;
|
|
147
210
|
continue;
|
|
148
211
|
}
|
|
149
212
|
index = i;
|
|
150
213
|
this.changed = true;
|
|
151
|
-
|
|
214
|
+
lines.splice(i, 1);
|
|
152
215
|
if (relativeIndex) {
|
|
153
216
|
relativeMatchFound = true;
|
|
154
217
|
break;
|
|
@@ -163,18 +226,23 @@ class Headers {
|
|
|
163
226
|
this.add(keyName, value, index);
|
|
164
227
|
}
|
|
165
228
|
|
|
229
|
+
/**
|
|
230
|
+
* @param {string | false} [lineEnd]
|
|
231
|
+
* @returns {Buffer}
|
|
232
|
+
*/
|
|
166
233
|
build(lineEnd) {
|
|
167
234
|
if (!this.changed && !lineEnd) {
|
|
168
|
-
return typeof this.headers === 'string' ? Buffer.from(this.headers, 'binary') : this.headers;
|
|
235
|
+
return typeof this.headers === 'string' ? Buffer.from(this.headers, 'binary') : this.headers || Buffer.alloc(0);
|
|
169
236
|
}
|
|
170
237
|
|
|
171
238
|
if (!this.parsed) {
|
|
172
239
|
this._parseHeaders();
|
|
173
240
|
}
|
|
241
|
+
let lines = this._getLines();
|
|
174
242
|
|
|
175
243
|
lineEnd = lineEnd || '\r\n';
|
|
176
244
|
|
|
177
|
-
let headers =
|
|
245
|
+
let headers = lines
|
|
178
246
|
.map(line => this._buildHeaderLine(line.line.replace(/\r?\n/g, lineEnd)))
|
|
179
247
|
.reduce((joined, line, idx) => {
|
|
180
248
|
if (idx) {
|
|
@@ -182,7 +250,7 @@ class Headers {
|
|
|
182
250
|
}
|
|
183
251
|
joined.push(line);
|
|
184
252
|
return joined;
|
|
185
|
-
}, []);
|
|
253
|
+
}, /** @type {Buffer[]} */ ([]));
|
|
186
254
|
|
|
187
255
|
headers.push(Buffer.from(lineEnd + lineEnd, 'binary'));
|
|
188
256
|
|
|
@@ -197,10 +265,27 @@ class Headers {
|
|
|
197
265
|
return Buffer.concat(headers);
|
|
198
266
|
}
|
|
199
267
|
|
|
268
|
+
/**
|
|
269
|
+
* @param {string} key
|
|
270
|
+
* @returns {string}
|
|
271
|
+
*/
|
|
200
272
|
_normalizeHeader(key) {
|
|
201
273
|
return (key || '').toLowerCase().trim();
|
|
202
274
|
}
|
|
203
275
|
|
|
276
|
+
/**
|
|
277
|
+
* @returns {HeaderLine[]}
|
|
278
|
+
*/
|
|
279
|
+
_getLines() {
|
|
280
|
+
if (!this.lines) {
|
|
281
|
+
this.lines = [];
|
|
282
|
+
}
|
|
283
|
+
return this.lines;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* @returns {void}
|
|
288
|
+
*/
|
|
204
289
|
_parseHeaders() {
|
|
205
290
|
if (!this.headers) {
|
|
206
291
|
this.lines = [];
|
|
@@ -208,18 +293,20 @@ class Headers {
|
|
|
208
293
|
return;
|
|
209
294
|
}
|
|
210
295
|
|
|
296
|
+
/** @type {Array<string | HeaderLine>} */
|
|
211
297
|
let lines = this.headers
|
|
212
298
|
.toString('binary')
|
|
213
299
|
.replace(/[\r\n]+$/, '')
|
|
214
300
|
.split(/\r?\n/);
|
|
215
301
|
|
|
216
302
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
217
|
-
let
|
|
303
|
+
let currentLine = /** @type {string} */ (lines[i]);
|
|
304
|
+
let chr = currentLine.charAt(0);
|
|
218
305
|
if (i && (chr === ' ' || chr === '\t')) {
|
|
219
|
-
lines[i - 1]
|
|
306
|
+
lines[i - 1] = /** @type {string} */ (lines[i - 1]) + '\r\n' + currentLine;
|
|
220
307
|
lines.splice(i, 1);
|
|
221
308
|
} else {
|
|
222
|
-
let line =
|
|
309
|
+
let line = currentLine;
|
|
223
310
|
if (!i && /^From /i.test(line)) {
|
|
224
311
|
// mbox file
|
|
225
312
|
this.mbox = line;
|
|
@@ -239,15 +326,23 @@ class Headers {
|
|
|
239
326
|
}
|
|
240
327
|
}
|
|
241
328
|
|
|
242
|
-
this.lines = lines;
|
|
329
|
+
this.lines = /** @type {HeaderLine[]} */ (lines);
|
|
243
330
|
this.parsed = true;
|
|
244
331
|
}
|
|
245
332
|
|
|
333
|
+
/**
|
|
334
|
+
* @param {string} line
|
|
335
|
+
* @returns {Buffer}
|
|
336
|
+
*/
|
|
246
337
|
_buildHeaderLine(line) {
|
|
247
338
|
let value = this._decodeHeaderValue(line);
|
|
248
339
|
return Buffer.from(value, value === line ? 'binary' : 'utf8');
|
|
249
340
|
}
|
|
250
341
|
|
|
342
|
+
/**
|
|
343
|
+
* @param {string} str
|
|
344
|
+
* @returns {string}
|
|
345
|
+
*/
|
|
251
346
|
_decodeHeaderValue(str) {
|
|
252
347
|
if (!str) {
|
|
253
348
|
return str;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
|
|
3
|
+
declare class MessageJoiner extends Transform {
|
|
4
|
+
constructor();
|
|
5
|
+
on(event: 'data', listener: (data: Buffer) => void): this;
|
|
6
|
+
once(event: 'data', listener: (data: Buffer) => void): this;
|
|
7
|
+
addListener(event: 'data', listener: (data: Buffer) => void): this;
|
|
8
|
+
prependListener(event: 'data', listener: (data: Buffer) => void): this;
|
|
9
|
+
emit(event: 'data', data: Buffer): boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export = MessageJoiner;
|
package/lib/message-joiner.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const Transform = require('stream').Transform;
|
|
4
4
|
|
|
5
|
+
/** @typedef {import('..').SplitterChunk} SplitterChunk */
|
|
6
|
+
|
|
5
7
|
class MessageJoiner extends Transform {
|
|
6
8
|
constructor() {
|
|
7
9
|
let options = {
|
|
@@ -11,6 +13,12 @@ class MessageJoiner extends Transform {
|
|
|
11
13
|
super(options);
|
|
12
14
|
}
|
|
13
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @param {SplitterChunk | Buffer} obj
|
|
18
|
+
* @param {BufferEncoding} encoding
|
|
19
|
+
* @param {import('stream').TransformCallback} callback
|
|
20
|
+
* @returns {void}
|
|
21
|
+
*/
|
|
14
22
|
_transform(obj, encoding, callback) {
|
|
15
23
|
if (Buffer.isBuffer(obj)) {
|
|
16
24
|
this.push(obj);
|
|
@@ -22,6 +30,10 @@ class MessageJoiner extends Transform {
|
|
|
22
30
|
return callback();
|
|
23
31
|
}
|
|
24
32
|
|
|
33
|
+
/**
|
|
34
|
+
* @param {import('stream').TransformCallback} callback
|
|
35
|
+
* @returns {void}
|
|
36
|
+
*/
|
|
25
37
|
_flush(callback) {
|
|
26
38
|
return callback();
|
|
27
39
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
import type { SplitterChunk, SplitterOptions } from './types';
|
|
3
|
+
|
|
4
|
+
declare class MessageSplitter extends Transform {
|
|
5
|
+
constructor(config?: SplitterOptions);
|
|
6
|
+
on(event: 'data', listener: (data: SplitterChunk) => void): this;
|
|
7
|
+
once(event: 'data', listener: (data: SplitterChunk) => void): this;
|
|
8
|
+
addListener(event: 'data', listener: (data: SplitterChunk) => void): this;
|
|
9
|
+
prependListener(event: 'data', listener: (data: SplitterChunk) => void): this;
|
|
10
|
+
emit(event: 'data', data: SplitterChunk): boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export = MessageSplitter;
|