@zone-eu/mailsplit 5.4.9 → 5.4.11

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/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
- * Class Headers to parse and handle message headers. Headers instance allows to
7
- * check existing, delete or add new headers
12
+ * Parses and builds message headers. A Headers instance allows callers to
13
+ * inspect, delete, update, and add header lines.
8
14
  */
9
15
  class Headers {
16
+ /**
17
+ * @param {string | Buffer | HeaderLine[] | false} [headers] Raw header source or already parsed lines.
18
+ * @param {LibmimeOptions} [config] Optional libmime configuration.
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
- this.headers = headers;
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 libmime.Libmime({ Iconv: config.Iconv });
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 this.lines.find(line => line.key === key) === 'object';
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 = this.lines.filter(line => line.key === key).map(line => this._decodeHeaderValue(line.line));
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 = this.lines.find(line => line.key === key);
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.lines;
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
- this.lines.unshift(header);
114
- } else if (index >= this.lines.length) {
115
- this.lines.push(header);
164
+ lines.unshift(header);
165
+ } else if (index >= lines.length) {
166
+ lines.push(header);
116
167
  } else {
117
- this.lines.splice(index, 0, header);
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 = this.lines.length - 1; i >= 0; i--) {
127
- if (this.lines[i].key === key) {
182
+ for (let i = lines.length - 1; i >= 0; i--) {
183
+ if (lines[i].key === key) {
128
184
  this.changed = true;
129
- this.lines.splice(i, 1);
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 = this.lines.length - 1; i >= 0; i--) {
144
- if (this.lines[i].key === key) {
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
- this.lines.splice(i, 1);
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 = this.lines
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 chr = lines[i].charAt(0);
303
+ let currentLine = /** @type {string} */ (lines[i]);
304
+ let chr = currentLine.charAt(0);
218
305
  if (i && (chr === ' ' || chr === '\t')) {
219
- lines[i - 1] += '\r\n' + lines[i];
306
+ lines[i - 1] = /** @type {string} */ (lines[i - 1]) + '\r\n' + currentLine;
220
307
  lines.splice(i, 1);
221
308
  } else {
222
- let line = lines[i];
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,56 @@
1
+ import { Transform } from 'node:stream';
2
+
3
+ /** Transform stream that joins splitter objects back into raw email bytes. */
4
+ declare class MessageJoiner extends Transform {
5
+ /**
6
+ * Creates a joiner that accepts splitter objects and emits Buffer chunks.
7
+ */
8
+ constructor();
9
+
10
+ /**
11
+ * Registers a listener for generated message bytes.
12
+ *
13
+ * @param event Event name.
14
+ * @param listener Receives each generated Buffer chunk.
15
+ * @returns This joiner instance.
16
+ */
17
+ on(event: 'data', listener: (data: Buffer) => void): this;
18
+
19
+ /**
20
+ * Registers a one-time listener for generated message bytes.
21
+ *
22
+ * @param event Event name.
23
+ * @param listener Receives the next generated Buffer chunk.
24
+ * @returns This joiner instance.
25
+ */
26
+ once(event: 'data', listener: (data: Buffer) => void): this;
27
+
28
+ /**
29
+ * Adds a listener for generated message bytes.
30
+ *
31
+ * @param event Event name.
32
+ * @param listener Receives each generated Buffer chunk.
33
+ * @returns This joiner instance.
34
+ */
35
+ addListener(event: 'data', listener: (data: Buffer) => void): this;
36
+
37
+ /**
38
+ * Prepends a listener for generated message bytes.
39
+ *
40
+ * @param event Event name.
41
+ * @param listener Receives each generated Buffer chunk.
42
+ * @returns This joiner instance.
43
+ */
44
+ prependListener(event: 'data', listener: (data: Buffer) => void): this;
45
+
46
+ /**
47
+ * Emits a generated message byte chunk.
48
+ *
49
+ * @param event Event name.
50
+ * @param data Buffer chunk to emit.
51
+ * @returns `true` when the event had listeners.
52
+ */
53
+ emit(event: 'data', data: Buffer): boolean;
54
+ }
55
+
56
+ export = MessageJoiner;
@@ -2,7 +2,15 @@
2
2
 
3
3
  const Transform = require('stream').Transform;
4
4
 
5
+ /** @typedef {import('..').SplitterChunk} SplitterChunk */
6
+
7
+ /**
8
+ * Transform stream that joins splitter objects back into raw message bytes.
9
+ */
5
10
  class MessageJoiner extends Transform {
11
+ /**
12
+ * Creates a joiner that accepts splitter objects and emits Buffer chunks.
13
+ */
6
14
  constructor() {
7
15
  let options = {
8
16
  readableObjectMode: false,
@@ -11,6 +19,12 @@ class MessageJoiner extends Transform {
11
19
  super(options);
12
20
  }
13
21
 
22
+ /**
23
+ * @param {SplitterChunk | Buffer} obj
24
+ * @param {BufferEncoding} encoding
25
+ * @param {import('stream').TransformCallback} callback
26
+ * @returns {void}
27
+ */
14
28
  _transform(obj, encoding, callback) {
15
29
  if (Buffer.isBuffer(obj)) {
16
30
  this.push(obj);
@@ -22,6 +36,10 @@ class MessageJoiner extends Transform {
22
36
  return callback();
23
37
  }
24
38
 
39
+ /**
40
+ * @param {import('stream').TransformCallback} callback
41
+ * @returns {void}
42
+ */
25
43
  _flush(callback) {
26
44
  return callback();
27
45
  }
@@ -0,0 +1,59 @@
1
+ import { Transform } from 'node:stream';
2
+ import type { SplitterChunk, SplitterOptions } from './types';
3
+
4
+ /** Transform stream that splits raw email bytes into MIME node and content chunks. */
5
+ declare class MessageSplitter extends Transform {
6
+ /**
7
+ * Creates a splitter that accepts Buffer input and emits `SplitterChunk` objects.
8
+ *
9
+ * @param config Optional parser limits and embedded-message behavior.
10
+ */
11
+ constructor(config?: SplitterOptions);
12
+
13
+ /**
14
+ * Registers a listener for parsed splitter chunks.
15
+ *
16
+ * @param event Event name.
17
+ * @param listener Receives each parsed MIME node, data chunk, or body chunk.
18
+ * @returns This splitter instance.
19
+ */
20
+ on(event: 'data', listener: (data: SplitterChunk) => void): this;
21
+
22
+ /**
23
+ * Registers a one-time listener for the next parsed splitter chunk.
24
+ *
25
+ * @param event Event name.
26
+ * @param listener Receives the next parsed MIME node, data chunk, or body chunk.
27
+ * @returns This splitter instance.
28
+ */
29
+ once(event: 'data', listener: (data: SplitterChunk) => void): this;
30
+
31
+ /**
32
+ * Adds a listener for parsed splitter chunks.
33
+ *
34
+ * @param event Event name.
35
+ * @param listener Receives each parsed MIME node, data chunk, or body chunk.
36
+ * @returns This splitter instance.
37
+ */
38
+ addListener(event: 'data', listener: (data: SplitterChunk) => void): this;
39
+
40
+ /**
41
+ * Prepends a listener for parsed splitter chunks.
42
+ *
43
+ * @param event Event name.
44
+ * @param listener Receives each parsed MIME node, data chunk, or body chunk.
45
+ * @returns This splitter instance.
46
+ */
47
+ prependListener(event: 'data', listener: (data: SplitterChunk) => void): this;
48
+
49
+ /**
50
+ * Emits a parsed splitter chunk.
51
+ *
52
+ * @param event Event name.
53
+ * @param data MIME node, data chunk, or body chunk to emit.
54
+ * @returns `true` when the event had listeners.
55
+ */
56
+ emit(event: 'data', data: SplitterChunk): boolean;
57
+ }
58
+
59
+ export = MessageSplitter;
@@ -3,13 +3,26 @@
3
3
  const Transform = require('stream').Transform;
4
4
  const MimeNode = require('./mime-node');
5
5
 
6
+ /** @typedef {import('..').MimeNode} MimeNodeType */
7
+ /** @typedef {import('..').MessageChunk} MessageChunk */
8
+ /** @typedef {import('..').SplitterChunk} SplitterChunk */
9
+ /** @typedef {import('..').SplitterGroup} SplitterGroup */
10
+ /** @typedef {import('..').SplitterOptions} SplitterOptions */
11
+ /** @typedef {(err?: (Error & {code?: string}) | null, data?: SplitterChunk | MessageChunk | false, flush?: boolean) => void} ProcessLineCallback */
12
+
6
13
  const MAX_HEAD_SIZE = 1 * 1024 * 1024;
7
14
  const MAX_CHILD_NODES = 1000;
8
15
 
9
16
  const HEAD = 0x01;
10
17
  const BODY = 0x02;
11
18
 
19
+ /**
20
+ * Transform stream that splits raw email bytes into MIME node and content chunks.
21
+ */
12
22
  class MessageSplitter extends Transform {
23
+ /**
24
+ * @param {SplitterOptions} [config]
25
+ */
13
26
  constructor(config) {
14
27
  let options = {
15
28
  readableObjectMode: true,
@@ -20,25 +33,41 @@ class MessageSplitter extends Transform {
20
33
  this.config = config || {};
21
34
  this.maxHeadSize = this.config.maxHeadSize || MAX_HEAD_SIZE;
22
35
  this.maxChildNodes = this.config.maxChildNodes || MAX_CHILD_NODES;
36
+ /** @type {MimeNodeType[]} */
23
37
  this.tree = [];
24
38
  this.nodeCounter = 0;
39
+ this.node = /** @type {MimeNodeType} */ (/** @type {unknown} */ (null));
25
40
  this.newNode();
26
41
  this.tree.push(this.node);
42
+ /** @type {Buffer | false} */
27
43
  this.line = false;
28
44
  this.hasFailed = false;
29
45
  }
30
46
 
47
+ /**
48
+ * @param {Buffer} chunk
49
+ * @param {BufferEncoding} encoding
50
+ * @param {import('stream').TransformCallback} callback
51
+ * @returns {void}
52
+ */
31
53
  _transform(chunk, encoding, callback) {
32
54
  // process line by line
33
55
  // find next line ending
34
56
  let pos = 0;
35
57
  let i = 0;
58
+ /** @type {SplitterGroup} */
36
59
  let group = {
37
60
  type: 'none'
38
61
  };
39
62
  let groupstart = this.line ? -this.line.length : 0;
40
63
  let groupend = 0;
41
64
 
65
+ /**
66
+ * Removes a pending line break from body data that belongs to a following boundary.
67
+ *
68
+ * @param {MessageChunk} data Body chunk to adjust in place.
69
+ * @returns {void}
70
+ */
42
71
  let checkTrailingLinebreak = data => {
43
72
  if (data.type === 'body' && data.node.parentNode && data.value && data.value.length) {
44
73
  if (data.value[data.value.length - 1] === 0x0a) {
@@ -67,6 +96,11 @@ class MessageSplitter extends Transform {
67
96
  }
68
97
  };
69
98
 
99
+ /**
100
+ * Iterates the current input chunk line by line and emits parsed groups.
101
+ *
102
+ * @returns {void}
103
+ */
70
104
  let iterateData = () => {
71
105
  for (let len = chunk.length; i < len; i++) {
72
106
  // find next <LF>
@@ -88,7 +122,7 @@ class MessageSplitter extends Transform {
88
122
 
89
123
  if (flush) {
90
124
  if (group && group.type !== 'none') {
91
- if (group.type === 'body' && groupend >= groupstart && group.node.parentNode) {
125
+ if (group.type === 'body' && groupend >= groupstart && group.node && group.node.parentNode) {
92
126
  // do not include the last line ending for body
93
127
  if (chunk[groupend - 1] === 0x0a) {
94
128
  groupend--;
@@ -99,7 +133,7 @@ class MessageSplitter extends Transform {
99
133
  }
100
134
  if (groupstart !== groupend) {
101
135
  group.value = chunk.slice(groupstart, groupend);
102
- if (groupend < i) {
136
+ if (groupend < i && 'value' in data) {
103
137
  data.value = chunk.slice(groupend, i);
104
138
  }
105
139
  }
@@ -118,7 +152,7 @@ class MessageSplitter extends Transform {
118
152
  // shift slice end position forward
119
153
  groupend = i;
120
154
  } else {
121
- if (group.type === 'body' && groupend >= groupstart && group.node.parentNode) {
155
+ if (group.type === 'body' && groupend >= groupstart && group.node && group.node.parentNode) {
122
156
  // do not include the last line ending for body
123
157
  if (chunk[groupend - 1] === 0x0a) {
124
158
  groupend--;
@@ -165,7 +199,7 @@ class MessageSplitter extends Transform {
165
199
  }
166
200
 
167
201
  // skip last linebreak for body
168
- if (pos >= groupstart + 1 && group.type === 'body' && group.node.parentNode) {
202
+ if (pos >= groupstart + 1 && group.type === 'body' && group.node && group.node.parentNode) {
169
203
  // do not include the last line ending for body
170
204
  if (chunk[pos - 1] === 0x0a) {
171
205
  pos--;
@@ -200,6 +234,10 @@ class MessageSplitter extends Transform {
200
234
  setImmediate(iterateData);
201
235
  }
202
236
 
237
+ /**
238
+ * @param {import('stream').TransformCallback} callback
239
+ * @returns {void}
240
+ */
203
241
  _flush(callback) {
204
242
  if (this.hasFailed) {
205
243
  return callback();
@@ -215,6 +253,12 @@ class MessageSplitter extends Transform {
215
253
  });
216
254
  }
217
255
 
256
+ /**
257
+ * @param {Buffer} line
258
+ * @param {number} startpos
259
+ * @param {Buffer} boundary
260
+ * @returns {1 | 2 | false}
261
+ */
218
262
  compareBoundary(line, startpos, boundary) {
219
263
  // --{boundary}\r\n or --{boundary}--\r\n
220
264
  if (line.length < boundary.length + 3 + startpos || line.length > boundary.length + 6 + startpos) {
@@ -256,6 +300,10 @@ class MessageSplitter extends Transform {
256
300
  return 2;
257
301
  }
258
302
 
303
+ /**
304
+ * @param {Buffer} line
305
+ * @returns {1 | 2 | 3 | 4 | false}
306
+ */
259
307
  checkBoundary(line) {
260
308
  let startpos = 0;
261
309
  if (line.length >= 1 && (line[0] === 0x0d || line[0] === 0x0a)) {
@@ -269,6 +317,7 @@ class MessageSplitter extends Transform {
269
317
  return false;
270
318
  }
271
319
 
320
+ /** @type {1 | 2 | false} */
272
321
  let boundary;
273
322
  if (this.node._boundary && (boundary = this.compareBoundary(line, startpos, this.node._boundary))) {
274
323
  // 1: next child
@@ -279,12 +328,18 @@ class MessageSplitter extends Transform {
279
328
  if (this.node._parentBoundary && (boundary = this.compareBoundary(line, startpos, this.node._parentBoundary))) {
280
329
  // 3: next sibling
281
330
  // 4: parent end
282
- return boundary + 2;
331
+ return /** @type {3 | 4} */ (boundary + 2);
283
332
  }
284
333
 
285
334
  return false;
286
335
  }
287
336
 
337
+ /**
338
+ * @param {Buffer | false} line
339
+ * @param {boolean} final
340
+ * @param {ProcessLineCallback} next
341
+ * @returns {void}
342
+ */
288
343
  processLine(line, final, next) {
289
344
  let flush = false;
290
345
 
@@ -301,7 +356,7 @@ class MessageSplitter extends Transform {
301
356
  }
302
357
 
303
358
  if (this.nodeCounter > this.maxChildNodes) {
304
- let err = new Error('Max allowed child nodes exceeded');
359
+ let err = /** @type {Error & {code?: string}} */ (new Error('Max allowed child nodes exceeded'));
305
360
  err.code = 'EMAXLEN';
306
361
  return next(err);
307
362
  }
@@ -339,7 +394,7 @@ class MessageSplitter extends Transform {
339
394
  }
340
395
  // move up
341
396
  if (this.tree.length) {
342
- this.node = this.tree.pop();
397
+ this.node = /** @type {MimeNodeType} */ (this.tree.pop());
343
398
  }
344
399
  this.state = BODY;
345
400
  break;
@@ -360,7 +415,7 @@ class MessageSplitter extends Transform {
360
415
  case HEAD: {
361
416
  this.node.addHeaderChunk(line);
362
417
  if (this.node._headerlen > this.maxHeadSize) {
363
- let err = new Error('Max header size for a MIME node exceeded');
418
+ let err = /** @type {Error & {code?: string}} */ (new Error('Max header size for a MIME node exceeded'));
364
419
  err.code = 'EMAXLEN';
365
420
  return next(err);
366
421
  }
@@ -412,8 +467,12 @@ class MessageSplitter extends Transform {
412
467
  next(null, false);
413
468
  }
414
469
 
470
+ /**
471
+ * @param {MimeNodeType | false} [parent]
472
+ * @returns {void}
473
+ */
415
474
  newNode(parent) {
416
- this.node = new MimeNode(parent || false, this.config);
475
+ this.node = /** @type {MimeNodeType} */ (new MimeNode(parent || false, this.config));
417
476
  this.state = HEAD;
418
477
  this.nodeCounter++;
419
478
  }