imapflow 1.4.9 → 1.6.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 (48) hide show
  1. package/.github/workflows/test.yml +20 -0
  2. package/.release-please-manifest.json +1 -1
  3. package/CHANGELOG.md +22 -0
  4. package/CLAUDE.md +3 -5
  5. package/lib/commands/fetch.js +18 -14
  6. package/lib/commands/idle.js +197 -104
  7. package/lib/commands/list.js +19 -8
  8. package/lib/commands/quota.js +3 -0
  9. package/lib/commands/select.js +5 -0
  10. package/lib/commands/status.js +10 -1
  11. package/lib/connection-deadline.js +98 -0
  12. package/lib/handler/imap-compiler.js +20 -14
  13. package/lib/handler/imap-stream.js +141 -50
  14. package/lib/handler/limits.js +43 -0
  15. package/lib/handler/token-parser.js +38 -1
  16. package/lib/imap-flow.d.ts +47 -5
  17. package/lib/imap-flow.js +594 -283
  18. package/lib/proxy-connection.js +393 -98
  19. package/lib/special-use.js +660 -51
  20. package/lib/tools.js +52 -3
  21. package/package.json +2 -2
  22. package/test/commands-branches-test.js +17 -1
  23. package/test/commands-integration-test.js +353 -2
  24. package/test/connection-edge-cases-test.js +4 -40
  25. package/test/fixtures/fake-timers.js +115 -0
  26. package/test/handler-branches-test.js +4 -28
  27. package/test/idle-polling-test.js +349 -0
  28. package/test/imap-compiler-test.js +85 -0
  29. package/test/imap-flow-compress-test.js +12 -0
  30. package/test/imap-flow-coverage-test.js +3 -3
  31. package/test/imap-flow-fetch-download-test.js +56 -0
  32. package/test/imap-flow-internals-test.js +23 -0
  33. package/test/imap-flow-proxy-paths-test.js +151 -0
  34. package/test/imap-flow-secure-test.js +182 -9
  35. package/test/imap-flow-server-test.js +229 -0
  36. package/test/imap-parser-test.js +112 -1
  37. package/test/imap-stream-test.js +46 -0
  38. package/test/integration/README.md +17 -5
  39. package/test/integration/rev2-live-test.js +125 -0
  40. package/test/integration/run-rev2-tests.sh +14 -0
  41. package/test/parser-limits-test.js +274 -0
  42. package/test/proxy-connection-test.js +553 -442
  43. package/test/reliability-improvements-test.js +87 -0
  44. package/test/search-compiler-test.js +17 -0
  45. package/test/special-use-test.js +337 -0
  46. package/test/tag-correlation-test.js +333 -0
  47. package/test/timer-policy-test.js +214 -0
  48. package/test/tools-test.js +42 -4
