@bobfrankston/rmfmail 1.1.217 → 1.1.219

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.
@@ -1307,12 +1307,36 @@ export class ImapManager extends EventEmitter {
1307
1307
  const qr = await (client as any).resyncFolder(folder.path, prevUidValidity, prevModSeq);
1308
1308
  console.log(` [qresync] ${accountId}/${folder.path}: vanished=${qr.vanishedUids.length} changed=${qr.changedMessages.length} newModSeq=${qr.newHighestModSeq} in ${Date.now() - __qrT0}ms`);
1309
1309
 
1310
+ // No real folder vanishes this many UIDs in one QRESYNC cycle.
1311
+ // iflow-direct can mis-expand a VANISHED *range* (e.g. the
1312
+ // server reports "VANISHED 1:4828883" when our modseq watermark
1313
+ // is stale) into millions of individual UIDs. Applying that
1314
+ // literally = 4.8M synchronous DB calls that wedge the whole
1315
+ // event loop (frozen window) AND delete every local row caught
1316
+ // in the range (INBOX wiped → the viewer's open message
1317
+ // vanishes / shows a stale unrelated eml). Bob 2026-06-04.
1318
+ const VANISHED_ABSURD_CAP = 50000;
1310
1319
  if (qr.uidValidityChanged) {
1311
1320
  // UIDVALIDITY rolled — our local UIDs are stale. Fall through
1312
1321
  // to the full set-diff path; that'll discover the new state
1313
1322
  // and the deletion safeguards (50% threshold) will keep us
1314
1323
  // from wiping anything we shouldn't.
1315
1324
  console.log(` [qresync] ${accountId}/${folder.path}: UIDVALIDITY changed (was ${prevUidValidity}, now ${qr.exists}); falling back to full sync`);
1325
+ } else if (qr.vanishedUids.length > VANISHED_ABSURD_CAP) {
1326
+ // Refuse the absurd VANISHED set; do NOT iterate/apply it.
1327
+ const localCount = this.db.getMessageCount(accountId, folderId);
1328
+ console.error(` [qresync] ${accountId}/${folder.path}: REFUSING VANISHED of ${qr.vanishedUids.length} UIDs (folder has ${localCount} locally) — mis-parsed range; self-healing (advance watermark) + falling back to set-diff.`);
1329
+ // SELF-HEAL: advance the modseq watermark to the server's
1330
+ // CURRENT value. Without this the next QRESYNC re-requests the
1331
+ // same giant `(EARLIER) 1:N` range every cycle forever (Bob saw
1332
+ // it recur 21:03 → 21:20) — refusing without advancing isn't
1333
+ // self-healing. The set-diff reconcile below discovers + applies
1334
+ // the ACTUAL deletions via Message-ID identity (bounded, 50%
1335
+ // guard), so skipping the literal VANISHED loses nothing; it
1336
+ // just breaks the stale-modseq loop so future QRESYNCs are clean.
1337
+ if (qr.newHighestModSeq !== undefined) {
1338
+ this.db.updateFolderSync(folderId, prevUidValidity, String(qr.newHighestModSeq));
1339
+ }
1316
1340
  } else {
1317
1341
  // Apply VANISHED — server says these UIDs are gone. No
1318
1342
  // tombstone, no diff, just delete the local rows.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-imap",
3
- "version": "0.1.82",
3
+ "version": "0.1.84",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bobfrankston/mailx-imap",
9
- "version": "0.1.82",
9
+ "version": "0.1.84",
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
12
  "@bobfrankston/iflow-direct": "^0.1.27",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-imap",
3
- "version": "0.1.82",
3
+ "version": "0.1.84",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-store",
3
- "version": "0.1.44",
3
+ "version": "0.1.45",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",