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,34 +1,21 @@
1
- 'use strict';
2
-
3
- const {
4
- decodePath,
5
- encodePath,
6
- normalizePath,
7
- enhanceCommandError,
8
- hasCapability,
9
- isRev2Active,
10
- buildStatusQueryAttributes,
11
- getStringList
12
- } = require('../tools.js');
13
- const { parseStatusList } = require('./status-fields.js');
14
- const { specialUse } = require('../special-use');
15
-
1
+ import { decodePath, encodePath, normalizePath, enhanceCommandError, hasCapability, isRev2Active, buildStatusQueryAttributes, getStringList } from '../tools.js';
2
+ import { parseStatusList } from './status-fields.js';
3
+ import { specialUse } from '../special-use.js';
16
4
  /**
17
5
  * Lists mailboxes from the server, including subscription status and special-use flags.
18
6
  *
19
- * @param {Object} connection - IMAP connection instance
20
- * @param {string} reference - Reference name (namespace prefix)
21
- * @param {string} mailbox - Mailbox name pattern with possible wildcards
22
- * @param {Object} [options] - List options
23
- * @param {boolean} [options.listOnly] - If true, return entries after LIST without LSUB or status queries
24
- * @param {Object} [options.statusQuery] - Status data items to query for each listed mailbox
25
- * @param {Object} [options.specialUseHints] - Hints mapping mailbox paths to special-use types (sent, junk, trash, drafts, archive)
26
- * @returns {Promise<Object[]>} Array of mailbox entries sorted by special-use flags and name
27
- * @throws {Error} If the LIST command fails
7
+ * @param connection - IMAP connection instance
8
+ * @param reference - Reference name (namespace prefix)
9
+ * @param mailbox - Mailbox name pattern with possible wildcards
10
+ * @param options - List options
11
+ * @param options.listOnly - If true, return entries after LIST without LSUB or status queries
12
+ * @param options.statusQuery - Status data items to query for each listed mailbox
13
+ * @param options.specialUseHints - Hints mapping mailbox paths to special-use types (sent, junk, trash, drafts, archive)
14
+ * @returns Array of mailbox entries sorted by special-use flags and name
15
+ * @throws If the LIST command fails
28
16
  */
