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,177 +0,0 @@
1
- /*eslint no-unused-expressions: 0, prefer-arrow-callback: 0 */
2
-
3
- 'use strict';
4
-
5
- const { ImapStream } = require('../lib/handler/imap-stream');
6
- const { parser } = require('../lib/handler/imap-handler');
7
-
8
- module.exports['Full input'] = test => {
9
- let input = Buffer.from(
10
- `A CAPABILITY
11
- A LOGIN "aaa" "bbb"
12
- A APPEND INBOX {5}
13
- 12345
14
- A LOGIN {5}
15
- 12345 {11}
16
- 12345678901 "another"
17
- A LOGOUT
18
- `.replace(/\r?\n/g, '\r\n')
19
- );
20
-
21
- let expecting = [
22
- { command: 'A CAPABILITY', literals: [] },
23
- { command: 'A LOGIN "aaa" "bbb"', literals: [] },
24
- { command: 'A APPEND INBOX {5}\r\n', literals: ['12345'] },
25
- { command: 'A LOGIN {5}\r\n {11}\r\n "another"', literals: ['12345', '12345678901'] },
26
- { command: 'A LOGOUT', literals: [] }
27
- ];
28
-
29
- let stream = new ImapStream();
30
-
31
- let reading = false;
32
- let reader = async () => {
33
- let cmd;
34
- while ((cmd = stream.read()) !== null) {
35
- test.deepEqual({ command: cmd.payload.toString(), literals: cmd.literals.map(literal => literal.toString()) }, expecting.shift());
36
-
37
- let parsed = await parser(cmd.payload, { literals: cmd.literals });
38
- console.log(parsed);
39
- cmd.next();
40
- }
41
- };
42
-
43
- stream.on('readable', () => {
44
- if (!reading) {
45
- reading = true;
46
- reader()
47
- .catch(err => console.error(err))
48
- .finally(() => {
49
- reading = false;
50
- });
51
- }
52
- });
53
-
54
- stream.on('error', err => {
55
- test.ifError(err);
56
- });
57
-
58
- stream.on('end', () => {
59
- test.done();
60
- });
61
-
62
- let writer = async () => {
63
- stream.end(input);
64
- };
65
-
66
- writer().catch(err => test.ifError(err));
67
- };
68
-
69
- module.exports['Literal8 marker activates literal extraction'] = test => {
70
- // RFC 9051 folds the FETCH side of BINARY into base IMAP4rev2 - servers answer
71
- // BINARY fetches with literal8 syntax (~{n}) whose content may contain NULs.
72
- // The stream must treat the trailing {n} as a literal marker regardless of the
73
- // '~' prefix and hand the raw bytes over unmodified.
74
- let input = Buffer.from('* 1 FETCH (BINARY[1] ~{5}\r\nhel\x00o)\r\n', 'binary');
75
-
76
- let stream = new ImapStream();
77
-
78
- let reading = false;
79
- let reader = async () => {
80
- let cmd;
81
- while ((cmd = stream.read()) !== null) {
82
- test.equal(cmd.payload.toString('binary'), '* 1 FETCH (BINARY[1] ~{5}\r\n)');
83
- test.equal(cmd.literals.length, 1);
84
- test.deepEqual(cmd.literals[0], Buffer.from('hel\x00o', 'binary'));
85
-
86
- // and the parser consumes the literal8 into a LITERAL node with the NUL intact
87
- let parsed = await parser(cmd.payload, { literals: cmd.literals });
88
- test.deepEqual(parsed.attributes[1][1], { type: 'LITERAL', value: Buffer.from('hel\x00o', 'binary') });
89
- cmd.next();
90
- }
91
- };
92
-
93
- stream.on('readable', () => {
94
- if (!reading) {
95
- reading = true;
96
- reader()
97
- .catch(err => test.ifError(err))
98
- .finally(() => {
99
- reading = false;
100
- });
101
- }
102
- });
103
-
104
- stream.on('error', err => {
105
- test.ifError(err);
106
- });
107
-
108
- stream.on('end', () => {
109
- test.done();
110
- });
111
-
112
- stream.end(input);
113
- };
114
-
115
- module.exports['Single byte'] = test => {
116
- let input = Buffer.from(
117
- `A CAPABILITY
118
- A LOGIN "aaa" "bbb"
119
- A APPEND INBOX {5}
120
- 12345
121
- A LOGIN {5}
122
- 12345 {11}
123
- 12345678901 "another"
124
- A LOGOUT
125
- `.replace(/\r?\n/g, '\r\n')
126
- );
127
-
128
- let expecting = [
129
- { command: 'A CAPABILITY', literals: [] },
130
- { command: 'A LOGIN "aaa" "bbb"', literals: [] },
131
- { command: 'A APPEND INBOX {5}\r\n', literals: ['12345'] },
132
- { command: 'A LOGIN {5}\r\n {11}\r\n "another"', literals: ['12345', '12345678901'] },
133
- { command: 'A LOGOUT', literals: [] }
134
- ];
135
-
136
- let stream = new ImapStream();
137
-
138
- let reading = false;
139
- let reader = async () => {
140
- let cmd;
141
- while ((cmd = stream.read()) !== null) {
142
- test.deepEqual({ command: cmd.payload.toString(), literals: cmd.literals.map(literal => literal.toString()) }, expecting.shift());
143
- cmd.next();
144
- }
145
- };
146
-
147
- stream.on('readable', () => {
148
- if (!reading) {
149
- reading = true;
150
- reader()
151
- .catch(err => console.error(err))
152
- .finally(() => {
153
- reading = false;
154
- });
155
- }
156
- });
157
-
158
- stream.on('error', err => {
159
- test.ifError(err);
160
- });
161
-
162
- stream.on('end', () => {
163
- test.done();
164
- });
165
-
166
- let writer = async () => {
167
- for (let i = 0; i < input.length; i++) {
168
- if (stream.write(Buffer.from([input[i]])) === false) {
169
- await new Promise(resolve => stream.once('drain', resolve));
170
- }
171
- await new Promise(resolve => setTimeout(resolve, 10));
172
- }
173
- stream.end();
174
- };
175
-
176
- writer().catch(err => test.ifError(err));
177
- };
@@ -1,258 +0,0 @@
1
- 'use strict';
2
-
3
- const { ImapFlow } = require('../lib/imap-flow');
4
-
5
- module.exports['Create imapflow instance'] = test => {
6
- let imapFlow = new ImapFlow();
7
- test.ok(imapFlow);
8
- test.done();
9
- };
10
-
11
- module.exports['Create imapflow instance with custom logger'] = async test => {
12
- class CustomLogger {
13
- constructor() {}
14
-
15
- debug(obj) {
16
- console.log(JSON.stringify(obj));
17
- }
18
-
19
- info(obj) {
20
- console.log(JSON.stringify(obj));
21
- }
22
-
23
- warn(obj) {
24
- console.log(JSON.stringify(obj));
25
- }
26
-
27
- // eslint-disable-next-line no-unused-vars
28
- error(obj) {
29
- // we don't actually want to log anything here.
30
- }
31
- }
32
-
33
- let imapFlow = new ImapFlow({
34
- logger: new CustomLogger()
35
- });
36
- test.ok(imapFlow);
37
- try {
38
- await imapFlow.connect();
39
- } catch (ex) {
40
- // it is PERFECTLY okay to have an exception here. We expect an ECONNREFUSED if an exception occurs.
41
- test.equal(ex.code, 'ECONNREFUSED');
42
- }
43
- test.done();
44
- };
45
-
46
- // ---------------------------------------------------------------------------
47
- // Helpers for testing prototype methods on a mock context
48
- // ---------------------------------------------------------------------------
49
- const createFetchContext = runOverride => ({
50
- id: 'test-cid',
51
- mailbox: { path: 'INBOX', exists: 10 },
52
- isClosed: false,
53
- socket: { destroyed: false },
54
- resolveRange: async range => range,
55
- createConnectionError: ImapFlow.prototype.createConnectionError,
56
- run: runOverride
57
- });
58
-
59
- function callFetch(ctx, range, query, options) {
60
- return ImapFlow.prototype.fetch.call(ctx, range, query, options);
61
- }
62
-
63
- // ---------------------------------------------------------------------------
64
- // setFlagColor tests
65
- // ---------------------------------------------------------------------------
66
-
67
- module.exports['setFlagColor with valid color calls STORE for add and remove'] = async test => {
68
- let storeCalls = [];
69
- const ctx = {
70
- resolveRange: async range => range,
71
- run: async (command, range, flags, opts) => {
72
- storeCalls.push({ command, range, flags, operation: opts.operation });
73
- return true;
74
- }
75
- };
76
-
77
- let result = await ImapFlow.prototype.setFlagColor.call(ctx, '1:*', 'orange');
78
-
79
- test.equal(storeCalls.length, 2, 'run should be called twice');
80
-
81
- test.equal(storeCalls[0].command, 'STORE');
82
- test.equal(storeCalls[0].operation, 'add');
83
- test.ok(storeCalls[0].flags.indexOf('\\Flagged') >= 0, 'add flags should include \\Flagged');
84
- test.ok(storeCalls[0].flags.indexOf('$MailFlagBit0') >= 0, 'add flags should include $MailFlagBit0');
85
-
86
- test.equal(storeCalls[1].command, 'STORE');
87
- test.equal(storeCalls[1].operation, 'remove');
88
- test.ok(storeCalls[1].flags.indexOf('$MailFlagBit1') >= 0, 'remove flags should include $MailFlagBit1');
89
- test.ok(storeCalls[1].flags.indexOf('$MailFlagBit2') >= 0, 'remove flags should include $MailFlagBit2');
90
-
91
- test.ok(result, 'should return truthy result');
92
- test.done();
93
- };
94
-
95
- module.exports['setFlagColor with invalid color returns false'] = async test => {
96
- let runCalled = false;
97
- const ctx = {
98
- resolveRange: async range => range,
99
- run: async () => {
100
- runCalled = true;
101
- }
102
- };
103
-
104
- let result = await ImapFlow.prototype.setFlagColor.call(ctx, '1:*', 'invalid');
105
-
106
- test.strictEqual(result, false, 'should return false for invalid color');
107
- test.ok(!runCalled, 'run should not be called');
108
- test.done();
109
- };
110
-
111
- module.exports['setFlagColor with empty range returns false'] = async test => {
112
- let runCalled = false;
113
- const ctx = {
114
- resolveRange: async () => null,
115
- run: async () => {
116
- runCalled = true;
117
- }
118
- };
119
-
120
- let result = await ImapFlow.prototype.setFlagColor.call(ctx, '1:*', 'orange');
121
-
122
- test.strictEqual(result, false, 'should return false for empty range');
123
- test.ok(!runCalled, 'run should not be called');
124
- test.done();
125
- };
126
-
127
- module.exports['setFlagColor with red calls STORE with Flagged in add'] = async test => {
128
- let storeCalls = [];
129
- const ctx = {
130
- resolveRange: async range => range,
131
- run: async (command, range, flags, opts) => {
132
- storeCalls.push({ command, range, flags, operation: opts.operation });
133
- return true;
134
- }
135
- };
136
-
137
- let result = await ImapFlow.prototype.setFlagColor.call(ctx, '1:*', 'red');
138
-
139
- // red is index 0 (all bits zero), so add should contain only \Flagged,
140
- // and remove should contain all three MailFlagBit flags
141
- test.ok(storeCalls.length >= 1, 'run should be called at least once');
142
-
143
- let addCall = storeCalls.find(c => c.operation === 'add');
144
- test.ok(addCall, 'should have an add operation');
145
- test.ok(addCall.flags.indexOf('\\Flagged') >= 0, 'add flags should include \\Flagged for red');
146
-
147
- let removeCall = storeCalls.find(c => c.operation === 'remove');
148
- test.ok(removeCall, 'should have a remove operation');
149
- test.ok(removeCall.flags.indexOf('$MailFlagBit0') >= 0, 'remove flags should include $MailFlagBit0');
150
- test.ok(removeCall.flags.indexOf('$MailFlagBit1') >= 0, 'remove flags should include $MailFlagBit1');
151
- test.ok(removeCall.flags.indexOf('$MailFlagBit2') >= 0, 'remove flags should include $MailFlagBit2');
152
-
153
- test.ok(result, 'should return truthy result');
154
- test.done();
155
- };
156
-
157
- // ---------------------------------------------------------------------------
158
- // status test
159
- // ---------------------------------------------------------------------------
160
-
161
- module.exports['status delegates to run with STATUS command'] = async test => {
162
- let runArgs = null;
163
- const ctx = {
164
- run: async (...args) => {
165
- runArgs = args;
166
- return { messages: 10, unseen: 3 };
167
- }
168
- };
169
-
170
- let result = await ImapFlow.prototype.status.call(ctx, 'INBOX', { unseen: true });
171
-
172
- test.deepEqual(runArgs, ['STATUS', 'INBOX', { unseen: true }], 'run should be called with correct arguments');
173
- test.deepEqual(result, { messages: 10, unseen: 3 }, 'should return the result from run');
174
- test.done();
175
- };
176
-
177
- // ---------------------------------------------------------------------------
178
- // getQuota tests
179
- // ---------------------------------------------------------------------------
180
-
181
- module.exports['getQuota delegates to run with QUOTA command'] = async test => {
182
- let runArgs = null;
183
- const ctx = {
184
- run: async (...args) => {
185
- runArgs = args;
186
- return { storage: { usage: 1024, limit: 10240 } };
187
- }
188
- };
189
-
190
- let result = await ImapFlow.prototype.getQuota.call(ctx, 'Sent');
191
-
192
- test.deepEqual(runArgs, ['QUOTA', 'Sent'], 'run should be called with correct arguments');
193
- test.ok(result.storage, 'should return quota result');
194
- test.done();
195
- };
196
-
197
- module.exports['getQuota defaults path to INBOX'] = async test => {
198
- let runArgs = null;
199
- const ctx = {
200
- run: async (...args) => {
201
- runArgs = args;
202
- return { storage: { usage: 512, limit: 10240 } };
203
- }
204
- };
205
-
206
- let result = await ImapFlow.prototype.getQuota.call(ctx);
207
-
208
- test.deepEqual(runArgs, ['QUOTA', 'INBOX'], 'run should be called with INBOX as default path');
209
- test.ok(result.storage, 'should return quota result');
210
- test.done();
211
- };
212
-
213
- // ---------------------------------------------------------------------------
214
- // fetch generator: error propagation tests
215
- // ---------------------------------------------------------------------------
216
-
217
- module.exports['fetch generator: run error propagates to consumer'] = async test => {
218
- const run = async () => {
219
- throw new Error('FETCH failed');
220
- };
221
- const ctx = createFetchContext(run);
222
- let caught = false;
223
- try {
224
- // eslint-disable-next-line no-unused-vars
225
- for await (let msg of callFetch(ctx, '1:5', { uid: true })) {
226
- // should not reach here
227
- }
228
- } catch (err) {
229
- caught = true;
230
- test.equal(err.message, 'FETCH failed');
231
- }
232
- test.ok(caught, 'error was caught');
233
- test.done();
234
- };
235
-
236
- module.exports['fetch generator: socket destroyed mid-iteration throws EConnectionClosed'] = async test => {
237
- const run = async (cmd, range, query, opts) => {
238
- for (let i = 0; i < 3; i++) {
239
- await new Promise(resolve => {
240
- opts.onUntaggedFetch({ seq: i + 1, uid: 400 + i }, () => resolve());
241
- });
242
- }
243
- };
244
- const ctx = createFetchContext(run);
245
- let caught = false;
246
- try {
247
- for await (let msg of callFetch(ctx, '1:3', { uid: true })) {
248
- if (msg.seq === 1) {
249
- ctx.socket.destroyed = true;
250
- }
251
- }
252
- } catch (err) {
253
- caught = true;
254
- test.equal(err.code, 'EConnectionClosed');
255
- }
256
- test.ok(caught, 'error was caught');
257
- test.done();
258
- };
@@ -1,52 +0,0 @@
1
- # Live IMAP4rev2 integration tests
2
-
3
- Runs the ImapFlow client against a real IMAP4rev2 server - Dovecot 2.4+ in
4
- Docker - instead of protocol mocks. Covers the ENABLE IMAP4rev2 negotiation,
5
- LIST RETURN (SUBSCRIBED) without LSUB, subscription round-trips, UTF-8 mailbox
6
- names, inline LIST-STATUS (including the rev2 SIZE and DELETED status items),
7
- ESEARCH responses to plain SEARCH, STATUS SIZE/DELETED, the rev2-shaped SELECT
8
- response (untagged LIST, CLOSED on re-select), the folded-in FETCH BINARY,
9
- COPYUID from the untagged OK on MOVE, and a message lifecycle smoke test with
10
- UID EXPUNGE.
11
-
12
- ## Running
13
-
14
- ```
15
- npm run test:rev2
16
- ```
17
-
18
- Requires Docker. The script pulls `dovecot/dovecot:2.4.4`, starts a container
19
- with the drop-in config from `dovecot-test.conf`, waits for the IMAP greeting
20
- on `127.0.0.1:31143`, runs `rev2-live-test.js` with nodeunit, and always
21
- removes the container afterwards.
22
-
23
- These tests are intentionally not part of `npm test` - the Gruntfile nodeunit
24
- config excludes `test/integration/**`, so plain test runs stay Docker-free.
25
- CI runs this suite in a dedicated `test-rev2` job on `ubuntu-latest` (amd64
26
- with Docker preinstalled), forcing `IMAPFLOW_DOVECOT_PLATFORM=linux/amd64` -
27
- that job is the authoritative linux/amd64 run, since Apple Silicon machines
28
- cannot execute the amd64 image (see below).
29
-
30
- If a local image for the configured tag exists but was pulled for a different
31
- architecture than the Docker host (e.g. an amd64 image left behind on an arm64
32
- host), the runner script detects the mismatch and re-pulls the host-native
33
- variant before starting the container.
34
-
35
- ## Environment overrides
36
-
37
- - `IMAPFLOW_DOVECOT_IMAGE` - image to run (default `dovecot/dovecot:2.4.4`;
38
- any 2.4.2+ tag supports IMAP4rev2)
39
- - `IMAPFLOW_DOVECOT_PLATFORM` - e.g. `linux/amd64`; defaults to the host
40
- platform. Forcing `linux/amd64` on Apple Silicon does not work - Rosetta
41
- cannot start Dovecot's privilege-separated login processes
42
- (`rosetta error: mmap_anonymous_rw mmap failed`; reconfirmed with
43
- dovecot/dovecot:2.4.4 in July 2026)
44
- - `IMAPFLOW_TEST_PORT` - host port to publish (default 31143)
45
-
46
- ## Test account model
47
-
48
- The container uses Dovecot's static passdb (`USER_PASSWORD=pass`): any
49
- username authenticates with the password `pass` and gets its own empty mail
50
- home, so every test connects as a brand-new user and needs no cleanup between
51
- runs. Special-use mailboxes (Sent/Drafts/Junk/Trash) are auto-created and
52
- subscribed via `dovecot-test.conf`.
@@ -1,27 +0,0 @@
1
- # Drop-in configuration for the ImapFlow IMAP4rev2 integration test container.
2
- # Mounted into /etc/dovecot/conf.d/ of the dovecot/dovecot:2.4.x image, which
3
- # already ships with imap4rev2_enable = yes and mail_utf8_extensions = yes.
4
-
5
- # The test client connects over plain TCP on the container-internal IMAP port
6
- # (31143), so cleartext logins must be allowed
7
- auth_allow_cleartext = yes
8
-
9
- # Special-use mailboxes, auto-created and subscribed for every test user
10
- namespace inbox {
11
- mailbox Sent {
12
- special_use = \Sent
13
- auto = subscribe
14
- }
15
- mailbox Drafts {
16
- special_use = \Drafts
17
- auto = subscribe
18
- }
19
- mailbox Junk {
20
- special_use = \Junk
21
- auto = subscribe
22
- }
23
- mailbox Trash {
24
- special_use = \Trash
25
- auto = subscribe
26
- }
27
- }