imapflow 1.7.1 → 1.7.2
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/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +8 -0
- package/lib/imap-flow.d.ts +13 -5
- package/lib/imap-flow.js +5 -5
- package/lib/tools.js +29 -7
- package/package.json +2 -2
- package/test/tools-test.js +113 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.7.2](https://github.com/postalsys/imapflow/compare/v1.7.1...v1.7.2) (2026-08-21)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **envelope:** stop inventing an address from a NIL host field ([253a7b0](https://github.com/postalsys/imapflow/commit/253a7b0747ebe3c59fb177d46eb9843da6b5e91e))
|
|
9
|
+
* **types:** accept string[] paths in status, getQuota, append, copy and move ([92f3607](https://github.com/postalsys/imapflow/commit/92f360749fd9518532f0d908bf246fb9402f2eaa)), closes [#382](https://github.com/postalsys/imapflow/issues/382)
|
|
10
|
+
|
|
3
11
|
## [1.7.1](https://github.com/postalsys/imapflow/compare/v1.7.0...v1.7.1) (2026-08-14)
|
|
4
12
|
|
|
5
13
|
|
package/lib/imap-flow.d.ts
CHANGED
|
@@ -787,7 +787,7 @@ export class ImapFlow extends EventEmitter {
|
|
|
787
787
|
close(): void;
|
|
788
788
|
|
|
789
789
|
/** Returns current quota */
|
|
790
|
-
getQuota(path?: string): Promise<QuotaResponse | false>;
|
|
790
|
+
getQuota(path?: string | string[]): Promise<QuotaResponse | false>;
|
|
791
791
|
|
|
792
792
|
/** Lists available mailboxes as an Array */
|
|
793
793
|
list(options?: ListOptions): Promise<ListResponse[]>;
|
|
@@ -821,7 +821,7 @@ export class ImapFlow extends EventEmitter {
|
|
|
821
821
|
|
|
822
822
|
/** Requests the status of the indicated mailbox */
|
|
823
823
|
status(
|
|
824
|
-
path: string,
|
|
824
|
+
path: string | string[],
|
|
825
825
|
query: {
|
|
826
826
|
messages?: boolean;
|
|
827
827
|
recent?: boolean;
|
|
@@ -855,13 +855,21 @@ export class ImapFlow extends EventEmitter {
|
|
|
855
855
|
messageDelete(range: SequenceString | number[] | SearchObject, options?: { uid?: boolean }): Promise<boolean>;
|
|
856
856
|
|
|
857
857
|
/** Appends a new message to a mailbox */
|
|
858
|
-
append(path: string, content: string | Buffer, flags?: string[], idate?: Date | string): Promise<AppendResponseObject | false>;
|
|
858
|
+
append(path: string | string[], content: string | Buffer, flags?: string[], idate?: Date | string): Promise<AppendResponseObject | false>;
|
|
859
859
|
|
|
860
860
|
/** Copies messages from current mailbox to destination mailbox */
|
|
861
|
-
messageCopy(
|
|
861
|
+
messageCopy(
|
|
862
|
+
range: SequenceString | number[] | SearchObject,
|
|
863
|
+
destination: string | string[],
|
|
864
|
+
options?: { uid?: boolean }
|
|
865
|
+
): Promise<CopyResponseObject | false>;
|
|
862
866
|
|
|
863
867
|
/** Moves messages from current mailbox to destination mailbox */
|
|
864
|
-
messageMove(
|
|
868
|
+
messageMove(
|
|
869
|
+
range: SequenceString | number[] | SearchObject,
|
|
870
|
+
destination: string | string[],
|
|
871
|
+
options?: { uid?: boolean }
|
|
872
|
+
): Promise<CopyResponseObject | false>;
|
|
865
873
|
|
|
866
874
|
/** Search messages from the currently opened mailbox — returns number[] (backward-compatible) */
|
|
867
875
|
search(query: SearchObject, options?: { uid?: boolean }): Promise<number[] | false>;
|
package/lib/imap-flow.js
CHANGED
|
@@ -2848,7 +2848,7 @@ class ImapFlow extends EventEmitter {
|
|
|
2848
2848
|
/**
|
|
2849
2849
|
* Returns current quota
|
|
2850
2850
|
*
|
|
2851
|
-
* @param {
|
|
2851
|
+
* @param {string|array} [path] Optional mailbox path if you want to check quota for specific folder. If value is an array then it is joined using current delimiter symbols. Namespace prefix is added automatically if required.
|
|
2852
2852
|
* @returns {Promise<QuotaResponse|Boolean>} Quota information or `false` if QUOTA extension is not supported or requested path does not exist
|
|
2853
2853
|
*
|
|
2854
2854
|
* @example
|
|
@@ -3101,7 +3101,7 @@ class ImapFlow extends EventEmitter {
|
|
|
3101
3101
|
/**
|
|
3102
3102
|
* Requests the status of the indicated mailbox. Only requested status values will be returned.
|
|
3103
3103
|
*
|
|
3104
|
-
* @param {
|
|
3104
|
+
* @param {string|array} path mailbox path to check for (unicode string). If value is an array then it is joined using current delimiter symbols. Namespace prefix is added automatically if required.
|
|
3105
3105
|
* @param {Object} query defines requested status items
|
|
3106
3106
|
* @param {Boolean} query.messages if `true` request count of messages
|
|
3107
3107
|
* @param {Boolean} query.recent if `true` request count of messages with \\Recent tag
|
|
@@ -3389,7 +3389,7 @@ class ImapFlow extends EventEmitter {
|
|
|
3389
3389
|
/**
|
|
3390
3390
|
* Appends a new message to a mailbox
|
|
3391
3391
|
*
|
|
3392
|
-
* @param {
|
|
3392
|
+
* @param {string|array} path Mailbox path to upload the message to (unicode string). If value is an array then it is joined using current delimiter symbols. Namespace prefix is added automatically if required.
|
|
3393
3393
|
* @param {string|Buffer} content RFC822 formatted email message
|
|
3394
3394
|
* @param {string[]} [flags] an array of flags to be set for the uploaded message
|
|
3395
3395
|
* @param {Date|string} [idate=now] internal date to be set for the message
|
|
@@ -3415,7 +3415,7 @@ class ImapFlow extends EventEmitter {
|
|
|
3415
3415
|
* Copies messages from current mailbox to destination mailbox
|
|
3416
3416
|
*
|
|
3417
3417
|
* @param {SequenceString | Number[] | SearchObject} range Range of messages to copy
|
|
3418
|
-
* @param {
|
|
3418
|
+
* @param {string|array} destination Mailbox path to copy the messages to. If value is an array then it is joined using current delimiter symbols. Namespace prefix is added automatically if required.
|
|
3419
3419
|
* @param {Object} [options]
|
|
3420
3420
|
* @param {Boolean} [options.uid] If `true` then uses UID {@link SequenceString} instead of sequence numbers
|
|
3421
3421
|
* @returns {Promise<CopyResponseObject>} info about copies messages
|
|
@@ -3439,7 +3439,7 @@ class ImapFlow extends EventEmitter {
|
|
|
3439
3439
|
* Moves messages from current mailbox to destination mailbox
|
|
3440
3440
|
*
|
|
3441
3441
|
* @param {SequenceString | Number[] | SearchObject} range Range of messages to move
|
|
3442
|
-
* @param {
|
|
3442
|
+
* @param {string|array} destination Mailbox path to move the messages to. If value is an array then it is joined using current delimiter symbols. Namespace prefix is added automatically if required.
|
|
3443
3443
|
* @param {Object} [options]
|
|
3444
3444
|
* @param {Boolean} [options.uid] If `true` then uses UID {@link SequenceString} instead of sequence numbers
|
|
3445
3445
|
* @returns {Promise<CopyResponseObject>} info about moved messages
|
package/lib/tools.js
CHANGED
|
@@ -764,6 +764,17 @@ const tools = {
|
|
|
764
764
|
return name;
|
|
765
765
|
},
|
|
766
766
|
|
|
767
|
+
/**
|
|
768
|
+
* Decodes an ENVELOPE text field for display: encoded words first, then the
|
|
769
|
+
* surrounding quotes some servers leave in place.
|
|
770
|
+
*
|
|
771
|
+
* @param {String} value - Raw field value from an ENVELOPE response
|
|
772
|
+
* @returns {String} Decoded, unquoted text
|
|
773
|
+
*/
|
|
774
|
+
decodeText(value) {
|
|
775
|
+
return tools.processName(libmime.decodeWords(value));
|
|
776
|
+
},
|
|
777
|
+
|
|
767
778
|
/**
|
|
768
779
|
* Parses a raw IMAP ENVELOPE response into a structured envelope object.
|
|
769
780
|
*
|
|
@@ -795,14 +806,25 @@ const tools = {
|
|
|
795
806
|
// throwing on the dereference and dropping the message
|
|
796
807
|
return false;
|
|
797
808
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
809
|
+
|
|
810
|
+
let name = tools.decodeText(getStrValue(addr[0]));
|
|
811
|
+
let mailbox = getStrValue(addr[2]) || '';
|
|
812
|
+
let host = getStrValue(addr[3]) || '';
|
|
813
|
+
|
|
814
|
+
if (!host) {
|
|
815
|
+
// RFC 9051 7.5.2: a NIL host field marks RFC 5322 group syntax, it is not
|
|
816
|
+
// an empty domain. A non-NIL mailbox then holds the group name phrase, a
|
|
817
|
+
// NIL one closes the group. Joining the fields anyway would invent an
|
|
818
|
+
// address that never appeared in the message, eg. "undisclosed-recipients@",
|
|
819
|
+
// so surface the group name as a display name and leave the address empty.
|
|
820
|
+
// End-of-group markers carry neither and the filter below drops them.
|
|
821
|
+
// The mirror case, a NIL mailbox with a host, is left alone on purpose:
|
|
822
|
+
// the grammar gives it no meaning, so a server sending it is simply
|
|
823
|
+
// malformed rather than signalling anything we could act on.
|
|
824
|
+
return { name: name || (mailbox && tools.decodeText(mailbox)), address: '' };
|
|
801
825
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
address
|
|
805
|
-
};
|
|
826
|
+
|
|
827
|
+
return { name, address: `${mailbox}@${host}` };
|
|
806
828
|
})
|
|
807
829
|
.filter(addr => addr && (addr.name || addr.address));
|
|
808
830
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imapflow",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"description": "IMAP Client for Node",
|
|
5
5
|
"main": "lib/imap-flow.js",
|
|
6
6
|
"types": "lib/imap-flow.d.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@eslint/js": "10.0.1",
|
|
33
33
|
"@types/node": "26.2.0",
|
|
34
34
|
"c8": "12.0.0",
|
|
35
|
-
"eslint": "10.
|
|
35
|
+
"eslint": "10.9.0",
|
|
36
36
|
"eslint-config-nodemailer": "1.2.0",
|
|
37
37
|
"eslint-config-prettier": "10.1.8",
|
|
38
38
|
"grunt": "1.6.3",
|
package/test/tools-test.js
CHANGED
|
@@ -716,6 +716,25 @@ module.exports['Tools: processName with short quoted'] = test => {
|
|
|
716
716
|
test.done();
|
|
717
717
|
};
|
|
718
718
|
|
|
719
|
+
// ============================================
|
|
720
|
+
// decodeText tests
|
|
721
|
+
// ============================================
|
|
722
|
+
|
|
723
|
+
module.exports['Tools: decodeText decodes encoded words and strips quotes'] = test => {
|
|
724
|
+
test.equal(tools.decodeText('=?utf-8?Q?T=C3=B5nu?='), 'Tõnu');
|
|
725
|
+
test.equal(tools.decodeText('"=?utf-8?Q?T=C3=B5nu?="'), 'Tõnu');
|
|
726
|
+
test.equal(tools.decodeText('Plain Name'), 'Plain Name');
|
|
727
|
+
test.done();
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
module.exports['Tools: decodeText tolerates missing values'] = test => {
|
|
731
|
+
// getStrValue returns false for a NIL envelope field
|
|
732
|
+
test.equal(tools.decodeText(false), '');
|
|
733
|
+
test.equal(tools.decodeText(null), '');
|
|
734
|
+
test.equal(tools.decodeText(undefined), '');
|
|
735
|
+
test.done();
|
|
736
|
+
};
|
|
737
|
+
|
|
719
738
|
// ============================================
|
|
720
739
|
// getFolderTree tests
|
|
721
740
|
// ============================================
|
|
@@ -885,6 +904,100 @@ module.exports['Tools: parseEnvelope with empty address parts'] = test => {
|
|
|
885
904
|
test.done();
|
|
886
905
|
};
|
|
887
906
|
|
|
907
|
+
module.exports['Tools: parseEnvelope keeps group syntax out of the address'] = test => {
|
|
908
|
+
// RFC 9051 7.5.2: a NIL host marks group syntax, so "undisclosed-recipients:;" must
|
|
909
|
+
// not turn into the invented address "undisclosed-recipients@"
|
|
910
|
+
let entry = [
|
|
911
|
+
null, // date
|
|
912
|
+
null, // subject
|
|
913
|
+
[], // from
|
|
914
|
+
[], // sender
|
|
915
|
+
[], // reply-to
|
|
916
|
+
[
|
|
917
|
+
[null, null, { value: 'undisclosed-recipients' }, null], // start of group
|
|
918
|
+
[null, null, null, null] // end of group
|
|
919
|
+
], // to
|
|
920
|
+
[], // cc
|
|
921
|
+
[], // bcc
|
|
922
|
+
null, // in-reply-to
|
|
923
|
+
null // message-id
|
|
924
|
+
];
|
|
925
|
+
|
|
926
|
+
let result = tools.parseEnvelope(entry);
|
|
927
|
+
// The end-of-group marker carries neither name nor address and is dropped
|
|
928
|
+
test.deepEqual(result.to, [{ name: 'undisclosed-recipients', address: '' }]);
|
|
929
|
+
test.done();
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
module.exports['Tools: parseEnvelope keeps group members alongside the markers'] = test => {
|
|
933
|
+
let entry = [
|
|
934
|
+
null, // date
|
|
935
|
+
null, // subject
|
|
936
|
+
[], // from
|
|
937
|
+
[], // sender
|
|
938
|
+
[], // reply-to
|
|
939
|
+
[
|
|
940
|
+
[null, null, { value: 'Team' }, null], // start of group
|
|
941
|
+
[{ value: 'Member One' }, null, { value: 'one' }, { value: 'example.com' }],
|
|
942
|
+
[{ value: 'Member Two' }, null, { value: 'two' }, { value: 'example.com' }],
|
|
943
|
+
[null, null, null, null] // end of group
|
|
944
|
+
], // to
|
|
945
|
+
[], // cc
|
|
946
|
+
[], // bcc
|
|
947
|
+
null, // in-reply-to
|
|
948
|
+
null // message-id
|
|
949
|
+
];
|
|
950
|
+
|
|
951
|
+
let result = tools.parseEnvelope(entry);
|
|
952
|
+
test.deepEqual(result.to, [
|
|
953
|
+
{ name: 'Team', address: '' },
|
|
954
|
+
{ name: 'Member One', address: 'one@example.com' },
|
|
955
|
+
{ name: 'Member Two', address: 'two@example.com' }
|
|
956
|
+
]);
|
|
957
|
+
test.done();
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
module.exports['Tools: parseEnvelope does not join a NIL host onto a mailbox'] = test => {
|
|
961
|
+
// Some servers parse a malformed header such as
|
|
962
|
+
// "To: user@example.com user@example.com" into a personal name plus a mailbox
|
|
963
|
+
// with a NIL host. Joining those produced the invalid address "example.com@".
|
|
964
|
+
let entry = [
|
|
965
|
+
null, // date
|
|
966
|
+
null, // subject
|
|
967
|
+
[], // from
|
|
968
|
+
[], // sender
|
|
969
|
+
[], // reply-to
|
|
970
|
+
[[{ value: 'user@example.com user@' }, null, { value: 'example.com' }, null]], // to
|
|
971
|
+
[], // cc
|
|
972
|
+
[], // bcc
|
|
973
|
+
null, // in-reply-to
|
|
974
|
+
null // message-id
|
|
975
|
+
];
|
|
976
|
+
|
|
977
|
+
let result = tools.parseEnvelope(entry);
|
|
978
|
+
test.deepEqual(result.to, [{ name: 'user@example.com user@', address: '' }]);
|
|
979
|
+
test.done();
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
module.exports['Tools: parseEnvelope decodes an encoded group name'] = test => {
|
|
983
|
+
let entry = [
|
|
984
|
+
null, // date
|
|
985
|
+
null, // subject
|
|
986
|
+
[], // from
|
|
987
|
+
[], // sender
|
|
988
|
+
[], // reply-to
|
|
989
|
+
[[null, null, { value: '=?utf-8?Q?T=C3=B5ny?=' }, null]], // to
|
|
990
|
+
[], // cc
|
|
991
|
+
[], // bcc
|
|
992
|
+
null, // in-reply-to
|
|
993
|
+
null // message-id
|
|
994
|
+
];
|
|
995
|
+
|
|
996
|
+
let result = tools.parseEnvelope(entry);
|
|
997
|
+
test.deepEqual(result.to, [{ name: 'Tõny', address: '' }]);
|
|
998
|
+
test.done();
|
|
999
|
+
};
|
|
1000
|
+
|
|
888
1001
|
// ============================================
|
|
889
1002
|
// getStructuredParams tests
|
|
890
1003
|
// ============================================
|