imapflow 1.7.8 → 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 +20 -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 +761 -1789
  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,16 +1,12 @@
1
1
  /* eslint no-control-regex:0 */
2
-
3
- 'use strict';
4
-
5
- const { formatDate, formatFlag, canUseFlag, toValidDate, isRev2Active } = require('./tools.js');
6
-
2
+ import { formatDate, formatFlag, canUseFlag, toValidDate, isRev2Active } from './tools.js';
7
3
  /**
8
4
  * Sets a boolean flag in the IMAP search attributes.
9
5
  * Automatically handles UN- prefixing for falsy values.
10
6
  *
11
- * @param {Array} attributes - Array to append the attribute to
12
- * @param {string} term - The flag name (e.g., 'SEEN', 'DELETED')
13
- * @param {boolean} value - Whether to set or unset the flag
7
+ * @param attributes - Array to append the attribute to
8
+ * @param term - The flag name (e.g., 'SEEN', 'DELETED')
9
+ * @param value - Whether to set or unset the flag
14
10
  * @example
15
11
  * setBoolOpt(attributes, 'SEEN', false) // Adds 'UNSEEN'
16
12
  * setBoolOpt(attributes, 'UNSEEN', false) // Adds 'SEEN' (removes UN prefix)
@@ -21,114 +17,99 @@ let setBoolOpt = (attributes, term, value) => {
21
17
  if (/^un/i.test(term)) {
22
18
  // Remove existing UN prefix
23
19
  term = term.slice(2);
24
- } else {
20
+ }
21
+ else {
25
22
  // Add UN prefix
26
23
  term = 'UN' + term;
27
24
  }
28
25
  }
29
-
30
26
  attributes.push({ type: 'ATOM', value: term.toUpperCase() });
31
27
  };
32
-
33
28
  /**
34
29
  * Normalizes a user-supplied sequence set (string, number, bigint, or an array of
35
30
  * them) into the single string value of a SEQUENCE token. An array is one
36
31
  * comma-joined set: separate tokens would be parsed by the server as extra
37
32
  * sequence-number search keys ANDed to the query, not as part of the set.
38
33
  *
39
- * @param {*} value - The sequence set value(s)
40
- * @returns {string} The joined sequence set string
34
+ * @param value - The sequence set value(s)
35
+ * @returns The joined sequence set string
41
36
  */
42
- let toSequenceValue = value => [].concat(value).join(',');
43
-
37
+ let toSequenceValue = (value) => [].concat(value).join(',');
44
38
  /**
45
39
  * Adds a search option with its value(s) to the attributes array.
46
40
  * Handles NOT operations and array values.
47
41
  *
48
- * @param {Array} attributes - Array to append the attribute to
49
- * @param {string} term - The search term (e.g., 'FROM', 'SUBJECT')
50
- * @param {*} value - The value for the search term (string, array, or falsy for NOT)
42
+ * @param attributes - Array to append the attribute to
43
+ * @param term - The search term (e.g., 'FROM', 'SUBJECT')
44
+ * @param value - The value for the search term (string, array, or falsy for NOT)
51
45
  */
52
46
  let setOpt = (attributes, term, value) => {
53
47
  // Handle NOT operations for false or null values
54
48
  if (value === false || value === null) {
55
49
  attributes.push({ type: 'ATOM', value: 'NOT' });
56
50
  }
57
-
58
51
  attributes.push({ type: 'ATOM', value: term.toUpperCase() });
59
-
60
52
  // Handle array values (e.g. HEADER name/value pairs)
61
53
  if (Array.isArray(value)) {
62
54
  value.forEach(entry => attributes.push({ type: 'ATOM', value: (entry || '').toString() }));
63
- } else {
55
+ }
56
+ else {
64
57
  attributes.push({ type: 'ATOM', value: value.toString() });
65
58
  }
66
59
  };
67
-
68
60
  /**
69
61
  * Processes date fields for IMAP search.
70
62
  * Converts JavaScript dates to IMAP date format.
71
63
  *
72
- * @param {Array} attributes - Array to append the attribute to
73
- * @param {string} term - The date search term (e.g., 'BEFORE', 'SINCE')
74
- * @param {Date|String} value - Date value to format
64
+ * @param attributes - Array to append the attribute to
65
+ * @param term - The date search term (e.g., 'BEFORE', 'SINCE')
66
+ * @param value - Date value to format
75
67
  */
