@bobfrankston/mailx 1.0.124 → 1.0.126
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/blowaway.cmd
CHANGED
package/package.json
CHANGED
|
@@ -323,6 +323,34 @@ export class ImapManager extends EventEmitter {
|
|
|
323
323
|
const fetched = await client.fetchMessagesSinceUid(folder.path, highestUid, { source: false });
|
|
324
324
|
// Filter out the last known message (IMAP * always returns at least one)
|
|
325
325
|
messages = fetched.filter(m => m.uid > highestUid);
|
|
326
|
+
// Gap detection: compare DB count vs IMAP count to catch interrupted syncs
|
|
327
|
+
const dbCount = this.db.getMessageCount(accountId, folderId);
|
|
328
|
+
try {
|
|
329
|
+
const imapCount = await client.getMessagesCount(folder.path);
|
|
330
|
+
const gap = imapCount - dbCount - messages.length;
|
|
331
|
+
if (gap > 0) {
|
|
332
|
+
console.log(` ${folder.path}: gap detected — IMAP has ${imapCount}, DB has ${dbCount}, ${messages.length} new → ${gap} missing`);
|
|
333
|
+
// Full UID reconciliation: get all IMAP UIDs, find missing ones
|
|
334
|
+
const existingUids = new Set(this.db.getUidsForFolder(accountId, folderId));
|
|
335
|
+
const newUids = new Set(messages.map(m => m.uid));
|
|
336
|
+
const allImapUids = await client.getUids(folder.path);
|
|
337
|
+
const missingUids = allImapUids.filter(uid => !existingUids.has(uid) && !newUids.has(uid));
|
|
338
|
+
if (missingUids.length > 0) {
|
|
339
|
+
console.log(` ${folder.path}: reconciling ${missingUids.length} missing messages`);
|
|
340
|
+
// Fetch in chunks to avoid huge commands
|
|
341
|
+
const chunkSize = 500;
|
|
342
|
+
for (let i = 0; i < missingUids.length; i += chunkSize) {
|
|
343
|
+
const chunk = missingUids.slice(i, i + chunkSize);
|
|
344
|
+
const range = chunk.join(",");
|
|
345
|
+
const recovered = await client.fetchMessages(folder.path, range, { source: false });
|
|
346
|
+
messages.push(...recovered);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
catch (e) {
|
|
352
|
+
console.error(` ${folder.path}: gap detection failed: ${e.message}`);
|
|
353
|
+
}
|
|
326
354
|
// Backfill: if historyDays extends further back than our oldest message, fetch the gap
|
|
327
355
|
const oldestDate = this.db.getOldestDate(accountId, folderId);
|
|
328
356
|
if (oldestDate > 0 && startDate.getTime() < oldestDate) {
|
|
@@ -497,7 +525,6 @@ export class ImapManager extends EventEmitter {
|
|
|
497
525
|
// Legacy fallback removed — was doubling connections.
|
|
498
526
|
// If native client has issues, set useNativeClient=false or use --legacy-imap flag.
|
|
499
527
|
await client.logout();
|
|
500
|
-
this.trackLogout(accountId);
|
|
501
528
|
client = null;
|
|
502
529
|
accountFolders.set(accountId, folders);
|
|
503
530
|
// Sync inbox immediately
|
|
@@ -510,7 +537,6 @@ export class ImapManager extends EventEmitter {
|
|
|
510
537
|
new Promise((_, reject) => setTimeout(() => reject(new Error("Sync timeout (60s)")), 60000))
|
|
511
538
|
]);
|
|
512
539
|
await client.logout();
|
|
513
|
-
this.trackLogout(accountId);
|
|
514
540
|
client = null;
|
|
515
541
|
}
|
|
516
542
|
catch (e) {
|
|
@@ -626,7 +652,6 @@ export class ImapManager extends EventEmitter {
|
|
|
626
652
|
await client.logout();
|
|
627
653
|
}
|
|
628
654
|
catch { /* */ }
|
|
629
|
-
this.trackLogout(accountId);
|
|
630
655
|
client = this.createClient(accountId);
|
|
631
656
|
}
|
|
632
657
|
}
|
|
@@ -714,7 +739,6 @@ export class ImapManager extends EventEmitter {
|
|
|
714
739
|
client = this.createClient(accountId);
|
|
715
740
|
const count = await client.getMessagesCount("INBOX");
|
|
716
741
|
await client.logout();
|
|
717
|
-
this.trackLogout(accountId);
|
|
718
742
|
client = null;
|
|
719
743
|
const prev = this.lastInboxCounts.get(accountId) ?? count;
|
|
720
744
|
this.lastInboxCounts.set(accountId, count);
|
|
@@ -723,7 +747,6 @@ export class ImapManager extends EventEmitter {
|
|
|
723
747
|
client = this.createClient(accountId);
|
|
724
748
|
await this.syncFolder(accountId, inbox.id, client);
|
|
725
749
|
await client.logout();
|
|
726
|
-
this.trackLogout(accountId);
|
|
727
750
|
client = null;
|
|
728
751
|
}
|
|
729
752
|
}
|
|
@@ -1267,7 +1290,6 @@ export class ImapManager extends EventEmitter {
|
|
|
1267
1290
|
await client.logout();
|
|
1268
1291
|
}
|
|
1269
1292
|
catch { }
|
|
1270
|
-
this.trackLogout(accountId);
|
|
1271
1293
|
return;
|
|
1272
1294
|
}
|
|
1273
1295
|
const sendingFlag = `$Sending-${this.hostname}`;
|
|
@@ -1373,7 +1395,6 @@ export class ImapManager extends EventEmitter {
|
|
|
1373
1395
|
await client.logout();
|
|
1374
1396
|
}
|
|
1375
1397
|
catch { /* ignore */ }
|
|
1376
|
-
this.trackLogout(accountId);
|
|
1377
1398
|
}
|
|
1378
1399
|
}
|
|
1379
1400
|
/** Start background Outbox worker — runs immediately then every 10 seconds */
|
|
@@ -60,6 +60,7 @@ export declare class MailxDB {
|
|
|
60
60
|
updateBodyPath(accountId: string, uid: number, bodyPath: string): void;
|
|
61
61
|
getHighestUid(accountId: string, folderId: number): number;
|
|
62
62
|
getOldestDate(accountId: string, folderId: number): number;
|
|
63
|
+
getMessageCount(accountId: string, folderId: number): number;
|
|
63
64
|
/** Get all UIDs for a folder */
|
|
64
65
|
getUidsForFolder(accountId: string, folderId: number): number[];
|
|
65
66
|
/** Delete a message by account + UID */
|
|
@@ -328,6 +328,10 @@ export class MailxDB {
|
|
|
328
328
|
const r = this.db.prepare("SELECT MIN(date) as minDate FROM messages WHERE account_id = ? AND folder_id = ?").get(accountId, folderId);
|
|
329
329
|
return r?.minDate || 0;
|
|
330
330
|
}
|
|
331
|
+
getMessageCount(accountId, folderId) {
|
|
332
|
+
const r = this.db.prepare("SELECT count(*) as cnt FROM messages WHERE account_id = ? AND folder_id = ?").get(accountId, folderId);
|
|
333
|
+
return r?.cnt || 0;
|
|
334
|
+
}
|
|
331
335
|
/** Get all UIDs for a folder */
|
|
332
336
|
getUidsForFolder(accountId, folderId) {
|
|
333
337
|
const rows = this.db.prepare("SELECT uid FROM messages WHERE account_id = ? AND folder_id = ?").all(accountId, folderId);
|