@bobfrankston/mailx 1.0.242 → 1.0.243

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.
@@ -1 +1 @@
1
- {"height":1344,"width":2151,"x":524,"y":191}
1
+ {"height":1344,"width":2151,"x":419,"y":152}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx",
3
- "version": "1.0.242",
3
+ "version": "1.0.243",
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,11 +20,11 @@
20
20
  "postinstall": "node bin/postinstall.js"
21
21
  },
22
22
  "dependencies": {
23
- "@bobfrankston/iflow-direct": "^0.1.11",
23
+ "@bobfrankston/iflow-direct": "^0.1.12",
24
24
  "@bobfrankston/iflow-node": "^0.1.2",
25
25
  "@bobfrankston/miscinfo": "^1.0.8",
26
26
  "@bobfrankston/oauthsupport": "^1.0.22",
27
- "@bobfrankston/msger": "^0.1.304",
27
+ "@bobfrankston/msger": "^0.1.305",
28
28
  "@capacitor/android": "^8.3.0",
29
29
  "@capacitor/cli": "^8.3.0",
30
30
  "@capacitor/core": "^8.3.0",
@@ -74,11 +74,11 @@
74
74
  },
75
75
  ".transformedSnapshot": {
76
76
  "dependencies": {
77
- "@bobfrankston/iflow-direct": "^0.1.11",
77
+ "@bobfrankston/iflow-direct": "^0.1.12",
78
78
  "@bobfrankston/iflow-node": "^0.1.2",
79
79
  "@bobfrankston/miscinfo": "^1.0.8",
80
80
  "@bobfrankston/oauthsupport": "^1.0.22",
81
- "@bobfrankston/msger": "^0.1.304",
81
+ "@bobfrankston/msger": "^0.1.305",
82
82
  "@capacitor/android": "^8.3.0",
83
83
  "@capacitor/cli": "^8.3.0",
84
84
  "@capacitor/core": "^8.3.0",
@@ -1430,7 +1430,13 @@ export class ImapManager extends EventEmitter {
1430
1430
  let totalFetched = 0;
1431
1431
  let deleted = 0;
1432
1432
  let errors = 0;
1433
+ let rateLimited = false;
1433
1434
  const ERROR_BUDGET = 20;
1435
+ // Pace body fetches to avoid slamming Gmail's rate limit. Without a
1436
+ // delay, 500+ body-fetch API calls fire in a burst, every one hits 429,
1437
+ // and the error budget drains before any bodies land.
1438
+ const FETCH_DELAY_MS = this.isGmailAccount(accountId) ? 1000 : 200;
1439
+ const RATE_LIMIT_PAUSE_MS = 30000;
1434
1440
  while (true) {
1435
1441
  const missing = this.db.getMessagesWithoutBody(accountId, 100);
1436
1442
  if (missing.length === 0)
@@ -1439,6 +1445,15 @@ export class ImapManager extends EventEmitter {
1439
1445
  console.log(` [prefetch] ${accountId}: ${missing.length}+ bodies to fetch`);
1440
1446
  let madeProgress = false;
1441
1447
  for (const msg of missing) {
1448
+ // If we hit a rate limit, pause before the next fetch
1449
+ if (rateLimited) {
1450
+ console.log(` [prefetch] ${accountId}: rate-limited — pausing ${RATE_LIMIT_PAUSE_MS / 1000}s`);
1451
+ await new Promise(r => setTimeout(r, RATE_LIMIT_PAUSE_MS));
1452
+ rateLimited = false;
1453
+ }
1454
+ else if (FETCH_DELAY_MS > 0) {
1455
+ await new Promise(r => setTimeout(r, FETCH_DELAY_MS));
1456
+ }
1442
1457
  try {
1443
1458
  const result = await this.fetchMessageBody(accountId, msg.folderId, msg.uid);
1444
1459
  if (result) {
@@ -1460,7 +1475,15 @@ export class ImapManager extends EventEmitter {
1460
1475
  catch { /* ignore */ }
1461
1476
  continue;
1462
1477
  }
1463
- errors++;
1478
+ // If the error is a rate limit (429), don't count against
1479
+ // the budget — just slow down. The API will accept requests
1480
+ // again after a brief pause.
1481
+ if (/429|rate|too many/i.test(String(e?.message || ""))) {
1482
+ rateLimited = true;
1483
+ }
1484
+ else {
1485
+ errors++;
1486
+ }
1464
1487
  if (errors >= ERROR_BUDGET) {
1465
1488
  console.error(` [prefetch] ${accountId}: stopping after ${errors} errors (${totalFetched} cached, ${deleted} pruned)`);
1466
1489
  return;