@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 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;
@@ -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 {String} delSp True if delsp option was used
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 libmime.Libmime({ Iconv: config.Iconv });
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 (!encoding !== 'buffer') {
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);
@@ -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
- 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,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;
@@ -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;