imapflow 2.9.0 → 2.10.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.10.0](https://github.com/postalsys/imapflow/compare/v2.9.1...v2.10.0) (2026-10-29)
4
+
5
+
6
+ ### Features
7
+
8
+ * add decodeText option to download() for transfer-decoded file bytes ([9c9bec2](https://github.com/postalsys/imapflow/commit/9c9bec225efe0dfb7b1b31271f4fa76783f15adb)), closes [#455](https://github.com/postalsys/imapflow/issues/455)
9
+
10
+ ## [2.9.1](https://github.com/postalsys/imapflow/compare/v2.9.0...v2.9.1) (2026-10-22)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * update dependencies ([bf20e37](https://github.com/postalsys/imapflow/commit/bf20e3734628a99969b63db3cd8458d6043a8ba5))
16
+
3
17
  ## [2.9.0](https://github.com/postalsys/imapflow/compare/v2.8.0...v2.9.0) (2026-10-22)
4
18
 
5
19
 
@@ -351,8 +351,8 @@ async function downloadMessage(client, range, part, options) {
351
351
  }
352
352
  // Build a decoder pipeline that progressively transforms the raw FETCH data:
353
353
  // 1. Transfer-encoding decoder (base64 or quoted-printable -> binary)
354
- // 2. Format decoder (format=flowed -> plain text, if applicable)
355
- // 3. Charset decoder (non-UTF-8 -> UTF-8, for text parts only)
354
+ // 2. Format decoder (format=flowed -> plain text, if applicable, unless decodeText is false)
355
+ // 3. Charset decoder (non-UTF-8 -> UTF-8, for text parts only, unless decodeText is false)
356
356
  // 4. Byte limiter (enforces maxBytes cap)
357
357
  // `stream` is the head of the pipeline (where raw chunks are written),
358
358
  // `output` is the tail (what the caller reads from).
@@ -394,7 +394,7 @@ async function downloadMessage(client, range, part, options) {
394
394
  return stage;
395
395
  };
396
396
  let isTextNode = ['text/html', 'text/plain', 'text/x-amp-html'].includes(meta.contentType) || (part === '1' && !meta.contentType);
397
- if ((!meta.disposition || meta.disposition === 'inline') && isTextNode) {
397
+ if (downloadOptions.decodeText !== false && (!meta.disposition || meta.disposition === 'inline') && isTextNode) {
398
398
  // RFC 3676 format=flowed text: unwrap soft line breaks
399
399
  if (meta.flowed) {
400
400
  // FlowedDecoder buffers its whole input before emitting, and being third party it
@@ -606,8 +606,8 @@ export declare class ImapFlow extends EventEmitter {
606
606
  fetchOne(seq: SequenceString, query: FetchQueryObject, options?: FetchOptions | undefined): Promise<FetchMessageObject | false>;
607
607
  /**
608
608
  * Download either full rfc822 formatted message or a specific bodystructure part as a Stream.
609
- * Bodystructure parts are decoded so the resulting stream is a binary file. Text content
610
- * is automatically converted to UTF-8 charset.
609
+ * Bodystructure parts are decoded so the resulting stream is a binary file. Inline text content
610
+ * is automatically converted to UTF-8 charset, unless `options.decodeText` is false.
611
611
  *
612
612
  * @param range UID or sequence number for the message to fetch
613
613
  * @param part If not set then downloads entire rfc822 formatted message, otherwise downloads specific bodystructure part
@@ -3451,8 +3451,8 @@ class ImapFlow extends node_events_1.EventEmitter {
3451
3451
  }
3452
3452
  /**
3453
3453
  * Download either full rfc822 formatted message or a specific bodystructure part as a Stream.
3454
- * Bodystructure parts are decoded so the resulting stream is a binary file. Text content
3455
- * is automatically converted to UTF-8 charset.
3454
+ * Bodystructure parts are decoded so the resulting stream is a binary file. Inline text content
3455
+ * is automatically converted to UTF-8 charset, unless `options.decodeText` is false.
3456
3456
  *
3457
3457
  * @param range UID or sequence number for the message to fetch
3458
3458
  * @param part If not set then downloads entire rfc822 formatted message, otherwise downloads specific bodystructure part
@@ -1,3 +1,3 @@
1
1
  export declare const name = "imapflow";
2
- export declare const version = "2.9.0";
2
+ export declare const version = "2.10.0";
3
3
  export declare const homepage = "https://imapflow.com/";
@@ -3,5 +3,5 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.homepage = exports.version = exports.name = void 0;
5
5
  exports.name = "imapflow";
6
- exports.version = "2.9.0";
6
+ exports.version = "2.10.0";
7
7
  exports.homepage = "https://imapflow.com/";
@@ -684,8 +684,18 @@ export interface DownloadOptions {
684
684
  chunkSize?: number | undefined;
685
685
  /** If true then requests the content with FETCH BINARY when the server supports it (BINARY or IMAP4rev2), so the server removes the transfer encoding */
686
686
  binary?: boolean | undefined;
687
+ /**
688
+ * If false then text parts are not decoded: inline `text/plain`, `text/html` and `text/x-amp-html`
689
+ * parts are streamed with only the transfer encoding removed, as for any other part, without
690
+ * unwrapping `format=flowed` lines or converting the charset to UTF-8, and `meta.charset` keeps the
691
+ * charset of the part. Use it to save a text part as a file. Defaults to true
692
+ */
693
+ decodeText?: boolean | undefined;
687
694
  }
688
- /** Options for downloadMany(): the download() options without `chunkSize`, as the parts come in one FETCH */
695
+ /**
696
+ * Options for downloadMany(): the download() options without `chunkSize`, as the parts come in one FETCH, and without
697
+ * `decodeText`, as downloadMany() only removes the transfer encoding and never converts text
698
+ */
689
699
  export type DownloadManyOptions = Pick<DownloadOptions, 'uid' | 'maxBytes' | 'binary'>;
690
700
  export interface DownloadManyPart {
691
701
  meta: DownloadMeta;
@@ -344,8 +344,8 @@ export async function downloadMessage(client, range, part, options) {
344
344
  }
345
345
  // Build a decoder pipeline that progressively transforms the raw FETCH data:
346
346
  // 1. Transfer-encoding decoder (base64 or quoted-printable -> binary)
347
- // 2. Format decoder (format=flowed -> plain text, if applicable)
348
- // 3. Charset decoder (non-UTF-8 -> UTF-8, for text parts only)
347
+ // 2. Format decoder (format=flowed -> plain text, if applicable, unless decodeText is false)
348
+ // 3. Charset decoder (non-UTF-8 -> UTF-8, for text parts only, unless decodeText is false)
349
349
  // 4. Byte limiter (enforces maxBytes cap)
350
350
  // `stream` is the head of the pipeline (where raw chunks are written),
351
351
  // `output` is the tail (what the caller reads from).
@@ -387,7 +387,7 @@ export async function downloadMessage(client, range, part, options) {
387
387
  return stage;
388
388
  };
389
389
  let isTextNode = ['text/html', 'text/plain', 'text/x-amp-html'].includes(meta.contentType) || (part === '1' && !meta.contentType);
390
- if ((!meta.disposition || meta.disposition === 'inline') && isTextNode) {
390
+ if (downloadOptions.decodeText !== false && (!meta.disposition || meta.disposition === 'inline') && isTextNode) {
391
391
  // RFC 3676 format=flowed text: unwrap soft line breaks
392
392
  if (meta.flowed) {
393
393
  // FlowedDecoder buffers its whole input before emitting, and being third party it
@@ -606,8 +606,8 @@ export declare class ImapFlow extends EventEmitter {
606
606
  fetchOne(seq: SequenceString, query: FetchQueryObject, options?: FetchOptions | undefined): Promise<FetchMessageObject | false>;
607
607
  /**
608
608
  * Download either full rfc822 formatted message or a specific bodystructure part as a Stream.
609
- * Bodystructure parts are decoded so the resulting stream is a binary file. Text content
610
- * is automatically converted to UTF-8 charset.
609
+ * Bodystructure parts are decoded so the resulting stream is a binary file. Inline text content
610
+ * is automatically converted to UTF-8 charset, unless `options.decodeText` is false.
611
611
  *
612
612
  * @param range UID or sequence number for the message to fetch
613
613
  * @param part If not set then downloads entire rfc822 formatted message, otherwise downloads specific bodystructure part
@@ -3408,8 +3408,8 @@ export class ImapFlow extends EventEmitter {
3408
3408
  }
3409
3409
  /**
3410
3410
  * Download either full rfc822 formatted message or a specific bodystructure part as a Stream.
3411
- * Bodystructure parts are decoded so the resulting stream is a binary file. Text content
3412
- * is automatically converted to UTF-8 charset.
3411
+ * Bodystructure parts are decoded so the resulting stream is a binary file. Inline text content
3412
+ * is automatically converted to UTF-8 charset, unless `options.decodeText` is false.
3413
3413
  *
3414
3414
  * @param range UID or sequence number for the message to fetch
3415
3415
  * @param part If not set then downloads entire rfc822 formatted message, otherwise downloads specific bodystructure part
@@ -1,3 +1,3 @@
1
1
  export declare const name = "imapflow";
2
- export declare const version = "2.9.0";
2
+ export declare const version = "2.10.0";
3
3
  export declare const homepage = "https://imapflow.com/";
@@ -1,4 +1,4 @@
1
1
  // Generated by scripts/build.js from package.json. Do not edit by hand.
2
2
  export const name = "imapflow";
3
- export const version = "2.9.0";
3
+ export const version = "2.10.0";
4
4
  export const homepage = "https://imapflow.com/";
@@ -684,8 +684,18 @@ export interface DownloadOptions {
684
684
  chunkSize?: number | undefined;
685
685
  /** If true then requests the content with FETCH BINARY when the server supports it (BINARY or IMAP4rev2), so the server removes the transfer encoding */
686
686
  binary?: boolean | undefined;
687
+ /**
688
+ * If false then text parts are not decoded: inline `text/plain`, `text/html` and `text/x-amp-html`
689
+ * parts are streamed with only the transfer encoding removed, as for any other part, without
690
+ * unwrapping `format=flowed` lines or converting the charset to UTF-8, and `meta.charset` keeps the
691
+ * charset of the part. Use it to save a text part as a file. Defaults to true
692
+ */
693
+ decodeText?: boolean | undefined;
687
694
  }
688
- /** Options for downloadMany(): the download() options without `chunkSize`, as the parts come in one FETCH */
695
+ /**
696
+ * Options for downloadMany(): the download() options without `chunkSize`, as the parts come in one FETCH, and without
697
+ * `decodeText`, as downloadMany() only removes the transfer encoding and never converts text
698
+ */
689
699
  export type DownloadManyOptions = Pick<DownloadOptions, 'uid' | 'maxBytes' | 'binary'>;
690
700
  export interface DownloadManyPart {
691
701
  meta: DownloadMeta;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imapflow",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "description": "IMAP Client for Node",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/imap-flow.js",
@@ -84,7 +84,7 @@
84
84
  "eslint-config-prettier": "10.1.8",
85
85
  "events": "3.3.0",
86
86
  "globals": "17.13.0",
87
- "imapkit": "5.1.0",
87
+ "imapkit": "5.2.0",
88
88
  "playwright-core": "1.64.1",
89
89
  "prettier": "3.9.10",
90
90
  "process": "0.11.10",
@@ -93,23 +93,20 @@
93
93
  "tsx": "4.23.15",
94
94
  "types-node-legacy": "npm:@types/node@20.0.0",
95
95
  "typescript": "6.0.3",
96
- "typescript-eslint": "8.72.0",
97
- "wrangler": "4.151.3"
96
+ "typescript-eslint": "8.72.1",
97
+ "wrangler": "4.155.0"
98
98
  },
99
99
  "dependencies": {
100
- "@zone-eu/mailsplit": "5.4.27",
100
+ "@zone-eu/mailsplit": "5.4.29",
101
101
  "encoding-japanese": "2.4.0",
102
102
  "iconv-lite": "0.7.3",
103
103
  "libbase64": "1.4.1",
104
- "libmime": "5.5.2",
104
+ "libmime": "5.6.3",
105
105
  "libqp": "3.0.1",
106
106
  "pino": "10.3.1",
107
107
  "socks": "2.8.10"
108
108
  },
109
109
  "engines": {
110
110
  "node": ">=20.0.0"
111
- },
112
- "overrides": {
113
- "sharp": "0.35.5"
114
111
  }
115
112
  }