imapflow 2.2.7 → 2.2.9
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 +22 -0
- package/dist/cjs/commands/authenticate.js +15 -4
- package/dist/cjs/commands/enable.js +6 -0
- package/dist/cjs/commands/fetch.js +22 -5
- package/dist/cjs/commands/select.d.ts +3 -14
- package/dist/cjs/commands/select.js +1 -4
- package/dist/cjs/commands/store.js +7 -6
- package/dist/cjs/download.js +139 -5
- package/dist/cjs/imap-flow.d.ts +2 -2
- package/dist/cjs/imap-flow.js +21 -6
- package/dist/cjs/package-info.d.ts +1 -1
- package/dist/cjs/package-info.js +1 -1
- package/dist/cjs/search-compiler.js +6 -3
- package/dist/cjs/tools.d.ts +4 -3
- package/dist/cjs/tools.js +39 -6
- package/dist/cjs/types.d.ts +9 -1
- package/dist/esm/commands/authenticate.js +15 -4
- package/dist/esm/commands/enable.js +6 -0
- package/dist/esm/commands/fetch.js +22 -5
- package/dist/esm/commands/select.d.ts +3 -14
- package/dist/esm/commands/select.js +1 -4
- package/dist/esm/commands/store.js +8 -7
- package/dist/esm/download.js +139 -5
- package/dist/esm/imap-flow.d.ts +2 -2
- package/dist/esm/imap-flow.js +21 -6
- package/dist/esm/package-info.d.ts +1 -1
- package/dist/esm/package-info.js +1 -1
- package/dist/esm/search-compiler.js +6 -3
- package/dist/esm/tools.d.ts +4 -3
- package/dist/esm/tools.js +39 -6
- package/dist/esm/types.d.ts +9 -1
- package/package.json +5 -3
package/dist/esm/tools.d.ts
CHANGED
|
@@ -293,9 +293,10 @@ export declare function getColorFlags(color: string | null | undefined): {
|
|
|
293
293
|
* @param untagged - Parsed untagged IMAP response
|
|
294
294
|
* @param mailbox - Current mailbox state object
|
|
295
295
|
* @param idHashAlgorithm - Hash for the fallback message id, `md5` unless the client was told otherwise
|
|
296
|
+
* @param connection - Connection the response arrived on, decodes Gmail labels like mailbox names
|
|
296
297
|
* @returns Formatted message object with properties like seq, uid, flags, envelope, etc.
|
|
297
298
|
*/
|
|
298
|
-
export declare function formatMessageResponse(untagged: ImapResponse, mailbox: MailboxObject, idHashAlgorithm?: string): Promise<FetchMessageObject>;
|
|
299
|
+
export declare function formatMessageResponse(untagged: ImapResponse, mailbox: MailboxObject, idHashAlgorithm?: string, connection?: ImapFlow): Promise<FetchMessageObject>;
|
|
299
300
|
/**
|
|
300
301
|
* Strips surrounding double quotes from a name string.
|
|
301
302
|
*
|
|
@@ -364,8 +365,8 @@ export declare function formatDate(value: unknown): string | undefined;
|
|
|
364
365
|
*/
|
|
365
366
|
export declare function formatDateTime(value: unknown): string | undefined;
|
|
366
367
|
/**
|
|
367
|
-
* Normalizes a flag string. Returns false for non-settable flags (e.g. \Recent)
|
|
368
|
-
* and capitalizes system flags properly.
|
|
368
|
+
* Normalizes a flag string. Returns false for non-settable flags (e.g. \Recent) and for
|
|
369
|
+
* values that are not valid flags (keywords must be atoms), and capitalizes system flags properly.
|
|
369
370
|
*
|
|
370
371
|
* @param flag - Flag string to normalize
|
|
371
372
|
* @returns Normalized flag string, or false if the flag cannot be set
|
package/dist/esm/tools.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import libmime from 'libmime';
|
|
3
3
|
import { resolveCharset } from './charsets.js';
|
|
4
4
|
import { compiler } from './handler/imap-handler.js';
|
|
5
|
+
import imapFormalSyntax from './handler/imap-formal-syntax.js';
|
|
5
6
|
import { createHash } from 'node:crypto';
|
|
6
7
|
import { JPDecoder } from './jp-decoder.js';
|
|
7
8
|
import iconv from 'iconv-lite';
|
|
@@ -280,7 +281,8 @@ export function buildStatusQueryAttributes(connection, statusQuery) {
|
|
|
280
281
|
}
|
|
281
282
|
break;
|
|
282
283
|
case 'HIGHESTMODSEQ':
|
|
283
|
-
|
|
284
|
+
// QRESYNC implies CONDSTORE (RFC 7162 3.2.3)
|
|
285
|
+
if (connection.capabilities.has('CONDSTORE') || connection.capabilities.has('QRESYNC')) {
|
|
284
286
|
attributes.push({ type: 'ATOM', value: key.toUpperCase() });
|
|
285
287
|
}
|
|
286
288
|
break;
|
|
@@ -661,24 +663,40 @@ export function getColorFlags(color) {
|
|
|
661
663
|
* @param untagged - Parsed untagged IMAP response
|
|
662
664
|
* @param mailbox - Current mailbox state object
|
|
663
665
|
* @param idHashAlgorithm - Hash for the fallback message id, `md5` unless the client was told otherwise
|
|
666
|
+
* @param connection - Connection the response arrived on, decodes Gmail labels like mailbox names
|
|
664
667
|
* @returns Formatted message object with properties like seq, uid, flags, envelope, etc.
|
|
665
668
|
*/
|
|
666
|
-
export async function formatMessageResponse(untagged, mailbox, idHashAlgorithm) {
|
|
669
|
+
export async function formatMessageResponse(untagged, mailbox, idHashAlgorithm, connection) {
|
|
667
670
|
let map = {};
|
|
668
671
|
// The sequence number indexes into mailbox state, so an unusable one is dropped rather
|
|
669
672
|
// than coerced to NaN or Infinity
|
|
670
673
|
map.seq = parseUintValue(untagged.command, MAX_UINT32_DIGITS) || undefined;
|
|
671
674
|
let key;
|
|
675
|
+
// the <origin> of a partial section ("BODY[2]<1024>"), kept for the value that follows
|
|
676
|
+
let origin = false;
|
|
677
|
+
let partialOrigins;
|
|
678
|
+
let recordOrigin = (sectionKey) => {
|
|
679
|
+
if (origin !== false) {
|
|
680
|
+
if (!partialOrigins) {
|
|
681
|
+
partialOrigins = new Map();
|
|
682
|
+
}
|
|
683
|
+
partialOrigins.set(sectionKey, origin);
|
|
684
|
+
}
|
|
685
|
+
};
|
|
672
686
|
let attributes = ((untagged.attributes && untagged.attributes[1]) || []);
|
|
673
687
|
for (let i = 0, len = attributes.length; i < len; i++) {
|
|
674
688
|
let attribute = attributes[i];
|
|
675
689
|
if (i % 2 === 0) {
|
|
690
|
+
origin = false;
|
|
676
691
|
key = (await compiler({
|
|
677
692
|
attributes: [attribute]
|
|
678
693
|
}))
|
|
679
694
|
.toString()
|
|
680
695
|
.toLowerCase()
|
|
681
|
-
.replace(
|
|
696
|
+
.replace(/<(\d+)(\.\d+)?>$/, (match, start) => {
|
|
697
|
+
origin = parseUintValue(start, MAX_UINT32_DIGITS);
|
|
698
|
+
return '';
|
|
699
|
+
});
|
|
682
700
|
continue;
|
|
683
701
|
}
|
|
684
702
|
/* c8 ignore start */ // defensive: key is always a string produced by the compiler above
|
|
@@ -723,6 +741,7 @@ export async function formatMessageResponse(untagged, mailbox, idHashAlgorithm)
|
|
|
723
741
|
case 'body[]':
|
|
724
742
|
case 'binary[]':
|
|
725
743
|
map.source = getBuffer(attribute);
|
|
744
|
+
recordOrigin('');
|
|
726
745
|
break;
|
|
727
746
|
case 'uid':
|
|
728
747
|
// A UID feeds mailbox.uidNext one line below, and from there every range
|
|
@@ -769,7 +788,8 @@ export async function formatMessageResponse(untagged, mailbox, idHashAlgorithm)
|
|
|
769
788
|
map.threadId = getString(attribute);
|
|
770
789
|
break;
|
|
771
790
|
case 'x-gm-labels':
|
|
772
|
-
|
|
791
|
+
// labels are mailbox names, modified UTF-7 unless UTF-8 is enabled
|
|
792
|
+
map.labels = new Set(getArray(attribute).map(label => (connection ? decodePath(connection, label) : label)));
|
|
773
793
|
break;
|
|
774
794
|
case 'rfc822.size':
|
|
775
795
|
map.size = getUint(attribute) || 0;
|
|
@@ -815,6 +835,7 @@ export async function formatMessageResponse(untagged, mailbox, idHashAlgorithm)
|
|
|
815
835
|
map.bodyParts = new Map();
|
|
816
836
|
}
|
|
817
837
|
map.bodyParts.set(partKey, value);
|
|
838
|
+
recordOrigin(partKey);
|
|
818
839
|
if (match[1].toLowerCase() === 'binary') {
|
|
819
840
|
// The part arrived via FETCH BINARY (RFC 3516, FETCH side folded
|
|
820
841
|
// into IMAP4rev2), so the server has already removed the
|
|
@@ -856,6 +877,10 @@ export async function formatMessageResponse(untagged, mailbox, idHashAlgorithm)
|
|
|
856
877
|
.update([path, mailbox.uidValidity?.toString() || '', map.uid.toString()].join(':'))
|
|
857
878
|
.digest('hex');
|
|
858
879
|
}
|
|
880
|
+
if (partialOrigins) {
|
|
881
|
+
// non-enumerable, so it stays out of logged and serialized fetch results
|
|
882
|
+
Object.defineProperty(map, 'partialOrigins', { value: partialOrigins, writable: true, configurable: true });
|
|
883
|
+
}
|
|
859
884
|
if (map.flags) {
|
|
860
885
|
let flagColor = getFlagColor(map.flags);
|
|
861
886
|
if (flagColor) {
|
|
@@ -1298,14 +1323,22 @@ export function formatDateTime(value) {
|
|
|
1298
1323
|
let timeStr = date.toISOString().substring(11, 19);
|
|
1299
1324
|
return `${dateStr} ${timeStr} +0000`;
|
|
1300
1325
|
}
|
|
1326
|
+
// the memoized ATOM-CHAR set of RFC 9051 section 9
|
|
1327
|
+
const atomChars = imapFormalSyntax['ATOM-CHAR'];
|
|
1301
1328
|
/**
|
|
1302
|
-
* Normalizes a flag string. Returns false for non-settable flags (e.g. \Recent)
|
|
1303
|
-
* and capitalizes system flags properly.
|
|
1329
|
+
* Normalizes a flag string. Returns false for non-settable flags (e.g. \Recent) and for
|
|
1330
|
+
* values that are not valid flags (keywords must be atoms), and capitalizes system flags properly.
|
|
1304
1331
|
*
|
|
1305
1332
|
* @param flag - Flag string to normalize
|
|
1306
1333
|
* @returns Normalized flag string, or false if the flag cannot be set
|
|
1307
1334
|
*/
|
|
1308
1335
|
export function formatFlag(flag) {
|
|
1336
|
+
// RFC 9051 section 9: flag-keyword is an atom and flag-extension is "\\" atom, the same check
|
|
1337
|
+
// the compiler uses to decide what it can send unquoted
|
|
1338
|
+
let atom = flag.charAt(0) === '\\' ? flag.slice(1) : flag;
|
|
1339
|
+
if (!atom || imapFormalSyntax.verify(atom, atomChars()) >= 0) {
|
|
1340
|
+
return false;
|
|
1341
|
+
}
|
|
1309
1342
|
switch (flag.toLowerCase()) {
|
|
1310
1343
|
case '\\recent':
|
|
1311
1344
|
// can not set or remove
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -499,6 +499,8 @@ export interface FetchQueryObject {
|
|
|
499
499
|
/** Include full message in the response, up to maxLength bytes */
|
|
500
500
|
maxLength?: number | undefined;
|
|
501
501
|
} | undefined;
|
|
502
|
+
/** Email ID (OBJECTID EMAILID or Gmail X-GM-MSGID) is always requested when the server supports either extension, so this is accepted but changes nothing */
|
|
503
|
+
emailId?: boolean | undefined;
|
|
502
504
|
/** If true then include thread ID in the response (only if server supports either OBJECTID or X-GM-EXT-1 extensions) */
|
|
503
505
|
threadId?: boolean | undefined;
|
|
504
506
|
/** If true then include GMail labels in the response (only if server supports X-GM-EXT-1 extension) */
|
|
@@ -653,9 +655,11 @@ export interface DownloadOptions {
|
|
|
653
655
|
maxBytes?: number | undefined;
|
|
654
656
|
/** How large content parts to ask from the server. Defaults to 65536 */
|
|
655
657
|
chunkSize?: number | undefined;
|
|
658
|
+
/** If true then requests the content with FETCH BINARY when the server supports it (BINARY or IMAP4rev2), so the server removes the transfer encoding */
|
|
659
|
+
binary?: boolean | undefined;
|
|
656
660
|
}
|
|
657
661
|
/** Options for downloadMany(): the download() options without `chunkSize`, as the parts come in one FETCH */
|
|
658
|
-
export type DownloadManyOptions = Pick<DownloadOptions, 'uid' | 'maxBytes'>;
|
|
662
|
+
export type DownloadManyOptions = Pick<DownloadOptions, 'uid' | 'maxBytes' | 'binary'>;
|
|
659
663
|
export interface DownloadManyPart {
|
|
660
664
|
meta: DownloadMeta;
|
|
661
665
|
content?: Buffer | null | undefined;
|
|
@@ -706,6 +710,10 @@ export interface MailboxOpenOptions {
|
|
|
706
710
|
readOnly?: boolean | undefined;
|
|
707
711
|
/** Optional description for mailbox lock tracking */
|
|
708
712
|
description?: string | undefined;
|
|
713
|
+
/** QRESYNC (RFC 7162): HIGHESTMODSEQ from an earlier session. With `uidValidity` and QRESYNC enabled, changes since then are reported as `flags` and `expunge` events. getMailboxLock() only applies it when it selects the mailbox, not when the mailbox is already open */
|
|
714
|
+
changedSince?: bigint | number | string | undefined;
|
|
715
|
+
/** QRESYNC (RFC 7162): the UIDVALIDITY known from the previous session, required with `changedSince` */
|
|
716
|
+
uidValidity?: bigint | number | string | undefined;
|
|
709
717
|
}
|
|
710
718
|
export interface MailboxLockOptions extends MailboxOpenOptions {
|
|
711
719
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imapflow",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.9",
|
|
4
4
|
"description": "IMAP Client for Node",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/imap-flow.js",
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
"lint": "npm run typecheck && eslint .",
|
|
52
52
|
"lint:fix": "eslint . --fix",
|
|
53
53
|
"prepare": "npm run build",
|
|
54
|
-
"update": "rm -rf node_modules package-lock.json && ncu -u && npm install"
|
|
54
|
+
"update": "rm -rf node_modules package-lock.json && ncu -u && npm install",
|
|
55
|
+
"test:james": "bash test/integration/run-james-tests.sh"
|
|
55
56
|
},
|
|
56
57
|
"repository": {
|
|
57
58
|
"type": "git",
|
|
@@ -74,12 +75,13 @@
|
|
|
74
75
|
"eslint": "10.12.0",
|
|
75
76
|
"eslint-config-prettier": "10.1.8",
|
|
76
77
|
"globals": "17.13.0",
|
|
78
|
+
"imapkit": "4.1.0",
|
|
77
79
|
"prettier": "3.9.9",
|
|
78
80
|
"tsx": "4.23.15",
|
|
79
81
|
"types-node-legacy": "npm:@types/node@20.0.0",
|
|
80
82
|
"typescript": "6.0.3",
|
|
81
83
|
"typescript-eslint": "8.71.1",
|
|
82
|
-
"wrangler": "4.
|
|
84
|
+
"wrangler": "4.148.0"
|
|
83
85
|
},
|
|
84
86
|
"dependencies": {
|
|
85
87
|
"@zone-eu/mailsplit": "5.4.20",
|