@bobfrankston/mailx-imap 0.1.43 → 0.1.44

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.
Files changed (3) hide show
  1. package/index.d.ts +5 -0
  2. package/index.js +31 -1
  3. package/package.json +3 -3
package/index.d.ts CHANGED
@@ -67,6 +67,11 @@ export declare class ImapManager extends EventEmitter {
67
67
  private accountErrorShown;
68
68
  private syncing;
69
69
  private inboxSyncing;
70
+ /** Wall-clock of ImapManager construction. Used by the lazy-folder-sync
71
+ * gate so non-priority folders defer their first-sync until ~3 min
72
+ * past startup, when the event loop has quieted down. Each restart
73
+ * resets this; the gate auto-lifts as time passes. */
74
+ private _startupAt;
70
75
  /** Use native IMAP client instead of imapflow. Set to true to enable. */
71
76
  useNativeClient: boolean;
72
77
  /** Per-account health counters. Incremented when the server misbehaves
package/index.js CHANGED
@@ -142,6 +142,11 @@ export class ImapManager extends EventEmitter {
142
142
  accountErrorShown = new Set();
143
143
  syncing = false;
144
144
  inboxSyncing = false;
145
+ /** Wall-clock of ImapManager construction. Used by the lazy-folder-sync
146
+ * gate so non-priority folders defer their first-sync until ~3 min
147
+ * past startup, when the event loop has quieted down. Each restart
148
+ * resets this; the gate auto-lifts as time passes. */
149
+ _startupAt = Date.now();
145
150
  /** Use native IMAP client instead of imapflow. Set to true to enable. */
146
151
  useNativeClient = false;
147
152
  // Connection management: see withConnection() below.
@@ -1716,12 +1721,37 @@ export class ImapManager extends EventEmitter {
1716
1721
  // timeout abandons a stalled command instead of waiting out
1717
1722
  // Dovecot's 300s server-side inactivity timer; the next sync tick
1718
1723
  // retries on a fresh socket.
1719
- const remaining = folders.filter(f => f.specialUse !== "inbox");
1724
+ // 2026-05-13: defer first-sync of non-priority folders by default
1725
+ // (C119 lazy folder sync). On bobma with 90+ folders, doing a
1726
+ // first-sync of every folder on every startup hammers the IMAP
1727
+ // socket buffer and pegs the daemon's event loop processing
1728
+ // FETCH literals — user clicks get a 20+ second IPC delay
1729
+ // because no IPC can squeeze in between FETCH chunks. Now: only
1730
+ // special-use folders (Sent/Drafts/Archive/Junk/Trash) + folders
1731
+ // mailx has previously seen (highestUid > 0, i.e. tracked
1732
+ // across the prior session) sync automatically. First-sync of
1733
+ // a "never-touched" non-special folder is deferred until the
1734
+ // user opens that folder (on-demand syncFolder call) or until
1735
+ // ~3 minutes after startup when the event loop is quiet.
1736
+ const STARTUP_LAZY_DELAY_MS = 3 * 60 * 1000;
1737
+ const startupQuietPoint = (this._startupAt || 0) + STARTUP_LAZY_DELAY_MS;
1738
+ const isLazyEligible = (f) => {
1739
+ if (f.specialUse && priorityOrder.includes(f.specialUse))
1740
+ return false;
1741
+ if (this.db.getHighestUid(accountId, f.id) > 0)
1742
+ return false;
1743
+ return true;
1744
+ };
1745
+ const remaining = folders.filter(f => f.specialUse !== "inbox" && !(isLazyEligible(f) && Date.now() < startupQuietPoint));
1720
1746
  remaining.sort((a, b) => {
1721
1747
  const pa = priorityOrder.indexOf(a.specialUse || "") >= 0 ? priorityOrder.indexOf(a.specialUse || "") : 5;
1722
1748
  const pb = priorityOrder.indexOf(b.specialUse || "") >= 0 ? priorityOrder.indexOf(b.specialUse || "") : 5;
1723
1749
  return pa - pb;
1724
1750
  });
1751
+ const deferredCount = folders.filter(f => f.specialUse !== "inbox" && isLazyEligible(f) && Date.now() < startupQuietPoint).length;
1752
+ if (deferredCount > 0) {
1753
+ console.log(` [sync] ${accountId}: deferring first-sync of ${deferredCount} non-priority folder(s) until ${new Date(startupQuietPoint).toLocaleTimeString()} or user opens them`);
1754
+ }
1725
1755
  const CONCURRENCY = 2;
1726
1756
  // First-sync of a fresh account on a cold Dovecot is dominated by
1727
1757
  // `UID SEARCH SINCE 30-days-ago`, which can take 5+ minutes on a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-imap",
3
- "version": "0.1.43",
3
+ "version": "0.1.44",
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.11",
13
13
  "@bobfrankston/mailx-settings": "^0.1.16",
14
- "@bobfrankston/mailx-store": "^0.1.23",
14
+ "@bobfrankston/mailx-store": "^0.1.24",
15
15
  "@bobfrankston/iflow-direct": "^0.1.44",
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.11",
41
41
  "@bobfrankston/mailx-settings": "^0.1.16",
42
- "@bobfrankston/mailx-store": "^0.1.23",
42
+ "@bobfrankston/mailx-store": "^0.1.24",
43
43
  "@bobfrankston/iflow-direct": "^0.1.44",
44
44
  "@bobfrankston/tcp-transport": "^0.1.6",
45
45
  "@bobfrankston/smtp-direct": "^0.1.8",