imapflow 1.0.191 → 1.0.193

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/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.193](https://github.com/postalsys/imapflow/compare/v1.0.192...v1.0.193) (2025-07-31)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * Improved exception handling ([0eb77ac](https://github.com/postalsys/imapflow/commit/0eb77ac28a0f36b085769b6f9a20fd89d9214060))
9
+
10
+ ## [1.0.192](https://github.com/postalsys/imapflow/compare/v1.0.191...v1.0.192) (2025-07-31)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * destroy _inflate and _deflate streams if set ([eab274b](https://github.com/postalsys/imapflow/commit/eab274b15b2ea2ee93aa027fd874249b1860167e))
16
+ * do not allow too large IMAP literals ([680a44d](https://github.com/postalsys/imapflow/commit/680a44d6550d51fc00602c49d6c5a377046c3504))
17
+ * Improved socket handler listener setup/cleanup ([0b72714](https://github.com/postalsys/imapflow/commit/0b727140866d220ff1df07d7245c60aa72e3be4a))
18
+ * remove unneeded timer ([1867606](https://github.com/postalsys/imapflow/commit/1867606aaf5ea37935c460aca1b615ab2a2b6534))
19
+
3
20
  ## [1.0.191](https://github.com/postalsys/imapflow/compare/v1.0.190...v1.0.191) (2025-07-10)
4
21
 
5
22
 
@@ -13,6 +13,9 @@ const NUM_9 = 0x39;
13
13
  const CURLY_OPEN = 0x7b;
14
14
  const CURLY_CLOSE = 0x7d;
15
15
 
16
+ // Maximum allowed literal size: 1GB (1073741824 bytes)
17
+ const MAX_LITERAL_SIZE = 1024 * 1024 * 1024;
18
+
16
19
  class ImapStream extends Transform {
17
20
  constructor(options) {
18
21
  super({
@@ -80,8 +83,19 @@ class ImapStream extends Transform {
80
83
  continue;
81
84
  }
82
85
  if (c === CURLY_OPEN && numBytes.length) {
86
+ const literalSize = Number(Buffer.from(numBytes).toString());
87
+
88
+ if (literalSize > MAX_LITERAL_SIZE) {
89
+ const err = new Error(`Literal size ${literalSize} exceeds maximum allowed size of ${MAX_LITERAL_SIZE} bytes`);
90
+ err.code = 'LiteralTooLarge';
91
+ err.literalSize = literalSize;
92
+ err.maxSize = MAX_LITERAL_SIZE;
93
+ this.emit('error', err);
94
+ return false;
95
+ }
96
+
83
97
  this.state = LITERAL;
84
- this.literalWaiting = Number(Buffer.from(numBytes).toString());
98
+ this.literalWaiting = literalSize;
85
99
  return true;
86
100
  }
87
101
  return false;
package/lib/imap-flow.js CHANGED
@@ -4,10 +4,6 @@
4
4
  * @module imapflow
5
5
  */
6
6
 
7
- // TODO:
8
- // * Use buffers for compiled commands
9
- // * OAuth2 authentication
10
-
11
7
  const tls = require('tls');
12
8
  const net = require('net');
13
9
  const crypto = require('crypto');
@@ -377,7 +373,6 @@ class ImapFlow extends EventEmitter {
377
373
  }
378
374
 
379
375
  this.log.error({ err, cid: this.id });
380
- setImmediate(() => this.close());
381
376
  this.emitError(err);
382
377
  });
383
378
 
@@ -386,6 +381,12 @@ class ImapFlow extends EventEmitter {
386
381
  }
387
382
 
388
383
  emitError(err) {
384
+ if (!err) {
385
+ return;
386
+ }
387
+ err._connId = err._connId || this.id;
388
+
389
+ setImmediate(() => this.close());
389
390
  this.emit('error', err);
390
391
  }
391
392
 
@@ -754,11 +755,13 @@ class ImapFlow extends EventEmitter {
754
755
  }
755
756
 
756
757
  setSocketHandlers() {
758
+ // Clear any existing handlers first to prevent duplicates
759
+ this.clearSocketHandlers();
760
+
757
761
  this._socketError =
758
762
  this._socketError ||
759
763
  (err => {
760
764
  this.log.error({ err, cid: this.id });
761
- setImmediate(() => this.close());
762
765
  this.emitError(err);
763
766
  });
764
767
  this._socketClose =
@@ -787,6 +790,10 @@ class ImapFlow extends EventEmitter {
787
790
  err.code = 'ETIMEOUT';
788
791
 
789
792
  if (this.idling) {
793
+ if (!this.usable || !this.socket || this.socket.destroyed) {
794
+ this.emitError(err);
795
+ return;
796
+ }
790
797
  // Attempt to recover IDLE connections
791
798
  this.run('NOOP')
792
799
  .then(() => this.idle())
@@ -794,8 +801,7 @@ class ImapFlow extends EventEmitter {
794
801
  } else {
795
802
  // Close immediately for non-IDLE operations
796
803
  this.log.debug({ msg: 'Socket timeout', cid: this.id });
797
- this.emit('error', err);
798
- this.close();
804
+ this.emitError(err);
799
805
  }
800
806
  });
801
807
 
@@ -806,14 +812,22 @@ class ImapFlow extends EventEmitter {
806
812
  this.socket.on('tlsClientError', this._socketError);
807
813
  this.socket.on('timeout', this._socketTimeout);
808
814
 
809
- this.writeSocket.on('error', this._socketError);
815
+ if (this.writeSocket && this.writeSocket !== this.socket) {
816
+ this.writeSocket.on('error', this._socketError);
817
+ }
810
818
  }
811
819
 
812
820
  clearSocketHandlers() {
821
+ if (!this.socket) {
822
+ return;
823
+ }
824
+
813
825
  if (this._socketError) {
814
826
  this.socket.removeListener('error', this._socketError);
815
827
  this.socket.removeListener('tlsClientError', this._socketError);
816
- this.writeSocket.removeListener('error', this._socketError);
828
+ if (this.writeSocket && this.writeSocket !== this.socket) {
829
+ this.writeSocket.removeListener('error', this._socketError);
830
+ }
817
831
  }
818
832
  if (this._socketTimeout) {
819
833
  this.socket.removeListener('timeout', this._socketTimeout);
@@ -1164,10 +1178,7 @@ class ImapFlow extends EventEmitter {
1164
1178
  }
1165
1179
 
1166
1180
  // ALWAYS emit the error so users can handle it
1167
- setImmediate(() => {
1168
- this.emit('error', err);
1169
- this.close();
1170
- });
1181
+ this.emitError(err);
1171
1182
  });
1172
1183
  }
1173
1184
 
@@ -1614,6 +1625,7 @@ class ImapFlow extends EventEmitter {
1614
1625
  clearTimeout(this.idleStartTimer);
1615
1626
  clearTimeout(this.upgradeTimeout);
1616
1627
  clearTimeout(this.connectTimeout);
1628
+ clearTimeout(this.greetingTimeout);
1617
1629
 
1618
1630
  this.usable = false;
1619
1631
  this.idling = false;
@@ -1673,6 +1685,27 @@ class ImapFlow extends EventEmitter {
1673
1685
  }
1674
1686
  }
1675
1687
 
1688
+ // cleanup compression streams if they exist
1689
+ if (this._inflate) {
1690
+ try {
1691
+ this._inflate.unpipe();
1692
+ this._inflate.destroy();
1693
+ this._inflate = null;
1694
+ } catch (err) {
1695
+ this.log.error({ err, info: 'Failed to destroy inflate stream', cid: this.id });
1696
+ }
1697
+ }
1698
+
1699
+ if (this._deflate) {
1700
+ try {
1701
+ this._deflate.unpipe();
1702
+ this._deflate.destroy();
1703
+ this._deflate = null;
1704
+ } catch (err) {
1705
+ this.log.error({ err, info: 'Failed to destroy deflate stream', cid: this.id });
1706
+ }
1707
+ }
1708
+
1676
1709
  if (this.socket && !this.socket.destroyed && this.writeSocket !== this.socket) {
1677
1710
  try {
1678
1711
  this.socket.destroy();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imapflow",
3
- "version": "1.0.191",
3
+ "version": "1.0.193",
4
4
  "description": "IMAP Client for Node",
5
5
  "main": "lib/imap-flow.js",
6
6
  "types": "lib/imap-flow.d.ts",
@@ -32,7 +32,7 @@
32
32
  "@babel/eslint-plugin": "7.27.1",
33
33
  "@babel/plugin-syntax-class-properties": "7.12.13",
34
34
  "@babel/preset-env": "7.28.0",
35
- "@types/node": "24.0.12",
35
+ "@types/node": "24.1.0",
36
36
  "eslint": "8.57.0",
37
37
  "eslint-config-nodemailer": "1.2.0",
38
38
  "eslint-config-prettier": "9.1.0",
@@ -40,8 +40,8 @@
40
40
  "grunt-cli": "1.5.0",
41
41
  "grunt-contrib-nodeunit": "5.0.0",
42
42
  "grunt-eslint": "24.3.0",
43
- "imapflow-jsdoc-template": "3.4.0-imapflow.2",
44
- "jsdoc": "3.6.11",
43
+ "imapflow-jsdoc-template": "3.4.0-imapflow.3",
44
+ "jsdoc": "4.0.4",
45
45
  "st": "3.0.2",
46
46
  "typescript": "5.8.3"
47
47
  },
@@ -51,9 +51,9 @@
51
51
  "libbase64": "1.3.0",
52
52
  "libmime": "5.3.7",
53
53
  "libqp": "2.1.1",
54
- "mailsplit": "5.4.5",
54
+ "mailsplit": "5.4.6",
55
55
  "nodemailer": "7.0.5",
56
56
  "pino": "9.7.0",
57
- "socks": "2.8.5"
57
+ "socks": "2.8.6"
58
58
  }
59
59
  }
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ const { ImapFlow } = require('../lib/imap-flow');
4
+ const { AuthenticationFailure } = require('../lib/tools');
5
+
6
+ module.exports['Authentication: Password auth configuration'] = test => {
7
+ let client = new ImapFlow({
8
+ host: 'imap.example.com',
9
+ auth: {
10
+ user: 'testuser',
11
+ pass: 'testpass'
12
+ }
13
+ });
14
+
15
+ test.equal(client.options.auth.user, 'testuser');
16
+ test.equal(client.options.auth.pass, 'testpass');
17
+ test.done();
18
+ };
19
+
20
+ module.exports['Authentication: OAuth2 auth configuration'] = test => {
21
+ let client = new ImapFlow({
22
+ host: 'imap.example.com',
23
+ auth: {
24
+ user: 'testuser',
25
+ accessToken: 'oauth2_token_here'
26
+ }
27
+ });
28
+
29
+ test.equal(client.options.auth.user, 'testuser');
30
+ test.equal(client.options.auth.accessToken, 'oauth2_token_here');
31
+ test.done();
32
+ };
33
+
34
+ module.exports['Authentication: Login method specification'] = test => {
35
+ let client = new ImapFlow({
36
+ host: 'imap.example.com',
37
+ auth: {
38
+ user: 'testuser',
39
+ pass: 'testpass',
40
+ loginMethod: 'AUTH=PLAIN'
41
+ }
42
+ });
43
+
44
+ test.equal(client.options.auth.loginMethod, 'AUTH=PLAIN');
45
+ test.done();
46
+ };
47
+
48
+ module.exports['Authentication: AuthenticationFailure error structure'] = test => {
49
+ let error = new AuthenticationFailure('Invalid credentials');
50
+
51
+ test.ok(error instanceof Error);
52
+ test.equal(error.constructor.name, 'AuthenticationFailure');
53
+ test.equal(error.message, 'Invalid credentials');
54
+ test.done();
55
+ };
56
+
57
+ module.exports['Authentication: Verify-only mode'] = test => {
58
+ let client = new ImapFlow({
59
+ host: 'imap.example.com',
60
+ auth: {
61
+ user: 'testuser',
62
+ pass: 'testpass'
63
+ },
64
+ verifyOnly: true
65
+ });
66
+
67
+ test.equal(client.options.verifyOnly, true);
68
+ test.done();
69
+ };
70
+
71
+ module.exports['Authentication: Disable auto IDLE'] = test => {
72
+ let client = new ImapFlow({
73
+ host: 'imap.example.com',
74
+ auth: {
75
+ user: 'testuser',
76
+ pass: 'testpass'
77
+ },
78
+ disableAutoIdle: true
79
+ });
80
+
81
+ test.equal(client.options.disableAutoIdle, true);
82
+ test.done();
83
+ };
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+
3
+ const { ImapFlow } = require('../lib/imap-flow');
4
+
5
+ module.exports['Commands: Client instantiation'] = test => {
6
+ let client = new ImapFlow({
7
+ host: 'imap.example.com',
8
+ auth: { user: 'test', pass: 'test' }
9
+ });
10
+
11
+ test.ok(client);
12
+ test.equal(typeof client.exec, 'function');
13
+ test.done();
14
+ };
15
+
16
+ module.exports['Commands: Method availability'] = test => {
17
+ let client = new ImapFlow({
18
+ host: 'imap.example.com',
19
+ auth: { user: 'test', pass: 'test' }
20
+ });
21
+
22
+ // Check that key IMAP methods exist
23
+ test.equal(typeof client.connect, 'function');
24
+ test.equal(typeof client.logout, 'function');
25
+ test.equal(typeof client.list, 'function');
26
+ test.equal(typeof client.mailboxOpen, 'function');
27
+ test.equal(typeof client.mailboxClose, 'function');
28
+ test.equal(typeof client.search, 'function');
29
+ test.equal(typeof client.fetch, 'function');
30
+ test.done();
31
+ };
32
+
33
+ module.exports['Commands: State management methods'] = test => {
34
+ let client = new ImapFlow({
35
+ host: 'imap.example.com',
36
+ auth: { user: 'test', pass: 'test' }
37
+ });
38
+
39
+ test.equal(typeof client.mailboxCreate, 'function');
40
+ test.equal(typeof client.mailboxDelete, 'function');
41
+ test.equal(typeof client.mailboxRename, 'function');
42
+ test.equal(typeof client.mailboxSubscribe, 'function');
43
+ test.equal(typeof client.mailboxUnsubscribe, 'function');
44
+ test.done();
45
+ };
46
+
47
+ module.exports['Commands: Message operation methods'] = test => {
48
+ let client = new ImapFlow({
49
+ host: 'imap.example.com',
50
+ auth: { user: 'test', pass: 'test' }
51
+ });
52
+
53
+ test.equal(typeof client.messageFlagsSet, 'function');
54
+ test.equal(typeof client.messageFlagsAdd, 'function');
55
+ test.equal(typeof client.messageFlagsRemove, 'function');
56
+ test.equal(typeof client.messageCopy, 'function');
57
+ test.equal(typeof client.messageMove, 'function');
58
+ test.equal(typeof client.messageDelete, 'function');
59
+ test.done();
60
+ };
61
+
62
+ module.exports['Commands: Utility methods'] = test => {
63
+ let client = new ImapFlow({
64
+ host: 'imap.example.com',
65
+ auth: { user: 'test', pass: 'test' }
66
+ });
67
+
68
+ test.equal(typeof client.noop, 'function');
69
+ test.equal(typeof client.getQuota, 'function');
70
+ test.equal(typeof client.stats, 'function');
71
+ test.equal(typeof client.getRandomId, 'function');
72
+ test.done();
73
+ };
@@ -0,0 +1,162 @@
1
+ 'use strict';
2
+
3
+ const { ImapFlow } = require('../lib/imap-flow');
4
+
5
+ module.exports['Connection: Basic connection options'] = test => {
6
+ let client = new ImapFlow({
7
+ host: 'imap.example.com',
8
+ port: 993,
9
+ auth: { user: 'test', pass: 'test' }
10
+ });
11
+
12
+ test.equal(client.host, 'imap.example.com');
13
+ test.equal(client.port, 993);
14
+ test.done();
15
+ };
16
+
17
+ module.exports['Connection: Default options'] = test => {
18
+ let client = new ImapFlow({
19
+ host: 'imap.example.com',
20
+ auth: { user: 'test', pass: 'test' }
21
+ });
22
+
23
+ test.equal(client.port, 110);
24
+ test.equal(client.secureConnection, false);
25
+ test.done();
26
+ };
27
+
28
+ module.exports['Connection: Secure connection defaults'] = test => {
29
+ let client = new ImapFlow({
30
+ host: 'imap.example.com',
31
+ port: 993,
32
+ auth: { user: 'test', pass: 'test' }
33
+ });
34
+
35
+ test.equal(client.secureConnection, true);
36
+ test.done();
37
+ };
38
+
39
+ module.exports['Connection: TLS options'] = test => {
40
+ let client = new ImapFlow({
41
+ host: 'imap.example.com',
42
+ auth: { user: 'test', pass: 'test' },
43
+ tls: {
44
+ rejectUnauthorized: false,
45
+ minVersion: 'TLSv1.3'
46
+ }
47
+ });
48
+
49
+ test.equal(client.options.tls.rejectUnauthorized, false);
50
+ test.equal(client.options.tls.minVersion, 'TLSv1.3');
51
+ test.done();
52
+ };
53
+
54
+ module.exports['Connection: Authentication options'] = test => {
55
+ let client = new ImapFlow({
56
+ host: 'imap.example.com',
57
+ auth: {
58
+ user: 'testuser',
59
+ pass: 'testpass',
60
+ accessToken: 'token123'
61
+ }
62
+ });
63
+
64
+ test.equal(client.options.auth.user, 'testuser');
65
+ test.equal(client.options.auth.pass, 'testpass');
66
+ test.equal(client.options.auth.accessToken, 'token123');
67
+ test.done();
68
+ };
69
+
70
+ module.exports['Connection: Proxy configuration'] = test => {
71
+ let client = new ImapFlow({
72
+ host: 'imap.example.com',
73
+ auth: { user: 'test', pass: 'test' },
74
+ proxy: 'socks5://proxy.example.com:1080'
75
+ });
76
+
77
+ test.equal(client.options.proxy, 'socks5://proxy.example.com:1080');
78
+ test.done();
79
+ };
80
+
81
+ module.exports['Connection: Client info'] = test => {
82
+ let clientInfo = {
83
+ name: 'Test Client',
84
+ version: '1.0.0',
85
+ vendor: 'Test Corp'
86
+ };
87
+
88
+ let client = new ImapFlow({
89
+ host: 'imap.example.com',
90
+ auth: { user: 'test', pass: 'test' },
91
+ clientInfo
92
+ });
93
+
94
+ test.equal(client.clientInfo.name, 'Test Client');
95
+ test.equal(client.clientInfo.version, '1.0.0');
96
+ test.equal(client.clientInfo.vendor, 'Test Corp');
97
+ test.done();
98
+ };
99
+
100
+ module.exports['Connection: Logger configuration'] = test => {
101
+ let client = new ImapFlow({
102
+ host: 'imap.example.com',
103
+ auth: { user: 'test', pass: 'test' },
104
+ logger: false
105
+ });
106
+
107
+ test.equal(client.options.logger, false);
108
+ test.done();
109
+ };
110
+
111
+ module.exports['Connection: Stats tracking'] = test => {
112
+ let client = new ImapFlow({
113
+ host: 'imap.example.com',
114
+ auth: { user: 'test', pass: 'test' }
115
+ });
116
+
117
+ let stats = client.stats();
118
+ test.ok(Object.prototype.hasOwnProperty.call(stats, 'sent'));
119
+ test.ok(Object.prototype.hasOwnProperty.call(stats, 'received'));
120
+ test.equal(typeof stats.sent, 'number');
121
+ test.equal(typeof stats.received, 'number');
122
+ test.done();
123
+ };
124
+
125
+ module.exports['Connection: Random ID generation'] = test => {
126
+ let client = new ImapFlow({
127
+ host: 'imap.example.com',
128
+ auth: { user: 'test', pass: 'test' }
129
+ });
130
+
131
+ let id1 = client.getRandomId();
132
+ let id2 = client.getRandomId();
133
+
134
+ test.ok(typeof id1 === 'string' && id1.length > 0);
135
+ test.ok(typeof id2 === 'string' && id2.length > 0);
136
+ test.notEqual(id1, id2, 'IDs should be unique');
137
+ test.done();
138
+ };
139
+
140
+ module.exports['Connection: State management'] = test => {
141
+ let client = new ImapFlow({
142
+ host: 'imap.example.com',
143
+ auth: { user: 'test', pass: 'test' }
144
+ });
145
+
146
+ test.equal(client.state, client.states.NOT_AUTHENTICATED);
147
+ test.equal(client.authenticated, false);
148
+ test.ok(client.capabilities instanceof Map);
149
+ test.done();
150
+ };
151
+
152
+ module.exports['Connection: Event emitter setup'] = test => {
153
+ let client = new ImapFlow({
154
+ host: 'imap.example.com',
155
+ auth: { user: 'test', pass: 'test' }
156
+ });
157
+
158
+ test.equal(typeof client.on, 'function');
159
+ test.equal(typeof client.emit, 'function');
160
+ test.equal(typeof client.removeListener, 'function');
161
+ test.done();
162
+ };
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ const { ImapFlow } = require('../lib/imap-flow');
4
+
5
+ module.exports['Integration: Basic client creation'] = test => {
6
+ let client = new ImapFlow({
7
+ host: 'imap.example.com',
8
+ auth: { user: 'test', pass: 'test' }
9
+ });
10
+
11
+ test.ok(client);
12
+ test.equal(client.state, client.states.NOT_AUTHENTICATED);
13
+ test.equal(client.authenticated, false);
14
+ test.done();
15
+ };
16
+
17
+ module.exports['Integration: Multiple client instances'] = test => {
18
+ let client1 = new ImapFlow({
19
+ host: 'imap1.example.com',
20
+ auth: { user: 'test1', pass: 'test1' }
21
+ });
22
+
23
+ let client2 = new ImapFlow({
24
+ host: 'imap2.example.com',
25
+ auth: { user: 'test2', pass: 'test2' }
26
+ });
27
+
28
+ test.ok(client1);
29
+ test.ok(client2);
30
+ test.notEqual(client1.id, client2.id);
31
+ test.done();
32
+ };
33
+
34
+ module.exports['Integration: Client configuration'] = test => {
35
+ let client = new ImapFlow({
36
+ host: 'imap.example.com',
37
+ port: 993,
38
+ secure: true,
39
+ auth: { user: 'test', pass: 'test' },
40
+ logger: false
41
+ });
42
+
43
+ test.equal(client.host, 'imap.example.com');
44
+ test.equal(client.port, 993);
45
+ test.equal(client.secureConnection, true);
46
+ test.done();
47
+ };
48
+
49
+ module.exports['Integration: Event emitter functionality'] = test => {
50
+ let client = new ImapFlow({
51
+ host: 'imap.example.com',
52
+ auth: { user: 'test', pass: 'test' }
53
+ });
54
+
55
+ let eventFired = false;
56
+ client.on('test-event', () => {
57
+ eventFired = true;
58
+ });
59
+
60
+ client.emit('test-event');
61
+
62
+ setTimeout(() => {
63
+ test.ok(eventFired);
64
+ test.done();
65
+ }, 10);
66
+ };
67
+
68
+ module.exports['Integration: Stats functionality'] = test => {
69
+ let client = new ImapFlow({
70
+ host: 'imap.example.com',
71
+ auth: { user: 'test', pass: 'test' }
72
+ });
73
+
74
+ let stats = client.stats();
75
+ test.ok(typeof stats === 'object');
76
+ test.ok(Object.prototype.hasOwnProperty.call(stats, 'sent'));
77
+ test.ok(Object.prototype.hasOwnProperty.call(stats, 'received'));
78
+
79
+ // Reset stats
80
+ let resetStats = client.stats(true);
81
+ test.ok(typeof resetStats === 'object');
82
+ test.done();
83
+ };
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ const { searchCompiler } = require('../lib/search-compiler');
4
+
5
+ // Mock connection for testing
6
+ let createMockConnection = () => ({
7
+ capabilities: new Map([
8
+ ['IMAP4rev1', true],
9
+ ['UTF8=ACCEPT', true]
10
+ ]),
11
+ enabled: new Set(['UTF8=ACCEPT'])
12
+ });
13
+
14
+ // Mock mailbox for testing
15
+ let createMockMailbox = () => ({
16
+ flags: new Set(['\\Seen', '\\Answered', '\\Flagged', '\\Deleted', '\\Draft']),
17
+ permanentFlags: new Set(['\\*'])
18
+ });
19
+
20
+ module.exports['Search Compiler: Basic functionality'] = test => {
21
+ let connection = createMockConnection();
22
+ let mailbox = createMockMailbox();
23
+
24
+ test.doesNotThrow(() => {
25
+ let compiled = searchCompiler(connection, { seen: false }, mailbox);
26
+ test.ok(Array.isArray(compiled));
27
+ });
28
+
29
+ test.done();
30
+ };
31
+
32
+ module.exports['Search Compiler: Text searches'] = test => {
33
+ let connection = createMockConnection();
34
+ let mailbox = createMockMailbox();
35
+
36
+ let compiled = searchCompiler(connection, {
37
+ from: 'user@example.com',
38
+ subject: 'Test'
39
+ }, mailbox);
40
+
41
+ test.ok(Array.isArray(compiled));
42
+ test.ok(compiled.length > 0);
43
+ test.done();
44
+ };
45
+
46
+ module.exports['Search Compiler: Flag searches'] = test => {
47
+ let connection = createMockConnection();
48
+ let mailbox = createMockMailbox();
49
+
50
+ let compiled = searchCompiler(connection, {
51
+ seen: true,
52
+ answered: false
53
+ }, mailbox);
54
+
55
+ test.ok(Array.isArray(compiled));
56
+ test.ok(compiled.length > 0);
57
+ test.done();
58
+ };
59
+
60
+ module.exports['Search Compiler: Date searches'] = test => {
61
+ let connection = createMockConnection();
62
+ let mailbox = createMockMailbox();
63
+
64
+ let compiled = searchCompiler(connection, {
65
+ since: new Date('2023-01-01')
66
+ }, mailbox);
67
+
68
+ test.ok(Array.isArray(compiled));
69
+ test.ok(compiled.length > 0);
70
+ test.done();
71
+ };
72
+
73
+ module.exports['Search Compiler: Empty query'] = test => {
74
+ let connection = createMockConnection();
75
+ let mailbox = createMockMailbox();
76
+
77
+ let compiled = searchCompiler(connection, {}, mailbox);
78
+
79
+ test.ok(Array.isArray(compiled));
80
+ test.done();
81
+ };
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ const specialUse = require('../lib/special-use');
4
+
5
+ module.exports['Special Use: flags array'] = test => {
6
+ test.ok(Array.isArray(specialUse.flags));
7
+ test.ok(specialUse.flags.includes('\\Sent'));
8
+ test.ok(specialUse.flags.includes('\\Drafts'));
9
+ test.ok(specialUse.flags.includes('\\Trash'));
10
+ test.ok(specialUse.flags.includes('\\Archive'));
11
+ test.done();
12
+ };
13
+
14
+ module.exports['Special Use: names object'] = test => {
15
+ test.ok(typeof specialUse.names === 'object');
16
+ test.ok(specialUse.names['\\Sent']);
17
+ test.ok(Array.isArray(specialUse.names['\\Sent']));
18
+ test.ok(specialUse.names['\\Sent'].includes('sent'));
19
+ test.done();
20
+ };
21
+
22
+ module.exports['Special Use: Sent folder names'] = test => {
23
+ let sentNames = specialUse.names['\\Sent'];
24
+ test.ok(sentNames.includes('sent'));
25
+ test.ok(sentNames.includes('sent items'));
26
+ test.ok(sentNames.includes('sent messages'));
27
+ test.done();
28
+ };
29
+
30
+ module.exports['Special Use: Drafts folder names'] = test => {
31
+ let draftsNames = specialUse.names['\\Drafts'];
32
+ test.ok(draftsNames.includes('drafts'));
33
+ test.done();
34
+ };
35
+
36
+ module.exports['Special Use: Trash folder names'] = test => {
37
+ let trashNames = specialUse.names['\\Trash'];
38
+ test.ok(trashNames.includes('trash'));
39
+ test.ok(trashNames.includes('deleted items'));
40
+ test.ok(trashNames.includes('deleted messages'));
41
+ test.done();
42
+ };
43
+
44
+ module.exports['Special Use: Junk folder names'] = test => {
45
+ let junkNames = specialUse.names['\\Junk'];
46
+ test.ok(junkNames.includes('spam'));
47
+ test.ok(junkNames.includes('junk'));
48
+ test.done();
49
+ };