@bobfrankston/mailx-imap 0.1.21 → 0.1.23

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 +9 -1
  2. package/index.js +25 -38
  3. package/package.json +7 -7
package/index.d.ts CHANGED
@@ -239,6 +239,10 @@ export declare class ImapManager extends EventEmitter {
239
239
  quickInboxCheck(): Promise<void>;
240
240
  /** Start periodic sync */
241
241
  startPeriodicSync(intervalMinutes: number): void;
242
+ /** One-shot full sync + IDLE restart. Public so callers (Reconciler,
243
+ * user-initiated "Sync now") can fire it on demand. The original body
244
+ * that lived inline in startPeriodicSync was lifted verbatim. */
245
+ runFullSync(): Promise<void>;
242
246
  /** Stop periodic sync */
243
247
  stopPeriodicSync(): void;
244
248
  /** Check if an account is OAuth (Gmail/Outlook — generous connection limits) */
@@ -292,7 +296,11 @@ export declare class ImapManager extends EventEmitter {
292
296
  private shouldSkipFolder;
293
297
  private recordFolderError;
294
298
  private clearFolderErrors;
295
- private prefetchBodies;
299
+ /** Background body-cache backfill. Public so the Reconciler can schedule
300
+ * the periodic tick under its priority/back-pressure rules; existing
301
+ * in-method post-sync nudges (sync, fetchSince, fetchOne) call this
302
+ * too and the per-account `prefetchingAccounts` set deduplicates. */
303
+ prefetchBodies(accountId: string): Promise<void>;
296
304
  private _prefetchBodies;
297
305
  /** Get the body store for direct access */
298
306
  getBodyStore(): FileMessageStore;
package/index.js CHANGED
@@ -1825,47 +1825,30 @@ export class ImapManager extends EventEmitter {
1825
1825
  }
1826
1826
  }, 30000);
1827
1827
  this.syncIntervals.set("actions", actionsInterval);
