@bobfrankston/mailx 1.0.82 → 1.0.85

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx",
3
- "version": "1.0.82",
3
+ "version": "1.0.85",
4
4
  "description": "Local-first email client with IMAP sync and standalone native app",
5
5
  "type": "module",
6
6
  "main": "bin/mailx.js",
@@ -20,10 +20,10 @@
20
20
  "postinstall": "node launcher/builder/postinstall.js"
21
21
  },
22
22
  "dependencies": {
23
- "@bobfrankston/iflow": "^1.0.37",
24
- "@bobfrankston/miscinfo": "^1.0.6",
25
- "@bobfrankston/oauthsupport": "^1.0.19",
26
- "@bobfrankston/rust-builder": "^0.1.2",
23
+ "@bobfrankston/iflow": "^1.0.38",
24
+ "@bobfrankston/miscinfo": "^1.0.7",
25
+ "@bobfrankston/oauthsupport": "^1.0.20",
26
+ "@bobfrankston/rust-builder": "^0.1.3",
27
27
  "@capacitor/android": "^8.3.0",
28
28
  "@capacitor/cli": "^8.3.0",
29
29
  "@capacitor/core": "^8.3.0",
@@ -31,6 +31,8 @@ export declare class ImapManager extends EventEmitter {
31
31
  private inboxSyncing;
32
32
  /** Use native IMAP client instead of imapflow. Set to true to enable. */
33
33
  useNativeClient: boolean;
34
+ /** Accounts hitting connection limits — back off until this time */
35
+ private connectionBackoff;
34
36
  constructor(db: MailxDB);
35
37
  /** Get OAuth access token for an account (for SMTP auth) */
36
38
  getOAuthToken(accountId: string): Promise<string | null>;
@@ -71,6 +71,8 @@ export class ImapManager extends EventEmitter {
71
71
  inboxSyncing = false;
72
72
  /** Use native IMAP client instead of imapflow. Set to true to enable. */
73
73
  useNativeClient = false;
74
+ /** Accounts hitting connection limits — back off until this time */
75
+ connectionBackoff = new Map();
74
76
  constructor(db) {
75
77
  super();
76
78
  this.db = db;
@@ -169,6 +171,11 @@ export class ImapManager extends EventEmitter {
169
171
  createClient(accountId) {
170
172
  if (this.reauthenticating.has(accountId))
171
173
  throw new Error(`Account ${accountId} is re-authenticating`);
174
+ // Check connection backoff
175
+ const backoffUntil = this.connectionBackoff.get(accountId);
176
+ if (backoffUntil && Date.now() < backoffUntil) {
177
+ throw new Error(`Account ${accountId} in connection backoff (${Math.round((backoffUntil - Date.now()) / 1000)}s remaining)`);
178
+ }
172
179
  const config = this.configs.get(accountId);
173
180
  if (!config)
174
181
  throw new Error(`No config for account ${accountId}`);
@@ -457,15 +464,23 @@ export class ImapManager extends EventEmitter {
457
464
  }
458
465
  }
459
466
  catch (e) {
460
- this.emit("syncError", accountId, imapError(e));
461
- console.error(`Sync error for ${accountId}: ${imapError(e)}`);
467
+ const errMsg = imapError(e);
468
+ this.emit("syncError", accountId, errMsg);
469
+ console.error(`Sync error for ${accountId}: ${errMsg}`);
470
+ // Connection limit — back off for 60 seconds
471
+ if (errMsg.includes("max_userip_connections") || errMsg.includes("Too many simultaneous")) {
472
+ this.connectionBackoff.set(accountId, Date.now() + 60000);
473
+ console.log(` [backoff] ${accountId}: connection limit hit, backing off 60s`);
474
+ }
462
475
  // Emit user-facing error once per account per session
463
476
  if (!this.accountErrorShown.has(accountId)) {
464
477
  this.accountErrorShown.add(accountId);
465
478
  const config = this.configs.get(accountId);
466
479
  const isOAuth = !!config?.tokenProvider;
467
- const hint = isOAuth ? "Authentication may have expired" : "Check server connectivity";
468
- this.emit("accountError", accountId, imapError(e), hint);
480
+ const hint = errMsg.includes("max_userip_connections") || errMsg.includes("Too many")
481
+ ? "Too many connections — backing off"
482
+ : isOAuth ? "Authentication may have expired" : "Check server connectivity";
483
+ this.emit("accountError", accountId, errMsg, hint);
469
484
  }
470
485
  }
471
486
  finally {