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
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ // Shared response-size limits for the IMAP parser. Kept in one place so the streaming parser
3
+ // (ImapStream) and the standalone token parser cannot drift apart, and so the documented
4
+ // defaults in the ImapFlowOptions type describe both paths.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createLiteralTooLargeError = exports.normalizeLimit = exports.MAX_RESPONSE_SIZE = exports.MAX_LINE_SIZE = exports.MAX_LITERAL_SIZE = void 0;
7
+ // Maximum allowed literal size: 1GB (1073741824 bytes)
8
+ exports.MAX_LITERAL_SIZE = 1024 * 1024 * 1024;
9
+ // Default maximum length of a single line (a response without a literal). Matches the literal cap:
10
+ // large literal-free responses (e.g. big SEARCH/LIST results) are legitimate, so this bound exists
11
+ // only to stop a server that never sends a line terminator, not to constrain normal traffic.
12
+ exports.MAX_LINE_SIZE = exports.MAX_LITERAL_SIZE;
13
+ // Default maximum total size of a single assembled response: every line segment and literal of
14
+ // one response combined. The per-line and per-literal caps alone cannot stop a server that
15
+ // spreads attacker-controlled bytes across an unbounded number of tokens of a single response
16
+ // (e.g. one FETCH answer carrying many maximum-size literals).
17
+ //
18
+ // Deliberately above the literal cap: the response total also carries the literal's marker line
19
+ // and the rest of the response framing, so a cap equal to MAX_LITERAL_SIZE would make a literal
20
+ // of exactly the maximum permitted size impossible to receive. Configuring both limits calls for
21
+ // the same headroom - set maxResponseSize above maxLiteralSize, not equal to it.
22
+ exports.MAX_RESPONSE_SIZE = 2 * exports.MAX_LITERAL_SIZE;
23
+ /**
24
+ * Normalizes a configured size limit. A non-negative integer is honored as-is (including 0, which
25
+ * means "reject anything non-empty"), and `Infinity` disables the limit; anything else falls back
26
+ * to the default, so an explicit 0 is not silently swallowed the way `value || DEFAULT` would
27
+ * swallow it.
28
+ *
29
+ * @param value - The configured value.
30
+ * @param defaultValue - Fallback when the value is not a usable limit.
31
+ * @returns The normalized limit.
32
+ */
33
+ const normalizeLimit = (value, defaultValue) => (Number.isInteger(value) || value === Infinity) && value >= 0 ? value : defaultValue;
34
+ exports.normalizeLimit = normalizeLimit;
35
+ /**
36
+ * Builds the `LiteralTooLarge` error. One shape for every place a literal is refused, so callers
37
+ * can rely on `code`, `literalSize` and `maxSize` regardless of which parser rejected it.
38
+ *
39
+ * @param literalSize - The declared literal size.
40
+ * @param maxSize - The bound that was exceeded.
41
+ * @param reason - What the bound was, when it is not the configured maximum.
42
+ * @returns The error to emit or throw.
43
+ */
44
+ const createLiteralTooLargeError = (literalSize, maxSize, reason) => {
45
+ const err = new Error(`Literal size ${literalSize} exceeds ${reason || `maximum allowed size of ${maxSize} bytes`}`);
46
+ err.code = 'LiteralTooLarge';
47
+ err.literalSize = literalSize;
48
+ err.maxSize = maxSize;
49
+ return err;
50
+ };
51
+ exports.createLiteralTooLargeError = createLiteralTooLargeError;
@@ -0,0 +1,68 @@
1
+ import type { ImapAttributeList, ParserOptions } from './types.js';
2
+ /**
3
+ * Parses a single IMAP response line into its structural components: tag, command,
4
+ * and attributes. Handles status responses (OK, NO, BAD, PREAUTH, BYE) with their
5
+ * human-readable text and response codes, as well as continuation responses ("+").
6
+ */
7
+ export declare class ParserInstance {
8
+ input: string;
9
+ options: ParserOptions;
10
+ remainder: string;
11
+ pos: number;
12
+ tag?: string | undefined;
13
+ command?: string | undefined;
14
+ humanReadable?: string | undefined;
15
+ /**
16
+ * Creates a new ParserInstance for parsing an IMAP response line.
17
+ *
18
+ * @param input - The raw IMAP response line to parse.
19
+ * @param options - Parser options passed through to the TokenParser for attribute parsing.
20
+ * @param options.literalPlus - Whether the LITERAL+ extension is in use.
21
+ * @param options.literals - Pre-parsed literal values from the stream.
22
+ */
23
+ constructor(input?: Buffer | string | null | undefined, options?: ParserOptions | undefined);
24
+ /**
25
+ * Extracts and returns the IMAP tag from the beginning of the response.
26
+ * The tag is typically "*" for untagged responses, "+" for continuation requests,
27
+ * or a client-assigned command tag like "A1".
28
+ *
29
+ * @returns The parsed tag string.
30
+ * @throws {Error} If the tag contains invalid characters.
31
+ */
32
+ getTag(): Promise<string>;
33
+ /**
34
+ * Extracts and returns the IMAP command or response name from the input.
35
+ * For continuation responses (tag "+"), returns an empty string and stores
36
+ * the remainder as human-readable text. For status responses (OK, NO, BAD,
37
+ * PREAUTH, BYE), separates the optional response code from the human-readable text.
38
+ *
39
+ * @returns The parsed command string.
40
+ * @throws {Error} If the command contains invalid characters or input ends unexpectedly.
41
+ */
42
+ getCommand(): Promise<string>;
43
+ /**
44
+ * Extracts the next whitespace-delimited element from the input and validates it
45
+ * against the given syntax character set. Advances the parser position past the element.
46
+ *
47
+ * @param syntax - A string of allowed characters for the element (as returned by imap-formal-syntax methods).
48
+ * @returns The extracted element string.
49
+ * @throws {Error} If the element contains characters not in the syntax set, or if input ends unexpectedly.
50
+ */
51
+ getElement(syntax: string): Promise<string>;
52
+ /**
53
+ * Consumes a single space character from the current position in the input.
54
+ * Advances the parser position by one.
55
+ *
56
+ * @throws {Error} If the current character is not a space, or if input has ended unexpectedly.
57
+ */
58
+ getSpace(): Promise<void>;
59
+ /**
60
+ * Parses the remaining input as IMAP attributes using the TokenParser.
61
+ * This handles complex structures including nested lists, literals, strings,
62
+ * atoms, sections, sequences, and partial ranges.
63
+ *
64
+ * @returns A promise that resolves to an array of parsed attribute objects.
65
+ * @throws {Error} If the input contains unexpected whitespace, invalid characters, or ends unexpectedly.
66
+ */
67
+ getAttributes(): Promise<ImapAttributeList>;
68
+ }
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+ /* eslint new-cap: 0 */
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ParserInstance = void 0;
8
+ const imap_formal_syntax_js_1 = __importDefault(require("./imap-formal-syntax.js"));
9
+ const token_parser_js_1 = require("./token-parser.js");
10
+ /**
11
+ * Parses a single IMAP response line into its structural components: tag, command,
12
+ * and attributes. Handles status responses (OK, NO, BAD, PREAUTH, BYE) with their
13
+ * human-readable text and response codes, as well as continuation responses ("+").
14
+ */
15
+ class ParserInstance {
16
+ /**
17
+ * Creates a new ParserInstance for parsing an IMAP response line.
18
+ *
19
+ * @param input - The raw IMAP response line to parse.
20
+ * @param options - Parser options passed through to the TokenParser for attribute parsing.
21
+ * @param options.literalPlus - Whether the LITERAL+ extension is in use.
22
+ * @param options.literals - Pre-parsed literal values from the stream.
23
+ */
24
+ constructor(input, options) {
25
+ this.input = (input || '').toString();
26
+ this.options = options || {};
27
+ this.remainder = this.input;
28
+ this.pos = 0;
29
+ }
30
+ /**
31
+ * Extracts and returns the IMAP tag from the beginning of the response.
32
+ * The tag is typically "*" for untagged responses, "+" for continuation requests,
33
+ * or a client-assigned command tag like "A1".
34
+ *
35
+ * @returns The parsed tag string.
36
+ * @throws {Error} If the tag contains invalid characters.
37
+ */
38
+ async getTag() {
39
+ if (!this.tag) {
40
+ this.tag = await this.getElement(imap_formal_syntax_js_1.default.tag() + '*+');
41
+ }
42
+ return this.tag;
43
+ }
44
+ /**
45
+ * Extracts and returns the IMAP command or response name from the input.
46
+ * For continuation responses (tag "+"), returns an empty string and stores
47
+ * the remainder as human-readable text. For status responses (OK, NO, BAD,
48
+ * PREAUTH, BYE), separates the optional response code from the human-readable text.
49
+ *
50
+ * @returns The parsed command string.
51
+ * @throws {Error} If the command contains invalid characters or input ends unexpectedly.
52
+ */
53
+ async getCommand() {
54
+ if (this.tag === '+') {
55
+ // special case
56
+ this.humanReadable = this.remainder.trim();
57
+ this.remainder = '';
58
+ return '';
59
+ }
60
+ if (!this.command) {
61
+ this.command = await this.getElement(imap_formal_syntax_js_1.default.command());
62
+ }
63
+ // Status responses have the format: TAG OK/NO/BAD [response-code] human-readable text
64
+ // Example: * OK [CAPABILITY IMAP4rev1] Server ready
65
+ // Example: A1 NO [AUTHENTICATIONFAILED] Invalid credentials
66
+ // We need to separate the optional [response-code] from the human-readable text.
67
+ switch ((this.command || '').toString().toUpperCase()) {
68
+ case 'OK':
69
+ case 'NO':
70
+ case 'BAD':
71
+ case 'PREAUTH':
72
+ case 'BYE':
73
+ {
74
+ let match = this.remainder.match(/^\s+\[/);
75
+ if (match) {
76
+ // Find the ']' that closes the response code. Inner brackets are
77
+ // tracked because servers do put bracketed values inside a code
78
+ // (e.g. a "[css3-page]" keyword in a PERMANENTFLAGS list), which a
79
+ // first-']' scan would cut in half.
80
+ let nesting = 1;
81
+ let end = -1;
82
+ for (let i = match[0].length; i < this.remainder.length; i++) {
83
+ let c = this.remainder[i];
84
+ if (c === '[') {
85
+ nesting++;
86
+ }
87
+ else if (c === ']') {
88
+ nesting--;
89
+ }
90
+ if (!nesting) {
91
+ end = i;
92
+ break;
93
+ }
94
+ }
95
+ // Unbalanced '[' inside the code: the RFC 9051 free-text form
96
+ // (`atom [SP 1*<any TEXT-CHAR except "]">]`) permits '[' but not
97
+ // ']', so the code really does end at the first ']' here. Without
98
+ // this fallback the scan finds no closing bracket at all and the
99
+ // human-readable text - what every error message is built from -
100
+ // is swallowed into the response code.
101
+ if (end < 0) {
102
+ end = this.remainder.indexOf(']', match[0].length);
103
+ }
104
+ if (end >= 0) {
105
+ this.humanReadable = this.remainder.substring(end + 1).trim();
106
+ this.remainder = this.remainder.substring(0, end + 1);
107
+ }
108
+ }
109
+ else {
110
+ this.humanReadable = this.remainder.trim();
111
+ this.remainder = '';
112
+ }
113
+ }
114
+ break;
115
+ }
116
+ return this.command;
117
+ }
118
+ /**
119
+ * Extracts the next whitespace-delimited element from the input and validates it
120
+ * against the given syntax character set. Advances the parser position past the element.
121
+ *
122
+ * @param syntax - A string of allowed characters for the element (as returned by imap-formal-syntax methods).
123
+ * @returns The extracted element string.
124
+ * @throws {Error} If the element contains characters not in the syntax set, or if input ends unexpectedly.
125
+ */
126
+ async getElement(syntax) {
127
+ let match, element, errPos;
128
+ if (/^\s/.test(this.remainder)) {
129
+ let error = new Error(`Unexpected whitespace at position ${this.pos} [E1]`);
130
+ error.code = 'ParserError1';
131
+ error.parserContext = { input: this.input, pos: this.pos };
132
+ throw error;
133
+ }
134
+ if ((match = this.remainder.match(/^\s*[^\s]+(?=\s|$)/))) {
135
+ element = match[0];
136
+ if ((errPos = imap_formal_syntax_js_1.default.verify(element, syntax)) >= 0) {
137
+ if (this.tag === 'Server' && element === 'Unavailable.') {
138
+ // Microsoft Exchange sometimes sends a non-standard response
139
+ // "Server Unavailable." instead of a proper IMAP tagged/untagged response.
140
+ // We detect this specific pattern and convert it into a synthetic BAD response
141
+ // so the rest of the parser can handle it gracefully.
142
+ let error = new Error(`Server returned an error: ${this.input}`);
143
+ error.code = 'ParserErrorExchange';
144
+ error.parserContext = {
145
+ input: this.input,
146
+ element,
147
+ pos: this.pos,
148
+ value: {
149
+ tag: '*',
150
+ command: 'BAD',
151
+ attributes: [{ type: 'TEXT', value: this.input }]
152
+ }
153
+ };
154
+ throw error;
155
+ }
156
+ let error = new Error(`Unexpected char at position ${this.pos + errPos} [E2: ${JSON.stringify(element.charAt(errPos))}]`);
157
+ error.code = 'ParserError2';
158
+ error.parserContext = { input: this.input, element, pos: this.pos };
159
+ throw error;
160
+ }
161
+ }
162
+ else {
163
+ let error = new Error(`Unexpected end of input at position ${this.pos} [E3]`);
164
+ error.code = 'ParserError3';
165
+ error.parserContext = { input: this.input, pos: this.pos };
166
+ throw error;
167
+ }
168
+ this.pos += match[0].length;
169
+ this.remainder = this.remainder.substr(match[0].length);
170
+ return element;
171
+ }
172
+ /**
173
+ * Consumes a single space character from the current position in the input.
174
+ * Advances the parser position by one.
175
+ *
176
+ * @throws {Error} If the current character is not a space, or if input has ended unexpectedly.
177
+ */
178
+ async getSpace() {
179
+ if (!this.remainder.length) {
180
+ if (this.tag === '+' && this.pos === 1) {
181
+ // special case, empty + response
182
+ return;
183
+ }
184
+ let error = new Error(`Unexpected end of input at position ${this.pos} [E4]`);
185
+ error.code = 'ParserError4';
186
+ error.parserContext = { input: this.input, pos: this.pos };
187
+ throw error;
188
+ }
189
+ if (imap_formal_syntax_js_1.default.verify(this.remainder.charAt(0), imap_formal_syntax_js_1.default.SP()) >= 0) {
190
+ let error = new Error(`Unexpected char at position ${this.pos} [E5: ${JSON.stringify(this.remainder.charAt(0))}]`);
191
+ error.code = 'ParserError5';
192
+ error.parserContext = { input: this.input, element: this.remainder, pos: this.pos };
193
+ throw error;
194
+ }
195
+ this.pos++;
196
+ this.remainder = this.remainder.substr(1);
197
+ }
198
+ /**
199
+ * Parses the remaining input as IMAP attributes using the TokenParser.
200
+ * This handles complex structures including nested lists, literals, strings,
201
+ * atoms, sections, sequences, and partial ranges.
202
+ *
203
+ * @returns A promise that resolves to an array of parsed attribute objects.
204
+ * @throws {Error} If the input contains unexpected whitespace, invalid characters, or ends unexpectedly.
205
+ */
206
+ async getAttributes() {
207
+ if (!this.remainder.length) {
208
+ let error = new Error(`Unexpected end of input at position ${this.pos} [E6]`);
209
+ error.code = 'ParserError6';
210
+ error.parserContext = { input: this.input, pos: this.pos };
211
+ throw error;
212
+ }
213
+ if (/^\s/.test(this.remainder)) {
214
+ let error = new Error(`Unexpected whitespace at position ${this.pos} [E7]`);
215
+ error.code = 'ParserError7';
216
+ error.parserContext = { input: this.input, element: this.remainder, pos: this.pos };
217
+ throw error;
218
+ }
219
+ const tokenParser = new token_parser_js_1.TokenParser(this, this.pos, this.remainder, this.options);
220
+ return await tokenParser.getAttributes();
221
+ }
222
+ }
223
+ exports.ParserInstance = ParserInstance;
@@ -0,0 +1,91 @@
1
+ import type { ImapAttributeList, ParserOptions } from './types.js';
2
+ import type { ParserInstance } from './parser-instance.js';
3
+ /**
4
+ * A node of the parse tree built by TokenParser. `type` is false for a node that has not
5
+ * been classified yet, 'TREE' for the root, and otherwise the token or structure type
6
+ * (ATOM, string, LITERAL, SEQUENCE, LIST, SECTION, PARTIAL).
7
+ */
8
+ export interface TokenNode {
9
+ childNodes: TokenNode[];
10
+ type: string | false;
11
+ value: string | Buffer;
12
+ isClosed: boolean;
13
+ parentNode?: TokenNode | undefined;
14
+ depth: number;
15
+ startPos?: number | undefined;
16
+ endPos?: number | undefined;
17
+ literalType?: string | undefined;
18
+ /** Digits accumulated as a string while the literal marker is read, converted to a number once the marker closes */
19
+ literalLength?: string | number | undefined;
20
+ literalPlus?: boolean | undefined;
21
+ started?: boolean | undefined;
22
+ chBuffer?: Buffer | undefined;
23
+ chPos?: number | undefined;
24
+ }
25
+ /**
26
+ * The parent object a TokenParser reads the parsed command from
27
+ */
28
+ export interface TokenParserParent {
29
+ command?: string | undefined;
30
+ }
31
+ /**
32
+ * Tokenizes an IMAP attribute string into a tree of typed nodes.
33
+ * Handles all IMAP data types: atoms, quoted strings, literals (including literal8),
34
+ * sequences, lists (parenthesized groups), sections (bracketed groups), and partial ranges.
35
+ * Enforces a maximum nesting depth of {@link MAX_NODE_DEPTH} to prevent stack overflow
36
+ * from malicious input.
37
+ */
38
+ export declare class TokenParser {
39
+ str: string;
40
+ options: ParserOptions;
41
+ parent: TokenParserParent | ParserInstance;
42
+ maxLiteralSize: number;
43
+ tree: TokenNode;
44
+ currentNode: TokenNode;
45
+ pos: number;
46
+ state: number;
47
+ expectedLiteralType?: string | false | undefined;
48
+ /**
49
+ * Creates a new TokenParser.
50
+ *
51
+ * @param parent - The parent ParserInstance that owns this token parser. Used to access the parsed command for context-sensitive parsing.
52
+ * @param startPos - The starting position offset in the original input, used for error reporting.
53
+ * @param str - The attribute string to tokenize.
54
+ * @param options - Parser options.
55
+ * @param options.literalPlus - Whether the LITERAL+ extension is in use.
56
+ * @param options.literals - Pre-parsed literal values from the input stream.
57
+ * @param options.maxLiteralSize - Maximum size (in bytes) of a literal parsed inline
58
+ * from the input, i.e. when no pre-parsed literal buffers were supplied. Defaults to 1GB.
59
+ */
60
+ constructor(parent: TokenParserParent | ParserInstance, startPos?: number | undefined, str?: string | null | undefined, options?: ParserOptions | undefined);
61
+ /**
62
+ * Processes the input string and returns the parsed attributes as a flat array of typed objects.
63
+ * Each attribute is an object with a `type` (e.g., "ATOM", "STRING", "LITERAL", "SEQUENCE")
64
+ * and a `value` property. Lists are represented as nested arrays. Sections and partials are
65
+ * attached as properties on the preceding attribute object.
66
+ *
67
+ * @returns A promise that resolves to an array of parsed attribute objects and nested arrays.
68
+ * @throws {Error} If the input contains syntax errors or unclosed nodes.
69
+ */
70
+ getAttributes(): Promise<ImapAttributeList>;
71
+ /**
72
+ * Creates a new node in the parse tree. Each node represents a token or structural
73
+ * element (e.g., atom, string, literal, list, section, partial). The node is automatically
74
+ * appended to the parent's childNodes array if a parent is provided.
75
+ *
76
+ * @param parentNode - The parent node to attach this node to. If omitted, creates a root node.
77
+ * @param startPos - The starting position of this node in the original input string.
78
+ * @returns The newly created node with childNodes, type, value, and isClosed properties.
79
+ * @throws {Error} If the nesting depth exceeds MAX_NODE_DEPTH.
80
+ */
81
+ createNode(parentNode?: TokenNode | undefined, startPos?: number | undefined): TokenNode;
82
+ /**
83
+ * Processes the entire input string character by character using a state machine.
84
+ * Transitions between states (NORMAL, ATOM, STRING, LITERAL, SEQUENCE, PARTIAL, TEXT)
85
+ * based on the current character and builds the parse tree. This is the main parsing
86
+ * loop that drives the tokenization.
87
+ *
88
+ * @throws {Error} If the input contains unexpected characters, unclosed structures, or other syntax errors.
89
+ */
90
+ processString(): Promise<void>;
91
+ }