29
- module.exports = async (connection, reference, mailbox, options) => {
17
+ export default async function list(connection, reference, mailbox, options) {
30
18
  options = options || {};
31
-
32
19
  // Special-use flags sorted by display priority (INBOX first, Trash last).
33
20
  // Used in the final sort to group special-use mailboxes at the top of the list.
34
21
  const FLAG_SORT_ORDER = ['\\Inbox', '\\Flagged', '\\Sent', '\\Drafts', '\\All', '\\Archive', '\\Junk', '\\Trash'];
@@ -41,27 +28,23 @@ module.exports = async (connection, reference, mailbox, options) => {
41
28
  // "name-guess" is an internal precedence tier only. It is reported as "name" so
42
29
  // that specialUseSource keeps its documented set of values for consumers.
43
30
  const PUBLIC_SOURCE = { 'name-guess': 'name' };
44
- const isNameSource = source => source === 'name' || source === 'name-guess';
45
-
31
+ const isNameSource = (source) => source === 'name' || source === 'name-guess';
46
32
  // Prefer XLIST (legacy Gmail extension) only if SPECIAL-USE (RFC 6154) is unavailable.
47
33
  // Both provide special-use flags, but SPECIAL-USE is the standardized approach.
48
34
  // SPECIAL-USE is checked with rev2 folding - a rev2 session implies SPECIAL-USE,
49
35
  // so LIST is preferred even if a rev2 server also advertised legacy XLIST.
50
36
  let listCommand = connection.capabilities.has('XLIST') && !hasCapability(connection, 'SPECIAL-USE') ? 'XLIST' : 'LIST';
51
-
52
37
  try {
53
38
  // Accumulators filled by the untagged LIST/STATUS handlers below. statusMap
54
39
  // caches STATUS responses received inline via LIST-STATUS extension, keyed by
55
40
  // normalized mailbox path (avoids separate STATUS commands per mailbox), and
56
41
  // specialUseMatches tracks candidate mailboxes for each special-use type.
57
42
  // (Re)initialized at the start of each retry stage of the main listing.
58
- let entries;
59
- let statusMap;
60
- let specialUseMatches;
61
-
43
+ let entries = [];
44
+ let statusMap = new Map();
45
+ let specialUseMatches = {};
62
46
  // STATUS data items to request (MESSAGES, UIDNEXT, etc.)
63
47
  let statusQueryAttributes = buildStatusQueryAttributes(connection, options.statusQuery);
64
-
65
48
  // Extended LIST syntax (RETURN options) is understood by servers advertising
66
49
  // LIST-EXTENDED (RFC 5258) or IMAP4rev2 (RFC 9051). Deliberately keyed on the
67
50
  // advertisement alone (not hasCapability/isRev2Active): the staged retry below
@@ -69,33 +52,27 @@ module.exports = async (connection, reference, mailbox, options) => {
69
52
  // is safe for anything it covers, while gates without a retry ladder stay
70
53
  // conservative.
71
54
  let supportsExtendedList = connection.capabilities.has('LIST-EXTENDED') || connection.capabilities.has('IMAP4rev2');
72
-
73
55
  // RETURN options for the LIST command. Servers occasionally advertise the
74
56
  // extensions but still reject RETURN options - the staged retry below then
75
57
  // re-runs the LIST with fewer options and latches a skip flag for the option
76
58
  // group the server proved to reject, keeping later listings efficient.
77
-
78
59
  // LIST-STATUS (RFC 5819, folded into base IMAP4rev2): request STATUS data
79
60
  // inline with LIST, avoiding a separate STATUS command for each mailbox.
80
- let canRequestStatus =
81
- listCommand === 'LIST' && !connection.skipListStatusArgs && hasCapability(connection, 'LIST-STATUS') && !!statusQueryAttributes.length;
82
-
61
+ let canRequestStatus = listCommand === 'LIST' && !connection.skipListStatusArgs && hasCapability(connection, 'LIST-STATUS') && !!statusQueryAttributes.length;
83
62
  // RETURN (SUBSCRIBED): request subscription state inline instead of a separate
84
63
  // LSUB command. IMAP4rev2 removed LSUB entirely, and some servers (e.g.
85
64
  // Exchange in IMAP4rev2 mode) reject it with BAD even while still advertising
86
65
  // IMAP4rev1.
87
66
  let canRequestSubscribed = listCommand === 'LIST' && !options.listOnly && !connection.skipListSubscribedArg && supportsExtendedList;
88
-
89
67
  // Auxiliary RETURN options (SPECIAL-USE/CHILDREN) that ride along with the
90
68
  // STATUS/SUBSCRIBED option groups. When RETURN options are present, servers
91
69
  // may report only what was explicitly requested (verified against Dovecot
92
70
  // 2.4: special-use and child attributes disappear from such responses), so
93
71
  // request everything a plain LIST would have provided.
94
72
  let auxArgsAvailable = hasCapability(connection, 'SPECIAL-USE') || connection.capabilities.has('CHILDREN') || supportsExtendedList;
95
- let stageHasAuxArgs = stage => (stage.status || stage.subscribed) && stage.aux !== false && !connection.skipListAuxArgs && auxArgsAvailable;
96
-
73
+ let stageHasAuxArgs = (stage) => (stage.status || stage.subscribed) && stage.aux !== false && !connection.skipListAuxArgs && auxArgsAvailable;
97
74
  // Builds the RETURN (...) argument list for one retry stage
98
- let buildListArgs = stage => {
75
+ let buildListArgs = (stage) => {
99
76
  let args = [];
100
77
  if (stage.status) {
101
78
  args.push({ type: 'ATOM', value: 'STATUS' }, statusQueryAttributes);
@@ -113,7 +90,6 @@ module.exports = async (connection, reference, mailbox, options) => {
113
90
  }
114
91
  return args;
115
92
  };
116
-
117
93
  // Multiple mailboxes may claim the same special-use type (e.g., \\Sent) via
118
94
  // different sources (user hint, server extension, name match). After listing,
119
95
  // the best match wins.
@@ -123,14 +99,13 @@ module.exports = async (connection, reference, mailbox, options) => {
123
99
  }
124
100
  specialUseMatches[type].push({ entry, source });
125
101
  };
126
-
127
102
  // RFC 5258: the \NonExistent attribute implies \Noselect. Some servers only
128
103
  // return \NonExistent for phantom folders, so add \Noselect as well to keep
129
104
  // the flags consistent for consumers that only check \Noselect.
130
105
  // RETURN (SUBSCRIBED) - and some LSUB implementations - report subscription
131
106
  // state as a \Subscribed attribute. Move it to the subscribed property so the
132
107
  // output shape is the same however the state was delivered.
133
- let normalizeFlags = entry => {
108
+ let normalizeFlags = (entry) => {
134
109
  if (entry.flags.has('\\NonExistent')) {
135
110
  entry.flags.add('\\Noselect');
136
111
  }
@@ -139,7 +114,6 @@ module.exports = async (connection, reference, mailbox, options) => {
139
114
  entry.subscribed = true;
140
115
  }
141
116
  };
142
-
143
117
  // User-provided hints map mailbox paths to special-use types (e.g., {sent: "Sent Items"}).
144
118
  // These override server-reported flags and name-based guesses. Converted to a
145
119
  // path-keyed lookup: { "Sent Items" => "\\Sent" }
@@ -148,52 +122,43 @@ module.exports = async (connection, reference, mailbox, options) => {
148
122
  let specialUseHints = Object.create(null);
149
123
  if (options.specialUseHints && typeof options.specialUseHints === 'object') {
150
124
  for (let type of Object.keys(options.specialUseHints)) {
151
- if (
152
- ['sent', 'junk', 'trash', 'drafts', 'archive'].includes(type) &&
125
+ if (['sent', 'junk', 'trash', 'drafts', 'archive'].includes(type) &&
153
126
  options.specialUseHints[type] &&
154
- typeof options.specialUseHints[type] === 'string'
155
- ) {
127
+ typeof options.specialUseHints[type] === 'string') {
156
128
  // Capitalize first letter: "sent" -> "\\Sent"
157
129
  specialUseHints[normalizePath(connection, options.specialUseHints[type])] = `\\${type.replace(/^./, c => c.toUpperCase())}`;
158
130
  }
159
131
  }
160
132
  }
161
-
162
133
  // Executes a LIST (or XLIST) command and collects mailbox entries.
163
134
  // Called once for the main listing and optionally again for INBOX if a
164
135
  // namespace prefix was used (INBOX may live outside the namespace).
165
136
  let runList = async (reference, mailbox, returnArgs) => {
166
137
  const cmdArgs = [encodePath(connection, reference), encodePath(connection, mailbox)];
167
-
168
138
  if (returnArgs.length) {
169
139
  cmdArgs.push({ type: 'ATOM', value: 'RETURN' }, returnArgs);
170
140
  }
171
-
172
141
  let response = await connection.exec(listCommand, cmdArgs, {
173
142
  untagged: {
174
143
  // Each untagged LIST response: * LIST (<flags>) "<delimiter>" "<mailbox name>"
175
144
  // attributes[0] = flags array, attributes[1] = delimiter, attributes[2] = mailbox name
176
- [listCommand]: async untagged => {
145
+ [listCommand]: async (untagged) => {
177
146
  if (!untagged.attributes || !untagged.attributes.length) {
178
147
  return;
179
148
  }
180
-
181
149
  let entry = {
182
150
  // Decode from modified UTF-7 wire format and normalize the path
183
- path: normalizePath(connection, decodePath(connection, (untagged.attributes[2] && untagged.attributes[2].value) || '')),
184
- pathAsListed: (untagged.attributes[2] && untagged.attributes[2].value) || '',
151
+ path: normalizePath(connection, decodePath(connection, ((untagged.attributes[2] && untagged.attributes[2].value) || ''))),
152
+ pathAsListed: ((untagged.attributes[2] && untagged.attributes[2].value) || ''),
185
153
  flags: new Set(getStringList(untagged.attributes[0])),
186
- delimiter: untagged.attributes[1] && untagged.attributes[1].value,
154
+ delimiter: (untagged.attributes[1] && untagged.attributes[1].value),
187
155
  listed: true
188
156
  };
189
-
190
157
  normalizeFlags(entry);
191
-
192
158
  // Check user-provided hints first (highest priority)
193
159
  if (specialUseHints[entry.path]) {
194
160
  addSpecialUseMatch(entry, specialUseHints[entry.path], 'user');
195
161
  }
196
-
197
162
  // XLIST marks INBOX with a \\Inbox flag. Remove it from flags
198
163
  // (it's not a standard flag) and register as special-use match.
199
164
  // XLIST may also use a localised name (e.g., "Posteingang" for German INBOX).
@@ -203,31 +168,23 @@ module.exports = async (connection, reference, mailbox, options) => {
203
168
  addSpecialUseMatch(entry, '\\Inbox', 'extension');
204
169
  }
205
170
  }
206
-
207
171
  // Name-based INBOX detection: any mailbox named "INBOX" (case-insensitive)
208
172
  // is the inbox per RFC 3501. Phantom \NonExistent entries (subscribed
209
173
  // leftovers of deleted mailboxes) must not claim the slot by name.
210
174
  if (entry.path.toUpperCase() === 'INBOX' && !entry.flags.has('\\NonExistent')) {
211
175
  addSpecialUseMatch(entry, '\\Inbox', 'name');
212
176
  }
213
-
214
177
  // Strip leading delimiter (some servers prepend it to paths)
215
178
  if (entry.delimiter && entry.path.charAt(0) === entry.delimiter) {
216
179
  entry.path = entry.path.slice(1);
217
180
  }
218
-
219
181
  // Build parent path hierarchy for tree construction and sorting
220
182
  entry.parentPath = entry.delimiter && entry.path ? entry.path.substr(0, entry.path.lastIndexOf(entry.delimiter)) : '';
221
183
  entry.parent = entry.delimiter ? entry.path.split(entry.delimiter) : [entry.path];
222
184
  entry.name = entry.parent.pop();
223
-
224
185
  // Try to detect special-use from server flags or well-known names
225
186
  // (e.g., "Sent", "Drafts", "Junk", "Trash")
226
- let { flag: specialUseFlag, source: flagSource } = specialUse(
227
- connection.capabilities.has('XLIST') || hasCapability(connection, 'SPECIAL-USE'),
228
- entry
229
- );
230
-
187
+ let { flag: specialUseFlag, source: flagSource } = specialUse(connection.capabilities.has('XLIST') || hasCapability(connection, 'SPECIAL-USE'), entry);
231
188
  // A name-based match for a \NonExistent phantom entry could win the
232
189
  // special-use slot over the real folder - only server-provided flags
233
190
  // are trusted for nonexistent entries. Covers every name-derived
@@ -235,34 +192,28 @@ module.exports = async (connection, reference, mailbox, options) => {
235
192
  if (specialUseFlag && (!isNameSource(flagSource) || !entry.flags.has('\\NonExistent'))) {
236
193
  addSpecialUseMatch(entry, specialUseFlag, flagSource);
237
194
  }
238
-
239
195
  entries.push(entry);
240
196
  },
241
-
242
197
  // Inline STATUS response from LIST-STATUS extension (RFC 5819).
243
198
  // Parses alternating key-value pairs (i % 2 pattern).
244
- STATUS: async untagged => {
245
- let statusPath = normalizePath(connection, decodePath(connection, (untagged.attributes[0] && untagged.attributes[0].value) || ''));
199
+ STATUS: async (untagged) => {
200
+ let statusPath = normalizePath(connection, decodePath(connection, ((untagged.attributes[0] && untagged.attributes[0].value) || '')));
246
201
  let statusList = untagged.attributes && Array.isArray(untagged.attributes[1]) ? untagged.attributes[1] : false;
247
202
  if (!statusList || !statusPath) {
248
203
  return;
249
204
  }
250
-
251
205
  let map = { path: statusPath };
252
206
  parseStatusList(statusList, (key, value) => {
253
207
  map[key] = value;
254
208
  });
255
-
256
209
  statusMap.set(statusPath, map);
257
210
  }
258
211
  }
259
212
  });
260
213
  response.next();
261
214
  };
262
-
263
215
  let normalizedReference = normalizePath(connection, reference || '');
264
216
  let normalizedMailbox = normalizePath(connection, mailbox || '', true);
265
-
266
217
  // Retry stages for the main listing: start with all applicable RETURN options
267
218
  // and drop one option group per retry. Consecutive stages differ by exactly one
268
219
  // group, so a success right after a rejection identifies the offending group
@@ -276,29 +227,25 @@ module.exports = async (connection, reference, mailbox, options) => {
276
227
  }
277
228
  if (canRequestStatus) {
278
229
  stages.push({ status: true, subscribed: false });
279
- } else if (canRequestSubscribed) {
230
+ }
231
+ else if (canRequestSubscribed) {
280
232
  stages.push({ status: false, subscribed: true });
281
233
  }
282
234
  stages.push({ status: false, subscribed: false });
283
-
284
235
  // A tagged BAD is how servers reject unrecognized RETURN options (RFC 9051
285
236
  // section 6.3.9). A tagged NO is an operational failure, and throttling
286
237
  // errors (code ETHROTTLE) also surface with a BAD status - neither says
287
238
  // anything about the RETURN options, so they propagate to the caller.
288
- let isRejectedCommand = err => err.responseStatus === 'BAD' && err.code !== 'ETHROTTLE';
289
-
239
+ let isRejectedCommand = (err) => err.responseStatus === 'BAD' && err.code !== 'ETHROTTLE';
290
240
  // Stage of the successful attempt - reused by the INBOX fixup and the LSUB
291
241
  // decision below
292
242
  let successStage = null;
293
-
294
243
  // Whether any source actually reported subscription state. RETURN (SUBSCRIBED)
295
244
  // and LSUB are the only two, and a server can refuse both
296
245
  let subscriptionStateKnown = false;
297
-
298
246
  // A server may also volunteer \Subscribed on a plain LIST, which normalizeFlags
299
247
  // folds into the entry - that counts as the state having been reported
300
248
  let anyEntrySubscribed = () => entries.some(entry => entry.subscribed);
301
-
302
249
  let lastRejectedStage = null;
303
250
  let auxRetryInserted = false;
304
251
  for (let i = 0; i < stages.length; i++) {
@@ -322,12 +269,10 @@ module.exports = async (connection, reference, mailbox, options) => {
322
269
  if (lastRejectedStage.status && !stage.status) {
323
270
  connection.skipListStatusArgs = true;
324
271
  }
325
- if (
326
- stageHasAuxArgs(lastRejectedStage) &&
272
+ if (stageHasAuxArgs(lastRejectedStage) &&
327
273
  stage.aux === false &&
328
274
  lastRejectedStage.status === stage.status &&
329
- lastRejectedStage.subscribed === stage.subscribed
330
- ) {
275
+ lastRejectedStage.subscribed === stage.subscribed) {
331
276
  // Same option groups, only the auxiliary args dropped - the
332
277
  // auxiliaries are proven to be what the server rejects
333
278
  connection.skipListAuxArgs = true;
@@ -336,7 +281,8 @@ module.exports = async (connection, reference, mailbox, options) => {
336
281
  successStage = stage;
337
282
  subscriptionStateKnown = !!stage.subscribed;
338
283
  break;
339
- } catch (err) {
284
+ }
285
+ catch (err) {
340
286
  if (i === stages.length - 1 || !isRejectedCommand(err)) {
341
287
  throw err;
342
288
  }
@@ -351,11 +297,9 @@ module.exports = async (connection, reference, mailbox, options) => {
351
297
  connection.log.warn({ msg: 'LIST RETURN options rejected, retrying with reduced options', err, cid: connection.id });
352
298
  }
353
299
  }
354
-
355
300
  if (options.listOnly) {
356
301
  return entries;
357
302
  }
358
-
359
303
  // When listing with a namespace prefix (e.g., "INBOX."), INBOX itself may
360
304
  // not appear in results. Run a separate LIST for INBOX to ensure it's included.
361
305
  if (normalizedReference && !specialUseMatches['\\Inbox']) {
@@ -371,7 +315,8 @@ module.exports = async (connection, reference, mailbox, options) => {
371
315
  }
372
316
  try {
373
317
  await runList('', 'INBOX', returnArgs);
374
- } catch (err) {
318
+ }
319
+ catch (err) {
375
320
  // The main listing just succeeded with the same RETURN options, so a
376
321
  // rejection here says nothing about the options themselves - retry
377
322
  // this one call plain without latching any skip flags. Accepted edge:
@@ -384,7 +329,8 @@ module.exports = async (connection, reference, mailbox, options) => {
384
329
  for (let type of Object.keys(specialUseMatches)) {
385
330
  if (!(type in specialUseCountsBefore)) {
386
331
  delete specialUseMatches[type];
387
- } else {
332
+ }
333
+ else {
388
334
  specialUseMatches[type].length = specialUseCountsBefore[type];
389
335
  }
390
336
  }
@@ -392,7 +338,6 @@ module.exports = async (connection, reference, mailbox, options) => {
392
338
  await runList('', 'INBOX', []);
393
339
  }
394
340
  }
395
-
396
341
  // Attach STATUS data to each selectable mailbox. If LIST-STATUS was used,
397
342
  // data is already in statusMap; otherwise, fall back to individual STATUS commands.
398
343
  if (options.statusQuery) {
@@ -408,18 +353,19 @@ module.exports = async (connection, reference, mailbox, options) => {
408
353
  if (syntheticRecent) {
409
354
  entry.status.recent = 0;
410
355
  }
411
- } else if (!statusMap.size) {
356
+ }
357
+ else if (!statusMap.size) {
412
358
  // Server didn't support LIST-STATUS; fall back to per-mailbox STATUS
413
359
  try {
414
360
  entry.status = await connection.run('STATUS', entry.path, options.statusQuery);
415
- } catch (err) {
361
+ }
362
+ catch (err) {
416
363
  entry.status = { error: err };
417
364
  }
418
365
  }
419
366
  }
420
367
  }
421
368
  }
422
-
423
369
  // LSUB (RFC 3501 6.3.9): queries which mailboxes the user is subscribed to.
424
370
  // We merge subscription info into the entries already collected from LIST.
425
371
  // Subscribed-only mailboxes that weren't in LIST are intentionally ignored
@@ -427,31 +373,26 @@ module.exports = async (connection, reference, mailbox, options) => {
427
373
  let runLsub = async () => {
428
374
  let response = await connection.exec('LSUB', [encodePath(connection, normalizedReference), encodePath(connection, normalizedMailbox)], {
429
375
  untagged: {
430
- LSUB: async untagged => {
376
+ LSUB: async (untagged) => {
431
377
  if (!untagged.attributes || !untagged.attributes.length) {
432
378
  return;
433
379
  }
434
-
435
380
  let entry = {
436
- path: normalizePath(connection, decodePath(connection, (untagged.attributes[2] && untagged.attributes[2].value) || '')),
437
- pathAsListed: (untagged.attributes[2] && untagged.attributes[2].value) || '',
381
+ path: normalizePath(connection, decodePath(connection, ((untagged.attributes[2] && untagged.attributes[2].value) || ''))),
382
+ pathAsListed: ((untagged.attributes[2] && untagged.attributes[2].value) || ''),
438
383
  flags: new Set(getStringList(untagged.attributes[0])),
439
- delimiter: untagged.attributes[1] && untagged.attributes[1].value,
384
+ delimiter: (untagged.attributes[1] && untagged.attributes[1].value),
440
385
  subscribed: true
441
386
  };
442
-
443
387
  if (entry.path.toUpperCase() === 'INBOX') {
444
388
  addSpecialUseMatch(entry, '\\Inbox', 'name');
445
389
  }
446
-
447
390
  if (entry.delimiter && entry.path.charAt(0) === entry.delimiter) {
448
391
  entry.path = entry.path.slice(1);
449
392
  }
450
-
451
393
  entry.parentPath = entry.delimiter && entry.path ? entry.path.substr(0, entry.path.lastIndexOf(entry.delimiter)) : '';
452
394
  entry.parent = entry.delimiter ? entry.path.split(entry.delimiter) : [entry.path];
453
395
  entry.name = entry.parent.pop();
454
-
455
396
  // Merge LSUB data into existing LIST entry if found
456
397
  let existing = entries.find(existing => existing.path === entry.path);
457
398
  if (existing) {
@@ -466,7 +407,6 @@ module.exports = async (connection, reference, mailbox, options) => {
466
407
  });
467
408
  response.next();
468
409
  };
469
-
470
410
  // Never sent on a rev2 session - LSUB is not part of that protocol version, and
471
411
  // some servers break the rest of the session over the rejection, so this is
472
412
  // decided up front rather than left to the skipLsub latch below. On rev1 it is
@@ -485,12 +425,14 @@ module.exports = async (connection, reference, mailbox, options) => {
485
425
  try {
486
426
  await runLsub();
487
427
  subscriptionStateKnown = true;
488
- } catch (err) {
428
+ }
429
+ catch (err) {
489
430
  if (isRejectedCommand(err)) {
490
431
  // Tagged BAD: the server does not implement LSUB despite advertising
491
432
  // rev1 - skip it for the rest of this connection
492
433
  connection.skipLsub = true;
493
- } else if (err.responseStatus !== 'NO' || err.code === 'ETHROTTLE') {
434
+ }
435
+ else if (err.responseStatus !== 'NO' || err.code === 'ETHROTTLE') {
494
436
  // Transport failures and throttling: rethrow, every follow-up
495
437
  // command would fail too or the caller needs to back off
496
438
  throw err;
@@ -500,7 +442,6 @@ module.exports = async (connection, reference, mailbox, options) => {
500
442
  connection.log.warn({ msg: 'Failed to request subscription info', err, cid: connection.id });
501
443
  }
502
444
  }
503
-
504
445
  // Resolve special-use conflicts: for each type, pick the best candidate
505
446
  // based on source priority (user > extension > name), then alphabetically.
506
447
  // Only the winning entry gets the specialUse property set.
@@ -513,14 +454,12 @@ module.exports = async (connection, reference, mailbox, options) => {
513
454
  }
514
455
  return aSource - bSource;
515
456
  });
516
-
517
457
  if (!sortedEntries[0].entry.specialUse) {
518
458
  let source = sortedEntries[0].source;
519
459
  sortedEntries[0].entry.specialUse = type;
520
460
  sortedEntries[0].entry.specialUseSource = PUBLIC_SOURCE[source] || source;
521
461
  }
522
462
  }
523
-
524
463
  // No source answered, so "not subscribed" was never actually reported for any of
525
464
  // these folders - the state is unknown, not false. Reporting the whole listing as
526
465
  // unsubscribed would hide every folder from a client that filters on subscription
@@ -535,13 +474,11 @@ module.exports = async (connection, reference, mailbox, options) => {
535
474
  }
536
475
  }
537
476
  }
538
-
539
477
  // INBOX should always appear as subscribed regardless of LSUB results
540
478
  let inboxEntry = entries.find(entry => entry.specialUse === '\\Inbox');
541
479
  if (inboxEntry && !inboxEntry.subscribed) {
542
480
  inboxEntry.subscribed = true;
543
481
  }
544
-
545
482
  // Sort: special-use mailboxes first (in FLAG_SORT_ORDER), then alphabetically
546
483
  // by path segments for a natural folder hierarchy ordering.
547
484
  return entries.sort((a, b) => {
@@ -554,10 +491,8 @@ module.exports = async (connection, reference, mailbox, options) => {
554
491
  if (a.specialUse && b.specialUse) {
555
492
  return FLAG_SORT_ORDER.indexOf(a.specialUse) - FLAG_SORT_ORDER.indexOf(b.specialUse);
556
493
  }
557
-
558
494
  let aList = [].concat(a.parent).concat(a.name);
559
495
  let bList = [].concat(b.parent).concat(b.name);
560
-
561
496
  for (let i = 0; i < aList.length; i++) {
562
497
  let aPart = aList[i];
563
498
  let bPart = bList[i];
@@ -565,14 +500,14 @@ module.exports = async (connection, reference, mailbox, options) => {
565
500
  return aPart.localeCompare(bPart || '');
566
501
  }
567
502
  }
568
-
569
503
  return a.path.localeCompare(b.path);
570
504
  });
571
- } catch (err) {
505
+ }
506
+ catch (err) {
572
507
  // Rewrite the parsed err.response into the response text and set
573
508
  // serverResponseCode, same as the other command modules
574
509
  await enhanceCommandError(err);
575
510
  connection.log.warn({ msg: 'Failed to list folders', err, cid: connection.id });
576
511
  throw err;
577
512
  }
578
- };
513
+ }
@@ -0,0 +1,11 @@
1
+ import type { ImapFlow } from '../imap-flow.js';
2
+ /**
3
+ * Authenticates user using the IMAP LOGIN command.
4
+ *
5
+ * @param connection - IMAP connection instance
6
+ * @param username - The username to authenticate with
7
+ * @param password - The password to authenticate with
8
+ * @returns The authenticated username, or undefined if already authenticated
9
+ * @throws If authentication fails, with authenticationFailed and serverResponseCode properties set
10
+ */
11
+ export default function login(connection: ImapFlow, username: string, password: string): Promise<string | undefined>;
@@ -1,22 +1,18 @@
1
- 'use strict';
2
-
3
- const { getStatusCode, getErrorText } = require('../tools.js');
4
-
1
+ import { getStatusCode, getErrorText } from '../tools.js';
5
2
  /**
6
3
  * Authenticates user using the IMAP LOGIN command.
7
4
  *
8
- * @param {Object} connection - IMAP connection instance
9
- * @param {string} username - The username to authenticate with
10
- * @param {string} password - The password to authenticate with
11
- * @returns {Promise<string|undefined>} The authenticated username, or undefined if already authenticated
12
- * @throws {Error} If authentication fails, with authenticationFailed and serverResponseCode properties set
5
+ * @param connection - IMAP connection instance
6
+ * @param username - The username to authenticate with
7
+ * @param password - The password to authenticate with
8
+ * @returns The authenticated username, or undefined if already authenticated
9
+ * @throws If authentication fails, with authenticationFailed and serverResponseCode properties set
13
10
  */
14
- module.exports = async (connection, username, password) => {
11
+ export default async function login(connection, username, password) {
15
12
  if (connection.state !== connection.states.NOT_AUTHENTICATED) {
16
13
  // nothing to do here
17
14
  return;
18
15
  }
19
-
20
16
  try {
21
17
  let response = await connection.exec('LOGIN', [
22
18
  { type: 'STRING', value: username },
@@ -24,13 +20,12 @@ module.exports = async (connection, username, password) => {
24
20
  { type: 'STRING', value: password, sensitive: true }
25
21
  ]);
26
22
  response.next();
27
-
28
23
  // Record that LOGIN was the method used, so the connection knows which
29
24
  // auth mechanism succeeded (used for reconnection and diagnostics).
30
25
  connection.authCapabilities.set('LOGIN', true);
31
-
32
26
  return username;
33
- } catch (err) {
27
+ }
28
+ catch (err) {
34
29
  let errorCode = getStatusCode(err.response);
35
30
  if (errorCode) {
36
31
  err.serverResponseCode = errorCode;
@@ -39,4 +34,4 @@ module.exports = async (connection, username, password) => {
39
34
  err.response = await getErrorText(err.response);
40
35
  throw err;
41
36
  }
42
- };
37
+ }
@@ -0,0 +1,8 @@
1
+ import type { ImapFlow } from '../imap-flow.js';
2
+ /**
3
+ * Logs out the user and closes the connection.
4
+ *
5
+ * @param connection - IMAP connection instance
6
+ * @returns True if logout command succeeded, false otherwise
7
+ */
8
+ export default function logout(connection: ImapFlow): Promise<boolean>;
@@ -1,29 +1,26 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Logs out the user and closes the connection.
5
3
  *
6
- * @param {Object} connection - IMAP connection instance
7
- * @returns {Promise<boolean>} True if logout command succeeded, false otherwise
4
+ * @param connection - IMAP connection instance
5
+ * @returns True if logout command succeeded, false otherwise
8
6
  */
9
- module.exports = async connection => {
7
+ export default async function logout(connection) {
10
8
  if (connection.state === connection.states.LOGOUT) {
11
9
  // nothing to do here
12
10
  return false;
13
11
  }
14
-
15
12
  if (connection.state === connection.states.NOT_AUTHENTICATED) {
16
- // Not yet authenticated -- no LOGOUT command needed; just close the socket.
13
+ // Not yet authenticated, no LOGOUT command needed; just close the socket.
17
14
  connection.state = connection.states.LOGOUT;
18
15
  connection.close();
19
16
  return false;
20
17
  }
21
-
22
18
  let response;
23
19
  try {
24
20
  response = await connection.exec('LOGOUT');
25
21
  return true;
26
- } catch (err) {
22
+ }
23
+ catch (err) {
27
24
  // If the connection is already gone, treat as successful logout
28
25
  if (err.code === 'NoConnection') {
29
26
  return true;
@@ -31,7 +28,8 @@ module.exports = async connection => {
31
28
  connection.log.warn({ err, cid: connection.id });
32
29
  return false;
33
30
  /* c8 ignore next */ // the catch above is exhaustive (never re-throws), so finally is only ever reached via normal completion
34
- } finally {
31
+ }
32
+ finally {
35
33
  // Set state to LOGOUT before closing to prevent any further commands from
36
34
  // being queued. The socket is closed unconditionally in this finally block
37
35
  // regardless of whether the LOGOUT command succeeded or failed.
@@ -41,4 +39,4 @@ module.exports = async connection => {
41
39
  }
42
40
  connection.close();
43
41
  }
44
- };
42
+ }