@bobfrankston/mailx-imap 0.1.81 → 0.1.83
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/index.js +34 -5
- package/package.json +3 -3
package/index.js
CHANGED
|
@@ -1272,6 +1272,15 @@ export class ImapManager extends EventEmitter {
|
|
|
1272
1272
|
const __qrT0 = Date.now();
|
|
1273
1273
|
const qr = await client.resyncFolder(folder.path, prevUidValidity, prevModSeq);
|
|
1274
1274
|
console.log(` [qresync] ${accountId}/${folder.path}: vanished=${qr.vanishedUids.length} changed=${qr.changedMessages.length} newModSeq=${qr.newHighestModSeq} in ${Date.now() - __qrT0}ms`);
|
|
1275
|
+
// No real folder vanishes this many UIDs in one QRESYNC cycle.
|
|
1276
|
+
// iflow-direct can mis-expand a VANISHED *range* (e.g. the
|
|
1277
|
+
// server reports "VANISHED 1:4828883" when our modseq watermark
|
|
1278
|
+
// is stale) into millions of individual UIDs. Applying that
|
|
1279
|
+
// literally = 4.8M synchronous DB calls that wedge the whole
|
|
1280
|
+
// event loop (frozen window) AND delete every local row caught
|
|
1281
|
+
// in the range (INBOX wiped → the viewer's open message
|
|
1282
|
+
// vanishes / shows a stale unrelated eml). Bob 2026-06-04.
|
|
1283
|
+
const VANISHED_ABSURD_CAP = 50000;
|
|
1275
1284
|
if (qr.uidValidityChanged) {
|
|
1276
1285
|
// UIDVALIDITY rolled — our local UIDs are stale. Fall through
|
|
1277
1286
|
// to the full set-diff path; that'll discover the new state
|
|
@@ -1279,6 +1288,15 @@ export class ImapManager extends EventEmitter {
|
|
|
1279
1288
|
// from wiping anything we shouldn't.
|
|
1280
1289
|
console.log(` [qresync] ${accountId}/${folder.path}: UIDVALIDITY changed (was ${prevUidValidity}, now ${qr.exists}); falling back to full sync`);
|
|
1281
1290
|
}
|
|
1291
|
+
else if (qr.vanishedUids.length > VANISHED_ABSURD_CAP) {
|
|
1292
|
+
// Refuse the absurd VANISHED set; do NOT iterate/apply it and
|
|
1293
|
+
// do NOT return — fall through to the bounded set-diff
|
|
1294
|
+
// reconcile (same as the UIDVALIDITY-changed path). The
|
|
1295
|
+
// modseq watermark is left intact, so a healthy later QRESYNC
|
|
1296
|
+
// can still succeed.
|
|
1297
|
+
const localCount = this.db.getMessageCount(accountId, folderId);
|
|
1298
|
+
console.error(` [qresync] ${accountId}/${folder.path}: REFUSING VANISHED of ${qr.vanishedUids.length} UIDs (folder has ${localCount} locally) — almost certainly a mis-parsed range; falling back to set-diff reconcile instead of mass-deleting/wedging.`);
|
|
1299
|
+
}
|
|
1282
1300
|
else {
|
|
1283
1301
|
// Apply VANISHED — server says these UIDs are gone. No
|
|
1284
1302
|
// tombstone, no diff, just delete the local rows.
|
|
@@ -1864,8 +1882,19 @@ export class ImapManager extends EventEmitter {
|
|
|
1864
1882
|
// a "never-touched" non-special folder is deferred until the
|
|
1865
1883
|
// user opens that folder (on-demand syncFolder call) or until
|
|
1866
1884
|
// ~3 minutes after startup when the event loop is quiet.
|
|
1867
|
-
|
|
1868
|
-
|
|
1885
|
+
// TRUE lazy folder sync (C119, completed 2026-06-01). A folder is
|
|
1886
|
+
// "lazy" if it's neither a priority special-use folder NOR one
|
|
1887
|
+
// mailx has ever pulled a message from (highestUid===0). Lazy
|
|
1888
|
+
// folders are NOT auto-synced at all — they sync on-demand the
|
|
1889
|
+
// moment the user opens them (the client fires `syncFolderNow` on
|
|
1890
|
+
// folder-open) and then join the periodic set automatically once
|
|
1891
|
+
// they have a message (highestUid>0). Previously they were only
|
|
1892
|
+
// deferred for a 3-minute startup window, after which a full
|
|
1893
|
+
// 96-folder sweep ran every cycle — re-saturating the IMAP
|
|
1894
|
+
// connection and pegging the event loop (the "mailx DOSes itself,
|
|
1895
|
+
// TB doesn't" storm, Bob 2026-06-01). Permanent deferral closes it:
|
|
1896
|
+
// a fresh rebuild now syncs only INBOX + special-use, and the rest
|
|
1897
|
+
// fill in as the user actually visits them.
|
|
1869
1898
|
const isLazyEligible = (f) => {
|
|
1870
1899
|
if (f.specialUse && priorityOrder.includes(f.specialUse))
|
|
1871
1900
|
return false;
|
|
@@ -1873,15 +1902,15 @@ export class ImapManager extends EventEmitter {
|
|
|
1873
1902
|
return false;
|
|
1874
1903
|
return true;
|
|
1875
1904
|
};
|
|
1876
|
-
const remaining = folders.filter(f => f.specialUse !== "inbox" && !
|
|
1905
|
+
const remaining = folders.filter(f => f.specialUse !== "inbox" && !isLazyEligible(f));
|
|
1877
1906
|
remaining.sort((a, b) => {
|
|
1878
1907
|
const pa = priorityOrder.indexOf(a.specialUse || "") >= 0 ? priorityOrder.indexOf(a.specialUse || "") : 5;
|
|
1879
1908
|
const pb = priorityOrder.indexOf(b.specialUse || "") >= 0 ? priorityOrder.indexOf(b.specialUse || "") : 5;
|
|
1880
1909
|
return pa - pb;
|
|
1881
1910
|
});
|
|
1882
|
-
const deferredCount = folders.filter(f => f.specialUse !== "inbox" && isLazyEligible(f)
|
|
1911
|
+
const deferredCount = folders.filter(f => f.specialUse !== "inbox" && isLazyEligible(f)).length;
|
|
1883
1912
|
if (deferredCount > 0) {
|
|
1884
|
-
console.log(` [sync] ${accountId}:
|
|
1913
|
+
console.log(` [sync] ${accountId}: lazy — ${deferredCount} never-touched folder(s) will sync on-demand when opened (not auto-swept)`);
|
|
1885
1914
|
}
|
|
1886
1915
|
const CONCURRENCY = 2;
|
|
1887
1916
|
// First-sync of a fresh account on a cold Dovecot is dominated by
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx-imap",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.83",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@bobfrankston/mailx-types": "^0.1.18",
|
|
13
13
|
"@bobfrankston/mailx-settings": "^0.1.26",
|
|
14
|
-
"@bobfrankston/mailx-store": "^0.1.
|
|
14
|
+
"@bobfrankston/mailx-store": "^0.1.44",
|
|
15
15
|
"@bobfrankston/iflow-direct": "^0.1.51",
|
|
16
16
|
"@bobfrankston/tcp-transport": "^0.1.6",
|
|
17
17
|
"@bobfrankston/smtp-direct": "^0.1.8",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bobfrankston/mailx-types": "^0.1.18",
|
|
41
41
|
"@bobfrankston/mailx-settings": "^0.1.26",
|
|
42
|
-
"@bobfrankston/mailx-store": "^0.1.
|
|
42
|
+
"@bobfrankston/mailx-store": "^0.1.44",
|
|
43
43
|
"@bobfrankston/iflow-direct": "^0.1.51",
|
|
44
44
|
"@bobfrankston/tcp-transport": "^0.1.6",
|
|
45
45
|
"@bobfrankston/smtp-direct": "^0.1.8",
|