imapflow 1.7.8 → 2.0.1

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 +520 -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 +3956 -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} +387 -517
  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 +60 -123
  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 +769 -1790
  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 -101
  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 -909
  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 -661
@@ -1,809 +0,0 @@
1
- /*eslint no-unused-expressions: 0, prefer-arrow-callback: 0 */
2
-
3
- 'use strict';
4
-
5
- const { parser, compiler } = require('../lib/handler/imap-handler');
6
-
7
- let asyncWrapper = (test, handler) => {
8
- handler(test)
9
- .then(() => test.done())
10
- .catch(err => {
11
- test.ifError(err);
12
- test.done();
13
- });
14
- };
15
-
16
- module.exports['IMAP Compiler: mixed'] = test =>
17
- asyncWrapper(test, async test => {
18
- const command =
19
- '* FETCH (ENVELOPE ("Mon, 2 Sep 2013 05:30:13 -0700 (PDT)" NIL ((NIL NIL "andris" "kreata.ee")) ((NIL NIL "andris" "kreata.ee")) ((NIL NIL "andris" "kreata.ee")) ((NIL NIL "andris" "tr.ee")) NIL NIL NIL "<-4730417346358914070@unknownmsgid>") BODYSTRUCTURE (("MESSAGE" "RFC822" NIL NIL NIL "7BIT" 105 (NIL NIL ((NIL NIL "andris" "kreata.ee")) ((NIL NIL "andris" "kreata.ee")) ((NIL NIL "andris" "kreata.ee")) ((NIL NIL "andris" "pangalink.net")) NIL NIL "<test1>" NIL) ("TEXT" "PLAIN" NIL NIL NIL "7BIT" 12 0 NIL NIL NIL) 5 NIL NIL NIL)("MESSAGE" "RFC822" NIL NIL NIL "7BIT" 83 (NIL NIL ((NIL NIL "andris" "kreata.ee")) ((NIL NIL "andris" "kreata.ee")) ((NIL NIL "andris" "kreata.ee")) ((NIL NIL "andris" "pangalink.net")) NIL NIL "NIL" NIL) ("TEXT" "PLAIN" NIL NIL NIL "7BIT" 12 0 NIL NIL NIL) 4 NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 19 0 NIL NIL NIL) "MIXED" ("BOUNDARY" "----mailcomposer-?=_1-1328088797399") NIL NIL))';
20
- const parsed = await parser(command, {
21
- allowUntagged: true
22
- });
23
- const compiled = (await compiler(parsed)).toString();
24
- test.equal(compiled, command);
25
- });
26
-
27
- module.exports['IMAP Compiler: no attributes'] = test =>
28
- asyncWrapper(test, async test =>
29
- test.equal(
30
- (
31
- await compiler({
32
- tag: '*',
33
- command: 'CMD'
34
- })
35
- ).toString(),
36
- '* CMD'
37
- )
38
- );
39
-
40
- module.exports['IMAP Compiler: TEXT'] = test =>
41
- asyncWrapper(test, async test =>
42
- test.equal(
43
- (
44
- await compiler({
45
- tag: '*',
46
- command: 'CMD',
47
- attributes: [
48
- {
49
- type: 'TEXT',
50
- value: 'Tere tere!'
51
- }
52
- ]
53
- })
54
- ).toString(),
55
- '* CMD Tere tere!'
56
- )
57
- );
58
-
59
- module.exports['IMAP Compiler: NUMBER attribute'] = test =>
60
- asyncWrapper(test, async test =>
61
- test.equal(
62
- (
63
- await compiler({
64
- tag: '*',
65
- command: 'CMD',
66
- attributes: [
67
- {
68
- type: 'NUMBER',
69
- value: 42
70
- }
71
- ]
72
- })
73
- ).toString(),
74
- '* CMD 42'
75
- )
76
- );
77
-
78
- module.exports['IMAP Compiler: NUMBER attribute defaults missing value to 0'] = test =>
79
- asyncWrapper(test, async test =>
80
- test.equal(
81
- (
82
- await compiler({
83
- tag: '*',
84
- command: 'CMD',
85
- attributes: [{ type: 'NUMBER' }]
86
- })
87
- ).toString(),
88
- '* CMD 0'
89
- )
90
- );
91
-
92
- module.exports['IMAP Compiler: unrecognized value type compiles to empty buffer'] = test =>
93
- asyncWrapper(test, async test => {
94
- // A SEQUENCE node whose value is neither string/number/Buffer falls through
95
- // formatRespEntry to an empty Buffer rather than throwing.
96
- let out = (
97
- await compiler({
98
- tag: '*',
99
- command: 'CMD',
100
- attributes: [{ type: 'SEQUENCE', value: { unexpected: true } }]
101
- })
102
- ).toString();
103
- test.equal(out, '* CMD ');
104
- });
105
-
106
- module.exports['IMAP Compiler: SECTION'] = test =>
107
- asyncWrapper(test, async test =>
108
- test.equal(
109
- (
110
- await compiler({
111
- tag: '*',
112
- command: 'CMD',
113
- attributes: [
114
- {
115
- type: 'SECTION',
116
- section: [
117
- {
118
- type: 'ATOM',
119
- value: 'ALERT'
120
- }
121
- ]
122
- }
123
- ]
124
- })
125
- ).toString(),
126
- '* CMD [ALERT]'
127
- )
128
- );
129
-
130
- module.exports['IMAP Compiler: escaped ATOM'] = test =>
131
- asyncWrapper(test, async test =>
132
- test.equal(
133
- (
134
- await compiler({
135
- tag: '*',
136
- command: 'CMD',
137
- attributes: [
138
- {
139
- type: 'ATOM',
140
- value: 'ALERT'
141
- },
142
- {
143
- type: 'ATOM',
144
- value: '\\ALERT'
145
- },
146
- {
147
- type: 'ATOM',
148
- value: 'NO ALERT'
149
- }
150
- ]
151
- })
152
- ).toString(),
153
- '* CMD ALERT \\ALERT "NO ALERT"'
154
- )
155
- );
156
-
157
- module.exports['IMAP Compiler: SEQUENCE'] = test =>
158
- asyncWrapper(test, async test =>
159
- test.equal(
160
- (
161
- await compiler({
162
- tag: '*',
163
- command: 'CMD',
164
- attributes: [
165
- {
166
- type: 'SEQUENCE',
167
- value: '*:4,5,6'
168
- }
169
- ]
170
- })
171
- ).toString(),
172
- '* CMD *:4,5,6'
173
- )
174
- );
175
-
176
- module.exports['IMAP Compiler: NIL'] = test =>
177
- asyncWrapper(test, async test =>
178
- test.equal(
179
- (
180
- await compiler({
181
- tag: '*',
182
- command: 'CMD',
183
- attributes: [null, null]
184
- })
185
- ).toString(),
186
- '* CMD NIL NIL'
187
- )
188
- );
189
-
190
- module.exports['IMAP Compiler: quoted TEXT'] = test =>
191
- asyncWrapper(test, async test =>
192
- test.equal(
193
- (
194
- await compiler({
195
- tag: '*',
196
- command: 'CMD',
197
- attributes: [
198
- {
199
- type: 'String',
200
- value: 'Tere tere!',
201
- sensitive: true
202
- },
203
- 'Vana kere'
204
- ]
205
- })
206
- ).toString(),
207
- '* CMD "Tere tere!" "Vana kere"'
208
- )
209
- );
210
-
211
- module.exports['IMAP Compiler: keep short strings'] = test =>
212
- asyncWrapper(test, async test =>
213
- test.equal(
214
- (
215
- await compiler(
216
- {
217
- tag: '*',
218
- command: 'CMD',
219
- attributes: [
220
- {
221
- type: 'String',
222
- value: 'Tere tere!'
223
- },
224
- 'Vana kere'
225
- ]
226
- },
227
- { asArray: false, isLogging: true }
228
- )
229
- ).toString(),
230
- '* CMD "Tere tere!" "Vana kere"'
231
- )
232
- );
233
-
234
- module.exports['IMAP Compiler: hide sensitive strings'] = test =>
235
- asyncWrapper(test, async test =>
236
- test.equal(
237
- (
238
- await compiler(
239
- {
240
- tag: '*',
241
- command: 'CMD',
242
- attributes: [
243
- {
244
- type: 'String',
245
- value: 'Tere tere!',
246
- sensitive: true
247
- },
248
- 'Vana kere'
249
- ]
250
- },
251
- { asArray: false, isLogging: true }
252
- )
253
- ).toString(),
254
- '* CMD "(* value hidden *)" "Vana kere"'
255
- )
256
- );
257
-
258
- module.exports['IMAP Compiler: hide long strings'] = test =>
259
- asyncWrapper(test, async test =>
260
- test.equal(
261
- (
262
- await compiler(
263
- {
264
- tag: '*',
265
- command: 'CMD',
266
- attributes: [
267
- {
268
- type: 'String',
269
- value: 'Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere! Tere tere!'
270
- },
271
- 'Vana kere'
272
- ]
273
- },
274
- { asArray: false, isLogging: true }
275
- )
276
- ).toString(),
277
- '* CMD "(* 219B string *)" "Vana kere"'
278
- )
279
- );
280
-
281
- module.exports['IMAP Compiler: no command'] = test =>
282
- asyncWrapper(test, async test =>
283
- test.equal(
284
- (
285
- await compiler({
286
- tag: '*',
287
- attributes: [
288
- 1,
289
- {
290
- type: 'ATOM',
291
- value: 'EXPUNGE'
292
- }
293
- ]
294
- })
295
- ).toString(),
296
- '* 1 EXPUNGE'
297
- )
298
- );
299
-
300
- module.exports['IMAP Compiler: LITERAL text'] = test =>
301
- asyncWrapper(test, async test =>
302
- test.equal(
303
- (
304
- await compiler({
305
- tag: '*',
306
- command: 'CMD',
307
- attributes: [
308
- // keep indentation
309
- {
310
- type: 'LITERAL',
311
- value: 'Tere tere!'
312
- },
313
- 'Vana kere'
314
- ]
315
- })
316
- ).toString(),
317
- '* CMD {10}\r\nTere tere! "Vana kere"'
318
- )
319
- );
320
-
321
- module.exports['IMAP Compiler: LITERAL literal'] = test =>
322
- asyncWrapper(test, async test =>
323
- test.equal(
324
- (
325
- await compiler({
326
- tag: '*',
327
- command: 'CMD',
328
- attributes: [
329
- // keep indentation
330
- {
331
- type: 'LITERAL',
332
- value: 'Tere\x00 tere!',
333
- isLiteral8: false
334
- },
335
- 'Vana kere'
336
- ]
337
- })
338
- ).toString(),
339
- '* CMD {11}\r\nTere\x00 tere! "Vana kere"'
340
- )
341
- );
342
-
343
- module.exports['IMAP Compiler: LITERAL literal8'] = test =>
344
- asyncWrapper(test, async test =>
345
- test.equal(
346
- (
347
- await compiler({
348
- tag: '*',
349
- command: 'CMD',
350
- attributes: [
351
- // keep indentation
352
- {
353
- type: 'LITERAL',
354
- value: 'Tere\x00 tere!',
355
- isLiteral8: true
356
- },
357
- 'Vana kere'
358
- ]
359
- })
360
- ).toString(),
361
- '* CMD ~{11}\r\nTere\x00 tere! "Vana kere"'
362
- )
363
- );
364
-
365
- // RFC 7888 (folded into IMAP4rev2): with LITERAL- a non-synchronizing literal
366
- // marker {n+} is only allowed for literals of up to 4096 bytes - anything larger
367
- // must use the synchronizing {n} form, in every compilation mode
368
- module.exports['IMAP Compiler: LITERAL- oversized literal stays synchronizing in single-buffer mode'] = test =>
369
- asyncWrapper(test, async test => {
370
- let payload = 'a'.repeat(4097);
371
- let compiled = (
372
- await compiler(
373
- {
374
- tag: '*',
375
- command: 'CMD',
376
- attributes: [
377
- {
378
- type: 'LITERAL',
379
- value: payload
380
- }
381
- ]
382
- },
383
- { literalMinus: true }
384
- )
385
- ).toString();
386
- test.ok(compiled.startsWith('* CMD {4097}\r\n'), `must not use a non-synchronizing marker: ${compiled.slice(0, 20)}`);
387
- });
388
-
389
- module.exports['IMAP Compiler: LITERAL- boundary: exactly 4096 bytes is non-synchronizing'] = test =>
390
- asyncWrapper(test, async test => {
391
- let payload = 'a'.repeat(4096);
392
- let parts = (
393
- await compiler(
394
- {
395
- tag: '*',
396
- command: 'CMD',
397
- attributes: [
398
- {
399
- type: 'LITERAL',
400
- value: payload
401
- }
402
- ]
403
- },
404
- { asArray: true, literalMinus: true }
405
- )
406
- ).map(entry => entry.toString());
407
- test.equal(parts.length, 1, 'literal must be appended inline without a continuation break');
408
- test.ok(parts[0].startsWith('* CMD {4096+}\r\n'));
409
- });
410
-
411
- module.exports['IMAP Compiler: LITERAL- boundary: 4097 bytes falls back to synchronizing'] = test =>
412
- asyncWrapper(test, async test => {
413
- let payload = 'a'.repeat(4097);
414
- let parts = (
415
- await compiler(
416
- {
417
- tag: '*',
418
- command: 'CMD',
419
- attributes: [
420
- {
421
- type: 'LITERAL',
422
- value: payload
423
- }
424
- ]
425
- },
426
- { asArray: true, literalMinus: true }
427
- )
428
- ).map(entry => entry.toString());
429
- test.equal(parts.length, 2, 'literal must wait for a continuation response');
430
- test.ok(parts[0].endsWith('{4097}\r\n'), `marker must be synchronizing: ${parts[0].slice(-12)}`);
431
- });
432
-
433
- // The literal size marker counts octets, not UTF-16 code units - a unicode string
434
- // value must declare its UTF-8 byte length
435
- module.exports['IMAP Compiler: LITERAL declares byte length for unicode string values'] = test =>
436
- asyncWrapper(test, async test => {
437
- let compiled = await compiler({
438
- tag: '*',
439
- command: 'CMD',
440
- attributes: [
441
- {
442
- type: 'LITERAL',
443
- value: 'Sõnumid'
444
- }
445
- ]
446
- });
447
- test.equal(compiled.toString(), '* CMD {8}\r\nSõnumid');
448
- });
449
-
450
- module.exports['IMAP Compiler: LITERAL array 1'] = test =>
451
- asyncWrapper(test, async test =>
452
- test.deepEqual(
453
- (
454
- await compiler(
455
- {
456
- tag: '*',
457
- command: 'CMD',
458
- attributes: [
459
- {
460
- type: 'LITERAL',
461
- value: 'Tere tere!'
462
- },
463
- {
464
- type: 'LITERAL',
465
- value: 'Vana kere'
466
- }
467
- ]
468
- },
469
- { asArray: true }
470
- )
471
- ).map(entry => entry.toString()),
472
- ['* CMD {10}\r\n', 'Tere tere! {9}\r\n', 'Vana kere']
473
- )
474
- );
475
-
476
- module.exports['IMAP Compiler: LITERAL array 2'] = test =>
477
- asyncWrapper(test, async test =>
478
- test.deepEqual(
479
- (
480
- await compiler(
481
- {
482
- tag: '*',
483
- command: 'CMD',
484
- attributes: [
485
- {
486
- type: 'LITERAL',
487
- value: 'Tere tere!'
488
- },
489
- {
490
- type: 'LITERAL',
491
- value: 'Vana kere'
492
- },
493
- 'zzz'
494
- ]
495
- },
496
- { asArray: true }
497
- )
498
- ).map(entry => entry.toString()),
499
- ['* CMD {10}\r\n', 'Tere tere! {9}\r\n', 'Vana kere "zzz"']
500
- )
501
- );
502
-
503
- module.exports['IMAP Compiler: LITERALPLUS array'] = test =>
504
- asyncWrapper(test, async test =>
505
- test.deepEqual(
506
- (
507
- await compiler(
508
- {
509
- tag: '*',
510
- command: 'CMD',
511
- attributes: [
512
- {
513
- type: 'LITERAL',
514
- value: 'Tere tere!'
515
- },
516
- {
517
- type: 'LITERAL',
518
- value: 'Vana kere'
519
- },
520
- 'zzz'
521
- ]
522
- },
523
- { asArray: true, literalPlus: true }
524
- )
525
- ).map(entry => entry.toString()),
526
- ['* CMD {10+}\r\nTere tere! {9+}\r\nVana kere "zzz"']
527
- )
528
- );
529
-
530
- module.exports['IMAP Compiler: LITERAL array without tag/command'] = test =>
531
- asyncWrapper(test, async test =>
532
- test.deepEqual(
533
- (
534
- await compiler(
535
- {
536
- attributes: [
537
- {
538
- type: 'LITERAL',
539
- value: 'Tere tere!'
540
- },
541
- {
542
- type: 'LITERAL',
543
- value: 'Vana kere'
544
- }
545
- ]
546
- },
547
- { asArray: true }
548
- )
549
- ).map(entry => entry.toString()),
550
- ['{10}\r\n', 'Tere tere! {9}\r\n', 'Vana kere']
551
- )
552
- );
553
-
554
- module.exports['IMAP Compiler: LITERAL byte length'] = test =>
555
- asyncWrapper(test, async test =>
556
- test.deepEqual(
557
- (
558
- await compiler(
559
- {
560
- tag: '*',
561
- command: 'CMD',
562
- attributes: [
563
- {
564
- type: 'LITERAL',
565
- value: 'Tere tere!'
566
- },
567
- 'Vana kere'
568
- ]
569
- },
570
- { asArray: false, isLogging: true }
571
- )
572
- ).toString(),
573
- '* CMD "(* 10B literal *)" "Vana kere"'
574
- )
575
- );
576
-
577
- module.exports['IMAP Compiler: MongoDB binary object is unwrapped'] = test =>
578
- asyncWrapper(test, async test => {
579
- // Create a non-Buffer object with a .buffer property (simulates MongoDB Binary)
580
- const mongoBinary = { buffer: Buffer.from('test data') };
581
- const compiled = (
582
- await compiler({
583
- tag: '*',
584
- command: 'CMD',
585
- attributes: [mongoBinary]
586
- })
587
- ).toString();
588
- // The buffer content should appear in the output (unwrapped from the MongoDB binary wrapper)
589
- test.ok(compiled.includes('test data'), 'should contain the buffer content after unwrapping');
590
- test.ok(compiled.includes('CMD'), 'should contain command');
591
- });
592
-
593
- module.exports['IMAP Compiler: long string truncated in logging mode'] = test =>
594
- asyncWrapper(test, async test => {
595
- const longString = 'x'.repeat(150);
596
- const compiled = (
597
- await compiler(
598
- {
599
- tag: '*',
600
- command: 'CMD',
601
- attributes: [longString]
602
- },
603
- { asArray: false, isLogging: true }
604
- )
605
- ).toString();
606
- test.ok(compiled.includes('150B string'), 'should show byte count instead of content');
607
- test.ok(!compiled.includes('x'.repeat(150)), 'should not contain the full string');
608
- });
609
-
610
- module.exports['IMAP Compiler: literal truncated in logging mode'] = test =>
611
- asyncWrapper(test, async test => {
612
- const compiled = (
613
- await compiler(
614
- {
615
- tag: '*',
616
- command: 'CMD',
617
- attributes: [{ type: 'LITERAL', value: Buffer.alloc(200) }]
618
- },
619
- { asArray: false, isLogging: true }
620
- )
621
- ).toString();
622
- test.ok(compiled.includes('200B literal'), 'should show byte count for literal');
623
- });
624
-
625
- module.exports['IMAP Compiler: partial range in SECTION'] = test =>
626
- asyncWrapper(test, async test => {
627
- const compiled = (
628
- await compiler({
629
- tag: '*',
630
- command: 'CMD',
631
- attributes: [
632
- {
633
- type: 'SECTION',
634
- section: [{ type: 'ATOM', value: 'BODY' }],
635
- partial: [0, 50]
636
- }
637
- ]
638
- })
639
- ).toString();
640
- test.ok(compiled.includes('<0.50>'), 'should contain partial range <0.50>');
641
- });
642
-
643
- // Returns the error a compile attempt raised, or undefined when it succeeded.
644
- // Several cases below assert that a token can never reach the wire, and the
645
- // try/catch is the only interesting part of each. Takes either an attributes
646
- // array (compiled under a default tag/command) or a full response object.
647
- const compileError = async input => {
648
- try {
649
- await compiler(Array.isArray(input) ? { tag: 'A', command: 'CMD', attributes: input } : input);
650
- } catch (err) {
651
- return err;
652
- }
653
- };
654
-
655
- module.exports['IMAP Compiler: SEQUENCE rejects values that are not sequence sets'] = test =>
656
- asyncWrapper(test, async test => {
657
- // Sequence sets are written verbatim, so a range string that reached the
658
- // compiler unvalidated would put a second command on the wire
659
- for (let value of ['1\r\nZZ1 LOGOUT', '1 2', '1;2', 'ALL', '1:2)', '1\t2', Buffer.from('1\r\nZZ NOOP')]) {
660
- let err = await compileError([{ type: 'SEQUENCE', value }]);
661
- test.equal(err && err.code, 'InvalidSequenceSet', `${JSON.stringify(value.toString())} must be rejected`);
662
- }
663
- });
664
-
665
- module.exports['IMAP Compiler: SEQUENCE accepts every valid sequence-set form'] = test =>
666
- asyncWrapper(test, async test => {
667
- // '$' is the RFC 5182 SEARCHRES saved-result marker, valid as the entire set
668
- for (let value of ['1', '*', '1:*', '*:1', '1,3,5', '1:3,7,9:*', '4294967295', '$']) {
669
- const compiled = (await compiler({ tag: 'A', command: 'FETCH', attributes: [{ type: 'SEQUENCE', value }] })).toString();
670
- test.equal(compiled, `A FETCH ${value}`);
671
- }
672
-
673
- // '$' only stands for the whole set, never an element of one
674
- for (let value of ['$,1', '1,$', '$:2']) {
675
- let err = await compileError([{ type: 'SEQUENCE', value }]);
676
- test.equal(err && err.code, 'InvalidSequenceSet', `${JSON.stringify(value)} must be rejected`);
677
- }
678
- });
679
-
680
- module.exports['IMAP Compiler: SEQUENCE validation stays linear on huge valid sets'] = test =>
681
- asyncWrapper(test, async test => {
682
- // The regex this replaced overflowed the engine's backtrack stack with an
683
- // uncoded RangeError at roughly a million comma-separated elements - a set
684
- // a fetch over a large mailbox can legitimately produce
685
- const huge = Array.from({ length: 1500000 }, (_, i) => i + 1).join(',');
686
- const compiled = await compiler({ tag: 'A', command: 'FETCH', attributes: [{ type: 'SEQUENCE', value: huge }] });
687
- test.ok(compiled.toString().endsWith(huge), 'the huge but valid set must compile');
688
- });
689
-
690
- module.exports['IMAP Compiler: SEQUENCE validation is skipped when logging'] = test =>
691
- asyncWrapper(test, async test => {
692
- // The incoming token parser accepts sequence-shaped tokens the strict grammar
693
- // rejects (an ESEARCH set like "1:2:3", a folder name like "12:30:00"). Every
694
- // parsed server response is re-compiled for the log, so a throw here would let
695
- // one quirky server line kill the whole connection.
696
- for (let value of ['1:2:3', '12:30:00', '1,2:3:4']) {
697
- const compiled = (await compiler({ tag: '*', command: 'OK', attributes: [{ type: 'SEQUENCE', value }] }, { isLogging: true })).toString();
698
- test.equal(compiled, `* OK ${value}`, 'logging re-compile must pass the token through');
699
- }
700
- });
701
-
702
- module.exports['IMAP Compiler: tag and command cannot carry a line terminator'] = test =>
703
- asyncWrapper(test, async test => {
704
- // The tag/command preamble is written verbatim ahead of any token, so it goes
705
- // through the same choke point as everything else
706
- let err = await compileError({ tag: 'A\r\nZZ LOGOUT', command: 'NOOP' });
707
- test.equal(err && err.code, 'InvalidTokenValue', 'CRLF in the tag must be rejected');
708
-
709
- err = await compileError({ tag: 'A', command: 'NOOP\r\nZZ LOGOUT' });
710
- test.equal(err && err.code, 'InvalidTokenValue', 'CRLF in the command must be rejected');
711
- });
712
-
713
- module.exports['IMAP Compiler: quoted strings use IMAP escaping, not JSON escaping'] = test =>
714
- asyncWrapper(test, async test => {
715
- // Only DQUOTE and backslash have escapes in the IMAP grammar - a tab must
716
- // survive as a raw byte rather than becoming a literal backslash-t
717
- const compiled = (await compiler({ tag: 'A', command: 'CMD', attributes: [{ type: 'STRING', value: 'a\tb"c\\d' }] })).toString();
718
- test.equal(compiled, 'A CMD "a\tb\\"c\\\\d"');
719
- });
720
-
721
- module.exports['IMAP Compiler: quoted strings reject CR, LF and NUL'] = test =>
722
- asyncWrapper(test, async test => {
723
- for (let value of ['a\rb', 'a\nb', 'a\0b']) {
724
- let err = await compileError([{ type: 'STRING', value }]);
725
- test.equal(err && err.code, 'InvalidStringValue', `${JSON.stringify(value)} cannot be sent as a quoted string`);
726
- }
727
- });
728
-
729
- module.exports['IMAP Compiler: a mailbox name with CRLF cannot reach the wire'] = test =>
730
- asyncWrapper(test, async test => {
731
- // Paths travel as ATOM tokens and get quoted when they fall outside ATOM-CHAR
732
- let err = await compileError([{ type: 'ATOM', value: 'INBOX\r\nZZ LOGOUT' }]);
733
- test.equal(err && err.code, 'InvalidStringValue');
734
- });
735
-
736
- module.exports['IMAP Compiler: response text cannot carry a line terminator'] = test =>
737
- asyncWrapper(test, async test => {
738
- // TEXT is written verbatim as well, so it gets the same guarantee even though
739
- // only the parser produces it today
740
- let err = await compileError([{ type: 'TEXT', value: 'oops\r\nZZ NOOP' }]);
741
- test.equal(err && err.code, 'InvalidTextValue');
742
- });
743
-
744
- module.exports['IMAP Compiler: NUMBER coerces its value instead of writing it through'] = test =>
745
- asyncWrapper(test, async test => {
746
- const compiled = (await compiler({ tag: 'A', command: 'CMD', attributes: [{ type: 'NUMBER', value: '1\r\nZZ NOOP' }] })).toString();
747
- test.equal(compiled, 'A CMD 0', 'a non-numeric value must not reach the wire verbatim');
748
- test.equal((await compiler({ tag: 'A', command: 'CMD', attributes: [{ type: 'NUMBER', value: '42' }] })).toString(), 'A CMD 42');
749
- });
750
-
751
- module.exports['IMAP Compiler: numeric tokens clamp to bounded non-negative integers'] = test =>
752
- asyncWrapper(test, async test => {
753
- // Infinity, negatives and unsafe magnitudes would otherwise emit bytes outside
754
- // the number alphabet ("Infinity", "-5", "1e+300") and get a server BAD blamed
755
- // on the server instead of the caller
756
- for (let [value, expected] of [
757
- [Infinity, '0'],
758
- ['Infinity', '0'],
759
- [-5, '0'],
760
- ['2e21', '0'],
761
- [4.6, '5']
762
- ]) {
763
- const compiled = (await compiler({ tag: 'A', command: 'CMD', attributes: [{ type: 'NUMBER', value }] })).toString();
764
- test.equal(compiled, `A CMD ${expected}`, `${JSON.stringify(value)} must clamp to ${expected}`);
765
- }
766
- });
767
-
768
- module.exports['IMAP Compiler: partial range coerces its elements'] = test =>
769
- asyncWrapper(test, async test => {
770
- // The partial range is the last token component written verbatim, so it is
771
- // coerced rather than joined as-is
772
- const attributes = [{ type: 'ATOM', value: 'BODY.PEEK', section: [], partial: ['0>\r\nZZ NOOP'] }];
773
- const compiled = (await compiler({ tag: 'A', command: 'FETCH', attributes })).toString();
774
- test.equal(compiled, 'A FETCH BODY.PEEK[]<0>');
775
-
776
- const normal = (
777
- await compiler({ tag: 'A', command: 'FETCH', attributes: [{ type: 'ATOM', value: 'BODY.PEEK', section: [], partial: [0, 1024] }] })
778
- ).toString();
779
- test.equal(normal, 'A FETCH BODY.PEEK[]<0.1024>');
780
-
781
- // Floats and unbounded numerics clamp the same way NUMBER tokens do, so no
782
- // non-digit byte ("512.5", "Infinity") can appear inside the partial range
783
- const float = (
784
- await compiler({ tag: 'A', command: 'FETCH', attributes: [{ type: 'ATOM', value: 'BODY.PEEK', section: [], partial: [0, 512.5] }] })
785
- ).toString();
786
- test.equal(float, 'A FETCH BODY.PEEK[]<0.513>');
787
-
788
- const unbounded = (
789
- await compiler({ tag: 'A', command: 'FETCH', attributes: [{ type: 'ATOM', value: 'BODY.PEEK', section: [], partial: ['Infinity', '2e21'] }] })
790
- ).toString();
791
- test.equal(unbounded, 'A FETCH BODY.PEEK[]<0.0>');
792
- });
793
-
794
- module.exports['IMAP Compiler: logging output never throws on unsendable values'] = test =>
795
- asyncWrapper(test, async test => {
796
- // The logging pass must survive whatever the wire pass refuses, so a rejected
797
- // command can still be logged
798
- const compiled = (
799
- await compiler(
800
- {
801
- tag: 'A',
802
- command: 'LOGIN',
803
- attributes: [{ type: 'STRING', value: 'a\r\nb' }]
804
- },
805
- { isLogging: true }
806
- )
807
- ).toString();
808
- test.ok(compiled.includes('\\r\\n'), 'control characters stay escaped for the log');
809
- });