@bobfrankston/rmfmail 1.2.77 → 1.2.79
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/android-bootstrap.bundle.js +58 -31
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/package.json +3 -3
- package/packages/mailx-store-web/android-bootstrap.d.ts.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.js +68 -41
- package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.ts +70 -39
- package/packages/mailx-store-web/package.json +1 -1
|
@@ -6753,7 +6753,11 @@ var GmailApiProvider = class _GmailApiProvider {
|
|
|
6753
6753
|
}
|
|
6754
6754
|
const cap = options.since ? 0 : 200;
|
|
6755
6755
|
const ids = await this.listMessageIds(query, cap);
|
|
6756
|
-
|
|
6756
|
+
const fresh = options.knownUids ? ids.filter((id) => !options.knownUids.has(idToUid(id))) : ids;
|
|
6757
|
+
if (options.knownUids) {
|
|
6758
|
+
console.log(`[gmail] fetchSince ${folder}: ${ids.length} listed, ${ids.length - fresh.length} already stored, fetching ${fresh.length}`);
|
|
6759
|
+
}
|
|
6760
|
+
return this.batchFetch(fresh, options);
|
|
6757
6761
|
}
|
|
6758
6762
|
async fetchByDate(folder, since, before, options = {}, onChunk) {
|
|
6759
6763
|
const afterDate = this.formatDate(since);
|
|
@@ -9796,6 +9800,11 @@ var AndroidSyncManager = class {
|
|
|
9796
9800
|
// advanced to fetching new mail, so the list stayed stale despite "Synced"
|
|
9797
9801
|
// (Bob 2026-06-27). Coalesce concurrent calls to one in-flight run.
|
|
9798
9802
|
syncAllInflight = null;
|
|
9803
|
+
// Separate guard for the Phase-2/3 background pass (other folders + body
|
|
9804
|
+
// prefetch). It runs DETACHED from the inbox guard so a long folder sweep
|
|
9805
|
+
// can't make the 60s poll skip the inbox refresh (new mail must keep
|
|
9806
|
+
// flowing). One background pass at a time — overlapping triggers no-op.
|
|
9807
|
+
bgSyncInflight = null;
|
|
9799
9808
|
on(_event, _handler) {
|
|
9800
9809
|
}
|
|
9801
9810
|
emit(event, ...args) {
|
|
@@ -9850,41 +9859,53 @@ var AndroidSyncManager = class {
|
|
|
9850
9859
|
}
|
|
9851
9860
|
async syncAll() {
|
|
9852
9861
|
if (this.syncAllInflight) {
|
|
9853
|
-
console.log("[sync]
|
|
9862
|
+
console.log("[sync] inbox sync already in flight \u2014 coalescing this trigger");
|
|
9854
9863
|
return this.syncAllInflight;
|
|
9855
9864
|
}
|
|
9856
9865
|
const t0 = Date.now();
|
|
9857
|
-
console.log("[sync] syncAll START");
|
|
9858
|
-
this.syncAllInflight = this.
|
|
9859
|
-
console.error(`[sync]
|
|
9866
|
+
console.log("[sync] syncAll START (inbox phase)");
|
|
9867
|
+
this.syncAllInflight = this._syncInboxes().catch((e) => {
|
|
9868
|
+
console.error(`[sync] inbox sync error: ${e?.message || e}`);
|
|
9860
9869
|
}).finally(() => {
|
|
9861
|
-
console.log(`[sync]
|
|
9870
|
+
console.log(`[sync] inbox phase DONE in ${Date.now() - t0}ms`);
|
|
9862
9871
|
this.syncAllInflight = null;
|
|
9863
9872
|
});
|
|
9873
|
+
this._kickBackgroundSync();
|
|
9864
9874
|
return this.syncAllInflight;
|
|
9865
9875
|
}
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
}
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
9876
|
+
/** Phase 1: sync every account's INBOX IN PARALLEL so a slow account (e.g.
|
|
9877
|
+
* Gmail's metadata fetch, or an auth-failing IMAP account) can't delay new
|
|
9878
|
+
* mail for the others. Each account is time-boxed; network fetches overlap
|
|
9879
|
+
* even though JS is single-threaded (the awaits yield). */
|
|
9880
|
+
async _syncInboxes() {
|
|
9881
|
+
const accounts = this.db.getAccounts().filter((a) => this.providers.has(a.id));
|
|
9882
|
+
vlog(`syncInboxes: ${accounts.length} accounts: ${accounts.map((a) => a.id).join(",")}`);
|
|
9883
|
+
await Promise.all(accounts.map(
|
|
9884
|
+
(account) => withTimeout(`${account.id} inbox`, 6e4, async () => {
|
|
9885
|
+
const folders = await this.syncFolders(account.id);
|
|
9886
|
+
const inbox = folders.find((f) => f.specialUse === "inbox");
|
|
9887
|
+
if (inbox) {
|
|
9888
|
+
console.log(`[sync] ${account.id}: fetching INBOX (folderId=${inbox.id})`);
|
|
9889
|
+
await this.syncFolder(account.id, inbox.id);
|
|
9890
|
+
console.log(`[sync] ${account.id}: INBOX fetch done`);
|
|
9891
|
+
emitEvent({ type: "syncComplete", accountId: account.id });
|
|
9892
|
+
}
|
|
9893
|
+
}).catch((e) => console.error(`[sync] ${account.id} inbox: ${e?.message || e}`))
|
|
9894
|
+
));
|
|
9895
|
+
}
|
|
9896
|
+
/** Phase 2 + 3: remaining folders, then body prefetch. Runs in the
|
|
9897
|
+
* background under its own single-flight guard. */
|
|
9898
|
+
_kickBackgroundSync() {
|
|
9899
|
+
if (this.bgSyncInflight) return;
|
|
9900
|
+
const t0 = Date.now();
|
|
9901
|
+
this.bgSyncInflight = this._syncRemainingAndPrefetch().catch((e) => console.error(`[sync] background sweep error: ${e?.message || e}`)).finally(() => {
|
|
9902
|
+
console.log(`[sync] background sweep DONE in ${Date.now() - t0}ms`);
|
|
9903
|
+
this.bgSyncInflight = null;
|
|
9904
|
+
});
|
|
9905
|
+
}
|
|
9906
|
+
async _syncRemainingAndPrefetch() {
|
|
9907
|
+
const accounts = this.db.getAccounts().filter((a) => this.providers.has(a.id));
|
|
9886
9908
|
for (const account of accounts) {
|
|
9887
|
-
if (!this.providers.has(account.id)) continue;
|
|
9888
9909
|
try {
|
|
9889
9910
|
const folders = this.db.getFolders(account.id);
|
|
9890
9911
|
const remaining = folders.filter((f) => f.specialUse !== "inbox");
|
|
@@ -9899,12 +9920,10 @@ var AndroidSyncManager = class {
|
|
|
9899
9920
|
emitEvent({ type: "syncComplete", accountId: account.id });
|
|
9900
9921
|
} catch (e) {
|
|
9901
9922
|
console.error(`[sync] ${account.id}: ${e.message}`);
|
|
9902
|
-
vlog(`syncAll: ${account.id} ERROR: ${e.message}`);
|
|
9903
9923
|
emitEvent({ type: "syncError", accountId: account.id, error: e.message });
|
|
9904
9924
|
}
|
|
9905
9925
|
}
|
|
9906
9926
|
for (const account of accounts) {
|
|
9907
|
-
if (!this.providers.has(account.id)) continue;
|
|
9908
9927
|
this.prefetchBodies(account.id).catch((e) => console.error(`[prefetch] ${account.id}: ${e.message}`));
|
|
9909
9928
|
}
|
|
9910
9929
|
}
|
|
@@ -10037,10 +10056,18 @@ var AndroidSyncManager = class {
|
|
|
10037
10056
|
emitEvent({ type: "syncProgress", accountId, phase: `sync:${folder.path}`, progress: 0 });
|
|
10038
10057
|
const highestUid = this.db.getHighestUid(accountId, folderId);
|
|
10039
10058
|
const startDate = new Date(Date.now() - 30 * 864e5);
|
|
10059
|
+
const account = this.db.getAccounts().find((a) => a.id === accountId);
|
|
10060
|
+
const isGoogle = !!account && isGoogleAccount(account);
|
|
10040
10061
|
let messages;
|
|
10041
10062
|
if (highestUid > 0) {
|
|
10042
|
-
|
|
10043
|
-
|
|
10063
|
+
const opts = { source: false };
|
|
10064
|
+
if (isGoogle) {
|
|
10065
|
+
opts.knownUids = new Set(this.db.getUidsForFolder(accountId, folderId));
|
|
10066
|
+
}
|
|
10067
|
+
messages = await provider.fetchSince(folder.path, highestUid, opts);
|
|
10068
|
+
if (!isGoogle) {
|
|
10069
|
+
messages = messages.filter((m) => m.uid > highestUid);
|
|
10070
|
+
}
|
|
10044
10071
|
} else {
|
|
10045
10072
|
const tomorrow = new Date(Date.now() + 864e5);
|
|
10046
10073
|
messages = await provider.fetchByDate(folder.path, startDate, tomorrow, { source: false });
|