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,138 +1,42 @@
1
- 'use strict';
2
-
3
- const {
4
- enhanceCommandError,
5
- hasCapability,
6
- isValidSequenceValue,
7
- parseBigIntValue,
8
- parseUintValue,
9
- MAX_UINT32_DIGITS,
10
- EXPANDED_RANGE_LIMIT
11
- } = require('../tools.js');
12
- const { searchCompiler } = require('../search-compiler.js');
13
-
1
+ import { enhanceCommandError, hasCapability, isValidSequenceValue, EXPANDED_RANGE_LIMIT } from '../tools.js';
2
+ import { searchCompiler } from '../search-compiler.js';
3
+ import { parseEsearchResponse } from './esearch-parser.js';
14
4
  /**
15
5
  * Strips the leading (TAG "X") correlator list and the optional UID atom from an
16
6
  * ESEARCH untagged response, leaving only the result keyword/value pairs.
17
7
  * The IMAP parser represents parenthesized groups as plain Arrays, not objects
18
8
  * with type: 'LIST'.
19
9
  *
20
- * @param {Array} attrs - Raw attribute array from the IMAP parser
21
- * @returns {Array} Attribute array starting at the first result keyword
10
+ * @param attrs - Raw attribute array from the IMAP parser
11
+ * @returns Attribute array starting at the first result keyword
22
12
  */
