@bobfrankston/rmfmail 1.2.27 → 1.2.29

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.
@@ -1374,6 +1374,7 @@ export class ImapManager extends EventEmitter {
1374
1374
  // where a full UID SEARCH is cheap and reveals server-side deletes
1375
1375
  // of older messages).
1376
1376
  let statusMessageCount: number | null = null;
1377
+ let serverUidNext: number | null = null;
1377
1378
  if (highestUid > 0) {
1378
1379
  try {
1379
1380
  console.log(` [sync-status] ${accountId}/${folder.path}: calling STATUS...`);
@@ -1383,6 +1384,7 @@ export class ImapManager extends EventEmitter {
1383
1384
  console.log(` [sync-status] ${accountId}/${folder.path}: STATUS returned in ${Date.now() - __statusT0}ms`);
1384
1385
  if (status && typeof status.messages === "number") statusMessageCount = status.messages;
1385
1386
  if (status && typeof status.uidNext === "number") {
1387
+ serverUidNext = status.uidNext;
1386
1388
  const serverHighest = status.uidNext - 1;
1387
1389
  const noNewUids = serverHighest <= highestUid;
1388
1390
  const countsMatch = typeof status.messages === "number" && status.messages === localCount;
@@ -1827,6 +1829,33 @@ export class ImapManager extends EventEmitter {
1827
1829
  // data (the "ubiquiti letter disappeared after reply" case had no trace).
1828
1830
  let deletedCount = 0;
1829
1831
  if (!firstSync) {
1832
+ // PHANTOM SWEEP (Bob 2026-06-17). A local row whose uid >= the server's
1833
+ // UIDNEXT is PROVABLY impossible: UIDNEXT is the next uid the server
1834
+ // will ever assign in this folder, so any uid >= it was never assigned
1835
+ // here. These are cross-wired rows ("UID is not identity" corruption —
1836
+ // CEBog had 106/120 rows with uid >= UIDNEXT=94, real messages stamped
1837
+ // with the wrong folder_id). They break prefetch: it asks the server
1838
+ // for uids the folder doesn't have → 0 FETCH responses → no body ever
1839
+ // caches. The normal set-diff reconcile WON'T remove them because the
1840
+ // 50%-deletion safety guard refuses (88% of CEBog would delete). But
1841
+ // these uids can't be a real message, a move target, or a transient
1842
+ // server hiccup — so delete them unconditionally (the real copy lives
1843
+ // in its true folder and re-syncs from there; the local store is a
1844
+ // cache). Only runs when STATUS gave a trustworthy UIDNEXT.
1845
+ if (serverUidNext != null && serverUidNext > 0) {
1846
+ try {
1847
+ const phantoms = this.db.getUidsForFolder(accountId, folderId).filter(u => u >= serverUidNext!);
1848
+ if (phantoms.length > 0) {
1849
+ for (const uid of phantoms) {
1850
+ this.unlinkBodyFile(accountId, uid, folderId).catch(() => {});
1851
+ this.db.deleteMessage(accountId, folderId, uid, `phantom: uid>=server UIDNEXT ${serverUidNext}`, `mailx-imap phantom sweep (${folder.path})`);
1852
+ }
1853
+ deletedCount += phantoms.length;
1854
+ this.db.recalcFolderCounts(folderId);
1855
+ console.log(` [phantom-sweep] ${accountId}/${folder.path}: removed ${phantoms.length} cross-wired row(s) with uid >= UIDNEXT ${serverUidNext}`);
1856
+ }
1857
+ } catch (e: any) { console.error(` [phantom-sweep] ${accountId}/${folder.path}: ${e?.message || e}`); }
1858
+ }
1830
1859
  try {
1831
1860
  // Reuse the server UID list set-diff already fetched.
1832
1861
  // Without this we made TWO `UID SEARCH` calls per folder
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-imap",
3
- "version": "0.1.98",
3
+ "version": "0.1.99",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bobfrankston/mailx-imap",
9
- "version": "0.1.98",
9
+ "version": "0.1.99",
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.98",
3
+ "version": "0.1.99",
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.53",
3
+ "version": "0.1.54",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",