imapflow 1.7.7 → 2.0.0

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.
Files changed (296) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +8 -2
  3. package/dist/cjs/charsets.d.ts +1 -0
  4. package/dist/cjs/charsets.js +294 -0
  5. package/dist/cjs/commands/append.d.ts +22 -0
  6. package/dist/cjs/commands/append.js +151 -0
  7. package/dist/cjs/commands/authenticate.d.ts +24 -0
  8. package/dist/cjs/commands/authenticate.js +223 -0
  9. package/dist/cjs/commands/capability.d.ts +8 -0
  10. package/dist/cjs/commands/capability.js +32 -0
  11. package/dist/cjs/commands/close.d.ts +8 -0
  12. package/dist/cjs/commands/close.js +39 -0
  13. package/dist/cjs/commands/compress.d.ts +8 -0
  14. package/dist/cjs/commands/compress.js +56 -0
  15. package/dist/cjs/commands/copy.d.ts +13 -0
  16. package/dist/cjs/commands/copy.js +44 -0
  17. package/dist/cjs/commands/copyuid-parser.d.ts +11 -0
  18. package/dist/cjs/commands/copyuid-parser.js +32 -0
  19. package/dist/cjs/commands/create.d.ts +11 -0
  20. package/dist/cjs/commands/create.js +80 -0
  21. package/dist/cjs/commands/delete.d.ts +11 -0
  22. package/dist/cjs/commands/delete.js +40 -0
  23. package/dist/cjs/commands/enable.d.ts +9 -0
  24. package/dist/cjs/commands/enable.js +61 -0
  25. package/dist/cjs/commands/esearch-parser.d.ts +17 -0
  26. package/dist/cjs/commands/esearch-parser.js +91 -0
  27. package/dist/cjs/commands/expunge.d.ts +12 -0
  28. package/dist/cjs/commands/expunge.js +60 -0
  29. package/dist/cjs/commands/fetch.d.ts +30 -0
  30. package/dist/cjs/commands/fetch.js +241 -0
  31. package/dist/cjs/commands/id.d.ts +10 -0
  32. package/dist/cjs/commands/id.js +80 -0
  33. package/dist/cjs/commands/idle.d.ts +9 -0
  34. package/dist/cjs/commands/idle.js +347 -0
  35. package/dist/cjs/commands/list.d.ts +16 -0
  36. package/dist/cjs/commands/list.js +518 -0
  37. package/dist/cjs/commands/login.d.ts +11 -0
  38. package/dist/cjs/commands/login.js +42 -0
  39. package/dist/cjs/commands/logout.d.ts +8 -0
  40. package/dist/cjs/commands/logout.js +47 -0
  41. package/dist/cjs/commands/move.d.ts +13 -0
  42. package/dist/cjs/commands/move.js +57 -0
  43. package/dist/cjs/commands/namespace.d.ts +25 -0
  44. package/dist/cjs/commands/namespace.js +139 -0
  45. package/dist/cjs/commands/noop.d.ts +8 -0
  46. package/dist/cjs/commands/noop.js +22 -0
  47. package/dist/cjs/commands/quota.d.ts +10 -0
  48. package/dist/cjs/commands/quota.js +119 -0
  49. package/dist/cjs/commands/rename.d.ts +12 -0
  50. package/dist/cjs/commands/rename.js +48 -0
  51. package/dist/cjs/commands/search.d.ts +15 -0
  52. package/dist/cjs/commands/search.js +228 -0
  53. package/dist/cjs/commands/select.d.ts +25 -0
  54. package/dist/cjs/commands/select.js +250 -0
  55. package/dist/cjs/commands/starttls.d.ts +8 -0
  56. package/dist/cjs/commands/starttls.js +30 -0
  57. package/dist/cjs/commands/status-fields.d.ts +14 -0
  58. package/dist/cjs/commands/status-fields.js +61 -0
  59. package/dist/cjs/commands/status.d.ts +12 -0
  60. package/dist/cjs/commands/status.js +108 -0
  61. package/dist/cjs/commands/store.d.ts +19 -0
  62. package/dist/cjs/commands/store.js +93 -0
  63. package/dist/cjs/commands/subscribe.d.ts +9 -0
  64. package/dist/cjs/commands/subscribe.js +31 -0
  65. package/dist/cjs/commands/unsubscribe.d.ts +9 -0
  66. package/dist/cjs/commands/unsubscribe.js +31 -0
  67. package/dist/cjs/connection-deadline.d.ts +49 -0
  68. package/dist/cjs/connection-deadline.js +91 -0
  69. package/dist/cjs/errors.d.ts +83 -0
  70. package/dist/cjs/errors.js +13 -0
  71. package/dist/cjs/handler/imap-compiler.d.ts +24 -0
  72. package/dist/cjs/handler/imap-compiler.js +285 -0
  73. package/dist/cjs/handler/imap-formal-syntax.d.ts +28 -0
  74. package/dist/cjs/handler/imap-formal-syntax.js +121 -0
  75. package/dist/cjs/handler/imap-handler.d.ts +9 -0
  76. package/dist/cjs/handler/imap-handler.js +10 -0
  77. package/dist/cjs/handler/imap-parser.d.ts +16 -0
  78. package/dist/cjs/handler/imap-parser.js +90 -0
  79. package/dist/cjs/handler/imap-stream.d.ts +181 -0
  80. package/dist/cjs/handler/imap-stream.js +446 -0
  81. package/dist/cjs/handler/limits.d.ts +25 -0
  82. package/dist/cjs/handler/limits.js +51 -0
  83. package/dist/cjs/handler/parser-instance.d.ts +68 -0
  84. package/dist/cjs/handler/parser-instance.js +223 -0
  85. package/dist/cjs/handler/token-parser.d.ts +91 -0
  86. package/dist/cjs/handler/token-parser.js +673 -0
  87. package/dist/cjs/handler/types.d.ts +91 -0
  88. package/dist/cjs/handler/types.js +4 -0
  89. package/dist/cjs/imap-commands.d.ts +16 -0
  90. package/dist/cjs/imap-commands.js +74 -0
  91. package/dist/cjs/imap-flow.d.ts +676 -0
  92. package/dist/cjs/imap-flow.js +3949 -0
  93. package/dist/cjs/jp-decoder.d.ts +12 -0
  94. package/dist/cjs/jp-decoder.js +79 -0
  95. package/dist/cjs/limited-passthrough.d.ts +25 -0
  96. package/dist/cjs/limited-passthrough.js +54 -0
  97. package/dist/cjs/logger.d.ts +3 -0
  98. package/dist/cjs/logger.js +11 -0
  99. package/dist/cjs/package-info.d.ts +3 -0
  100. package/dist/cjs/package-info.js +7 -0
  101. package/dist/cjs/package.json +3 -0
  102. package/dist/cjs/proxy-connection.d.ts +33 -0
  103. package/dist/cjs/proxy-connection.js +392 -0
  104. package/dist/cjs/search-compiler.d.ts +34 -0
  105. package/dist/cjs/search-compiler.js +476 -0
  106. package/dist/cjs/special-use.d.ts +22 -0
  107. package/dist/cjs/special-use.js +911 -0
  108. package/dist/cjs/tools.d.ts +427 -0
  109. package/dist/cjs/tools.js +1496 -0
  110. package/{lib/imap-flow.d.ts → dist/cjs/types.d.ts} +386 -516
  111. package/dist/cjs/types.js +5 -0
  112. package/dist/esm/charsets.d.ts +1 -0
  113. package/{lib → dist/esm}/charsets.js +1 -6
  114. package/dist/esm/commands/append.d.ts +22 -0
  115. package/{lib → dist/esm}/commands/append.js +22 -52
  116. package/dist/esm/commands/authenticate.d.ts +24 -0
  117. package/{lib → dist/esm}/commands/authenticate.js +62 -87
  118. package/dist/esm/commands/capability.d.ts +8 -0
  119. package/{lib → dist/esm}/commands/capability.js +6 -9
  120. package/dist/esm/commands/close.d.ts +8 -0
  121. package/{lib → dist/esm}/commands/close.js +6 -10
  122. package/dist/esm/commands/compress.d.ts +8 -0
  123. package/{lib → dist/esm}/commands/compress.js +7 -11
  124. package/dist/esm/commands/copy.d.ts +13 -0
  125. package/{lib → dist/esm}/commands/copy.js +12 -20
  126. package/dist/esm/commands/copyuid-parser.d.ts +11 -0
  127. package/{lib → dist/esm}/commands/copyuid-parser.js +9 -15
  128. package/dist/esm/commands/create.d.ts +11 -0
  129. package/{lib → dist/esm}/commands/create.js +13 -27
  130. package/dist/esm/commands/delete.d.ts +11 -0
  131. package/{lib → dist/esm}/commands/delete.js +9 -14
  132. package/dist/esm/commands/enable.d.ts +9 -0
  133. package/{lib → dist/esm}/commands/enable.js +23 -30
  134. package/dist/esm/commands/esearch-parser.d.ts +17 -0
  135. package/dist/esm/commands/esearch-parser.js +88 -0
  136. package/dist/esm/commands/expunge.d.ts +12 -0
  137. package/{lib → dist/esm}/commands/expunge.js +17 -22
  138. package/dist/esm/commands/fetch.d.ts +30 -0
  139. package/{lib → dist/esm}/commands/fetch.js +32 -64
  140. package/dist/esm/commands/id.d.ts +10 -0
  141. package/{lib → dist/esm}/commands/id.js +17 -23
  142. package/dist/esm/commands/idle.d.ts +9 -0
  143. package/{lib → dist/esm}/commands/idle.js +47 -81
  144. package/dist/esm/commands/list.d.ts +16 -0
  145. package/{lib → dist/esm}/commands/list.js +56 -121
  146. package/dist/esm/commands/login.d.ts +11 -0
  147. package/{lib → dist/esm}/commands/login.js +10 -15
  148. package/dist/esm/commands/logout.d.ts +8 -0
  149. package/{lib → dist/esm}/commands/logout.js +9 -11
  150. package/dist/esm/commands/move.d.ts +13 -0
  151. package/{lib → dist/esm}/commands/move.js +13 -21
  152. package/dist/esm/commands/namespace.d.ts +25 -0
  153. package/{lib → dist/esm}/commands/namespace.js +34 -44
  154. package/dist/esm/commands/noop.d.ts +8 -0
  155. package/{lib → dist/esm}/commands/noop.js +6 -7
  156. package/dist/esm/commands/quota.d.ts +10 -0
  157. package/{lib → dist/esm}/commands/quota.js +18 -36
  158. package/dist/esm/commands/rename.d.ts +12 -0
  159. package/{lib → dist/esm}/commands/rename.js +10 -15
  160. package/dist/esm/commands/search.d.ts +15 -0
  161. package/{lib → dist/esm}/commands/search.js +36 -135
  162. package/dist/esm/commands/select.d.ts +25 -0
  163. package/{lib → dist/esm}/commands/select.js +33 -64
  164. package/dist/esm/commands/starttls.d.ts +8 -0
  165. package/{lib → dist/esm}/commands/starttls.js +6 -8
  166. package/dist/esm/commands/status-fields.d.ts +14 -0
  167. package/{lib → dist/esm}/commands/status-fields.js +5 -16
  168. package/dist/esm/commands/status.d.ts +12 -0
  169. package/{lib → dist/esm}/commands/status.js +18 -29
  170. package/dist/esm/commands/store.d.ts +19 -0
  171. package/{lib → dist/esm}/commands/store.js +24 -37
  172. package/dist/esm/commands/subscribe.d.ts +9 -0
  173. package/{lib → dist/esm}/commands/subscribe.js +8 -12
  174. package/dist/esm/commands/unsubscribe.d.ts +9 -0
  175. package/{lib → dist/esm}/commands/unsubscribe.js +8 -12
  176. package/dist/esm/connection-deadline.d.ts +49 -0
  177. package/{lib → dist/esm}/connection-deadline.js +14 -25
  178. package/dist/esm/errors.d.ts +83 -0
  179. package/dist/esm/errors.js +9 -0
  180. package/dist/esm/handler/imap-compiler.d.ts +24 -0
  181. package/{lib → dist/esm}/handler/imap-compiler.js +22 -80
  182. package/dist/esm/handler/imap-formal-syntax.d.ts +28 -0
  183. package/dist/esm/handler/imap-formal-syntax.js +117 -0
  184. package/dist/esm/handler/imap-handler.d.ts +9 -0
  185. package/dist/esm/handler/imap-handler.js +9 -0
  186. package/dist/esm/handler/imap-parser.d.ts +16 -0
  187. package/{lib → dist/esm}/handler/imap-parser.js +31 -44
  188. package/dist/esm/handler/imap-stream.d.ts +181 -0
  189. package/{lib → dist/esm}/handler/imap-stream.js +29 -121
  190. package/dist/esm/handler/limits.d.ts +25 -0
  191. package/{lib → dist/esm}/handler/limits.js +13 -22
  192. package/dist/esm/handler/parser-instance.d.ts +68 -0
  193. package/{lib → dist/esm}/handler/parser-instance.js +19 -47
  194. package/dist/esm/handler/token-parser.d.ts +91 -0
  195. package/{lib → dist/esm}/handler/token-parser.js +71 -155
  196. package/dist/esm/handler/types.d.ts +91 -0
  197. package/dist/esm/handler/types.js +3 -0
  198. package/dist/esm/imap-commands.d.ts +16 -0
  199. package/dist/esm/imap-commands.js +67 -0
  200. package/dist/esm/imap-flow.d.ts +676 -0
  201. package/{lib → dist/esm}/imap-flow.js +785 -1802
  202. package/dist/esm/jp-decoder.d.ts +12 -0
  203. package/{lib → dist/esm}/jp-decoder.js +6 -21
  204. package/dist/esm/limited-passthrough.d.ts +25 -0
  205. package/{lib → dist/esm}/limited-passthrough.js +7 -20
  206. package/dist/esm/logger.d.ts +3 -0
  207. package/dist/esm/logger.js +4 -0
  208. package/dist/esm/package-info.d.ts +3 -0
  209. package/dist/esm/package-info.js +4 -0
  210. package/dist/esm/package.json +3 -0
  211. package/dist/esm/proxy-connection.d.ts +33 -0
  212. package/{lib → dist/esm}/proxy-connection.js +56 -127
  213. package/dist/esm/search-compiler.d.ts +34 -0
  214. package/{lib → dist/esm}/search-compiler.js +54 -110
  215. package/dist/esm/special-use.d.ts +22 -0
  216. package/dist/esm/special-use.js +907 -0
  217. package/dist/esm/tools.d.ts +427 -0
  218. package/dist/esm/tools.js +1446 -0
  219. package/dist/esm/types.d.ts +828 -0
  220. package/dist/esm/types.js +4 -0
  221. package/package.json +60 -20
  222. package/.gitattributes +0 -1
  223. package/.github/CODE_OF_CONDUCT.md +0 -76
  224. package/.github/FUNDING.yml +0 -4
  225. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  226. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  227. package/.github/contributing.md +0 -17
  228. package/.github/workflows/release.yaml +0 -36
  229. package/.github/workflows/stale.yml +0 -29
  230. package/.github/workflows/test.yml +0 -51
  231. package/.ncurc.js +0 -4
  232. package/.prettierignore +0 -4
  233. package/.prettierrc.js +0 -8
  234. package/.release-please-manifest.json +0 -3
  235. package/CLAUDE.md +0 -104
  236. package/Gruntfile.js +0 -23
  237. package/eslint.config.js +0 -45
  238. package/lib/handler/imap-formal-syntax.js +0 -189
  239. package/lib/handler/imap-handler.js +0 -17
  240. package/lib/imap-commands.js +0 -45
  241. package/lib/logger.js +0 -5
  242. package/lib/special-use.js +0 -923
  243. package/lib/tools.js +0 -1612
  244. package/release-please-config.json +0 -10
  245. package/test/authentication-test.js +0 -101
  246. package/test/auto-idle-test.js +0 -470
  247. package/test/bodystructure-test.js +0 -899
  248. package/test/charsets-test.js +0 -161
  249. package/test/commands-branches-test.js +0 -1095
  250. package/test/commands-integration-test.js +0 -11124
  251. package/test/commands-test.js +0 -73
  252. package/test/connection-edge-cases-test.js +0 -1828
  253. package/test/connection-test.js +0 -162
  254. package/test/copyuid-parser-test.js +0 -173
  255. package/test/fetch-generator-test.js +0 -218
  256. package/test/fixtures/fake-timers.js +0 -115
  257. package/test/fixtures/serialized-mimetorture.js +0 -2738
  258. package/test/fixtures/test-client.js +0 -57
  259. package/test/fixtures/test-tls.js +0 -8
  260. package/test/handler-branches-test.js +0 -310
  261. package/test/idle-polling-test.js +0 -518
  262. package/test/imap-compiler-test.js +0 -809
  263. package/test/imap-flow-compress-test.js +0 -166
  264. package/test/imap-flow-coverage-test.js +0 -612
  265. package/test/imap-flow-fetch-download-test.js +0 -873
  266. package/test/imap-flow-internals-test.js +0 -725
  267. package/test/imap-flow-methods-test.js +0 -889
  268. package/test/imap-flow-proxy-paths-test.js +0 -366
  269. package/test/imap-flow-secure-test.js +0 -573
  270. package/test/imap-flow-server-test.js +0 -1474
  271. package/test/imap-formal-syntax-test.js +0 -293
  272. package/test/imap-parser-test.js +0 -1474
  273. package/test/imap-stream-edge-cases-test.js +0 -666
  274. package/test/imap-stream-test.js +0 -177
  275. package/test/imapflow-test.js +0 -258
  276. package/test/integration/README.md +0 -52
  277. package/test/integration/dovecot-test.conf +0 -27
  278. package/test/integration/rev2-live-test.js +0 -431
  279. package/test/integration/run-rev2-tests.sh +0 -75
  280. package/test/integration-test.js +0 -83
  281. package/test/jp-decoder-test.js +0 -304
  282. package/test/limited-passthrough-test.js +0 -299
  283. package/test/memory-cleanup-test.js +0 -144
  284. package/test/memory-leak-test.js +0 -667
  285. package/test/parser-limits-test.js +0 -292
  286. package/test/proxy-connection-test.js +0 -738
  287. package/test/reliability-improvements-test.js +0 -548
  288. package/test/search-compiler-test.js +0 -1300
  289. package/test/search-test.js +0 -329
  290. package/test/special-use-test.js +0 -418
  291. package/test/starttls-injection-test.js +0 -181
  292. package/test/tag-correlation-test.js +0 -333
  293. package/test/timer-policy-test.js +0 -227
  294. package/test/token-parser-test.js +0 -456
  295. package/test/tools-test.js +0 -2013
  296. package/test/unhandled-rejection-test.js +0 -593
