@bobfrankston/rmfmail 1.0.548 → 1.0.549
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/client/app.js +7 -4
- package/client/app.js.map +1 -1
- package/client/app.ts +10 -7
- package/client/compose/compose.js +2 -2
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +2 -2
- package/package.json +1 -1
- package/packages/mailx-imap/index.d.ts +9 -1
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +25 -38
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +30 -42
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-service/local-store.d.ts.map +1 -1
- package/packages/mailx-service/local-store.js +5 -1
- package/packages/mailx-service/local-store.js.map +1 -1
- package/packages/mailx-service/local-store.ts +5 -1
- package/packages/mailx-service/reconciler.d.ts +28 -0
- package/packages/mailx-service/reconciler.d.ts.map +1 -1
- package/packages/mailx-service/reconciler.js +70 -2
- package/packages/mailx-service/reconciler.js.map +1 -1
- package/packages/mailx-service/reconciler.ts +81 -2
- package/packages/mailx-settings/docs/contacts.md +6 -1
- package/packages/mailx-settings/docs/search.md +99 -0
- package/packages/mailx-settings/package.json +1 -1
- package/packages/mailx-store/file-store.d.ts +6 -0
- package/packages/mailx-store/file-store.d.ts.map +1 -1
- package/packages/mailx-store/file-store.js +8 -0
- package/packages/mailx-store/file-store.js.map +1 -1
- package/packages/mailx-store/file-store.ts +9 -0
- package/packages/mailx-store/package.json +1 -1
|
@@ -1855,51 +1855,35 @@ export class ImapManager extends EventEmitter {
|
|
|
1855
1855
|
}, 30000);
|
|
1856
1856
|
this.syncIntervals.set("actions", actionsInterval);
|
|
1857
1857
|
|
|
1858
|
-
// Body prefetch
|
|
1859
|
-
//
|
|
1860
|
-
//
|
|
1861
|
-
//
|
|
1862
|
-
//
|
|
1863
|
-
//
|
|
1864
|
-
|
|
1865
|
-
//
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
};
|
|
1874
|
-
// Fire once now so the "not downloaded" dots start filling in
|
|
1875
|
-
// immediately on app start, don't make the user wait a minute.
|
|
1876
|
-
setTimeout(kickPrefetch, 2000);
|
|
1877
|
-
const prefetchInterval = setInterval(kickPrefetch, 60000);
|
|
1878
|
-
this.syncIntervals.set("prefetch", prefetchInterval);
|
|
1879
|
-
console.log(` [periodic] body prefetch every 60s (independent of sync)`);
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
// Tombstone prune: age out local-delete records older than 30 days.
|
|
1883
|
-
// Runs hourly — cheap (one indexed DELETE).
|
|
1884
|
-
const TOMBSTONE_RETENTION_DAYS = 30;
|
|
1885
|
-
const pruneTombstones = (): void => {
|
|
1886
|
-
const cutoff = Date.now() - TOMBSTONE_RETENTION_DAYS * 86400_000;
|
|
1887
|
-
const n = this.db.pruneTombstones(cutoff);
|
|
1888
|
-
if (n > 0) console.log(` [tombstones] pruned ${n} older than ${TOMBSTONE_RETENTION_DAYS} days`);
|
|
1889
|
-
};
|
|
1890
|
-
setTimeout(pruneTombstones, 30_000); // first run after startup settles
|
|
1891
|
-
this.syncIntervals.set("tombstone-prune", setInterval(pruneTombstones, 3600_000));
|
|
1892
|
-
|
|
1893
|
-
// Full sync (all folders + IDLE restart) at configured interval
|
|
1894
|
-
const fullInterval = setInterval(async () => {
|
|
1895
|
-
console.log(` [periodic] Full sync at ${new Date().toLocaleTimeString()}`);
|
|
1896
|
-
await this.syncAll();
|
|
1897
|
-
await this.stopWatching();
|
|
1898
|
-
await this.startWatching();
|
|
1858
|
+
// Body prefetch + tombstone prune moved to Reconciler.start —
|
|
1859
|
+
// prefetch needs back-pressure from the body-fetch lane, and
|
|
1860
|
+
// tombstone prune is pure DB bookkeeping with no IMAP coupling.
|
|
1861
|
+
// Quick STATUS check (above) and actions/outbox drain (above)
|
|
1862
|
+
// stay here because they're tightly tied to IMAP connection state
|
|
1863
|
+
// and the `syncing` flag.
|
|
1864
|
+
|
|
1865
|
+
// Full sync (all folders + IDLE restart) at configured interval.
|
|
1866
|
+
// Stays here because callers pass `intervalMinutes` directly —
|
|
1867
|
+
// moving it would mean threading the value through MailxService
|
|
1868
|
+
// → Reconciler with a separate setter, for no behavior gain.
|
|
1869
|
+
const fullInterval = setInterval(() => {
|
|
1870
|
+
this.runFullSync().catch(e =>
|
|
1871
|
+
console.error(` [periodic] full sync error: ${e?.message || e}`)
|
|
1872
|
+
);
|
|
1899
1873
|
}, intervalMinutes * 60000);
|
|
1900
1874
|
this.syncIntervals.set("all", fullInterval);
|
|
1901
1875
|
}
|
|
1902
1876
|
|
|
1877
|
+
/** One-shot full sync + IDLE restart. Public so callers (Reconciler,
|
|
1878
|
+
* user-initiated "Sync now") can fire it on demand. The original body
|
|
1879
|
+
* that lived inline in startPeriodicSync was lifted verbatim. */
|
|
1880
|
+
async runFullSync(): Promise<void> {
|
|
1881
|
+
console.log(` [periodic] Full sync at ${new Date().toLocaleTimeString()}`);
|
|
1882
|
+
await this.syncAll();
|
|
1883
|
+
await this.stopWatching();
|
|
1884
|
+
await this.startWatching();
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1903
1887
|
/** Stop periodic sync */
|
|
1904
1888
|
stopPeriodicSync(): void {
|
|
1905
1889
|
for (const [key, interval] of this.syncIntervals) {
|
|
@@ -2097,7 +2081,11 @@ export class ImapManager extends EventEmitter {
|
|
|
2097
2081
|
this.folderErrorCooldown.delete(`${accountId}:${folderPath}`);
|
|
2098
2082
|
}
|
|
2099
2083
|
|
|
2100
|
-
|
|
2084
|
+
/** Background body-cache backfill. Public so the Reconciler can schedule
|
|
2085
|
+
* the periodic tick under its priority/back-pressure rules; existing
|
|
2086
|
+
* in-method post-sync nudges (sync, fetchSince, fetchOne) call this
|
|
2087
|
+
* too and the per-account `prefetchingAccounts` set deduplicates. */
|
|
2088
|
+
async prefetchBodies(accountId: string): Promise<void> {
|
|
2101
2089
|
if (this.prefetchingAccounts.has(accountId)) return;
|
|
2102
2090
|
this.prefetchingAccounts.add(accountId);
|
|
2103
2091
|
try { await this._prefetchBodies(accountId); }
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx-imap",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bobfrankston/mailx-imap",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.23",
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@bobfrankston/iflow-direct": "^0.1.27",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-store.d.ts","sourceRoot":"","sources":["local-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,KAAK,EACR,eAAe,EAAW,YAAY,EAAE,WAAW,EACtD,MAAM,2BAA2B,CAAC;AAuCnC;;;;mEAImE;AACnE,MAAM,WAAW,YAAa,SAAQ,eAAe;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxG,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uBAAuB,EAAE,OAAO,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACtB;AAED,qBAAa,UAAU;IAEf,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,SAAS;gBADT,EAAE,EAAE,OAAO,EACX,SAAS,EAAE,gBAAgB;IAMvC;;;6DAGyD;IACzD,WAAW,IAAI;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE;IAM9E,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,EAAE;IAMpC;;4CAEwC;IACxC,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAK7F,iEAAiE;IACjE,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,CAAC,eAAe,CAAC;IAI9D,mEAAmE;IACnE,eAAe,CAAC,IAAI,SAAI,EAAE,QAAQ,SAAK,GAAG,WAAW,CAAC,eAAe,CAAC;IAItE,sEAAsE;IACtE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAI,EAAE,QAAQ,SAAK,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,eAAe,CAAC;IAM3H;;;;;;;sEAOkE;IAC5D,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"local-store.d.ts","sourceRoot":"","sources":["local-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,KAAK,EACR,eAAe,EAAW,YAAY,EAAE,WAAW,EACtD,MAAM,2BAA2B,CAAC;AAuCnC;;;;mEAImE;AACnE,MAAM,WAAW,YAAa,SAAQ,eAAe;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxG,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uBAAuB,EAAE,OAAO,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACtB;AAED,qBAAa,UAAU;IAEf,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,SAAS;gBADT,EAAE,EAAE,OAAO,EACX,SAAS,EAAE,gBAAgB;IAMvC;;;6DAGyD;IACzD,WAAW,IAAI;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE;IAM9E,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,EAAE;IAMpC;;4CAEwC;IACxC,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAK7F,iEAAiE;IACjE,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,CAAC,eAAe,CAAC;IAI9D,mEAAmE;IACnE,eAAe,CAAC,IAAI,SAAI,EAAE,QAAQ,SAAK,GAAG,WAAW,CAAC,eAAe,CAAC;IAItE,sEAAsE;IACtE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAI,EAAE,QAAQ,SAAK,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,eAAe,CAAC;IAM3H;;;;;;;sEAOkE;IAC5D,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAuJvH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,GAAG,EAAE;IAIzE,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,UAAQ,GAAG,GAAG,EAAE;IAI5D,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,GAAG,EAAE;IAIhD,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAI,EAAE,QAAQ,SAAM,GAAG,GAAG;CAK7D"}
|
|
@@ -238,7 +238,11 @@ export class LocalStore {
|
|
|
238
238
|
cached: true,
|
|
239
239
|
deliveredTo, returnPath,
|
|
240
240
|
listUnsubscribe, listUnsubscribeMail, listUnsubscribeHttp, listUnsubscribeOneClick,
|
|
241
|
-
|
|
241
|
+
// body_path in the DB is stored relative to the body-store
|
|
242
|
+
// basePath; resolve to absolute so the UI's "Source" button
|
|
243
|
+
// can hand the path to an OS file open without re-deriving
|
|
244
|
+
// basePath every time.
|
|
245
|
+
emlPath: this.bodyStore.absolutePath(storedPath),
|
|
242
246
|
isFlagged,
|
|
243
247
|
};
|
|
244
248
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-store.js","sourceRoot":"","sources":["local-store.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"local-store.js","sourceRoot":"","sources":["local-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK1C,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD;;8EAE8E;AAC9E,SAAS,oBAAoB,CAAC,OAAY;IACtC,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,OAAQ,GAAW,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAE,GAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACvH,IAAI,MAAM,EAAE,CAAC;QACT,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QACjD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAClC,IAAI,CAAC,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,IAAI,GAAG,GAAG,CAAC;iBAC1C,IAAI,CAAC,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,IAAI,GAAG,GAAG,CAAC;QACxD,CAAC;IACL,CAAC;IACD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,WAAW,EAAE,WAAW,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC;YACtC,IAAI,KAAK,CAAC,GAAG;gBAAE,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;YAC1E,IAAI,KAAK,CAAC,IAAI;gBAAE,IAAI,GAAG,UAAU,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAC9F,CAAC;IACL,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,OAAQ,IAAY,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7H,IAAI,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,QAAQ,GAAG,IAAI,CAAC;IAE5D,OAAO,EAAE,mBAAmB,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,uBAAuB,EAAE,QAAQ,EAAE,CAAC;AACvG,CAAC;AAwBD,MAAM,OAAO,UAAU;IAEP;IACA;IAFZ,YACY,EAAW,EACX,SAA2B;QAD3B,OAAE,GAAF,EAAE,CAAS;QACX,cAAS,GAAT,SAAS,CAAkB;IACpC,CAAC;IAEJ,qEAAqE;IACrE,6DAA6D;IAE7D;;;6DAGyD;IACzD,WAAW;QACP,OAAO,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;IACjC,CAAC;IAED,gBAAgB;IAEhB,UAAU,CAAC,SAAiB;QACxB,OAAO,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;IAED,0BAA0B;IAE1B;;4CAEwC;IACxC,kBAAkB,CAAC,SAAiB,EAAE,GAAW,EAAE,QAAiB;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,SAAS,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC9D,OAAO,GAAG,IAAI,IAAI,CAAC;IACvB,CAAC;IAED,iEAAiE;IACjE,WAAW,CAAC,KAAmB;QAC3B,OAAO,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,mEAAmE;IACnE,eAAe,CAAC,IAAI,GAAG,CAAC,EAAE,QAAQ,GAAG,EAAE;QACnC,OAAO,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACnD,CAAC;IAED,sEAAsE;IACtE,cAAc,CAAC,KAAa,EAAE,IAAI,GAAG,CAAC,EAAE,QAAQ,GAAG,EAAE,EAAE,SAAkB,EAAE,QAAiB;QACxF,OAAO,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC9E,CAAC;IAED,2CAA2C;IAE3C;;;;;;;sEAOkE;IAClE,KAAK,CAAC,UAAU,CAAC,SAAiB,EAAE,GAAW,EAAE,WAAoB,EAAE,QAAiB;QACpF,MAAM,QAAQ,GAAQ,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,SAAS,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QACxE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,MAAM,SAAS,GAAG,aAAa,EAAS,CAAC;QACzC,MAAM,UAAU,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAChE,MAAM,YAAY,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACpD,MAAM,OAAO,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAErF,kEAAkE;QAClE,gEAAgE;QAChE,IAAI,CAAC,WAAW,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,CAAC,SAAS,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YACtF,MAAM,OAAO,GAAG,CAAC,SAAS,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YACtF,MAAM,UAAU,GAAG,CAAC,SAAS,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YAC5F,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAC5B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;gBAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtD,WAAW,GAAG,IAAI,CAAC;YACvB,CAAC;QACL,CAAC;QAED,MAAM,SAAS,GAAG,CAAC,CAAC,CAChB,CAAC,SAAS,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC;YAC5F,CAAC,SAAS,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,CACjG,CAAC;QAEF,iEAAiE;QACjE,mEAAmE;QACnE,IAAI,UAAU,GAAG,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,UAAU;YAAE,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,kBAAkB,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QAE/E,MAAM,KAAK,GAAiB;YACxB,GAAG,QAAQ;YACX,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE;YAC1B,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW;YACnD,WAAW,EAAE,EAAE;YACf,MAAM,EAAE,KAAK;YACb,WAAW,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE;YAC/B,eAAe,EAAE,EAAE,EAAE,mBAAmB,EAAE,EAAE,EAAE,mBAAmB,EAAE,EAAE,EAAE,uBAAuB,EAAE,KAAK;YACrG,OAAO,EAAE,EAAE;YACX,SAAS;SACZ,CAAC;QAEF,IAAI,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QAC9B,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC;YAAE,OAAO,KAAK,CAAC;QAE9D,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACD,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACL,4DAA4D;YAC5D,8DAA8D;YAC9D,4DAA4D;YAC5D,cAAc;YACd,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,mEAAmE;QACnE,8DAA8D;QAC9D,gBAAgB;QAChB,MAAM,QAAQ,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,QAAQ,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACnC,IAAI,gBAAgB,GAAG,KAAK,CAAC;QAC7B,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1D,EAAE,EAAE,CAAC;YACL,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,cAAc,CAAC,EAAE;YACzC,QAAQ,EAAE,CAAC,CAAC,WAAW,IAAI,0BAA0B;YACrD,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC;YACjB,SAAS,EAAE,CAAC,CAAC,SAAS,IAAI,EAAE;SAC/B,CAAC,CAAC,CAAC;QAEJ,IAAI,QAAQ,IAAI,CAAC,WAAW,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACtC,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC;YACvB,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;QAC/C,CAAC;QAED,iEAAiE;QACjE,iEAAiE;QACjE,uDAAuD;QACvD,MAAM,QAAQ,GAAI,YAAY,EAAU,IAAI,EAAE,CAAC;QAC/C,MAAM,UAAU,GAAG,CAAC,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;QACxF,MAAM,YAAY,GAAa,UAAU,CAAC,YAAY,IAAI,EAAE,CAAC;QAC7D,MAAM,QAAQ,GAAa,UAAU,CAAC,iBAAiB,IAAI,EAAE,CAAC;QAE9D,IAAI,WAAW,GAAG,EAAE,CAAC;QACrB,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACxD,IAAI,YAAY,EAAE,CAAC;YACf,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;YAClF,KAAK,IAAI,CAAC,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBACjD,MAAM,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;gBAC3B,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAS,EAAE,IAAI,IAAK,CAAS,EAAE,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC9F,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;oBACpD,WAAW,GAAG,IAAI,CAAC;oBACnB,MAAM;gBACV,CAAC;YACL,CAAC;YACD,IAAI,CAAC,WAAW,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3C,MAAM,CAAC,GAAG,aAAa,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBAClD,WAAW,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAS,EAAE,IAAI,IAAK,CAAS,EAAE,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;YACnG,CAAC;YACD,IAAI,WAAW,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC/C,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;oBAC5B,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;wBAC3B,WAAW,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;wBACxD,MAAM;oBACV,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;QAED,MAAM,GAAG,GAAG,CAAC,GAAW,EAAU,EAAE;YAChC,IAAI,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,CAAC,CAAC;gBAAE,OAAO,EAAE,CAAC;YAClB,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;gBAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/B,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,OAAO,CAAC,CAAC;YACpC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;gBACtC,IAAI,MAAM,IAAI,CAAC;oBAAE,OAAQ,CAAS,CAAC,IAAI,IAAI,EAAE,CAAC;gBAC9C,IAAI,OAAO,IAAI,CAAC;oBAAE,OAAO,MAAM,CAAE,CAAS,CAAC,KAAK,CAAC,CAAC;gBAClD,IAAI,SAAS,IAAI,CAAC;oBAAE,OAAQ,CAAS,CAAC,OAAO,IAAI,EAAE,CAAC;YACxD,CAAC;YACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC,CAAC;QACF,MAAM,UAAU,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC3D,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,GACvE,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzC,MAAM,eAAe,GAAG,mBAAmB,IAAI,mBAAmB,CAAC;QAEnE,OAAO;YACH,GAAG,QAAQ;YACX,QAAQ,EAAE,QAAQ;YAClB,gBAAgB,EAAE,aAAa,EAAE,WAAW;YAC5C,WAAW;YACX,MAAM,EAAE,IAAI;YACZ,WAAW,EAAE,UAAU;YACvB,eAAe,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,uBAAuB;YAClF,2DAA2D;YAC3D,4DAA4D;YAC5D,2DAA2D;YAC3D,uBAAuB;YACvB,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,UAAU,CAAC;YAChD,SAAS;SACZ,CAAC;IACN,CAAC;IAED,iDAAiD;IAEjD,iBAAiB,CAAC,SAAiB,EAAE,MAAc,EAAE,IAAY;QAC7D,OAAO,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAC9D,CAAC;IAED,QAAQ,CAAC,SAAiB,EAAE,gBAAgB,GAAG,KAAK;QAChD,OAAO,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;IACzD,CAAC;IAED,cAAc,CAAC,KAAa,EAAE,KAAK,GAAG,EAAE;QACpC,OAAO,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAChD,CAAC;IAED,YAAY,CAAC,KAAa,EAAE,IAAI,GAAG,CAAC,EAAE,QAAQ,GAAG,GAAG;QAChD,OAAO,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvD,CAAC;CAGJ"}
|
|
@@ -280,7 +280,11 @@ export class LocalStore {
|
|
|
280
280
|
cached: true,
|
|
281
281
|
deliveredTo, returnPath,
|
|
282
282
|
listUnsubscribe, listUnsubscribeMail, listUnsubscribeHttp, listUnsubscribeOneClick,
|
|
283
|
-
|
|
283
|
+
// body_path in the DB is stored relative to the body-store
|
|
284
|
+
// basePath; resolve to absolute so the UI's "Source" button
|
|
285
|
+
// can hand the path to an OS file open without re-deriving
|
|
286
|
+
// basePath every time.
|
|
287
|
+
emlPath: this.bodyStore.absolutePath(storedPath),
|
|
284
288
|
isFlagged,
|
|
285
289
|
};
|
|
286
290
|
}
|
|
@@ -37,6 +37,19 @@ export interface ReconcilerOptions {
|
|
|
37
37
|
bodyFetchConcurrency?: number;
|
|
38
38
|
/** Periodic emit of syncStateChanged — drives the UI status pill. */
|
|
39
39
|
statusEmitIntervalMs?: number;
|
|
40
|
+
/** How often to fire the prefetch tick. Default 60s. */
|
|
41
|
+
prefetchIntervalMs?: number;
|
|
42
|
+
/** First prefetch fires this soon after start so the "not downloaded"
|
|
43
|
+
* dots fill in without making the user wait a minute. */
|
|
44
|
+
prefetchInitialDelayMs?: number;
|
|
45
|
+
/** Skip prefetch when the interactive body-fetch lane has at least
|
|
46
|
+
* this many items pending. Back-pressure keeps clicks responsive. */
|
|
47
|
+
prefetchBackpressureThreshold?: number;
|
|
48
|
+
/** Tombstone prune interval — bookkeeping only, hourly default. */
|
|
49
|
+
tombstonePruneIntervalMs?: number;
|
|
50
|
+
/** Tombstone retention — local-delete records older than this are
|
|
51
|
+
* removed by the periodic prune. */
|
|
52
|
+
tombstoneRetentionDays?: number;
|
|
40
53
|
}
|
|
41
54
|
export declare class Reconciler {
|
|
42
55
|
private db;
|
|
@@ -45,10 +58,25 @@ export declare class Reconciler {
|
|
|
45
58
|
private opts;
|
|
46
59
|
private running;
|
|
47
60
|
private statusTimer;
|
|
61
|
+
private prefetchTimer;
|
|
62
|
+
private tombstoneTimer;
|
|
48
63
|
private inFlightFetches;
|
|
64
|
+
/** Last status payload — used to suppress no-op syncStateChanged events.
|
|
65
|
+
* During a heavy multi-folder sync the 5s tick fires a lot of identical
|
|
66
|
+
* events; the WebView's event handler runs DOM work for each, which
|
|
67
|
+
* stacks up under load. Diff-only emits keep the channel quiet. */
|
|
68
|
+
private lastStatus;
|
|
49
69
|
constructor(db: MailxDB, imapManager: ImapManager, queue: SyncQueue, opts?: ReconcilerOptions);
|
|
50
70
|
start(): void;
|
|
51
71
|
stop(): void;
|
|
72
|
+
/** Hourly bookkeeping — drop tombstones older than the retention window
|
|
73
|
+
* so the deleted-message lookup stays fast. */
|
|
74
|
+
private runTombstonePrune;
|
|
75
|
+
/** Fire prefetchBodies on each account unless the interactive body-fetch
|
|
76
|
+
* lane has user-clicked work pending — in which case skip this tick.
|
|
77
|
+
* ImapManager's per-account `prefetchingAccounts` guard de-dupes if
|
|
78
|
+
* the previous tick is still in flight. */
|
|
79
|
+
private runPrefetchTick;
|
|
52
80
|
/** User-visible "sync now" — runs an immediate poll on all accounts
|
|
53
81
|
* (or just one) without waiting for the next periodic tick. */
|
|
54
82
|
syncNow(accountId?: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconciler.d.ts","sourceRoot":"","sources":["reconciler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAC9B;;sDAEkD;IAClD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qEAAqE;IACrE,oBAAoB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"reconciler.d.ts","sourceRoot":"","sources":["reconciler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAC9B;;sDAEkD;IAClD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qEAAqE;IACrE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wDAAwD;IACxD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;8DAC0D;IAC1D,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;0EACsE;IACtE,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC,mEAAmE;IACnE,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;yCACqC;IACrC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACnC;AAYD,qBAAa,UAAU;IAcf,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,KAAK;IAfjB,OAAO,CAAC,IAAI,CAA8B;IAC1C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAA+C;IAClE,OAAO,CAAC,aAAa,CAA+C;IACpE,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,eAAe,CAAK;IAC5B;;;wEAGoE;IACpE,OAAO,CAAC,UAAU,CAAgE;gBAGtE,EAAE,EAAE,OAAO,EACX,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,SAAS,EACxB,IAAI,GAAE,iBAAsB;IAKhC,KAAK,IAAI,IAAI;IAyCb,IAAI,IAAI,IAAI;IAOZ;oDACgD;IAChD,OAAO,CAAC,iBAAiB;IAMzB;;;gDAG4C;IAC5C,OAAO,CAAC,eAAe;IAcvB;oEACgE;IAChE,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAOjC;;2DAEuD;IACvD,OAAO,CAAC,eAAe;IAmDvB,yEAAyE;IACzE,IAAI,IAAI,IAAI;CAGf"}
|
|
@@ -30,6 +30,11 @@
|
|
|
30
30
|
const DEFAULTS = {
|
|
31
31
|
bodyFetchConcurrency: 2,
|
|
32
32
|
statusEmitIntervalMs: 5_000,
|
|
33
|
+
prefetchIntervalMs: 60_000,
|
|
34
|
+
prefetchInitialDelayMs: 2_000,
|
|
35
|
+
prefetchBackpressureThreshold: 1,
|
|
36
|
+
tombstonePruneIntervalMs: 3600_000, // hourly
|
|
37
|
+
tombstoneRetentionDays: 30,
|
|
33
38
|
};
|
|
34
39
|
export class Reconciler {
|
|
35
40
|
db;
|
|
@@ -38,7 +43,14 @@ export class Reconciler {
|
|
|
38
43
|
opts;
|
|
39
44
|
running = false;
|
|
40
45
|
statusTimer = null;
|
|
46
|
+
prefetchTimer = null;
|
|
47
|
+
tombstoneTimer = null;
|
|
41
48
|
inFlightFetches = 0;
|
|
49
|
+
/** Last status payload — used to suppress no-op syncStateChanged events.
|
|
50
|
+
* During a heavy multi-folder sync the 5s tick fires a lot of identical
|
|
51
|
+
* events; the WebView's event handler runs DOM work for each, which
|
|
52
|
+
* stacks up under load. Diff-only emits keep the channel quiet. */
|
|
53
|
+
lastStatus = { messageActions: -1, bodyFetches: -1, inFlightFetches: -1 };
|
|
42
54
|
constructor(db, imapManager, queue, opts = {}) {
|
|
43
55
|
this.db = db;
|
|
44
56
|
this.imapManager = imapManager;
|
|
@@ -50,11 +62,35 @@ export class Reconciler {
|
|
|
50
62
|
return;
|
|
51
63
|
this.running = true;
|
|
52
64
|
// Status pill: emit current totals every few seconds so the UI
|
|
53
|
-
// can show "Syncing N items" without polling.
|
|
65
|
+
// can show "Syncing N items" without polling. Diff-only — emit
|
|
66
|
+
// ONLY when something actually changed since the last tick so
|
|
67
|
+
// we don't pipe a constant idle stream through the WebView's
|
|
68
|
+
// event channel during a heavy sync (where the WebView main
|
|
69
|
+
// thread is already busy painting list updates).
|
|
54
70
|
this.statusTimer = setInterval(() => {
|
|
55
71
|
const counts = this.queue.pendingCount();
|
|
56
|
-
|
|
72
|
+
const next = { ...counts, inFlightFetches: this.inFlightFetches };
|
|
73
|
+
if (next.messageActions === this.lastStatus.messageActions
|
|
74
|
+
&& next.bodyFetches === this.lastStatus.bodyFetches
|
|
75
|
+
&& next.inFlightFetches === this.lastStatus.inFlightFetches) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.lastStatus = next;
|
|
79
|
+
this.imapManager.emit("syncStateChanged", next);
|
|
57
80
|
}, this.opts.statusEmitIntervalMs);
|
|
81
|
+
// Periodic prefetch tick — owned here (not in ImapManager) so it
|
|
82
|
+
// can back off when the interactive body-fetch lane has work.
|
|
83
|
+
// First tick fires soon after startup so "not downloaded" dots
|
|
84
|
+
// start filling in promptly.
|
|
85
|
+
const prefetchTick = () => this.runPrefetchTick();
|
|
86
|
+
setTimeout(prefetchTick, this.opts.prefetchInitialDelayMs);
|
|
87
|
+
this.prefetchTimer = setInterval(prefetchTick, this.opts.prefetchIntervalMs);
|
|
88
|
+
// Tombstone prune — bookkeeping only. Trivial cost (one indexed
|
|
89
|
+
// DELETE per hour), kept off the IMAP-touching code path because
|
|
90
|
+
// it has nothing to do with sync.
|
|
91
|
+
const pruneTick = () => this.runTombstonePrune();
|
|
92
|
+
setTimeout(pruneTick, 30_000); // first run after startup settles
|
|
93
|
+
this.tombstoneTimer = setInterval(pruneTick, this.opts.tombstonePruneIntervalMs);
|
|
58
94
|
// Kick the body-fetch loop.
|
|
59
95
|
this.pumpBodyFetches();
|
|
60
96
|
}
|
|
@@ -64,6 +100,38 @@ export class Reconciler {
|
|
|
64
100
|
clearInterval(this.statusTimer);
|
|
65
101
|
this.statusTimer = null;
|
|
66
102
|
}
|
|
103
|
+
if (this.prefetchTimer) {
|
|
104
|
+
clearInterval(this.prefetchTimer);
|
|
105
|
+
this.prefetchTimer = null;
|
|
106
|
+
}
|
|
107
|
+
if (this.tombstoneTimer) {
|
|
108
|
+
clearInterval(this.tombstoneTimer);
|
|
109
|
+
this.tombstoneTimer = null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** Hourly bookkeeping — drop tombstones older than the retention window
|
|
113
|
+
* so the deleted-message lookup stays fast. */
|
|
114
|
+
runTombstonePrune() {
|
|
115
|
+
const cutoff = Date.now() - this.opts.tombstoneRetentionDays * 86400_000;
|
|
116
|
+
const n = this.db.pruneTombstones(cutoff);
|
|
117
|
+
if (n > 0)
|
|
118
|
+
console.log(` [tombstones] pruned ${n} older than ${this.opts.tombstoneRetentionDays} days`);
|
|
119
|
+
}
|
|
120
|
+
/** Fire prefetchBodies on each account unless the interactive body-fetch
|
|
121
|
+
* lane has user-clicked work pending — in which case skip this tick.
|
|
122
|
+
* ImapManager's per-account `prefetchingAccounts` guard de-dupes if
|
|
123
|
+
* the previous tick is still in flight. */
|
|
124
|
+
runPrefetchTick() {
|
|
125
|
+
if (!this.running)
|
|
126
|
+
return;
|
|
127
|
+
const counts = this.queue.pendingCount();
|
|
128
|
+
if (counts.bodyFetches >= this.opts.prefetchBackpressureThreshold) {
|
|
129
|
+
// Interactive work pending — let it drain. Next tick reconsiders.
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
for (const acct of this.db.getAccounts()) {
|
|
133
|
+
this.imapManager.prefetchBodies(acct.id).catch(e => console.error(` [prefetch] ${acct.id}: ${e?.message || e}`));
|
|
134
|
+
}
|
|
67
135
|
}
|
|
68
136
|
/** User-visible "sync now" — runs an immediate poll on all accounts
|
|
69
137
|
* (or just one) without waiting for the next periodic tick. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconciler.js","sourceRoot":"","sources":["reconciler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;
|
|
1
|
+
{"version":3,"file":"reconciler.js","sourceRoot":"","sources":["reconciler.ts"],"names":[],"mappings":"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"}
|
|
@@ -39,18 +39,43 @@ export interface ReconcilerOptions {
|
|
|
39
39
|
bodyFetchConcurrency?: number;
|
|
40
40
|
/** Periodic emit of syncStateChanged — drives the UI status pill. */
|
|
41
41
|
statusEmitIntervalMs?: number;
|
|
42
|
+
/** How often to fire the prefetch tick. Default 60s. */
|
|
43
|
+
prefetchIntervalMs?: number;
|
|
44
|
+
/** First prefetch fires this soon after start so the "not downloaded"
|
|
45
|
+
* dots fill in without making the user wait a minute. */
|
|
46
|
+
prefetchInitialDelayMs?: number;
|
|
47
|
+
/** Skip prefetch when the interactive body-fetch lane has at least
|
|
48
|
+
* this many items pending. Back-pressure keeps clicks responsive. */
|
|
49
|
+
prefetchBackpressureThreshold?: number;
|
|
50
|
+
/** Tombstone prune interval — bookkeeping only, hourly default. */
|
|
51
|
+
tombstonePruneIntervalMs?: number;
|
|
52
|
+
/** Tombstone retention — local-delete records older than this are
|
|
53
|
+
* removed by the periodic prune. */
|
|
54
|
+
tombstoneRetentionDays?: number;
|
|
42
55
|
}
|
|
43
56
|
|
|
44
57
|
const DEFAULTS: Required<ReconcilerOptions> = {
|
|
45
58
|
bodyFetchConcurrency: 2,
|
|
46
59
|
statusEmitIntervalMs: 5_000,
|
|
60
|
+
prefetchIntervalMs: 60_000,
|
|
61
|
+
prefetchInitialDelayMs: 2_000,
|
|
62
|
+
prefetchBackpressureThreshold: 1,
|
|
63
|
+
tombstonePruneIntervalMs: 3600_000, // hourly
|
|
64
|
+
tombstoneRetentionDays: 30,
|
|
47
65
|
};
|
|
48
66
|
|
|
49
67
|
export class Reconciler {
|
|
50
68
|
private opts: Required<ReconcilerOptions>;
|
|
51
69
|
private running = false;
|
|
52
70
|
private statusTimer: ReturnType<typeof setInterval> | null = null;
|
|
71
|
+
private prefetchTimer: ReturnType<typeof setInterval> | null = null;
|
|
72
|
+
private tombstoneTimer: ReturnType<typeof setInterval> | null = null;
|
|
53
73
|
private inFlightFetches = 0;
|
|
74
|
+
/** Last status payload — used to suppress no-op syncStateChanged events.
|
|
75
|
+
* During a heavy multi-folder sync the 5s tick fires a lot of identical
|
|
76
|
+
* events; the WebView's event handler runs DOM work for each, which
|
|
77
|
+
* stacks up under load. Diff-only emits keep the channel quiet. */
|
|
78
|
+
private lastStatus = { messageActions: -1, bodyFetches: -1, inFlightFetches: -1 };
|
|
54
79
|
|
|
55
80
|
constructor(
|
|
56
81
|
private db: MailxDB,
|
|
@@ -66,12 +91,38 @@ export class Reconciler {
|
|
|
66
91
|
this.running = true;
|
|
67
92
|
|
|
68
93
|
// Status pill: emit current totals every few seconds so the UI
|
|
69
|
-
// can show "Syncing N items" without polling.
|
|
94
|
+
// can show "Syncing N items" without polling. Diff-only — emit
|
|
95
|
+
// ONLY when something actually changed since the last tick so
|
|
96
|
+
// we don't pipe a constant idle stream through the WebView's
|
|
97
|
+
// event channel during a heavy sync (where the WebView main
|
|
98
|
+
// thread is already busy painting list updates).
|
|
70
99
|
this.statusTimer = setInterval(() => {
|
|
71
100
|
const counts = this.queue.pendingCount();
|
|
72
|
-
|
|
101
|
+
const next = { ...counts, inFlightFetches: this.inFlightFetches };
|
|
102
|
+
if (next.messageActions === this.lastStatus.messageActions
|
|
103
|
+
&& next.bodyFetches === this.lastStatus.bodyFetches
|
|
104
|
+
&& next.inFlightFetches === this.lastStatus.inFlightFetches) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.lastStatus = next;
|
|
108
|
+
this.imapManager.emit("syncStateChanged", next);
|
|
73
109
|
}, this.opts.statusEmitIntervalMs);
|
|
74
110
|
|
|
111
|
+
// Periodic prefetch tick — owned here (not in ImapManager) so it
|
|
112
|
+
// can back off when the interactive body-fetch lane has work.
|
|
113
|
+
// First tick fires soon after startup so "not downloaded" dots
|
|
114
|
+
// start filling in promptly.
|
|
115
|
+
const prefetchTick = (): void => this.runPrefetchTick();
|
|
116
|
+
setTimeout(prefetchTick, this.opts.prefetchInitialDelayMs);
|
|
117
|
+
this.prefetchTimer = setInterval(prefetchTick, this.opts.prefetchIntervalMs);
|
|
118
|
+
|
|
119
|
+
// Tombstone prune — bookkeeping only. Trivial cost (one indexed
|
|
120
|
+
// DELETE per hour), kept off the IMAP-touching code path because
|
|
121
|
+
// it has nothing to do with sync.
|
|
122
|
+
const pruneTick = (): void => this.runTombstonePrune();
|
|
123
|
+
setTimeout(pruneTick, 30_000); // first run after startup settles
|
|
124
|
+
this.tombstoneTimer = setInterval(pruneTick, this.opts.tombstonePruneIntervalMs);
|
|
125
|
+
|
|
75
126
|
// Kick the body-fetch loop.
|
|
76
127
|
this.pumpBodyFetches();
|
|
77
128
|
}
|
|
@@ -79,6 +130,34 @@ export class Reconciler {
|
|
|
79
130
|
stop(): void {
|
|
80
131
|
this.running = false;
|
|
81
132
|
if (this.statusTimer) { clearInterval(this.statusTimer); this.statusTimer = null; }
|
|
133
|
+
if (this.prefetchTimer) { clearInterval(this.prefetchTimer); this.prefetchTimer = null; }
|
|
134
|
+
if (this.tombstoneTimer) { clearInterval(this.tombstoneTimer); this.tombstoneTimer = null; }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Hourly bookkeeping — drop tombstones older than the retention window
|
|
138
|
+
* so the deleted-message lookup stays fast. */
|
|
139
|
+
private runTombstonePrune(): void {
|
|
140
|
+
const cutoff = Date.now() - this.opts.tombstoneRetentionDays * 86400_000;
|
|
141
|
+
const n = this.db.pruneTombstones(cutoff);
|
|
142
|
+
if (n > 0) console.log(` [tombstones] pruned ${n} older than ${this.opts.tombstoneRetentionDays} days`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Fire prefetchBodies on each account unless the interactive body-fetch
|
|
146
|
+
* lane has user-clicked work pending — in which case skip this tick.
|
|
147
|
+
* ImapManager's per-account `prefetchingAccounts` guard de-dupes if
|
|
148
|
+
* the previous tick is still in flight. */
|
|
149
|
+
private runPrefetchTick(): void {
|
|
150
|
+
if (!this.running) return;
|
|
151
|
+
const counts = this.queue.pendingCount();
|
|
152
|
+
if (counts.bodyFetches >= this.opts.prefetchBackpressureThreshold) {
|
|
153
|
+
// Interactive work pending — let it drain. Next tick reconsiders.
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
for (const acct of this.db.getAccounts()) {
|
|
157
|
+
this.imapManager.prefetchBodies(acct.id).catch(e =>
|
|
158
|
+
console.error(` [prefetch] ${acct.id}: ${e?.message || e}`)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
82
161
|
}
|
|
83
162
|
|
|
84
163
|
/** User-visible "sync now" — runs an immediate poll on all accounts
|
|
@@ -18,7 +18,11 @@
|
|
|
18
18
|
],
|
|
19
19
|
"discovered": [
|
|
20
20
|
{ "name": "Bob", "email": "bob@example.com", "useCount": 715, "lastUsed": 1777921829000 }
|
|
21
|
-
]
|
|
21
|
+
],
|
|
22
|
+
"groups": {
|
|
23
|
+
"Joes": ["joe@example.com", "jo@example.com"],
|
|
24
|
+
"Family": ["alice@example.com", "Joes"]
|
|
25
|
+
}
|
|
22
26
|
}
|
|
23
27
|
```
|
|
24
28
|
|
|
@@ -27,6 +31,7 @@
|
|
|
27
31
|
- **preferred** — manually-curated, top-ranked in autocomplete. Add people you actually want to reach quickly.
|
|
28
32
|
- **denylist** — addresses (lowercased) excluded from autocomplete entirely.
|
|
29
33
|
- **discovered** — auto-collected from sent/received mail. `useCount` × recency drives autocomplete ranking. Junk patterns (see below) are dropped at insertion.
|
|
34
|
+
- **groups** — mailing lists. Type the group name in To/Cc/Bcc and compose expands it into the member addresses on send. Each value is an array of either email addresses (`"a@b.com"` or `"Name <a@b.com>"`) or other group names (recursive aliasing — cycles are detected, depth capped at 10). Group lookup is case-insensitive on the key. The two `Joes` example above plus the `Family` example show flat membership and one group nested inside another.
|
|
30
35
|
|
|
31
36
|
## Global junk filter
|
|
32
37
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Search
|
|
2
|
+
|
|
3
|
+
> **Owned by rmfmail. Do not edit this file** — it's reference documentation.
|
|
4
|
+
|
|
5
|
+
Search in rmfmail runs in one of three modes depending on what you've selected:
|
|
6
|
+
|
|
7
|
+
- **All folders** (default) — searches your **local cache** using SQLite FTS5
|
|
8
|
+
- **This folder** — instant client-side filter on visible rows; full FTS5 search on Enter
|
|
9
|
+
- **Server** (the "Server" checkbox) — sends the query to the mail server (IMAP SEARCH on bobma-style accounts; **not yet wired to Gmail API for Gmail accounts** — see "Limitations" below)
|
|
10
|
+
|
|
11
|
+
The mode you're in dictates which query operators work. Scope matters.
|
|
12
|
+
|
|
13
|
+
## Common shortcuts (work in every mode)
|
|
14
|
+
|
|
15
|
+
These are parsed by mailx before dispatch, so they apply to all three scopes:
|
|
16
|
+
|
|
17
|
+
| Form | Effect |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `from:bob` | Match sender substring |
|
|
20
|
+
| `to:eleanor` | Match recipient substring |
|
|
21
|
+
| `subject:lunch` | Match subject substring (everything until the next `keyword:` or end) |
|
|
22
|
+
| `/regex/` | Client-side regex over the currently-visible rows. Local only — never sent to the server. |
|
|
23
|
+
|
|
24
|
+
The remaining (unqualified) text becomes the **body** search term in the server query, or a free-text FTS5 phrase in local mode.
|
|
25
|
+
|
|
26
|
+
## Mode 1: Local search (FTS5) — default
|
|
27
|
+
|
|
28
|
+
Uses SQLite's FTS5 full-text index built from envelopes + locally-cached bodies. Fast, indexed, ~1ms per query.
|
|
29
|
+
|
|
30
|
+
Supported FTS5 syntax (when the search box has more than one word and no scope checkbox):
|
|
31
|
+
|
|
32
|
+
| Operator | Example | Meaning |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `term1 term2` | `bob lunch` | both must appear (implicit AND) |
|
|
35
|
+
| `term1 OR term2` | `bob OR eleanor` | either |
|
|
36
|
+
| `term1 NOT term2` | `bob NOT spam` | first without the second |
|
|
37
|
+
| `"phrase"` | `"happy birthday"` | exact phrase |
|
|
38
|
+
| `term*` | `lunch*` | prefix |
|
|
39
|
+
| `^col:term` | `^subject:budget` | restrict to one indexed column (subject, body, from, to) |
|
|
40
|
+
| `NEAR(a b, 5)` | `NEAR(bob lunch, 5)` | both within 5 tokens of each other |
|
|
41
|
+
|
|
42
|
+
**Case**: FTS5 is **case-insensitive**. `AND`/`OR`/`NOT` work in any case (`and`, `OR`, `Not` are equivalent).
|
|
43
|
+
|
|
44
|
+
What's actually indexed: subject, from, to, cc, snippet (first ~200 chars of body), and the full body text *if* the body has been downloaded locally (the blue dot in the list). Bodies that haven't been prefetched aren't searchable until they are.
|
|
45
|
+
|
|
46
|
+
## Mode 2: This-folder filter
|
|
47
|
+
|
|
48
|
+
When the scope dropdown is set to "This folder" and you type, mailx **client-side filters the currently rendered rows** instantly — no server hit, no FTS5. Just substring match across each visible row's text.
|
|
49
|
+
|
|
50
|
+
Press **Enter** to escalate to a full FTS5 search of the folder.
|
|
51
|
+
|
|
52
|
+
`/regex/` works here for fast on-screen filtering.
|
|
53
|
+
|
|
54
|
+
## Mode 3: Server search
|
|
55
|
+
|
|
56
|
+
Tick the "Server" checkbox and the query goes to the mail server.
|
|
57
|
+
|
|
58
|
+
### bobma / Dovecot (IMAP SEARCH)
|
|
59
|
+
|
|
60
|
+
Standard IMAP keys, server-side. Combinable with implicit AND. mailx parses your `from:`, `to:`, `subject:` qualifiers and forwards them as IMAP keys; remaining text becomes a `BODY` search.
|
|
61
|
+
|
|
62
|
+
| Key | Example | Meaning |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `BODY <text>` | (default for unqualified words) | Substring of body |
|
|
65
|
+
| `TEXT <text>` | (not exposed in the UI yet) | Substring of header + body |
|
|
66
|
+
| `SUBJECT <text>` | `subject:budget` | Substring of subject |
|
|
67
|
+
| `FROM <addr>` | `from:bob` | Substring of From line |
|
|
68
|
+
| `TO <addr>` | `to:eleanor` | Substring of To line |
|
|
69
|
+
| `SINCE <date>` | (not exposed) | RFC 822 date |
|
|
70
|
+
|
|
71
|
+
**Case**: IMAP SEARCH is **case-insensitive** (per RFC 3501 §6.4.4). `bob AND eleanor` and `bob and eleanor` behave the same on Dovecot.
|
|
72
|
+
|
|
73
|
+
**`AND` / `OR`**: IMAP's grammar is implicit-AND between keys. There's no literal `AND` keyword — typing `bob AND eleanor` would be sent as a body search for the literal three-word string. To OR keys you'd use IMAP's `OR <key1> <key2>` prefix syntax, which mailx doesn't expose in the UI today.
|
|
74
|
+
|
|
75
|
+
So if you typed `bob AND eleanor` on bobma and got results, what likely happened is the body literally contained the string "bob and eleanor" (case-insensitive). Apparent "AND" success is coincidence with the literal text.
|
|
76
|
+
|
|
77
|
+
### Gmail (server-search currently unwired)
|
|
78
|
+
|
|
79
|
+
`searchAndFetchOnServer` is IMAP-only and `withConnection` doesn't exist for Gmail-API accounts. Server-scope queries on Gmail today **return nothing new** — they only hit local cache. This is a known gap; the fix is to add a Gmail-API path that uses Google's `q=` query parameter (full Gmail search syntax: `from:`, `subject:`, `has:attachment`, `older_than:1y`, `label:`, etc., with proper boolean `AND`/`OR`).
|
|
80
|
+
|
|
81
|
+
That explains the asymmetry you may have noticed: `bob AND eleanor` "worked" against bobma (literal-text coincidence) but not against Gmail (no IMAP/API call ever fired).
|
|
82
|
+
|
|
83
|
+
## Limitations
|
|
84
|
+
|
|
85
|
+
- **No regex on the server side** (any provider). `/pattern/` only filters the visible local rows.
|
|
86
|
+
- **Server search results are stored locally** (envelope-only) on first hit. So a server search is also a backfill of envelopes for those messages — they appear next to local results without re-fetching.
|
|
87
|
+
- **Body search on bodies you haven't prefetched** only works server-side. The local FTS5 index can't search what isn't downloaded.
|
|
88
|
+
|
|
89
|
+
## Practical patterns
|
|
90
|
+
|
|
91
|
+
| Goal | What to type | Scope |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| Quick keyword in current folder | `lunch` | This folder (instant) |
|
|
94
|
+
| All-time mail from someone | `from:bob.frankston@example.com` | All folders (FTS5) |
|
|
95
|
+
| Mail mentioning two people | `bob eleanor` | All folders (FTS5; implicit AND) |
|
|
96
|
+
| Either of two terms | `bob OR eleanor` | All folders (FTS5) |
|
|
97
|
+
| Exact phrase | `"open source"` | All folders (FTS5) |
|
|
98
|
+
| Regex over what's on screen | `/^Re: \[mailx\]/` | Any (client-side) |
|
|
99
|
+
| Old mail beyond `historyDays` | type query, then check the **Server** checkbox | Server (Dovecot only today) |
|