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,12 @@
1
+ import { Transform, type TransformCallback } from 'node:stream';
2
+ export declare class JPDecoder extends Transform {
3
+ charset: string;
4
+ chunks: Buffer[];
5
+ chunklen: number;
6
+ maxBytes: number;
7
+ limited: boolean;
8
+ constructor(charset: string, maxBytes?: number | undefined);
9
+ _transform(chunk: Buffer | string, encoding: BufferEncoding, done: TransformCallback): void;
10
+ _flush(done: TransformCallback): void;
11
+ _destroy(err: Error | null, callback: (error: Error | null) => void): void;
12
+ }
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.JPDecoder = void 0;
7
+ const node_stream_1 = require("node:stream");
8
+ const encoding_japanese_1 = __importDefault(require("encoding-japanese"));
9
+ const limited_passthrough_js_1 = require("./limited-passthrough.js");
10
+ // A Transform stream for decoding Japanese character sets (Shift_JIS, EUC-JP, ISO-2022-JP).
11
+ // Unlike iconv-lite which can decode incrementally, encoding-japanese requires the complete
12
+ // input buffer for accurate charset detection and stateful decoding (especially ISO-2022-JP
13
+ // which uses escape sequences to switch between ASCII and multi-byte modes). Therefore,
14
+ // this stream buffers all input during _transform and performs the actual decoding in _flush.
15
+ class JPDecoder extends node_stream_1.Transform {
16
+ constructor(charset, maxBytes) {
17
+ super();
18
+ this.charset = charset;
19
+ this.chunks = [];
20
+ this.chunklen = 0;
21
+ // Upper bound for the buffered bytes, normalized the same way LimitedPassthrough
22
+ // normalizes its own. The whole-input buffering defeats a downstream maxBytes limiter
23
+ // (nothing is emitted until _flush), so without an internal bound a server could force
24
+ // unbounded memory use through a caller that asked for a limited download. Excess input
25
+ // is truncated, mirroring the truncation a maxBytes download applies anyway.
26
+ this.maxBytes = (0, limited_passthrough_js_1.normalizeByteLimit)(maxBytes);
27
+ // Also mirroring LimitedPassthrough: true once the bound is reached and every further
28
+ // chunk is being discarded. The download loop reads this to stop pulling from the
29
+ // server, which the limiter at the tail of the pipeline cannot tell it, because nothing
30
+ // is emitted from here until _flush().
31
+ this.limited = false;
32
+ }
33
+ // Buffer all incoming chunks (up to maxBytes); no decoding happens here because
34
+ // Japanese charsets require the complete input for accurate conversion.
35
+ _transform(chunk, encoding, done) {
36
+ if (typeof chunk === 'string') {
37
+ chunk = Buffer.from(chunk, encoding);
38
+ }
39
+ if (this.chunklen + chunk.length > this.maxBytes) {
40
+ chunk = chunk.slice(0, Math.max(0, this.maxBytes - this.chunklen));
41
+ }
42
+ if (chunk.length) {
43
+ this.chunks.push(chunk);
44
+ this.chunklen += chunk.length;
45
+ }
46
+ if (this.chunklen >= this.maxBytes) {
47
+ this.limited = true;
48
+ }
49
+ done();
50
+ }
51
+ // Perform the actual charset conversion once all input has been received.
52
+ // Uses the encoding-japanese library to convert from the source charset to Unicode.
53
+ // On failure (corrupt or unrecognizable data), passes through the raw bytes unchanged.
54
+ _flush(done) {
55
+ let input = Buffer.concat(this.chunks, this.chunklen);
56
+ try {
57
+ let output = encoding_japanese_1.default.convert(input, {
58
+ to: 'UNICODE', // to_encoding
59
+ from: this.charset, // from_encoding
60
+ type: 'string'
61
+ });
62
+ if (typeof output === 'string') {
63
+ output = Buffer.from(output);
64
+ }
65
+ this.push(output);
66
+ }
67
+ catch {
68
+ // keep as is on errors
69
+ this.push(input);
70
+ }
71
+ done();
72
+ }
73
+ _destroy(err, callback) {
74
+ this.chunks = [];
75
+ this.chunklen = 0;
76
+ callback(err);
77
+ }
78
+ }
79
+ exports.JPDecoder = JPDecoder;
@@ -0,0 +1,25 @@
1
+ import { Transform, type TransformCallback } from 'node:stream';
2
+ /**
3
+ * Normalizes a byte budget for the download pipeline. Any finite positive number is honored and
4
+ * floored, because byte counts are integers: with a fractional bound a counter can only ever
5
+ * reach its floor, so a stage would never report itself full and a loop polling that flag would
6
+ * keep pulling forever. Anything else - 0, NaN, a non-numeric value - means "no limit".
7
+ *
8
+ * Lives here rather than in tools.ts because tools.ts imports jp-decoder.ts, which needs this.
9
+ *
10
+ * @param value - The configured budget.
11
+ * @returns The normalized budget, or Infinity when unbounded.
12
+ */
13
+ export declare const normalizeByteLimit: (value: unknown) => number;
14
+ export interface LimitedPassthroughOptions {
15
+ /** Maximum number of bytes to pass through. Anything else means "no limit" */
16
+ maxBytes?: number | undefined;
17
+ }
18
+ export declare class LimitedPassthrough extends Transform {
19
+ options: LimitedPassthroughOptions;
20
+ maxBytes: number;
21
+ processed: number;
22
+ limited: boolean;
23
+ constructor(options?: LimitedPassthroughOptions | undefined);
24
+ _transform(chunk: Buffer, encoding: BufferEncoding, done: TransformCallback): void;
25
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LimitedPassthrough = exports.normalizeByteLimit = void 0;
4
+ const node_stream_1 = require("node:stream");
5
+ /**
6
+ * Normalizes a byte budget for the download pipeline. Any finite positive number is honored and
7
+ * floored, because byte counts are integers: with a fractional bound a counter can only ever
8
+ * reach its floor, so a stage would never report itself full and a loop polling that flag would
9
+ * keep pulling forever. Anything else - 0, NaN, a non-numeric value - means "no limit".
10
+ *
11
+ * Lives here rather than in tools.ts because tools.ts imports jp-decoder.ts, which needs this.
12
+ *
13
+ * @param value - The configured budget.
14
+ * @returns The normalized budget, or Infinity when unbounded.
15
+ */
16
+ const normalizeByteLimit = (value) => {
17
+ let bytes = Number(value);
18
+ // Math.max keeps a sub-1 budget from flooring to 0, which would read back as "no limit"
19
+ return Number.isFinite(bytes) && bytes > 0 ? Math.max(Math.floor(bytes), 1) : Infinity;
20
+ };
21
+ exports.normalizeByteLimit = normalizeByteLimit;
22
+ // A Transform stream that passes through data up to a maximum byte limit,
23
+ // then silently discards all subsequent chunks. Used to enforce download
24
+ // size limits when fetching message content from the IMAP server.
25
+ class LimitedPassthrough extends node_stream_1.Transform {
26
+ constructor(options) {
27
+ super();
28
+ this.options = options || {};
29
+ this.maxBytes = (0, exports.normalizeByteLimit)(this.options.maxBytes);
30
+ this.processed = 0;
31
+ this.limited = false;
32
+ }
33
+ _transform(chunk, encoding, done) {
34
+ // If the limit was already reached, discard the chunk immediately
35
+ if (this.limited) {
36
+ return done();
37
+ }
38
+ const remainingBytes = this.maxBytes - this.processed;
39
+ if (remainingBytes < 1) {
40
+ return done();
41
+ }
42
+ // Slice the chunk to fit within the remaining byte budget
43
+ if (chunk.length > remainingBytes) {
44
+ chunk = chunk.subarray(0, remainingBytes);
45
+ }
46
+ this.processed += chunk.length;
47
+ if (this.processed >= this.maxBytes) {
48
+ this.limited = true;
49
+ }
50
+ this.push(chunk);
51
+ done();
52
+ }
53
+ }
54
+ exports.LimitedPassthrough = LimitedPassthrough;
@@ -0,0 +1,3 @@
1
+ import pino from 'pino';
2
+ declare const logger: pino.Logger<never, boolean>;
3
+ export default logger;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const pino_1 = __importDefault(require("pino"));
7
+ const logger = (0, pino_1.default)();
8
+ logger.level = 'trace';
9
+ exports.default = logger;
10
+ module.exports = exports.default;
11
+ Object.defineProperty(module.exports, 'default', { value: exports.default, enumerable: false, writable: true, configurable: true });
@@ -0,0 +1,3 @@
1
+ export declare const name = "imapflow";
2
+ export declare const version = "2.0.1";
3
+ export declare const homepage = "https://imapflow.com/";
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ // Generated by scripts/build.js from package.json. Do not edit by hand.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.homepage = exports.version = exports.name = void 0;
5
+ exports.name = 'imapflow';
6
+ exports.version = '2.0.1';
7
+ exports.homepage = 'https://imapflow.com/';
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,33 @@
1
+ import net from 'node:net';
2
+ import { ConnectionDeadline } from './connection-deadline.js';
3
+ import type { InternalLogger } from './types.js';
4
+ /**
5
+ * A socket handed out by the proxy helpers, carrying the early error handler installed by
6
+ * attachEarlyErrorHandler() until the caller takes ownership
7
+ */
8
+ export type ProxySocket = net.Socket & {
9
+ _earlyErrorHandler?: ((err: Error) => void) | null | undefined;
10
+ };
11
+ export interface ProxyConnectionOptions {
12
+ /**
13
+ * Shared connection deadline. Proxy DNS and negotiation run inside it, so a stalled proxy
14
+ * cannot exceed the configured connectionTimeout.
15
+ */
16
+ deadline?: ConnectionDeadline | undefined;
17
+ /** Used to build a deadline when none was passed */
18
+ connectionTimeout?: number | undefined;
19
+ }
20
+ declare const detachEarlyErrorHandler: (socket: ProxySocket | false | null | undefined) => void;
21
+ /**
22
+ * Opens a socket to `host`:`port` through the configured proxy.
23
+ *
24
+ * @param logger Logger instance.
25
+ * @param connectionUrl Proxy URL (http, https, socks, socks4, socks4a, socks5).
26
+ * @param host Destination host, passed through unresolved wherever the proxy protocol
27
+ * can resolve it itself.
28
+ * @param port Destination port.
29
+ * @param options Deadline options, see ProxyConnectionOptions.
30
+ * @returns The tunnelled socket, or undefined for an unknown protocol.
31
+ */
32
+ declare const proxyConnection: (logger: InternalLogger, connectionUrl: string, host: string, port: number, options?: ProxyConnectionOptions | undefined) => Promise<ProxySocket | undefined>;
33
+ export { proxyConnection, detachEarlyErrorHandler };
@@ -0,0 +1,392 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.detachEarlyErrorHandler = exports.proxyConnection = void 0;
7
+ const socks_1 = require("socks");
8
+ const node_dns_1 = __importDefault(require("node:dns"));
9
+ const node_net_1 = __importDefault(require("node:net"));
10
+ const node_tls_1 = __importDefault(require("node:tls"));
11
+ const connection_deadline_js_1 = require("./connection-deadline.js");
12
+ const tools_js_1 = require("./tools.js");
13
+ // Cap the CONNECT response buffered before the header terminator, so a proxy that never sends
14
+ // \r\n\r\n cannot grow memory without bound.
15
+ const MAX_RESPONSE_HEADER_BYTES = 64 * 1024;
16
+ const DEFAULT_SOCKS_PORT = 1080;
17
+ // URL hostnames keep the brackets around an IPv6 literal ("[2001:db8::1]"), which is neither a
18
+ // valid input for net.isIP() nor an address net/tls/socks can connect to. Strip them for socket
19
+ // options; the parsed URL itself stays intact for logging and credentials.
20
+ const unbracketAddress = (host) => (typeof host === 'string' && host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host);
21
+ // CONNECT request lines and Host headers need an IPv6 destination wrapped in brackets. Hostnames
22
+ // and IPv4 literals are used as-is, and an already bracketed literal is not bracketed twice.
23
+ const formatAuthority = (host, port) => {
24
+ let address = unbracketAddress(host);
25
+ return node_net_1.default.isIPv6(address) ? `[${address}]:${port}` : `${address}:${port}`;
26
+ };
27
+ // Password-free rendering of the proxy URL, used in every log path. The caller's URL object is
28
+ // left untouched so credentials stay available for authentication.
29
+ const redactUrl = (proxyUrl) => {
30
+ let redacted = new URL(proxyUrl.href);
31
+ if (redacted.password) {
32
+ redacted.password = '(hidden)';
33
+ }
34
+ return redacted.href;
35
+ };
36
+ const proxyError = (message, code) => {
37
+ let err = new Error(message);
38
+ err.code = code || 'ProxyError';
39
+ return err;
40
+ };
41
+ // URL userinfo is percent-encoded, so it has to be decoded before it can be used as credentials.
42
+ // A password containing a bare '%' is not valid percent-encoding and makes decodeURIComponent
43
+ // throw, so such values are used as they came in rather than failing the connection.
44
+ const decodeUserInfo = (value) => {
45
+ try {
46
+ return decodeURIComponent(value);
47
+ }
48
+ catch {
49
+ return value;
50
+ }
51
+ };
52
+ // The socks client attaches its full options object - proxy password included - to the errors it
53
+ // throws, and Node's URL errors carry the rejected string in `input`. Any logger that serializes
54
+ // error properties would then write that password out in clear text, so the credentials are
55
+ // dropped before the error is logged or handed to the caller.
56
+ const stripProxyCredentials = (err) => {
57
+ if (err && typeof err === 'object') {
58
+ delete err.options;
59
+ delete err.input;
60
+ }
61
+ return err;
62
+ };
63
+ // Attaches a benign 'error' listener as soon as the proxied socket exists, so an early
64
+ // socket error (before ImapFlow installs its own handlers) cannot surface as an unhandled
65
+ // 'error' event and crash the process. The handler is stored on the socket so the caller
66
+ // can remove it once it takes ownership of the socket.
67
+ const attachEarlyErrorHandler = (logger, socket) => {
68
+ if (!socket || typeof socket.on !== 'function') {
69
+ return;
70
+ }
71
+ socket._earlyErrorHandler = (err) => {
72
+ logger.error({ msg: 'Proxy socket error before connection setup', err });
73
+ };
74
+ socket.on('error', socket._earlyErrorHandler);
75
+ };
76
+ // Removes the handler installed by attachEarlyErrorHandler once the caller takes ownership
77
+ // of the socket. Keeps the internal `_earlyErrorHandler` contract inside this module.
78
+ const detachEarlyErrorHandler = (socket) => {
79
+ if (socket && socket._earlyErrorHandler) {
80
+ socket.removeListener('error', socket._earlyErrorHandler);
81
+ socket._earlyErrorHandler = null;
82
+ }
83
+ };
84
+ exports.detachEarlyErrorHandler = detachEarlyErrorHandler;
85
+ /**
86
+ * Establishes a tunnel through an HTTP or HTTPS proxy with a CONNECT request.
87
+ *
88
+ * ImapFlow owns this instead of using a bundled helper because the connection-wide deadline has
89
+ * to apply here: the socket is retained as soon as it exists, so an expiry destroys the in-flight
90
+ * socket immediately, and concurrent connections cannot share a single process-global timeout.
91
+ *
92
+ * The destination hostname is passed through unresolved - resolving it is the proxy's job, which
93
+ * is also what keeps DNS traffic off the client for HTTP proxies.
94
+ *
95
+ * @returns The established socket, tunnelled to the destination.
96
+ */
97
+ const httpConnect = async ({ logger, proxyUrl, secureProxy, proxyHost, proxyPort, host, port, deadline }) => {
98
+ // Reject CRLF in the destination before it reaches the CONNECT request line and Host header.
99
+ // A tainted host/port could otherwise inject additional headers (HTTP request splitting).
100
+ let destinationPort = Number(port) || 0;
101
+ if (!destinationPort || /[\r\n]/.test(host)) {
102
+ throw proxyError('Invalid proxy destination', 'EPROXY');
103
+ }
104
+ let authority = formatAuthority(host, destinationPort);
105
+ let remaining = deadline.remaining();
106
+ if (!remaining) {
107
+ throw deadline.error();
108
+ }
109
+ let socket = null;
110
+ return await new Promise((resolve, reject) => {
111
+ let settled = false;
112
+ let timer = null;
113
+ let headers = '';
114
+ const onSocketData = (chunk) => {
115
+ // Scan only the newly arrived bytes (plus the 3 that a terminator could straddle),
116
+ // so a proxy that dribbles its headers cannot turn this into a quadratic rescan.
117
+ let searchFrom = Math.max(0, headers.length - 3);
118
+ headers += chunk.toString('binary');
119
+ let terminator = headers.indexOf('\r\n\r\n', searchFrom);
120
+ if (terminator < 0) {
121
+ if (headers.length > MAX_RESPONSE_HEADER_BYTES) {
122
+ fail(proxyError('Proxy response headers too large', 'EPROXY'));
123
+ }
124
+ return;
125
+ }
126
+ // The header block is complete, so this listener must stop consuming before anything
127
+ // is put back: unshifting while still subscribed re-emits the data straight back into
128
+ // this handler, which would swallow it. Pausing hands the socket over cleanly - the
129
+ // next owner resumes it (ImapFlow pipes it into the parser).
130
+ socket.removeListener('data', onSocketData);
131
+ socket.pause();
132
+ // Anything after the header terminator already belongs to the tunnelled stream (a
133
+ // server greeting that the proxy coalesced with its own response) and has to be
134
+ // preserved for the next consumer. It is put back as the original bytes, taken from
135
+ // this chunk rather than round-tripped through a string.
136
+ let headerBytes = terminator + 4;
137
+ let consumedFromChunk = chunk.length - (headers.length - headerBytes);
138
+ if (consumedFromChunk < chunk.length) {
139
+ socket.unshift(chunk.subarray(consumedFromChunk));
140
+ }
141
+ headers = headers.slice(0, terminator);
142
+ let status = headers.match(/^HTTP\/\d+\.\d+ (\d+)/i);
143
+ if (!status || (status[1] || '').charAt(0) !== '2') {
144
+ return fail(proxyError(`Invalid response from proxy${status ? `: ${status[1]}` : ''}`, 'EPROXY'));
145
+ }
146
+ succeed();
147
+ };
148
+ // Single settlement path: temporary listeners and the deadline timer are dropped exactly
149
+ // once, so a late socket event cannot settle the promise twice or leave a timer armed.
150
+ const cleanup = () => {
151
+ (0, tools_js_1.clearTimer)(timer);
152
+ timer = null;
153
+ if (socket) {
154
+ // Every temporary listener goes, the connect callback included: after settlement
155
+ // no socket event may run any of this again.
156
+ socket.removeListener('connect', onConnected);
157
+ socket.removeListener('data', onSocketData);
158
+ socket.removeListener('error', fail);
159
+ socket.removeListener('close', onEarlyClose);
160
+ }
161
+ };
162
+ function fail(err) {
163
+ if (settled) {
164
+ return;
165
+ }
166
+ settled = true;
167
+ cleanup();
168
+ if (socket) {
169
+ socket.destroy();
170
+ }
171
+ reject(err);
172
+ }
173
+ function succeed() {
174
+ settled = true;
175
+ cleanup();
176
+ resolve(socket);
177
+ }
178
+ function onEarlyClose() {
179
+ fail(proxyError('Proxy closed the connection before the tunnel was established', 'EPROXY'));
180
+ }
181
+ timer = setTimeout(() => fail(deadline.error()), remaining);
182
+ let connectOptions = { host: proxyHost, port: proxyPort };
183
+ if (secureProxy) {
184
+ // Verify the proxy's certificate (Node default) and target SNI plus hostname
185
+ // verification at the proxy endpoint rather than the IMAP destination. An IP-literal
186
+ // endpoint gets no servername, which would be an invalid SNI value.
187
+ if (!node_net_1.default.isIP(proxyHost)) {
188
+ connectOptions.servername = proxyHost;
189
+ }
190
+ }
191
+ // Declared as a function so cleanup() above can detach it (the connect callback is
192
+ // registered as a one-shot 'connect' listener by net/tls).
193
+ function onConnected() {
194
+ let requestHeaders = {
195
+ Host: authority,
196
+ Connection: 'close'
197
+ };
198
+ if (proxyUrl.username || proxyUrl.password) {
199
+ let credentials = `${decodeUserInfo(proxyUrl.username)}:${decodeUserInfo(proxyUrl.password)}`;
200
+ requestHeaders['Proxy-Authorization'] = `Basic ${Buffer.from(credentials).toString('base64')}`;
201
+ }
202
+ socket.write(`CONNECT ${authority} HTTP/1.1\r\n` +
203
+ Object.keys(requestHeaders)
204
+ .map(key => `${key}: ${requestHeaders[key]}`)
205
+ .join('\r\n') +
206
+ '\r\n\r\n');
207
+ socket.on('data', onSocketData);
208
+ }
209
+ // The socket is retained as soon as it is created, so an expiry can destroy it at once.
210
+ socket = secureProxy ? node_tls_1.default.connect(connectOptions, onConnected) : node_net_1.default.connect(connectOptions, onConnected);
211
+ socket.once('error', fail);
212
+ socket.once('close', onEarlyClose);
213
+ })
214
+ .then(established => {
215
+ logger.info({
216
+ msg: `Established a socket via HTTP proxy`,
217
+ proxyUrl: redactUrl(proxyUrl),
218
+ port,
219
+ host
220
+ });
221
+ attachEarlyErrorHandler(logger, established);
222
+ return established;
223
+ })
224
+ .catch(err => {
225
+ logger.error({
226
+ msg: 'Failed to establish a socket via HTTP proxy',
227
+ proxyUrl: redactUrl(proxyUrl),
228
+ port,
229
+ host,
230
+ err
231
+ });
232
+ throw err;
233
+ });
234
+ };
235
+ /**
236
+ * Resolves a destination hostname to an IPv4 address. Only used for SOCKS4, which has no IPv6
237
+ * destination address type and no hostname form of its own.
238
+ *
239
+ * @param hostname Destination hostname.
240
+ * @param deadline Shared connection deadline.
241
+ * @returns An IPv4 address.
242
+ */
243
+ const resolveIPv4 = async (hostname, deadline) => {
244
+ let addresses = await deadline.race(node_dns_1.default.promises.resolve4(hostname));
245
+ if (!addresses || !addresses.length) {
246
+ throw proxyError(`Could not resolve an IPv4 address for ${hostname}`, 'EPROXY');
247
+ }
248
+ return addresses[0];
249
+ };
250
+ /**
251
+ * Establishes a tunnel through a SOCKS proxy.
252
+ *
253
+ * DNS policy per protocol, because the `socks` client picks the SOCKS4 or SOCKS4a wire format from
254
+ * the destination value alone and offers no switch of its own:
255
+ * * SOCKS4 - destination hostnames are resolved locally to IPv4. Passing a hostname would
256
+ * silently produce a SOCKS4a request that a plain SOCKS4 proxy cannot answer.
257
+ * * SOCKS4a - destination hostnames are preserved for remote DNS.
258
+ * * SOCKS5 - destination hostnames are preserved for remote DNS, IP literals pass through.
259
+ * IPv6 destination literals are rejected for both SOCKS4 and SOCKS4a: neither can carry them, and
260
+ * the dependency would write the literal into the SOCKS4a hostname field instead.
261
+ *
262
+ * @returns The established socket, tunnelled to the destination.
263
+ */
264
+ const socksConnect = async ({ logger, proxyUrl, protocol, proxyHost, proxyPort, host, port, deadline }) => {
265
+ let proxyType = protocol === 'socks4' || protocol === 'socks4a' ? 4 : 5;
266
+ let destinationHost = unbracketAddress(host);
267
+ try {
268
+ if (proxyType === 4) {
269
+ if (node_net_1.default.isIPv6(destinationHost)) {
270
+ throw proxyError(`SOCKS4 and SOCKS4a cannot address IPv6 destinations (${destinationHost})`, 'UnsupportedProxyAddress');
271
+ }
272
+ if (protocol === 'socks4' && !node_net_1.default.isIP(destinationHost)) {
273
+ destinationHost = await resolveIPv4(destinationHost, deadline);
274
+ }
275
+ }
276
+ let connectionOpts = {
277
+ proxy: {
278
+ // The endpoint is handed to net.Socket.connect() by the dependency, so a hostname
279
+ // is left unresolved and gets Node's normal lookup and connection behavior.
280
+ host: proxyHost,
281
+ port: proxyPort,
282
+ type: proxyType
283
+ },
284
+ destination: {
285
+ host: destinationHost,
286
+ port
287
+ },
288
+ command: 'connect',
289
+ set_tcp_nodelay: true
290
+ };
291
+ if (proxyUrl.username || proxyUrl.password) {
292
+ connectionOpts.proxy.userId = proxyUrl.username;
293
+ connectionOpts.proxy.password = proxyUrl.password;
294
+ }
295
+ // The dependency treats a zero timeout as its own 30 second default, so only a strictly
296
+ // positive remaining budget may be passed.
297
+ let remaining = deadline.remaining();
298
+ if (!remaining) {
299
+ throw deadline.error();
300
+ }
301
+ connectionOpts.timeout = remaining;
302
+ const info = await deadline.race(socks_1.SocksClient.createConnection(connectionOpts));
303
+ if (!info || !info.socket) {
304
+ throw proxyError('SOCKS proxy did not return a socket', 'EPROXY');
305
+ }
306
+ logger.info({
307
+ msg: 'Established a socket via SOCKS proxy',
308
+ proxyUrl: redactUrl(proxyUrl),
309
+ port,
310
+ host
311
+ });
312
+ attachEarlyErrorHandler(logger, info.socket);
313
+ return info.socket;
314
+ }
315
+ catch (caught) {
316
+ // A dependency expiry and the shared deadline are reported with the same
317
+ // CONNECT_TIMEOUT shape, so a caller does not need to know which noticed first.
318
+ let err = deadline.normalize(stripProxyCredentials(caught));
319
+ stripProxyCredentials(err._err);
320
+ logger.error({
321
+ msg: 'Failed to establish a socket via SOCKS proxy',
322
+ proxyUrl: redactUrl(proxyUrl),
323
+ port,
324
+ host,
325
+ err
326
+ });
327
+ throw err;
328
+ }
329
+ };
330
+ /**
331
+ * Opens a socket to `host`:`port` through the configured proxy.
332
+ *
333
+ * @param logger Logger instance.
334
+ * @param connectionUrl Proxy URL (http, https, socks, socks4, socks4a, socks5).
335
+ * @param host Destination host, passed through unresolved wherever the proxy protocol
336
+ * can resolve it itself.
337
+ * @param port Destination port.
338
+ * @param options Deadline options, see ProxyConnectionOptions.
339
+ * @returns The tunnelled socket, or undefined for an unknown protocol.
340
+ */
341
+ const proxyConnection = async (logger, connectionUrl, host, port, options) => {
342
+ options = options || {};
343
+ let deadline = options.deadline || new connection_deadline_js_1.ConnectionDeadline(options.connectionTimeout);
344
+ deadline.check();
345
+ let proxyUrl;
346
+ try {
347
+ proxyUrl = new URL(connectionUrl);
348
+ }
349
+ catch (err) {
350
+ // new URL() attaches the string it rejected to err.input, which here is the full proxy
351
+ // endpoint including its password. Any logger that serializes error properties would
352
+ // write that out in clear text, so the cause is reported without carrying the value.
353
+ throw proxyError('Invalid proxy URL', err.code || 'ERR_INVALID_URL');
354
+ }
355
+ let protocol = proxyUrl.protocol.replace(/:$/, '').toLowerCase();
356
+ // ImapFlow performs no DNS lookup of its own for the proxy endpoint: net, tls and the SOCKS
357
+ // client all resolve a hostname endpoint themselves, which keeps Node's normal connection
358
+ // behavior (including address-family selection) instead of pinning one address.
359
+ let proxyHost = unbracketAddress(proxyUrl.hostname);
360
+ switch (protocol) {
361
+ // Connect using a HTTP CONNECT method
362
+ case 'http':
363
+ case 'https':
364
+ return await httpConnect({
365
+ logger,
366
+ proxyUrl,
367
+ secureProxy: protocol === 'https',
368
+ proxyHost,
369
+ proxyPort: Number(proxyUrl.port) || (protocol === 'https' ? 443 : 80),
370
+ host,
371
+ port,
372
+ deadline
373
+ });
374
+ // SOCKS proxy
375
+ case 'socks':
376
+ case 'socks5':
377
+ case 'socks4':
378
+ case 'socks4a':
379
+ return await socksConnect({
380
+ logger,
381
+ proxyUrl,
382
+ protocol,
383
+ proxyHost,
384
+ proxyPort: Number(proxyUrl.port) || DEFAULT_SOCKS_PORT,
385
+ host,
386
+ port,
387
+ deadline
388
+ });
389
+ }
390
+ return undefined;
391
+ };
392
+ exports.proxyConnection = proxyConnection;
@@ -0,0 +1,34 @@
1
+ import type { ImapFlow } from './imap-flow.js';
2
+ import type { ImapAttributeNode } from './handler/types.js';
3
+ import type { SearchObject } from './types.js';
4
+ /**
5
+ * A compiled search attribute: a token, or a parenthesized group of tokens
6
+ */
7
+ export type SearchAttribute = ImapAttributeNode | SearchAttribute[];
8
+ /**
9
+ * Compiles a JavaScript object query into IMAP search command attributes.
10
+ * Supports standard IMAP search criteria and extensions like OBJECTID and Gmail extensions.
11
+ *
12
+ * @param connection - IMAP connection object (capabilities, enabled extensions and the current mailbox are read)
13
+ * @param query - Search query object
14
+ * @returns Array of IMAP search attributes
15
+ * @throws {Error} When required server extensions are not available
16
+ *
17
+ * @example
18
+ * // Simple search for unseen messages from a sender
19
+ * searchCompiler(connection, {
20
+ * unseen: true,
21
+ * from: 'sender@example.com'
22
+ * });
23
+ *
24
+ * @example
25
+ * // Complex OR search with date range
26
+ * searchCompiler(connection, {
27
+ * or: [
28
+ * { from: 'alice@example.com' },
29
+ * { from: 'bob@example.com' }
30
+ * ],
31
+ * since: new Date('2024-01-01')
32
+ * });
33
+ */
34
+ export declare const searchCompiler: (connection: ImapFlow, query: SearchObject) => SearchAttribute[];