76
68
  let processDateField = (attributes, term, value) => {
77
69
  // Normalize first. A Date brand check is not enough on its own: an invalid
78
70
  // Date is still a Date and toISOString() throws on it. Normalizing here also
79
71
  // means a date string behaves exactly like the equivalent Date object.
80
- value = toValidDate(value);
81
- if (!value) {
72
+ let date = toValidDate(value);
73
+ if (!date) {
82
74
  return;
83
75
  }
84
-
85
- if (['BEFORE', 'SENTBEFORE'].includes(term.toUpperCase()) && value.toISOString().substring(11) !== '00:00:00.000Z') {
76
+ if (['BEFORE', 'SENTBEFORE'].includes(term.toUpperCase()) && date.toISOString().substring(11) !== '00:00:00.000Z') {
86
77
  // Set to next day to include current day as well, othwerise BEFORE+AFTER
87
78
  // searches for the same day but different time values do not match anything
88
- value = new Date(value.getTime() + 24 * 3600 * 1000);
79
+ date = new Date(date.getTime() + 24 * 3600 * 1000);
89
80
  }
90
-
91
81
  // Still reachable after the guard above: the +24h shift can push a near-max
92
82
  // Date past the representable range
93
- let date = formatDate(value);
94
- if (!date) {
83
+ let formatted = formatDate(date);
84
+ if (!formatted) {
95
85
  return;
96
86
  }
97
-
98
- setOpt(attributes, term, date);
87
+ setOpt(attributes, term, formatted);
99
88
  };
100
-
101
89
  // Pre-compiled regex for better performance
102
90
  const UNICODE_PATTERN = /[^\x00-\x7F]/;
103
-
104
91
  /**
105
92
  * Checks if a string contains Unicode characters.
106
93
  * Used to determine if CHARSET UTF-8 needs to be specified.
107
94
  *
108
- * @param {*} str - String to check
109
- * @returns {boolean} True if string contains non-ASCII characters
95
+ * @param str - String to check
96
+ * @returns True if string contains non-ASCII characters
110
97
  */
111
- let isUnicodeString = str => {
98
+ let isUnicodeString = (str) => {
112
99
  if (!str || typeof str !== 'string') {
113
100
  return false;
114
101
  }
115
-
116
102
  // Regex test is ~3-5x faster than Buffer.byteLength
117
103
  // Matches any character outside ASCII range (0x00-0x7F)
118
104
  return UNICODE_PATTERN.test(str);
119
105
  };
120
-
121
106
  /**
122
107
  * Compiles a JavaScript object query into IMAP search command attributes.
123
108
  * Supports standard IMAP search criteria and extensions like OBJECTID and Gmail extensions.
124
109
  *
125
- * @param {Object} connection - IMAP connection object
126
- * @param {Map} connection.capabilities - Map of server capabilities
127
- * @param {Set} connection.enabled - Set of enabled extensions
128
- * @param {Object} connection.mailbox - Current mailbox information
129
- * @param {Set} connection.mailbox.flags - Available flags in the mailbox
130
- * @param {Object} query - Search query object
131
- * @returns {Array} Array of IMAP search attributes
110
+ * @param connection - IMAP connection object (capabilities, enabled extensions and the current mailbox are read)
111
+ * @param query - Search query object
112
+ * @returns Array of IMAP search attributes
132
113
  * @throws {Error} When required server extensions are not available
133
114
  *
134
115
  * @example
@@ -148,29 +129,26 @@ let isUnicodeString = str => {
148
129
  * since: new Date('2024-01-01')
149
130
  * });
150
131
  */
151
- module.exports.searchCompiler = (connection, query) => {
132
+ export const searchCompiler = (connection, query) => {
152
133
  const attributes = [];
153
-
154
134
  // Track if we need to specify UTF-8 charset
155
135
  let hasUnicode = false;
156
136
  const mailbox = connection.mailbox;
157
-
158
137
  /**
159
138
  * Recursively walks through the query object and builds IMAP attributes.
160
- * @param {Object} params - Query parameters to process
139
+ * @param params - Query parameters to process
161
140
  */
162
- const walk = params => {
141
+ const walk = (params) => {
163
142
  // Walks a query object and wraps the resulting attributes in a
164
143
  // sub-array so the IMAP compiler emits parentheses around them.
165
144
  // Used when a single search-key is required (NOT, OR operands)
166
145
  // but the condition has multiple keys (RFC 3501 Section 6.4.4).
167
- let walkGrouped = obj => {
146
+ let walkGrouped = (obj) => {
168
147
  let startIdx = attributes.length;
169
148
  walk(obj);
170
149
  let subAttrs = attributes.splice(startIdx);
171
150
  attributes.push(subAttrs);
172
151
  };
173
-
174
152
  Object.keys(params || {}).forEach(term => {
175
153
  switch (term.toUpperCase()) {
176
154
  // Custom sequence range support (non-standard)
@@ -186,7 +164,6 @@ module.exports.searchCompiler = (connection, query) => {
186
164
  }
187
165
  }
188
166
  break;
189
-
190
167
  // Boolean flags that support UN- prefixing
191
168
  case 'ANSWERED':
192
169
  case 'DELETED':
@@ -201,14 +178,12 @@ module.exports.searchCompiler = (connection, query) => {
201
178
  // toggles UN-prefix for falsy values
202
179
  setBoolOpt(attributes, term, !!params[term]);
203
180
  break;
204
-
205
181
  // Simple boolean flags without UN- support
206
182
  case 'ALL':
207
183
  if (params[term]) {
208
184
  setBoolOpt(attributes, term, true);
209
185
  }
210
186
  break;
211
-
212
187
  case 'NEW':
213
188
  case 'OLD':
214
189
  case 'RECENT':
@@ -225,7 +200,6 @@ module.exports.searchCompiler = (connection, query) => {
225
200
  setBoolOpt(attributes, term, true);
226
201
  }
227
202
  break;
228
-
229
203
  // Numeric comparisons
230
204
  case 'LARGER':
231
205
  case 'SMALLER':
@@ -234,7 +208,6 @@ module.exports.searchCompiler = (connection, query) => {
234
208
  setOpt(attributes, term, params[term]);
235
209
  }
236
210
  break;
237
-
238
211
  // Text search fields - check for Unicode
239
212
  case 'BCC':
240
213
  case 'BODY':
@@ -250,7 +223,6 @@ module.exports.searchCompiler = (connection, query) => {
250
223
  setOpt(attributes, term, params[term]);
251
224
  }
252
225
  break;
253
-
254
226
  // UID sequences. The key stays an ATOM and only the value is a
255
227
  // SEQUENCE token, so the compiler validates the sequence set
256
228
  // itself rather than the "UID" keyword in front of it.
@@ -260,27 +232,26 @@ module.exports.searchCompiler = (connection, query) => {
260
232
  attributes.push({ type: 'SEQUENCE', value: toSequenceValue(params[term]) });
261
233
  }
262
234
  break;
263
-
264
235
  // Email ID support (OBJECTID or Gmail extension)
265
236
  case 'EMAILID':
266
237
  if (connection.capabilities.has('OBJECTID')) {
267
238
  setOpt(attributes, 'EMAILID', params[term]);
268
- } else if (connection.capabilities.has('X-GM-EXT-1')) {
239
+ }
240
+ else if (connection.capabilities.has('X-GM-EXT-1')) {
269
241
  // Fallback to Gmail message ID
270
242
  setOpt(attributes, 'X-GM-MSGID', params[term]);
271
243
  }
272
244
  break;
273
-
274
245
  // Thread ID support (OBJECTID or Gmail extension)
275
246
  case 'THREADID':
276
247
  if (connection.capabilities.has('OBJECTID')) {
277
248
  setOpt(attributes, 'THREADID', params[term]);
278
- } else if (connection.capabilities.has('X-GM-EXT-1')) {
249
+ }
250
+ else if (connection.capabilities.has('X-GM-EXT-1')) {
279
251
  // Fallback to Gmail thread ID
280
252
  setOpt(attributes, 'X-GM-THRID', params[term]);
281
253
  }
282
254
  break;
283
-
284
255
  // Gmail raw search
285
256
  case 'GMRAW':
286
257
  case 'GMAILRAW': // alias for GMRAW
@@ -289,13 +260,13 @@ module.exports.searchCompiler = (connection, query) => {
289
260
  hasUnicode = true;
290
261
  }
291
262
  setOpt(attributes, 'X-GM-RAW', params[term]);
292
- } else {
263
+ }
264
+ else {
293
265
  let error = new Error('Server does not support X-GM-EXT-1 extension required for X-GM-RAW');
294
266
  error.code = 'MissingServerExtension';
295
267
  throw error;
296
268
  }
297
269
  break;
298
-
299
270
  // Gmail label search. Compiles { has, not } into an X-GM-RAW "label:"/"-label:" query
300
271
  // since Gmail labels are not a native IMAP SEARCH key. Gmail-only (X-GM-EXT-1).
301
272
  case 'LABELS': {
@@ -303,16 +274,14 @@ module.exports.searchCompiler = (connection, query) => {
303
274
  if (!labelQuery || typeof labelQuery !== 'object') {
304
275
  break;
305
276
  }
306
-
307
277
  // Collapse whitespace/quotes and quote multi-word names so they survive as a single token
308
- let formatLabel = name => {
309
- name = (name || '')
278
+ let formatLabel = (name) => {
279
+ let label = (name || '')
310
280
  .toString()
311
281
  .replace(/[\s"]+/g, ' ')
312
282
  .trim();
313
- return name.indexOf(' ') >= 0 ? `"${name}"` : name;
283
+ return label.indexOf(' ') >= 0 ? `"${label}"` : label;
314
284
  };
315
-
316
285
  let rawParts = [];
317
286
  for (let name of [].concat(labelQuery.has || [])) {
318
287
  if (name) {
@@ -324,18 +293,15 @@ module.exports.searchCompiler = (connection, query) => {
324
293
  rawParts.push(`-label:${formatLabel(name)}`);
325
294
  }
326
295
  }
327
-
328
296
  // Empty filter is a no-op on any server (do not require the extension)
329
297
  if (!rawParts.length) {
330
298
  break;
331
299
  }
332
-
333
300
  if (!connection.capabilities.has('X-GM-EXT-1')) {
334
301
  let error = new Error('Server does not support X-GM-EXT-1 extension required for label search');
335
302
  error.code = 'MissingServerExtension';
336
303
  throw error;
337
304
  }
338
-
339
305
  let rawQuery = rawParts.join(' ');
340
306
  if (isUnicodeString(rawQuery)) {
341
307
  hasUnicode = true;
@@ -343,7 +309,6 @@ module.exports.searchCompiler = (connection, query) => {
343
309
  setOpt(attributes, 'X-GM-RAW', rawQuery);
344
310
  break;
345
311
  }
346
-
347
312
  // Date searches with WITHIN extension support
348
313
  case 'BEFORE':
349
314
  case 'SINCE':
@@ -354,7 +319,6 @@ module.exports.searchCompiler = (connection, query) => {
354
319
  if (!value) {
355
320
  break;
356
321
  }
357
-
358
322
  // Use WITHIN extension for better timezone handling if available
359
323
  if (connection.capabilities.has('WITHIN')) {
360
324
  // Convert to seconds ago from now
@@ -364,12 +328,10 @@ module.exports.searchCompiler = (connection, query) => {
364
328
  setOpt(attributes, withinKeyword, withinSeconds.toString());
365
329
  break;
366
330
  }
367
-
368
331
  // Fallback to standard date search
369
332
  processDateField(attributes, term, value);
370
333
  }
371
334
  break;
372
-
373
335
  // Standard date searches
374
336
  case 'ON':
375
337
  case 'SENTBEFORE':
@@ -377,7 +339,6 @@ module.exports.searchCompiler = (connection, query) => {
377
339
  case 'SENTSINCE':
378
340
  processDateField(attributes, term, params[term]);
379
341
  break;
380
-
381
342
  // Keyword/flag searches
382
343
  case 'KEYWORD':
383
344
  case 'UNKEYWORD':
@@ -389,51 +350,44 @@ module.exports.searchCompiler = (connection, query) => {
389
350
  }
390
351
  }
391
352
  break;
392
-
393
353
  // Header field searches
394
354
  case 'HEADER':
395
355
  if (params[term] && typeof params[term] === 'object') {
396
356
  Object.keys(params[term]).forEach(header => {
397
357
  let value = params[term][header];
398
-
399
358
  // Allow boolean true to search for header existence
400
359
  if (value === true) {
401
360
  value = '';
402
361
  }
403
-
404
362
  // Skip non-string values (after true->'' conversion)
405
363
  if (typeof value !== 'string') {
406
364
  return;
407
365
  }
408
-
409
366
  if (isUnicodeString(value)) {
410
367
  hasUnicode = true;
411
368
  }
412
-
413
369
  setOpt(attributes, term, [header.toUpperCase().trim(), value]);
414
370
  });
415
371
  }
416
372
  break;
417
-
418
373
  // NOT operator
419
374
  case 'NOT':
420
375
  if (params[term] && typeof params[term] === 'object') {
421
376
  attributes.push({ type: 'ATOM', value: 'NOT' });
422
377
  if (Object.keys(params[term]).length > 1) {
423
378
  walkGrouped(params[term]);
424
- } else {
379
+ }
380
+ else {
425
381
  walk(params[term]);
426
382
  }
427
383
  }
428
384
  break;
429
-
430
385
  // OR operator - complex logic for building OR trees
431
386
  case 'OR':
432
387
  {
433
388
  if (!params[term] || !Array.isArray(params[term]) || !params[term].length) {
434
389
  break;
435
390
  }
436
-
437
391
  // Single element - just process it directly
438
392
  if (params[term].length === 1) {
439
393
  if (typeof params[term][0] === 'object' && params[term][0]) {
@@ -441,56 +395,49 @@ module.exports.searchCompiler = (connection, query) => {
441
395
  }
442
396
  break;
443
397
  }
444
-
445
398
  /**
446
399
  * Generates a binary tree structure for OR operations.
447
400
  * IMAP OR takes exactly 2 operands, so we need to nest them.
448
401
  *
449
- * @param {Array} list - List of conditions to OR together
450
- * @returns {Array} Binary tree structure
402
+ * @param list - List of conditions to OR together
403
+ * @returns Binary tree structure
451
404
  */
452
- let genOrTree = list => {
405
+ let genOrTree = (list) => {
453
406
  let group = false;
454
407
  let groups = [];
455
-
456
408
  // Group items in pairs
457
409
  list.forEach((entry, i) => {
458
410
  if (i % 2 === 0) {
459
411
  group = [entry];
460
- } else {
412
+ }
413
+ else {
461
414
  group.push(entry);
462
415
  groups.push(group);
463
416
  group = false;
464
417
  }
465
418
  });
466
-
467
419
  // Handle odd number of items
468
420
  if (group && group.length) {
469
421
  while (group.length === 1 && Array.isArray(group[0])) {
470
422
  group = group[0];
471
423
  }
472
-
473
424
  groups.push(group);
474
425
  }
475
-
476
426
  // Recursively group until we have a binary tree
477
427
  while (groups.length > 2) {
478
428
  groups = genOrTree(groups);
479
429
  }
480
-
481
430
  // Flatten single-element arrays
482
431
  while (groups.length === 1 && Array.isArray(groups[0])) {
483
432
  groups = groups[0];
484
433
  }
485
-
486
434
  return groups;
487
435
  };
488
-
489
436
  /**
490
437
  * Walks the OR tree and generates IMAP commands.
491
- * @param {Array|Object} entry - Tree node to process
438
+ * @param entry - Tree node to process
492
439
  */
493
- let walkOrTree = entry => {
440
+ let walkOrTree = (entry) => {
494
441
  if (Array.isArray(entry)) {
495
442
  if (entry.length > 1) {
496
443
  attributes.push({ type: 'ATOM', value: 'OR' });
@@ -501,28 +448,25 @@ module.exports.searchCompiler = (connection, query) => {
501
448
  if (entry && typeof entry === 'object') {
502
449
  if (Object.keys(entry).length > 1) {
503
450
  walkGrouped(entry);
504
- } else {
451
+ }
452
+ else {
505
453
  walk(entry);
506
454
  }
507
455
  }
508
456
  };
509
-
510
457
  walkOrTree(genOrTree(params[term]));
511
458
  }
512
459
  break;
513
460
  }
514
461
  });
515
462
  };
516
-
517
463
  // Process the query
518
464
  walk(query);
519
-
520
465
  // If we encountered Unicode strings and UTF-8 is not already accepted,
521
466
  // prepend CHARSET UTF-8 to the search command
522
467
  if (hasUnicode && !connection.enabled.has('UTF8=ACCEPT')) {
523
468
  attributes.unshift({ type: 'ATOM', value: 'UTF-8' });
524
469
  attributes.unshift({ type: 'ATOM', value: 'CHARSET' });
525
470
  }
526
-
527
471
  return attributes;
528
472
  };
@@ -0,0 +1,22 @@
1
+ export declare const flags: string[];
2
+ export declare const names: {
3
+ [flag: string]: string[];
4
+ };
5
+ /**
6
+ * The parts of a listed folder that special-use detection reads
7
+ */
8
+ export interface SpecialUseFolder {
9
+ /** Flags reported for the folder by LIST */
10
+ flags: Set<string>;
11
+ /** Folder name, the last path component */
12
+ name: string;
13
+ }
14
+ /** How a special-use flag was determined */
15
+ export type SpecialUseSource = 'extension' | 'name' | 'name-guess';
16
+ export interface SpecialUseResult {
17
+ /** The special-use flag, or null when the folder is not a special-use folder */
18
+ flag: string | null;
19
+ /** Set when a flag was found */
20
+ source?: SpecialUseSource | undefined;
21
+ }
22
+ export declare const specialUse: (hasSpecialUseExtension: boolean, folder: SpecialUseFolder) => SpecialUseResult;