@bobfrankston/rmfmail 1.2.76 → 1.2.77
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 +60 -7
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/package.json +1 -1
- package/packages/mailx-store-web/android-bootstrap.d.ts.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.js +63 -7
- package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.ts +59 -7
- package/packages/mailx-store-web/package.json +1 -1
|
@@ -9749,6 +9749,32 @@ function vlog(msg) {
|
|
|
9749
9749
|
} catch {
|
|
9750
9750
|
}
|
|
9751
9751
|
}
|
|
9752
|
+
function withTimeout(label, ms, fn) {
|
|
9753
|
+
return new Promise((resolve, reject) => {
|
|
9754
|
+
let done = false;
|
|
9755
|
+
const timer = setTimeout(() => {
|
|
9756
|
+
if (done) return;
|
|
9757
|
+
done = true;
|
|
9758
|
+
reject(new Error(`timeout after ${ms}ms: ${label}`));
|
|
9759
|
+
}, ms);
|
|
9760
|
+
fn().then(
|
|
9761
|
+
(v) => {
|
|
9762
|
+
if (!done) {
|
|
9763
|
+
done = true;
|
|
9764
|
+
clearTimeout(timer);
|
|
9765
|
+
resolve(v);
|
|
9766
|
+
}
|
|
9767
|
+
},
|
|
9768
|
+
(e) => {
|
|
9769
|
+
if (!done) {
|
|
9770
|
+
done = true;
|
|
9771
|
+
clearTimeout(timer);
|
|
9772
|
+
reject(e);
|
|
9773
|
+
}
|
|
9774
|
+
}
|
|
9775
|
+
);
|
|
9776
|
+
});
|
|
9777
|
+
}
|
|
9752
9778
|
var AndroidSyncManager = class {
|
|
9753
9779
|
constructor(db2, bodyStore2) {
|
|
9754
9780
|
this.db = db2;
|
|
@@ -9762,6 +9788,14 @@ var AndroidSyncManager = class {
|
|
|
9762
9788
|
// spawning parallel fetch loops that race on IndexedDB and blow through
|
|
9763
9789
|
// Gmail's per-user quota.
|
|
9764
9790
|
prefetchingAccounts = /* @__PURE__ */ new Set();
|
|
9791
|
+
// Single-flight guard for syncAll. It's triggered from THREE places — boot,
|
|
9792
|
+
// a 60s interval, and every visibilitychange→visible — with no coordination.
|
|
9793
|
+
// On Android everything (incl. wa-sqlite) runs on the main thread, so
|
|
9794
|
+
// overlapping runs stacked up: re-listing folders repeatedly, contending on
|
|
9795
|
+
// the DB, and freezing the event loop ~1s at a time — the sync never
|
|
9796
|
+
// advanced to fetching new mail, so the list stayed stale despite "Synced"
|
|
9797
|
+
// (Bob 2026-06-27). Coalesce concurrent calls to one in-flight run.
|
|
9798
|
+
syncAllInflight = null;
|
|
9765
9799
|
on(_event, _handler) {
|
|
9766
9800
|
}
|
|
9767
9801
|
emit(event, ...args) {
|
|
@@ -9815,17 +9849,36 @@ var AndroidSyncManager = class {
|
|
|
9815
9849
|
return this.providers.get(accountId) || null;
|
|
9816
9850
|
}
|
|
9817
9851
|
async syncAll() {
|
|
9852
|
+
if (this.syncAllInflight) {
|
|
9853
|
+
console.log("[sync] syncAll already in flight \u2014 coalescing this trigger");
|
|
9854
|
+
return this.syncAllInflight;
|
|
9855
|
+
}
|
|
9856
|
+
const t0 = Date.now();
|
|
9857
|
+
console.log("[sync] syncAll START");
|
|
9858
|
+
this.syncAllInflight = this._syncAllImpl().catch((e) => {
|
|
9859
|
+
console.error(`[sync] syncAll error: ${e?.message || e}`);
|
|
9860
|
+
}).finally(() => {
|
|
9861
|
+
console.log(`[sync] syncAll DONE in ${Date.now() - t0}ms`);
|
|
9862
|
+
this.syncAllInflight = null;
|
|
9863
|
+
});
|
|
9864
|
+
return this.syncAllInflight;
|
|
9865
|
+
}
|
|
9866
|
+
async _syncAllImpl() {
|
|
9818
9867
|
const accounts = this.db.getAccounts();
|
|
9819
9868
|
vlog(`syncAll: ${accounts.length} accounts in DB: ${accounts.map((a) => a.id).join(",")}`);
|
|
9820
9869
|
for (const account of accounts) {
|
|
9821
9870
|
if (!this.providers.has(account.id)) continue;
|
|
9822
9871
|
try {
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9872
|
+
await withTimeout(`${account.id} inbox`, 6e4, async () => {
|
|
9873
|
+
const folders = await this.syncFolders(account.id);
|
|
9874
|
+
const inbox = folders.find((f) => f.specialUse === "inbox");
|
|
9875
|
+
if (inbox) {
|
|
9876
|
+
console.log(`[sync] ${account.id}: fetching INBOX (folderId=${inbox.id})`);
|
|
9877
|
+
await this.syncFolder(account.id, inbox.id);
|
|
9878
|
+
console.log(`[sync] ${account.id}: INBOX fetch done`);
|
|
9879
|
+
emitEvent({ type: "syncComplete", accountId: account.id });
|
|
9880
|
+
}
|
|
9881
|
+
});
|
|
9829
9882
|
} catch (e) {
|
|
9830
9883
|
console.error(`[sync] ${account.id} inbox: ${e.message}`);
|
|
9831
9884
|
}
|
|
@@ -9837,7 +9890,7 @@ var AndroidSyncManager = class {
|
|
|
9837
9890
|
const remaining = folders.filter((f) => f.specialUse !== "inbox");
|
|
9838
9891
|
for (const folder of remaining) {
|
|
9839
9892
|
try {
|
|
9840
|
-
await this.syncFolder(account.id, folder.id);
|
|
9893
|
+
await withTimeout(`${account.id} ${folder.path}`, 45e3, () => this.syncFolder(account.id, folder.id));
|
|
9841
9894
|
} catch (e) {
|
|
9842
9895
|
console.error(`[sync] Skip ${folder.path}: ${e.message}`);
|
|
9843
9896
|
}
|