@@ -1,162 +0,0 @@
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, 143);
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
- };
@@ -1,173 +0,0 @@
1
- 'use strict';
2
-
3
- const { parseCopyUid } = require('../lib/commands/copyuid-parser');
4
-
5
- /**
6
- * Builds a minimal IMAP response object with a COPYUID-style section.
7
- *
8
- * @param {Array} section - Array of section elements with value properties
9
- * @returns {Object} Response object suitable for parseCopyUid
10
- */
11
- function makeResponse(section) {
12
- return { attributes: [{ section }] };
13
- }
14
-
15
- // ============================================
16
- // Single UID pair
17
- // ============================================
18
-
19
- module.exports['CopyUID Parser: single UID pair sets uidValidity and uidMap entry'] = test => {
20
- let map = {};
21
- parseCopyUid(makeResponse([{ value: 'COPYUID' }, { value: '12345' }, { value: '1' }, { value: '100' }]), map);
22
-
23
- test.equal(map.uidValidity, BigInt('12345'));
24
- test.ok(map.uidMap instanceof Map);
25
- test.equal(map.uidMap.get(1), 100);
26
- test.equal(map.uidMap.size, 1);
27
- test.done();
28
- };
29
-
30
- // ============================================
31
- // UID ranges
32
- // ============================================
33
-
34
- module.exports['CopyUID Parser: contiguous UID range maps all source UIDs to destinations'] = test => {
35
- let map = {};
36
- parseCopyUid(makeResponse([{ value: 'COPYUID' }, { value: '99' }, { value: '1:3' }, { value: '10:12' }]), map);
37
-
38
- test.equal(map.uidValidity, BigInt('99'));
39
- test.ok(map.uidMap instanceof Map);
40
- test.equal(map.uidMap.size, 3);
41
- test.equal(map.uidMap.get(1), 10);
42
- test.equal(map.uidMap.get(2), 11);
43
- test.equal(map.uidMap.get(3), 12);
44
- test.done();
45
- };
46
-
47
- // ============================================
48
- // Comma-separated UIDs
49
- // ============================================
50
-
51
- module.exports['CopyUID Parser: comma-separated UIDs map each source to its destination'] = test => {
52
- let map = {};
53
- parseCopyUid(makeResponse([{ value: 'COPYUID' }, { value: '200' }, { value: '1,5,9' }, { value: '100,105,109' }]), map);
54
-
55
- test.equal(map.uidMap.size, 3);
56
- test.equal(map.uidMap.get(1), 100);
57
- test.equal(map.uidMap.get(5), 105);
58
- test.equal(map.uidMap.get(9), 109);
59
- test.done();
60
- };
61
-
62
- // ============================================
63
- // Non-COPYUID response code
64
- // ============================================
65
-
66
- module.exports['CopyUID Parser: non-COPYUID response code leaves map unchanged'] = test => {
67
- let map = {};
68
- parseCopyUid(makeResponse([{ value: 'APPENDUID' }, { value: '12345' }, { value: '1' }, { value: '100' }]), map);
69
-
70
- test.equal(map.uidValidity, undefined);
71
- test.equal(map.uidMap, undefined);
72
- test.done();
73
- };
74
-
75
- // ============================================
76
- // Missing / malformed input
77
- // ============================================
78
-
79
- module.exports['CopyUID Parser: missing attributes leaves map unchanged'] = test => {
80
- let map = {};
81
- parseCopyUid({}, map);
82
-
83
- test.equal(map.uidValidity, undefined);
84
- test.equal(map.uidMap, undefined);
85
- test.done();
86
- };
87
-
88
- module.exports['CopyUID Parser: empty attributes array leaves map unchanged'] = test => {
89
- let map = {};
90
- parseCopyUid({ attributes: [] }, map);
91
-
92
- test.equal(map.uidValidity, undefined);
93
- test.equal(map.uidMap, undefined);
94
- test.done();
95
- };
96
-
97
- module.exports['CopyUID Parser: null section leaves map unchanged'] = test => {
98
- let map = {};
99
- parseCopyUid({ attributes: [{ section: null }] }, map);
100
-
101
- test.equal(map.uidValidity, undefined);
102
- test.equal(map.uidMap, undefined);
103
- test.done();
104
- };
105
-
106
- // ============================================
107
- // Non-numeric uidValidity
108
- // ============================================
109
-
110
- module.exports['CopyUID Parser: non-numeric uidValidity skips uidValidity but still builds uidMap'] = test => {
111
- let map = {};
112
- parseCopyUid(makeResponse([{ value: 'COPYUID' }, { value: 'abc' }, { value: '1' }, { value: '100' }]), map);
113
-
114
- test.equal(map.uidValidity, undefined);
115
- test.ok(map.uidMap instanceof Map);
116
- test.equal(map.uidMap.get(1), 100);
117
- test.done();
118
- };
119
-
120
- // ============================================
121
- // Mismatched source/destination lengths
122
- // ============================================
123
-
124
- module.exports['CopyUID Parser: mismatched source and destination lengths sets uidValidity but skips uidMap'] = test => {
125
- let map = {};
126
- parseCopyUid(makeResponse([{ value: 'COPYUID' }, { value: '12345' }, { value: '1:3' }, { value: '10' }]), map);
127
-
128
- test.equal(map.uidValidity, BigInt('12345'));
129
- test.equal(map.uidMap, undefined);
130
- test.done();
131
- };
132
-
133
- // ============================================
134
- // Large uidValidity
135
- // ============================================
136
-
137
- module.exports['CopyUID Parser: large uidValidity is stored as BigInt'] = test => {
138
- let map = {};
139
- parseCopyUid(makeResponse([{ value: 'COPYUID' }, { value: '999999999999' }, { value: '1' }, { value: '100' }]), map);
140
-
141
- test.equal(map.uidValidity, BigInt('999999999999'));
142
- test.done();
143
- };
144
-
145
- module.exports['CopyUID Parser: uidValidity zero is stored as BigInt(0)'] = test => {
146
- let map = {};
147
- parseCopyUid(makeResponse([{ value: 'COPYUID' }, { value: '0' }, { value: '1' }, { value: '100' }]), map);
148
-
149
- test.strictEqual(map.uidValidity, BigInt(0));
150
- test.ok(map.uidMap instanceof Map);
151
- test.equal(map.uidMap.get(1), 100);
152
- test.done();
153
- };
154
-
155
- // ============================================
156
- // Hostile server input
157
- // ============================================
158
-
159
- module.exports['CopyUID Parser: non-decimal uidValidity is ignored, uidMap still parsed'] = test => {
160
- // isNaN() passed values like "1e5" and "Infinity" through, and BigInt() then threw,
161
- // losing the whole COPYUID result
162
- let map = {};
163
- parseCopyUid(makeResponse([{ value: 'COPYUID' }, { value: '1e5' }, { value: '1' }, { value: '100' }]), map);
164
- test.equal(map.uidValidity, undefined);
165
- test.ok(map.uidMap instanceof Map);
166
- test.equal(map.uidMap.get(1), 100);
167
-
168
- let map2 = {};
169
- parseCopyUid(makeResponse([{ value: 'COPYUID' }, { value: 'Infinity' }, { value: '1' }, { value: '100' }]), map2);
170
- test.equal(map2.uidValidity, undefined);
171
- test.equal(map2.uidMap.get(1), 100);
172
- test.done();
173
- };
@@ -1,218 +0,0 @@
1
- 'use strict';
2
-
3
- const { ImapFlow } = require('../lib/imap-flow');
4
-
5
- // Helper: create a minimal mock context with the fetch() generator bound to it.
6
- // The `run` override controls how untagged FETCH responses are delivered.
7
- const createFetchContext = runOverride => ({
8
- id: 'test-cid',
9
- mailbox: { path: 'INBOX', exists: 10 },
10
- isClosed: false,
11
- socket: { destroyed: false },
12
- resolveRange: async range => range,
13
- createConnectionError: ImapFlow.prototype.createConnectionError,
14
- run: runOverride
15
- });
16
-
17
- // Helper: invoke the fetch generator on a mock context
18
- function callFetch(ctx, range, query, options) {
19
- return ImapFlow.prototype.fetch.call(ctx, range, query, options);
20
- }
21
-
22
- // Helper: create a mock `run` that delivers `count` untagged FETCH responses,
23
- // each with a `next` callback for backpressure. Returns a tracking object.
24
- function createMockRun(count) {
25
- const tracker = {
26
- nextCalls: [], // records which message indices had next() called
27
- onUntaggedFetch: null,
28
- runResolved: false
29
- };
30
-
31
- const run = async (_cmd, _range, _query, opts) => {
32
- tracker.onUntaggedFetch = opts.onUntaggedFetch;
33
-
34
- for (let i = 0; i < count; i++) {
35
- await new Promise(resolve => {
36
- opts.onUntaggedFetch({ seq: i + 1, uid: 100 + i }, () => {
37
- tracker.nextCalls.push(i);
38
- resolve();
39
- });
40
- });
41
- }
42
- tracker.runResolved = true;
43
- };
44
-
45
- return { tracker, run };
46
- }
47
-
48
- // ---------------------------------------------------------------------------
49
- // Test: early break releases backpressure
50
- // ---------------------------------------------------------------------------
51
- module.exports['fetch generator: early break releases backpressure'] = async test => {
52
- const { tracker, run } = createMockRun(5);
53
- const ctx = createFetchContext(run);
54
-
55
- const gen = callFetch(ctx, '1:5', { uid: true });
56
- // Consume only the first message and break
57
- for await (let msg of gen) {
58
- test.ok(msg.seq === 1, 'should receive the first message');
59
- break;
60
- }
61
-
62
- // Allow microtasks to flush (the finally block runs synchronously with
63
- // generator return, but the remaining run() deliveries are async)
64
- await new Promise(r => setTimeout(r, 50));
65
-
66
- // The first message had next() called by the yield loop (res.next()).
67
- // Remaining queued/future messages should have next() called by the
68
- // finally block or the aborted guard.
69
- test.equal(tracker.nextCalls.length, 5, 'all 5 messages should have next() called');
70
- test.done();
71
- };
72
-
73
- // ---------------------------------------------------------------------------
74
- // Test: subsequent operations work after early exit
75
- // ---------------------------------------------------------------------------
76
- module.exports['fetch generator: connection usable after early break'] = async test => {
77
- let runCallCount = 0;
78
-
79
- const run = async (_cmd, _range, _query, opts) => {
80
- runCallCount++;
81
- // Deliver 3 messages
82
- for (let i = 0; i < 3; i++) {
83
- await new Promise(resolve => {
84
- opts.onUntaggedFetch({ seq: i + 1, uid: 200 + i }, () => resolve());
85
- });
86
- }
87
- };
88
-
89
- const ctx = createFetchContext(run);
90
-
91
- // First fetch: break early
92
- const gen1 = callFetch(ctx, '1:3', { uid: true });
93
- for await (let msg of gen1) {
94
- test.ok(msg.seq === 1);
95
- break;
96
- }
97
-
98
- await new Promise(r => setTimeout(r, 50));
99
-
100
- // Second fetch: should work (run gets called again)
101
- const gen2 = callFetch(ctx, '1:3', { uid: true });
102
- let secondFetchMessages = [];
103
- for await (let msg of gen2) {
104
- secondFetchMessages.push(msg);
105
- }
106
-
107
- test.ok(runCallCount === 2, 'run was called for both fetch operations');
108
- test.ok(secondFetchMessages.length === 3, 'second fetch received all 3 messages');
109
- test.done();
110
- };
111
-
112
- // ---------------------------------------------------------------------------
113
- // Test: normal full iteration works correctly
114
- // ---------------------------------------------------------------------------
115
- module.exports['fetch generator: normal full iteration receives all messages'] = async test => {
116
- const { tracker, run } = createMockRun(4);
117
- const ctx = createFetchContext(run);
118
-
119
- let messages = [];
120
- for await (let msg of callFetch(ctx, '1:4', { uid: true })) {
121
- messages.push(msg);
122
- }
123
-
124
- test.equal(messages.length, 4, 'should receive all 4 messages');
125
- test.equal(messages[0].seq, 1);
126
- test.equal(messages[3].seq, 4);
127
- test.equal(tracker.nextCalls.length, 4, 'all next() callbacks were called');
128
- test.done();
129
- };
130
-
131
- // ---------------------------------------------------------------------------
132
- // Test: error thrown in loop body releases backpressure
133
- // ---------------------------------------------------------------------------
134
- module.exports['fetch generator: error in loop body releases backpressure'] = async test => {
135
- const { tracker, run } = createMockRun(5);
136
- const ctx = createFetchContext(run);
137
-
138
- const gen = callFetch(ctx, '1:5', { uid: true });
139
-
140
- let caught = false;
141
- try {
142
- for await (let msg of gen) {
143
- if (msg.seq === 2) {
144
- throw new Error('intentional test error');
145
- }
146
- }
147
- } catch (err) {
148
- caught = true;
149
- test.equal(err.message, 'intentional test error');
150
- }
151
-
152
- await new Promise(r => setTimeout(r, 50));
153
-
154
- test.ok(caught, 'error was caught');
155
- // Messages 0 and 1 had next() called by the yield loop. The rest should
156
- // be drained by the finally block or the aborted guard.
157
- test.equal(tracker.nextCalls.length, 5, 'all 5 messages should have next() called');
158
- test.done();
159
- };
160
-
161
- // ---------------------------------------------------------------------------
162
- // Test: zero messages from server
163
- // ---------------------------------------------------------------------------
164
- module.exports['fetch generator: zero messages yields nothing'] = async test => {
165
- const { tracker, run } = createMockRun(0);
166
- const ctx = createFetchContext(run);
167
-
168
- let messages = [];
169
- for await (let msg of callFetch(ctx, '1:*', { uid: true })) {
170
- messages.push(msg);
171
- }
172
-
173
- test.equal(messages.length, 0, 'no messages should be yielded');
174
- test.equal(tracker.nextCalls.length, 0, 'no next() calls needed');
175
- test.ok(tracker.runResolved, 'run should have resolved');
176
- test.done();
177
- };
178
-
179
- // ---------------------------------------------------------------------------
180
- // Test: connection closed during iteration throws and releases backpressure
181
- // ---------------------------------------------------------------------------
182
- module.exports['fetch generator: connection closed mid-iteration throws and cleans up'] = async test => {
183
- const tracker = {
184
- nextCalls: []
185
- };
186
-
187
- const run = async (_cmd, _range, _query, opts) => {
188
- for (let i = 0; i < 3; i++) {
189
- await new Promise(resolve => {
190
- opts.onUntaggedFetch({ seq: i + 1, uid: 300 + i }, () => {
191
- tracker.nextCalls.push(i);
192
- resolve();
193
- });
194
- });
195
- }
196
- };
197
-
198
- const ctx = createFetchContext(run);
199
-
200
- let caught = false;
201
- try {
202
- for await (let msg of callFetch(ctx, '1:3', { uid: true })) {
203
- if (msg.seq === 1) {
204
- // Simulate connection closing after first message
205
- ctx.isClosed = true;
206
- }
207
- }
208
- } catch (err) {
209
- caught = true;
210
- test.equal(err.code, 'EConnectionClosed', 'should throw EConnectionClosed');
211
- }
212
-
213
- await new Promise(r => setTimeout(r, 50));
214
-
215
- test.ok(caught, 'error was caught');
216
- test.equal(tracker.nextCalls.length, 3, 'all next() callbacks should be called for cleanup');
217
- test.done();
218
- };
@@ -1,115 +0,0 @@
1
- 'use strict';
2
-
3
- // Deterministic stand-in for global.setTimeout/clearTimeout, so tests can drive timer-based
4
- // loops (auto-IDLE, IDLE restart, fallback polling) without wall-clock sleeps.
5
- //
6
- // It is the suite's existing "swap global.setTimeout for the duration of a test and restore it
7
- // in finally" idiom, made reusable and able to handle recursively scheduled timers: firing a
8
- // timer whose callback schedules the next one records the new timer instead of losing it.
9
- //
10
- // Only timers scheduled while installed are faked. Everything else (setImmediate, socket
11
- // timeouts, nodeunit's own bookkeeping) is untouched, and clearTimeout still forwards real
12
- // handles to the real implementation. Install for as short a window as possible and always
13
- // restore in a finally block.
14
- const installFakeTimers = () => {
15
- const realSetTimeout = global.setTimeout;
16
- const realClearTimeout = global.clearTimeout;
17
-
18
- let seq = 0;
19
- const timers = new Map();
20
- // Every timer ever scheduled, including cleared and fired ones, so tests can assert timer
21
- // identity and cleanup (which timer was armed, was it unref'd, was it cleared) without sleeps.
22
- const history = [];
23
-
24
- global.setTimeout = (fn, delay, ...args) => {
25
- const id = ++seq;
26
- const timer = { id, fn, delay, args, unrefd: false, cleared: false, fired: false };
27
- timers.set(id, timer);
28
- history.push(timer);
29
- return {
30
- _fakeTimerId: id,
31
- unref() {
32
- const timer = timers.get(id);
33
- if (timer) {
34
- timer.unrefd = true;
35
- }
36
- return this;
37
- },
38
- ref() {
39
- return this;
40
- }
41
- };
42
- };
43
-
44
- global.clearTimeout = handle => {
45
- if (handle && handle._fakeTimerId) {
46
- const timer = timers.get(handle._fakeTimerId);
47
- if (timer) {
48
- timer.cleared = true;
49
- timers.delete(timer.id);
50
- }
51
- return;
52
- }
53
- return realClearTimeout(handle);
54
- };
55
-
56
- // Lets pending asynchronous work (a poll's promise chain) settle between timer firings.
57
- const drain = async (turns = 3) => {
58
- for (let i = 0; i < turns; i++) {
59
- await new Promise(resolve => setImmediate(resolve));
60
- }
61
- };
62
-
63
- return {
64
- // Scheduled timers that have not fired or been cleared yet, in scheduling order
65
- pending: () => Array.from(timers.values()).map(timer => ({ id: timer.id, delay: timer.delay, unrefd: timer.unrefd })),
66
- count: () => timers.size,
67
-
68
- // Every timer scheduled while installed, in scheduling order, with its final state
69
- history: () => history.map(timer => ({ id: timer.id, delay: timer.delay, unrefd: timer.unrefd, cleared: timer.cleared, fired: timer.fired })),
70
-
71
- // Fires every currently pending timer once, oldest first, letting async callbacks settle.
72
- // Timers scheduled by those callbacks stay pending for the next fire() call, which is what
73
- // makes a recursive loop (poll -> schedule -> poll) observable one step at a time.
74
- fire: async () => {
75
- const due = Array.from(timers.values());
76
- for (const timer of due) {
77
- if (!timers.has(timer.id)) {
78
- // cleared by an earlier callback in this batch
79
- continue;
80
- }
81
- timers.delete(timer.id);
82
- timer.fired = true;
83
- timer.fn(...timer.args);
84
- await drain();
85
- }
86
- },
87
-
88
- drain,
89
-
90
- restore: () => {
91
- global.setTimeout = realSetTimeout;
92
- global.clearTimeout = realClearTimeout;
93
- timers.clear();
94
- }
95
- };
96
- };
97
-
98
- /**
99
- * Runs `fn` with faked timers installed, restoring the real ones afterwards. Always use this
100
- * rather than installing by hand: leaking faked globals into the rest of the run breaks
101
- * unrelated tests.
102
- *
103
- * @param {Function} fn - Receives the timer controller.
104
- * @returns {Promise<*>} Whatever `fn` returns.
105
- */
106
- const withFakeTimers = async fn => {
107
- const timers = installFakeTimers();
108
- try {
109
- return await fn(timers);
110
- } finally {
111
- timers.restore();
112
- }
113
- };
114
-
115
- module.exports = { installFakeTimers, withFakeTimers };