@bobfrankston/mailx-imap 0.1.73 → 0.1.75
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 +33 -6
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -594,7 +594,19 @@ export class ImapManager extends EventEmitter {
|
|
|
594
594
|
if (!config)
|
|
595
595
|
throw new Error(`No config for account ${accountId}`);
|
|
596
596
|
const host = config.server || accountId;
|
|
597
|
-
|
|
597
|
+
// IDLE bypasses the host semaphore. The semaphore caps concurrent
|
|
598
|
+
// connection OPENS so a multi-account-on-one-host setup can't blow the
|
|
599
|
+
// server's connection limit — but IDLE is a SINGLE long-lived socket
|
|
600
|
+
// per account (the subscription), established once and parked. Gating
|
|
601
|
+
// it behind the same 4 permits that sync/prefetch/fast churn through
|
|
602
|
+
// meant that when sync saturated the pool (folders timing out at 360s),
|
|
603
|
+
// IDLE could not get a slot to (re)subscribe — so new mail stopped
|
|
604
|
+
// arriving by push and the user fell back to slow polling (Bob
|
|
605
|
+
// 2026-05-29: "you should be subscribed and not depend on sync"). The
|
|
606
|
+
// subscription must NEVER be starved by sync. One extra socket per
|
|
607
|
+
// account is well within any server cap.
|
|
608
|
+
const skipSemaphore = purpose === "idle";
|
|
609
|
+
const releaseHostSlot = skipSemaphore ? (() => { }) : await this.acquireHostSlot(host);
|
|
598
610
|
let client;
|
|
599
611
|
try {
|
|
600
612
|
// Verbose IMAP wire trace for ops connections only — that's the
|
|
@@ -2467,14 +2479,29 @@ export class ImapManager extends EventEmitter {
|
|
|
2467
2479
|
// Quick STATUS check (above) and actions/outbox drain (above)
|
|
2468
2480
|
// stay here because they're tightly tied to IMAP connection state
|
|
2469
2481
|
// and the `syncing` flag.
|
|
2470
|
-
//
|
|
2471
|
-
//
|
|
2472
|
-
//
|
|
2473
|
-
//
|
|
2482
|
+
// LAZY FOLDER SYNC (Thunderbird model — Bob 2026-05-28). The full
|
|
2483
|
+
// sweep of ALL folders no longer runs on the tight `intervalMinutes`
|
|
2484
|
+
// (5 min) timer. On a mailbox with 79 folders + a 135k INBOX, that
|
|
2485
|
+
// sweep ran longer than 5 minutes (folders timing out at 360s) and
|
|
2486
|
+
// was therefore ALWAYS running, saturating the 4-permit connection
|
|
2487
|
+
// semaphore. That starved the three things that actually matter —
|
|
2488
|
+
// new INBOX mail (IDLE re-establish), body prefetch, and click-to-read
|
|
2489
|
+
// — producing the session's running complaints: "slow to get codes",
|
|
2490
|
+
// "lots not prefetched", "Loading… forever".
|
|
2491
|
+
//
|
|
2492
|
+
// New model:
|
|
2493
|
+
// - INBOX: IDLE push (instant) + the 5-min quick STATUS check above
|
|
2494
|
+
// (safety net). Unchanged — INBOX stays fresh.
|
|
2495
|
+
// - Every other folder: synced ON OPEN (the client fires syncFolder
|
|
2496
|
+
// when the user selects a folder) + a slow background sweep at
|
|
2497
|
+
// FULL_SWEEP_MIN so nothing goes stale indefinitely.
|
|
2498
|
+
// The slow sweep keeps connection headroom free for the hot paths.
|
|
2499
|
+
const FULL_SWEEP_MIN = 30;
|
|
2474
2500
|
const fullInterval = setInterval(() => {
|
|
2475
2501
|
this.runFullSync().catch(e => console.error(` [periodic] full sync error: ${e?.message || e}`));
|
|
2476
|
-
},
|
|
2502
|
+
}, FULL_SWEEP_MIN * 60000);
|
|
2477
2503
|
this.syncIntervals.set("all", fullInterval);
|
|
2504
|
+
console.log(` [periodic] full all-folders sweep every ${FULL_SWEEP_MIN}min (lazy model — folders also sync on open); INBOX stays live via IDLE + ${intervalMinutes}min quick check`);
|
|
2478
2505
|
}
|
|
2479
2506
|
/** One-shot full sync + IDLE restart. Public so callers (Reconciler,
|
|
2480
2507
|
* user-initiated "Sync now") can fire it on demand. The original body
|