@bobfrankston/rmfmail 1.2.75 → 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.
@@ -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
- const folders = await this.syncFolders(account.id);
9824
- const inbox = folders.find((f) => f.specialUse === "inbox");
9825
- if (inbox) {
9826
- await this.syncFolder(account.id, inbox.id);
9827
- emitEvent({ type: "syncComplete", accountId: account.id });
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
  }