23
- const stripEsearchPrefix = attrs => {
13
+ const stripEsearchPrefix = (attrs) => {
24
14
  let start = 0;
25
- if (attrs[start] && Array.isArray(attrs[start])) start++;
26
- if (attrs[start] && typeof attrs[start].value === 'string' && attrs[start].value.toUpperCase() === 'UID') start++;
15
+ if (attrs[start] && Array.isArray(attrs[start]))
16
+ start++;
17
+ if (attrs[start] && typeof attrs[start].value === 'string' && attrs[start].value.toUpperCase() === 'UID')
18
+ start++;
27
19
  return attrs.slice(start);
28
20
  };
29
-
30
- /**
31
- * Parses the key-value attributes from an ESEARCH untagged response.
32
- *
33
- * Receives the attribute list AFTER stripping the leading (TAG "X") list
34
- * and the UID atom — i.e. only the result keyword/value pairs remain.
35
- *
36
- * ALL and PARTIAL.messages are kept as compact sequence-set strings.
37
- * Use expandRange() from tools.js if you need to expand them.
38
- * MODSEQ (RFC 7162, sent when the search used a MODSEQ criterion) is
39
- * returned as a BigInt.
40
- *
41
- * @param {Array} attrs - Attribute array from the IMAP parser
42
- * @returns {Object} ESearchResult object
43
- */
44
- function parseEsearchResponse(attrs) {
45
- const result = {};
46
- let i = 0;
47
- while (i < attrs.length) {
48
- const token = attrs[i];
49
- if (!token || token.type !== 'ATOM') {
50
- i++;
51
- continue;
52
- }
53
- const key = token.value.toUpperCase();
54
- if (i + 1 >= attrs.length) {
55
- i++;
56
- continue;
57
- }
58
- switch (key) {
59
- // COUNT is a plain message count; MIN and MAX are sequence numbers or UIDs. All
60
- // three are bounded decimal runs - isNaN() would also admit '1e400' (Infinity)
61
- case 'COUNT': {
62
- const n = parseUintValue(attrs[++i]?.value, MAX_UINT32_DIGITS);
63
- if (n !== false) result.count = n;
64
- break;
65
- }
66
- case 'MIN': {
67
- const n = parseUintValue(attrs[++i]?.value, MAX_UINT32_DIGITS);
68
- if (n !== false) result.min = n;
69
- break;
70
- }
71
- case 'MAX': {
72
- const n = parseUintValue(attrs[++i]?.value, MAX_UINT32_DIGITS);
73
- if (n !== false) result.max = n;
74
- break;
75
- }
76
- case 'MODSEQ': {
77
- // RFC 7162 section 3.1.5: present when the SEARCH used a MODSEQ
78
- // criterion on a CONDSTORE-enabled session. BigInt because
79
- // mod-sequence values are unsigned 63-bit
80
- const modseq = parseBigIntValue(attrs[++i]?.value);
81
- if (modseq !== false) result.modseq = modseq;
82
- break;
83
- }
84
- case 'ALL': {
85
- const allToken = attrs[++i];
86
- if (allToken && typeof allToken.value === 'string') {
87
- result.all = allToken.value;
88
- }
89
- break;
90
- }
91
- case 'PARTIAL': {
92
- const listToken = attrs[++i];
93
- const items = Array.isArray(listToken) ? listToken : null;
94
- if (!items || items.length < 2) break;
95
- result.partial = {
96
- range: items[0].value,
97
- messages: items[1].value
98
- };
99
- break;
100
- }
101
- default:
102
- // Skip the value token for unknown keys to keep the stream aligned.
103
- // The loop's unconditional i++ at the bottom advances past the key;
104
- // this extra i++ advances past the value token.
105
- i++;
106
- break;
107
- }
108
- i++;
109
- }
110
- return result;
111
- }
112
-
113
21
  /**
114
22
  * Searches for messages matching the specified criteria.
115
23
  *
116
- * @param {Object} connection - IMAP connection instance
117
- * @param {Object|boolean} query - Search query object, or true/empty object to match all messages
118
- * @param {Object} [options] - Search options
119
- * @param {boolean} [options.uid] - If true, use UID SEARCH instead of SEARCH
120
- * @param {Array} [options.returnOptions] - ESEARCH RETURN options. When present AND the
24
+ * @param connection - IMAP connection instance
25
+ * @param query - Search query object, or true/empty object to match all messages
26
+ * @param options - Search options
27
+ * @param options.uid - If true, use UID SEARCH instead of SEARCH
28
+ * @param options.returnOptions - ESEARCH RETURN options. When present AND the
121
29
  * server advertises ESEARCH capability, triggers ESEARCH and returns an ESearchResult.
122
30
  * Items are strings ('MIN','MAX','COUNT','ALL') or objects ({ partial: '1:100' }).
123
31
  * When server lacks ESEARCH, falls back to plain SEARCH and returns number[].
124
- * @returns {Promise<number[]|Object|boolean>}
125
32
  */
126
- module.exports = async (connection, query, options) => {
33
+ export default async function search(connection, query, options) {
127
34
  if (connection.state !== connection.states.SELECTED) {
128
35
  // nothing to do here
129
36
  return false;
130
37
  }
131
-
132
38
  options = options || {};
133
-
134
39
  let attributes;
135
-
136
40
  // Three query branches:
137
41
  // 1. Empty/truthy/all-only query -> use IMAP "SEARCH ALL" to match every message
138
42
  // 2. Non-empty object -> compile into IMAP SEARCH criteria via searchCompiler
@@ -140,67 +44,67 @@ module.exports = async (connection, query, options) => {
140
44
  if (!query || query === true || (typeof query === 'object' && (!Object.keys(query).length || (Object.keys(query).length === 1 && query.all)))) {
141
45
  // search for all messages
142
46
  attributes = [{ type: 'ATOM', value: 'ALL' }];
143
- } else if (query && typeof query === 'object') {
47
+ }
48
+ else if (query && typeof query === 'object') {
144
49
  // normal query
145
50
  attributes = searchCompiler(connection, query);
146
- } else {
51
+ }
52
+ else {
147
53
  return false;
148
54
  }
149
-
150
55
  // ESEARCH is part of base IMAP4rev2
151
56
  const useEsearch = options.returnOptions && options.returnOptions.length > 0 && hasCapability(connection, 'ESEARCH');
152
-
153
57
  if (useEsearch) {
154
58
  // Build RETURN (...) item list
155
59
  const returnItems = [];
156
60
  for (const opt of options.returnOptions) {
157
61
  if (typeof opt === 'string') {
158
62
  returnItems.push({ type: 'ATOM', value: opt.toUpperCase() });
159
- } else if (opt && typeof opt.partial === 'string') {
63
+ }
64
+ else if (opt && typeof opt.partial === 'string') {
160
65
  // RFC 9394: PARTIAL is an atom followed by the range atom, both inside RETURN (...)
161
66
  returnItems.push({ type: 'ATOM', value: 'PARTIAL' });
162
67
  returnItems.push({ type: 'ATOM', value: opt.partial });
163
68
  }
164
69
  }
165
-
166
70
  // If all returnOptions entries were invalid (e.g. objects lacking a string
167
71
  // `partial` field), returnItems would be empty. Emitting "RETURN ()" is
168
72
  // technically valid per RFC 4731 but returns nothing useful. Fall through
169
73
  // to the legacy SEARCH path instead so the caller gets a usable result.
170
74
  if (returnItems.length > 0) {
171
75
  const returnClause = [{ type: 'ATOM', value: 'RETURN' }, returnItems];
172
-
173
76
  let esearchResult = {};
174
77
  let response;
175
78
  try {
176
79
  response = await connection.exec(options.uid ? 'UID SEARCH' : 'SEARCH', [...returnClause, ...attributes], {
177
80
  untagged: {
178
- ESEARCH: async untagged => {
179
- if (!untagged || !untagged.attributes) return;
81
+ ESEARCH: async (untagged) => {
82
+ if (!untagged || !untagged.attributes)
83
+ return;
180
84
  esearchResult = parseEsearchResponse(stripEsearchPrefix(untagged.attributes));
181
85
  }
182
86
  }
183
87
  });
184
88
  response.next();
185
89
  return esearchResult;
186
- } catch (err) {
90
+ }
91
+ catch (err) {
187
92
  await enhanceCommandError(err);
188
93
  connection.log.warn({ err, cid: connection.id });
189
94
  return false;
190
95
  }
191
96
  }
192
- // returnItems was empty — fall through to legacy SEARCH path below
97
+ // returnItems was empty - fall through to legacy SEARCH path below
193
98
  }
194
-
195
- // ── Legacy SEARCH path (no returnOptions, or server lacks ESEARCH) ────
196
- // Use a Set to deduplicate sequence numbers/UIDs -- servers may return
99
+ // Legacy SEARCH path (no returnOptions, or server lacks ESEARCH)
100
+ // Use a Set to deduplicate sequence numbers/UIDs - servers may return
197
101
  // duplicates across multiple untagged SEARCH responses.
198
102
  let results = new Set();
199
103
  let response;
200
104
  try {
201
105
  response = await connection.exec(options.uid ? 'UID SEARCH' : 'SEARCH', attributes, {
202
106
  untagged: {
203
- SEARCH: async untagged => {
107
+ SEARCH: async (untagged) => {
204
108
  if (untagged && untagged.attributes && untagged.attributes.length) {
205
109
  let truncated = false;
206
110
  let discarded = false;
@@ -235,11 +139,10 @@ module.exports = async (connection, query, options) => {
235
139
  }
236
140
  }
237
141
  },
238
-
239
142
  // IMAP4rev2 servers answer even a plain SEARCH with an untagged
240
143
  // ESEARCH response (RFC 9051 deprecated the SEARCH response), so
241
144
  // both forms are collected into the same result set
242
- ESEARCH: async untagged => {
145
+ ESEARCH: async (untagged) => {
243
146
  if (!untagged || !untagged.attributes) {
244
147
  return;
245
148
  }
@@ -263,7 +166,7 @@ module.exports = async (connection, query, options) => {
263
166
  // absolute ceiling expandRange() uses - a hostile server cannot
264
167
  // bypass it by inflating EXISTS first
265
168
  let overBudget = () => results.size >= existsCount() || results.size >= EXPANDED_RANGE_LIMIT;
266
- let resolveId = part => (part === '*' ? (options.uid ? 0 : existsCount()) : Number(part));
169
+ let resolveId = (part) => (part === '*' ? (options.uid ? 0 : existsCount()) : Number(part));
267
170
  let truncated = false;
268
171
  let discarded = false;
269
172
  sequenceSetLoop: for (let part of parsed.all.split(',')) {
@@ -311,12 +214,10 @@ module.exports = async (connection, query, options) => {
311
214
  response.next();
312
215
  // Sort numerically for consistent, predictable output order
313
216
  return Array.from(results).sort((a, b) => a - b);
314
- } catch (err) {
217
+ }
218
+ catch (err) {
315
219
  await enhanceCommandError(err);
316
220
  connection.log.warn({ err, cid: connection.id });
317
221
  return false;
318
222
  }
319
- };
320
-
321
- // Exported for unit testing — not intended as public library API
322
- module.exports.parseEsearchResponse = parseEsearchResponse;
223
+ }
@@ -0,0 +1,25 @@
1
+ import type { ImapFlow } from '../imap-flow.js';
2
+ import type { MailboxObject, MailboxOpenOptions } from '../types.js';
3
+ /**
4
+ * Options for SELECT/EXAMINE: the public open options plus the QRESYNC resynchronization
5
+ * parameters, which are only honored when the QRESYNC extension has been enabled
6
+ */
7
+ export interface SelectOptions extends MailboxOpenOptions {
8
+ /** QRESYNC modseq value to fetch changes since */
9
+ changedSince?: bigint | number | string | undefined;
10
+ /** QRESYNC UID validity value */
11
+ uidValidity?: bigint | number | string | undefined;
12
+ }
13
+ /**
14
+ * Selects or examines a mailbox, making it the current mailbox for subsequent operations.
15
+ *
16
+ * @param connection - IMAP connection instance
17
+ * @param path - Mailbox path to select
18
+ * @param options - Select options
19
+ * @param options.readOnly - If true, use EXAMINE instead of SELECT (read-only access)
20
+ * @param options.changedSince - QRESYNC modseq value to fetch changes since
21
+ * @param options.uidValidity - QRESYNC UID validity value
22
+ * @returns Mailbox info object with path, flags, exists, uidNext, uidValidity, highestModseq, etc., or undefined if preconditions not met
23
+ * @throws If the SELECT/EXAMINE command fails
24
+ */
25
+ export default function select(connection: ImapFlow, pathInput: string | string[], options?: SelectOptions | undefined): Promise<MailboxObject | undefined>;
@@ -1,7 +1,4 @@
1
- 'use strict';
2
-
3
- const { encodePath, normalizePath, enhanceCommandError, parseBigIntValue, parseUintValue, getStringList, MAX_UINT32_DIGITS } = require('../tools.js');
4
-
1
+ import { encodePath, normalizePath, enhanceCommandError, parseBigIntValue, parseUintValue, getStringList, MAX_UINT32_DIGITS } from '../tools.js';
5
2
  // Response codes carrying a value that SELECT/EXAMINE may write to the mailbox object, keyed by
6
3
  // the lowercased code, mapped to the fixed public property name and the parser for the value.
7
4
  // Every parser returns false for a value it cannot use, and the field is then left unset.
@@ -18,62 +15,53 @@ const VALUED_RESPONSE_CODES = Object.assign(Object.create(null), {
18
15
  // A value that is not a bounded digit run is dropped rather than stored raw: every consumer
19
16
  // compares highestModseq relationally, and a relational compare against a non-numeric string
20
17
  // is false in both directions, so the value could never advance and delta sync would stop.
21
- highestmodseq: { key: 'highestModseq', parse: value => parseBigIntValue(value) },
22
-
18
+ highestmodseq: { key: 'highestModseq', parse: (value) => parseBigIntValue(value) },
23
19
  // Unique identifier validity. If this changes between sessions, all previously cached UIDs
24
20
  // are invalid and the client must re-sync from scratch. Nominally 32-bit, but stored as a
25
21
  // BigInt precisely so a server that exceeds that still round-trips, hence the wider bound.
26
- uidvalidity: { key: 'uidValidity', parse: value => parseBigIntValue(value) },
27
-
22
+ uidvalidity: { key: 'uidValidity', parse: (value) => parseBigIntValue(value) },
28
23
  // The next UID to be assigned in this mailbox, useful for detecting new arrivals. A huge
29
24
  // digit run would coerce to Infinity and corrupt every later UID range computation.
30
- uidnext: { key: 'uidNext', parse: value => parseUintValue(value, MAX_UINT32_DIGITS) },
31
-
25
+ uidnext: { key: 'uidNext', parse: (value) => parseUintValue(value, MAX_UINT32_DIGITS) },
32
26
  // Sequence number of the first unseen message (RFC 3501 section 7.1). Not a count of unseen
33
27
  // messages - use mailboxStatus() with {unseen: true} for that.
34
- unseen: { key: 'unseen', parse: value => parseUintValue(value, MAX_UINT32_DIGITS) },
35
-
28
+ unseen: { key: 'unseen', parse: (value) => parseUintValue(value, MAX_UINT32_DIGITS) },
36
29
  // APPENDLIMIT (RFC 7889): largest message size in octets the server accepts for APPEND into
37
30
  // this mailbox. Spelled all lowercase, unlike the camelCase fields around it, because that is
38
31
  // the name this object has always exposed.
39
- appendlimit: { key: 'appendlimit', parse: value => parseUintValue(value) },
40
-
32
+ appendlimit: { key: 'appendlimit', parse: (value) => parseUintValue(value) },
41
33
  // OBJECTID (RFC 8474): server-assigned mailbox identifier that survives renames. Sent as a
42
34
  // parenthesized list, but servers in the wild send it bare too.
43
35
  mailboxid: {
44
36
  key: 'mailboxId',
45
- parse: value => (Array.isArray(value) ? value.length > 0 && value[0] : typeof value === 'string' && value)
37
+ parse: (value) => (Array.isArray(value) ? value.length > 0 && value[0] : typeof value === 'string' && value)
46
38
  },
47
-
48
39
  // Flags the client may change permanently on messages in this mailbox, including \* if the
49
40
  // server allows custom flags. Only the parenthesized form carries flags, and a malformed
50
41
  // value must leave permanentFlags unset rather than set an empty Set: canUseFlag() reads
51
42
  // unset as permissive and empty as deny-all, so an empty Set would turn every later flag
52
43
  // update into a silent no-op for the rest of the session.
53
- permanentflags: { key: 'permanentFlags', parse: value => Array.isArray(value) && new Set(value) }
44
+ permanentflags: { key: 'permanentFlags', parse: (value) => Array.isArray(value) && new Set(value) }
54
45
  });
55
-
56
46
  /**
57
47
  * Selects or examines a mailbox, making it the current mailbox for subsequent operations.
58
48
  *
59
- * @param {Object} connection - IMAP connection instance
60
- * @param {string} path - Mailbox path to select
61
- * @param {Object} [options] - Select options
62
- * @param {boolean} [options.readOnly] - If true, use EXAMINE instead of SELECT (read-only access)
63
- * @param {string} [options.changedSince] - QRESYNC modseq value to fetch changes since
64
- * @param {BigInt} [options.uidValidity] - QRESYNC UID validity value
65
- * @returns {Promise<Object|undefined>} Mailbox info object with path, flags, exists, uidNext, uidValidity, highestModseq, etc., or undefined if preconditions not met
66
- * @throws {Error} If the SELECT/EXAMINE command fails
49
+ * @param connection - IMAP connection instance
50
+ * @param path - Mailbox path to select
51
+ * @param options - Select options
52
+ * @param options.readOnly - If true, use EXAMINE instead of SELECT (read-only access)
53
+ * @param options.changedSince - QRESYNC modseq value to fetch changes since
54
+ * @param options.uidValidity - QRESYNC UID validity value
55
+ * @returns Mailbox info object with path, flags, exists, uidNext, uidValidity, highestModseq, etc., or undefined if preconditions not met
56
+ * @throws If the SELECT/EXAMINE command fails
67
57
  */
68
- module.exports = async (connection, path, options) => {
58
+ export default async function select(connection, pathInput, options) {
69
59
  if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state)) {
70
60
  // nothing to do here
71
61
  return;
72
62
  }
73
63
  options = options || {};
74
-
75
- path = normalizePath(connection, path);
76
-
64
+ let path = normalizePath(connection, pathInput);
77
65
  // Ensure we have folder metadata (delimiter, flags, specialUse) by running LIST if needed.
78
66
  // This is cached in connection.folders to avoid repeated LIST calls.
79
67
  // Note: this uses run() rather than runInternal(), so it terminates a running IDLE first,
@@ -90,9 +78,7 @@ module.exports = async (connection, path, options) => {
90
78
  connection.folders.set(folder.path, folder);
91
79
  });
92
80
  }
93
-
94
81
  let folderListData = connection.folders.has(path) ? connection.folders.get(path) : false;
95
-
96
82
  let response;
97
83
  try {
98
84
  let map = { path };
@@ -103,7 +89,6 @@ module.exports = async (connection, path, options) => {
103
89
  }
104
90
  });
105
91
  }
106
-
107
92
  // QRESYNC (RFC 7162): allows efficient mailbox resynchronization by sending
108
93
  // the last known UIDVALIDITY and HIGHESTMODSEQ. Server responds with only
109
94
  // the changes (new flags, expunged UIDs) since that point.
@@ -118,9 +103,7 @@ module.exports = async (connection, path, options) => {
118
103
  ]);
119
104
  map.qresync = true;
120
105
  }
121
-
122
106
  let encodedPath = encodePath(connection, path);
123
-
124
107
  // SELECT opens the mailbox read-write; EXAMINE opens it read-only.
125
108
  // Path encoding: if the encoded path contains '&' (UTF-7 encoding marker),
126
109
  // send as quoted STRING to avoid parser issues with the ampersand.
@@ -129,7 +112,6 @@ module.exports = async (connection, path, options) => {
129
112
  /* c8 ignore next */ // extraArgs is always initialised to an array, so the [] fallback is unreachable
130
113
  arguments: [{ type: encodedPath.indexOf('&') >= 0 ? 'STRING' : 'ATOM', value: encodedPath }].concat(extraArgs || [])
131
114
  };
132
-
133
115
  response = await connection.exec(selectCommand.command, selectCommand.arguments, {
134
116
  untagged: {
135
117
  // Untagged OK responses carry response codes in brackets, e.g.:
@@ -137,7 +119,7 @@ module.exports = async (connection, path, options) => {
137
119
  // * OK [PERMANENTFLAGS (\Seen \Answered \*)] Flags permitted
138
120
  // The section array holds the parsed bracket contents: section[0] is the
139
121
  // key (e.g., "UIDVALIDITY"), section[1] is the value or list.
140
- OK: async untagged => {
122
+ OK: async (untagged) => {
141
123
  if (!untagged.attributes || !untagged.attributes.length) {
142
124
  return;
143
125
  }
@@ -146,16 +128,15 @@ module.exports = async (connection, path, options) => {
146
128
  if (section && section.length > 1 && section[0] && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
147
129
  let key = section[0].value.toLowerCase();
148
130
  let value;
149
-
150
131
  // Value can be a single string or a list of strings (e.g., PERMANENTFLAGS).
151
132
  // section[1] can be a parsed NIL (null), and so can any element inside a
152
133
  // parenthesized list, so both levels need the guard
153
134
  if (section[1] && typeof section[1].value === 'string') {
154
135
  value = section[1].value;
155
- } else if (Array.isArray(section[1])) {
136
+ }
137
+ else if (Array.isArray(section[1])) {
156
138
  value = getStringList(section[1]);
157
139
  }
158
-
159
140
  let field = VALUED_RESPONSE_CODES[key];
160
141
  if (field) {
161
142
  let parsed = field.parse(value);
@@ -164,52 +145,49 @@ module.exports = async (connection, path, options) => {
164
145
  }
165
146
  }
166
147
  }
167
-
168
148
  // Handle response codes with only a keyword (no value), e.g., [NOMODSEQ].
169
149
  // NOMODSEQ means the mailbox does not support mod-sequences, so the
170
150
  // CONDSTORE/QRESYNC features are unavailable for it.
171
- if (section && section.length === 1 && section[0] && section[0].type === 'ATOM' && section[0].value?.toUpperCase() === 'NOMODSEQ') {
151
+ if (section &&
152
+ section.length === 1 &&
153
+ section[0] &&
154
+ section[0].type === 'ATOM' &&
155
+ section[0].value?.toUpperCase() === 'NOMODSEQ') {
172
156
  map.noModseq = true;
173
157
  }
174
158
  },
175
-
176
159
  // Untagged FLAGS response lists all flags defined for this mailbox
177
160
  // (both system flags and custom flags). Example: * FLAGS (\Seen \Answered \Flagged)
178
- FLAGS: async untagged => {
161
+ FLAGS: async (untagged) => {
179
162
  if (!untagged.attributes || !untagged.attributes.length || !Array.isArray(untagged.attributes[0])) {
180
163
  return;
181
164
  }
182
165
  map.flags = new Set(getStringList(untagged.attributes[0]));
183
166
  },
184
-
185
167
  // Untagged EXISTS response: "* <count> EXISTS" tells us the total number
186
168
  // of messages in the mailbox. The count is in the command field (numeric prefix).
187
- EXISTS: async untagged => {
169
+ EXISTS: async (untagged) => {
188
170
  // Not a usable count: anything but a bounded digit run. A long digit run
189
171
  // coerces to Infinity, which would corrupt every later range computation
190
172
  let num = parseUintValue(untagged.command, MAX_UINT32_DIGITS);
191
173
  if (num === false) {
192
174
  return false;
193
175
  }
194
-
195
176
  map.exists = num;
196
177
  },
197
-
198
178
  // VANISHED responses (QRESYNC): server reports UIDs that have been expunged
199
179
  // since the client's last known state. Only received when QRESYNC was requested.
200
180
  // A dummy mailbox object is passed because the mailbox isn't officially open yet.
201
- VANISHED: async untagged => {
181
+ VANISHED: async (untagged) => {
202
182
  await connection.untaggedVanished(untagged, { path, uidNext: false, uidValidity: false });
203
183
  },
204
-
205
184
  // Untagged FETCH during SELECT/EXAMINE: only occurs with QRESYNC, delivering
206
185
  // updated flags for messages that changed since the client's last modseq.
207
- FETCH: async untagged => {
186
+ FETCH: async (untagged) => {
208
187
  await connection.untaggedFetch(untagged, { path, uidNext: false, uidValidity: false });
209
188
  }
210
189
  }
211
190
  });
212
-
213
191
  // The tagged OK response to SELECT/EXAMINE includes [READ-ONLY] or [READ-WRITE]
214
192
  // in its response code, indicating the access mode the server granted. A tagged OK
215
193
  // with no resp-text has no `attributes` property at all, and unlike the untagged
@@ -220,7 +198,6 @@ module.exports = async (connection, path, options) => {
220
198
  if (section && section.length && section[0] && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
221
199
  map.readOnly = section[0].value.toUpperCase() === 'READ-ONLY';
222
200
  }
223
-
224
201
  // Validate QRESYNC preconditions (RFC 7162 Section 3.2.5):
225
202
  // QRESYNC results are only valid if UIDVALIDITY matches, HIGHESTMODSEQ is
226
203
  // present, and the mailbox supports mod-sequences. If any condition fails,
@@ -228,49 +205,41 @@ module.exports = async (connection, path, options) => {
228
205
  if (map.qresync && (options.uidValidity !== map.uidValidity || !map.highestModseq || map.noModseq)) {
229
206
  map.qresync = false;
230
207
  }
231
-
232
208
  // Transition mailbox state: save previous mailbox reference, temporarily
233
209
  // clear it, then emit events and set the new mailbox.
234
210
  let currentMailbox = connection.mailbox;
235
211
  connection.mailbox = false;
236
-
237
212
  // Emit mailboxClose if we're switching from a different mailbox.
238
213
  // Re-selecting the same mailbox (e.g., for resync) does not trigger close/open.
239
214
  if (currentMailbox && currentMailbox.path !== path) {
240
215
  connection.emit('mailboxClose', currentMailbox);
241
216
  }
242
-
243
217
  connection.mailbox = map;
244
218
  // Save the SELECT command for potential re-use (e.g., NOOP fallback polling
245
219
  // re-issues the SELECT to detect changes on servers without IDLE support).
246
220
  connection.currentSelectCommand = selectCommand;
247
221
  connection.state = connection.states.SELECTED;
248
-
249
222
  if (!currentMailbox || currentMailbox.path !== path) {
250
223
  connection.emit('mailboxOpen', connection.mailbox);
251
224
  }
252
-
253
225
  response.next();
254
226
  return map;
255
- } catch (err) {
227
+ }
228
+ catch (err) {
256
229
  await enhanceCommandError(err);
257
-
258
230
  // If SELECT/EXAMINE fails while a mailbox was already selected, we must
259
231
  // reset to AUTHENTICATED state since the server has implicitly deselected
260
232
  // the previous mailbox on failure (RFC 3501 Section 6.3.1).
261
233
  if (connection.state === connection.states.SELECTED) {
262
234
  let currentMailbox = connection.mailbox;
263
-
264
235
  connection.mailbox = false;
265
236
  connection.currentSelectCommand = false;
266
237
  connection.state = connection.states.AUTHENTICATED;
267
-
268
238
  if (currentMailbox) {
269
239
  connection.emit('mailboxClose', currentMailbox);
270
240
  }
271
241
  }
272
-
273
242
  connection.log.warn({ err, cid: connection.id });
274
243
  throw err;
275
244
  }
276
- };
245
+ }
@@ -0,0 +1,8 @@
1
+ import type { ImapFlow } from '../imap-flow.js';
2
+ /**
3
+ * Initiates STARTTLS connection upgrade.
4
+ *
5
+ * @param connection - IMAP connection instance
6
+ * @returns True if STARTTLS was initiated, false if not supported or already secure
7
+ */
8
+ export default function starttls(connection: ImapFlow): Promise<boolean>;
@@ -1,17 +1,14 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Initiates STARTTLS connection upgrade.
5
3
  *
6
- * @param {Object} connection - IMAP connection instance
7
- * @returns {Promise<boolean>} True if STARTTLS was initiated, false if not supported or already secure
4
+ * @param connection - IMAP connection instance
5
+ * @returns True if STARTTLS was initiated, false if not supported or already secure
8
6
  */
9
- module.exports = async connection => {
7
+ export default async function starttls(connection) {
10
8
  if (!connection.capabilities.has('STARTTLS') || connection.secureConnection) {
11
9
  // nothing to do here
12
10
  return false;
13
11
  }
14
-
15
12
  let response;
16
13
  try {
17
14
  response = await connection.exec('STARTTLS');
@@ -20,8 +17,9 @@ module.exports = async connection => {
20
17
  connection._starttlsHadTrailingData = !!(response && response.hasTrailingData);
21
18
  response.next();
22
19
  return true;
23
- } catch (err) {
20
+ }
21
+ catch (err) {
24
22
  connection.log.warn({ err, cid: connection.id });
25
23
  return false;
26
24
  }
27
- };
25
+ }
@@ -0,0 +1,14 @@
1
+ import type { ImapAttributeList } from '../handler/types.js';
2
+ /**
3
+ * A parsed STATUS value: counts and sizes as numbers, UIDVALIDITY and HIGHESTMODSEQ as BigInts
4
+ */
5
+ export type StatusFieldValue = number | bigint;
6
+ /**
7
+ * Walks a STATUS data-item list - alternating item-name and item-value tokens - and reports
8
+ * every recognized field that parsed successfully. Unknown item names and unusable values are
9
+ * skipped, so one bad field never costs the rest of the response.
10
+ *
11
+ * @param list - Parsed attribute list from the untagged STATUS response.
12
+ * @param onField - Called as (key, value) for each usable field.
13
+ */
14
+ export declare const parseStatusList: (list: ImapAttributeList, onField: (key: string, value: StatusFieldValue) => void) => void;