@@ -42,7 +42,22 @@ export interface ImapFlowOptions {
42
42
  verifyOnly?: boolean;
43
43
  /** If true and verifyOnly is set, lists mailboxes */
44
44
  includeMailboxes?: boolean;
45
- /** Proxy URL. Supports HTTP CONNECT (http:, https:) and SOCKS (socks:, socks4:, socks5:) proxies */
45
+ /**
46
+ * Proxy URL. Supports HTTP CONNECT (http:, https:) and SOCKS (socks:, socks4:, socks4a:,
47
+ * socks5:) proxies. IPv6 proxy endpoints are given in URL form, e.g. `socks5://[2001:db8::1]:1080`.
48
+ *
49
+ * DNS behaviour depends on the proxy protocol:
50
+ * - `http:`/`https:` - the destination hostname is sent to the proxy unresolved
51
+ * - `socks4:` - destination hostnames are resolved locally to IPv4 (SOCKS4 has no IPv6
52
+ * destination address type; IPv6 destinations are rejected)
53
+ * - `socks4a:` - destination hostnames are sent to the proxy for remote DNS (IPv6
54
+ * destinations are rejected)
55
+ * - `socks:`/`socks5:` - destination hostnames are sent to the proxy for remote DNS, IPv4
56
+ * and IPv6 literals are passed through
57
+ *
58
+ * The proxy endpoint itself is never resolved by ImapFlow; a hostname endpoint is handed to
59
+ * Node as-is. Proxy DNS and negotiation run inside `connectionTimeout`.
60
+ */
46
61
  proxy?: string;
47
62
  /** If true, then use QRESYNC instead of CONDSTORE. EXPUNGE notifications will include UID instead of sequence number */
48
63
  qresync?: boolean;
@@ -56,7 +71,11 @@ export interface ImapFlowOptions {
56
71
  disableAutoEnable?: boolean;
57
72
  /** If true, do not enable IMAP4rev2 mode even if the server supports it */
58
73
  disableIMAP4rev2?: boolean;
59
- /** How long to wait for the connection to be established. Defaults to 90 seconds */
74
+ /**
75
+ * How long to wait for a usable transport, covering DNS resolution, proxy negotiation and the
76
+ * TCP/TLS handshake as a single budget. Defaults to 90 seconds. An expiry in any of those
77
+ * phases rejects with error code `CONNECT_TIMEOUT`.
78
+ */
60
79
  connectionTimeout?: number;
61
80
  /** How long to wait for the greeting. Defaults to 16 seconds */
62
81
  greetingTimeout?: number;
@@ -65,12 +84,17 @@ export interface ImapFlowOptions {
65
84
  /**
66
85
  * Maximum allowed length in bytes of a single response line (a response without a literal).
67
86
  * Guards against a malicious or broken server that never sends a line terminator. Defaults to
68
- * 1GB.
87
+ * 1GB. The line terminator counts towards the limit and a line exactly at the limit is
88
+ * accepted. Exceeding it is terminal: the connection fails with error code `LineTooLarge` and
89
+ * no further input is parsed.
69
90
  */
70
91
  maxLineLength?: number;
71
92
  /**
72
93
  * Maximum allowed size in bytes of a single IMAP literal block. Bounds peak memory allocation
73
- * against a malicious or broken server announcing an oversized literal. Defaults to 1GB.
94
+ * against a malicious or broken server announcing an oversized literal. Defaults to 1GB. A
95
+ * literal exactly at the limit is accepted. Exceeding it is terminal: the connection fails
96
+ * with error code `LiteralTooLarge`, and neither the marker line nor any byte of the rejected
97
+ * literal is interpreted as protocol.
74
98
  */
75
99
  maxLiteralSize?: number;
76
100
  /**
@@ -184,6 +208,8 @@ export interface ListResponse {
184
208
  flags: Set<string>;
185
209
  /** One of special-use flags (if applicable) */
186
210
  specialUse?: string;
211
+ /** How specialUse was determined: "user" (from specialUseHints), "extension" (SPECIAL-USE or XLIST flag reported by the server) or "name" (matched against known localized folder names) */
212
+ specialUseSource?: 'user' | 'extension' | 'name';
187
213
  /** True if mailbox was found from the output of LIST command */
188
214
  listed: boolean;
189
215
  /** True if the mailbox is subscribed - reported by LSUB or by LIST RETURN (SUBSCRIBED) on LIST-EXTENDED/IMAP4rev2 servers */
@@ -207,6 +233,10 @@ export interface ListOptions {
207
233
  unseen?: boolean;
208
234
  /** If true request last known modseq value */
209
235
  highestModseq?: boolean;
236
+ /** If true request total mailbox size in octets (requires STATUS=SIZE or IMAP4rev2) */
237
+ size?: boolean;
238
+ /** If true request count of messages with \Deleted flag (requires IMAP4rev2) */
239
+ deleted?: boolean;
210
240
  };
211
241
  /** Set specific paths as special use folders */
212
242
  specialUseHints?: {
@@ -218,6 +248,8 @@ export interface ListOptions {
218
248
  junk?: string;
219
249
  /** Path to "Drafts" folder */
220
250
  drafts?: string;
251
+ /** Path to "Archive" folder */
252
+ archive?: string;
221
253
  };
222
254
  }
223
255
 
@@ -282,6 +314,10 @@ export interface StatusObject {
282
314
  unseen?: number;
283
315
  /** Last known modseq value (if CONDSTORE extension is enabled) */
284
316
  highestModseq?: bigint;
317
+ /** Total size of the mailbox in octets (only if requested and the server supports STATUS=SIZE or IMAP4rev2) */
318
+ size?: number;
319
+ /** Count of messages with \Deleted flag (only if requested and IMAP4rev2 is active) */
320
+ deleted?: number;
285
321
  }
286
322
 
287
323
  export type SequenceString = string | number | bigint;
@@ -493,6 +529,8 @@ export interface FetchMessageObject {
493
529
  internalDate?: Date | string;
494
530
  /** A Map of message body parts where key is requested part identifier and value is a Buffer */
495
531
  bodyParts?: Map<string, Buffer>;
532
+ /** Part identifiers from bodyParts that arrived via FETCH BINARY, i.e. with the content-transfer-encoding already decoded by the server */
533
+ binaryParts?: Set<string>;
496
534
  /** Requested header lines as Buffer */
497
535
  headers?: Buffer;
498
536
  /** Account unique ID for this email */
@@ -755,6 +793,10 @@ export class ImapFlow extends EventEmitter {
755
793
  uidValidity?: boolean;
756
794
  unseen?: boolean;
757
795
  highestModseq?: boolean;
796
+ /** Requires STATUS=SIZE or IMAP4rev2 */
797
+ size?: boolean;
798
+ /** Requires IMAP4rev2 */
799
+ deleted?: boolean;
758
800
  }
759
801
  ): Promise<StatusObject>;
760
802
 
@@ -861,7 +903,7 @@ export class ImapFlow extends EventEmitter {
861
903
  /** Mailbox was opened */
862
904
  on(event: 'mailboxOpen', listener: (mailbox: MailboxObject) => void): this;
863
905
 
864
- /** Mailbox was closed */
906
+ /** Mailbox was closed, either explicitly or because the connection went away while a mailbox was still selected */
865
907
  on(event: 'mailboxClose', listener: (mailbox: MailboxObject) => void): this;
866
908
 
867
909
  /** Log event if emitLogs=true */