1828
- // Body prefetch as a first-class background task independent of
1829
- // sync success. Prefetch was previously only triggered from inside
1830
- // sync, so any account with slow/failing IMAP had its "not downloaded"
1831
- // dots stuck forever even though body fetches use a separate
1832
- // connection that might succeed. Every 60s, for every account, fire
1833
- // prefetchBodies() (cheap when body_path is already populated — just a
1834
- // DB query that returns 0 rows; the prefetchingAccounts guard
1835
- // short-circuits concurrent triggers).
1836
- if (getPrefetch()) {
1837
- const kickPrefetch = () => {
1838
- for (const [accountId] of this.configs) {
1839
- this.prefetchBodies(accountId).catch(e => console.error(` [prefetch] ${accountId}: ${e?.message || e}`));
1840
- }
1841
- };
1842
- // Fire once now so the "not downloaded" dots start filling in
1843
- // immediately on app start, don't make the user wait a minute.
1844
- setTimeout(kickPrefetch, 2000);
1845
- const prefetchInterval = setInterval(kickPrefetch, 60000);
1846
- this.syncIntervals.set("prefetch", prefetchInterval);
1847
- console.log(` [periodic] body prefetch every 60s (independent of sync)`);
1848
- }
1849
- // Tombstone prune: age out local-delete records older than 30 days.
1850
- // Runs hourly — cheap (one indexed DELETE).
1851
- const TOMBSTONE_RETENTION_DAYS = 30;
1852
- const pruneTombstones = () => {
1853
- const cutoff = Date.now() - TOMBSTONE_RETENTION_DAYS * 86400_000;
1854
- const n = this.db.pruneTombstones(cutoff);
1855
- if (n > 0)
1856
- console.log(` [tombstones] pruned ${n} older than ${TOMBSTONE_RETENTION_DAYS} days`);
1857
- };
1858
- setTimeout(pruneTombstones, 30_000); // first run after startup settles
1859
- this.syncIntervals.set("tombstone-prune", setInterval(pruneTombstones, 3600_000));
1860
- // Full sync (all folders + IDLE restart) at configured interval
1861
- const fullInterval = setInterval(async () => {
1862
- console.log(` [periodic] Full sync at ${new Date().toLocaleTimeString()}`);
1863
- await this.syncAll();
1864
- await this.stopWatching();
1865
- await this.startWatching();
1828
+ // Body prefetch + tombstone prune moved to Reconciler.start
1829
+ // prefetch needs back-pressure from the body-fetch lane, and
1830
+ // tombstone prune is pure DB bookkeeping with no IMAP coupling.
1831
+ // Quick STATUS check (above) and actions/outbox drain (above)
1832
+ // stay here because they're tightly tied to IMAP connection state
1833
+ // and the `syncing` flag.
1834
+ // Full sync (all folders + IDLE restart) at configured interval.
1835
+ // Stays here because callers pass `intervalMinutes` directly —
1836
+ // moving it would mean threading the value through MailxService
1837
+ // Reconciler with a separate setter, for no behavior gain.
1838
+ const fullInterval = setInterval(() => {
1839
+ this.runFullSync().catch(e => console.error(` [periodic] full sync error: ${e?.message || e}`));
1866
1840
  }, intervalMinutes * 60000);
1867
1841
  this.syncIntervals.set("all", fullInterval);
1868
1842
  }
1843
+ /** One-shot full sync + IDLE restart. Public so callers (Reconciler,
1844
+ * user-initiated "Sync now") can fire it on demand. The original body
1845
+ * that lived inline in startPeriodicSync was lifted verbatim. */
1846
+ async runFullSync() {
1847
+ console.log(` [periodic] Full sync at ${new Date().toLocaleTimeString()}`);
1848
+ await this.syncAll();
1849
+ await this.stopWatching();
1850
+ await this.startWatching();
1851
+ }
1869
1852
  /** Stop periodic sync */
1870
1853
  stopPeriodicSync() {
1871
1854
  for (const [key, interval] of this.syncIntervals) {
@@ -2065,6 +2048,10 @@ export class ImapManager extends EventEmitter {
2065
2048
  clearFolderErrors(accountId, folderPath) {
2066
2049
  this.folderErrorCooldown.delete(`${accountId}:${folderPath}`);
2067
2050
  }
2051
+ /** Background body-cache backfill. Public so the Reconciler can schedule
2052
+ * the periodic tick under its priority/back-pressure rules; existing
2053
+ * in-method post-sync nudges (sync, fetchSince, fetchOne) call this
2054
+ * too and the per-account `prefetchingAccounts` set deduplicates. */
2068
2055
  async prefetchBodies(accountId) {
2069
2056
  if (this.prefetchingAccounts.has(accountId))
2070
2057
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-imap",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -9,9 +9,9 @@
9
9
  },
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
- "@bobfrankston/mailx-types": "^0.1.8",
13
- "@bobfrankston/mailx-settings": "^0.1.10",
14
- "@bobfrankston/mailx-store": "^0.1.8",
12
+ "@bobfrankston/mailx-types": "^0.1.10",
13
+ "@bobfrankston/mailx-settings": "^0.1.11",
14
+ "@bobfrankston/mailx-store": "^0.1.9",
15
15
  "@bobfrankston/iflow-direct": "^0.1.30",
16
16
  "@bobfrankston/tcp-transport": "^0.1.5",
17
17
  "@bobfrankston/smtp-direct": "^0.1.5",
@@ -37,9 +37,9 @@
37
37
  },
38
38
  ".transformedSnapshot": {
39
39
  "dependencies": {
40
- "@bobfrankston/mailx-types": "^0.1.8",
41
- "@bobfrankston/mailx-settings": "^0.1.10",
42
- "@bobfrankston/mailx-store": "^0.1.8",
40
+ "@bobfrankston/mailx-types": "^0.1.10",
41
+ "@bobfrankston/mailx-settings": "^0.1.11",
42
+ "@bobfrankston/mailx-store": "^0.1.9",
43
43
  "@bobfrankston/iflow-direct": "^0.1.30",
44
44
  "@bobfrankston/tcp-transport": "^0.1.5",
45
45
  "@bobfrankston/smtp-direct": "^0.1.5",