@bobfrankston/mailx 1.0.146 → 1.0.147

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/bin/mailx.js CHANGED
@@ -488,8 +488,17 @@ async function main() {
488
488
  console.log("No mailx configuration found.");
489
489
  await runSetup();
490
490
  }
491
- // TODO: re-enable console redirect once wry crash is fixed
492
- // The redirect itself somehow causes wry WebView2 to crash with InvalidUri
491
+ // Redirect console to log file keep terminal clean
492
+ if (!verbose) {
493
+ const home = process.env.USERPROFILE || process.env.HOME || ".";
494
+ const logDir = path.join(home, ".mailx", "logs");
495
+ fs.mkdirSync(logDir, { recursive: true });
496
+ const logDate = new Date().toISOString().slice(0, 10);
497
+ const logPath = path.join(logDir, `mailx-${logDate}.log`);
498
+ const logStream = fs.createWriteStream(logPath, { flags: "a" });
499
+ console.log = (...a) => { logStream.write(a.join(" ") + "\n"); };
500
+ console.error = (...a) => { logStream.write("ERROR " + a.join(" ") + "\n"); };
501
+ }
493
502
  // --server mode: Express + HTTP (for dev/remote access)
494
503
  if (serverMode) {
495
504
  console.log("Starting mailx HTTP server...");
@@ -520,20 +529,34 @@ async function main() {
520
529
  const imapManager = new ImapManager(db);
521
530
  imapManager.useNativeClient = true;
522
531
  const svc = new MailxService(db, imapManager);
523
- // Open msger in service mode — file:// URL, no HTTP
532
+ // Open msger in service mode — custom protocol serves files from client dir
524
533
  const clientDir = path.join(import.meta.dirname, "..", "client");
525
- const indexPath = path.join(clientDir, "index.html");
526
534
  const mailxapiPath = path.join(clientDir, "lib", "mailxapi.js");
527
535
  const mailxapiScript = fs.readFileSync(mailxapiPath, "utf-8");
528
536
  const handle = showService({
529
- url: indexPath,
537
+ url: "index.html",
538
+ contentDir: clientDir,
530
539
  initScript: mailxapiScript,
531
540
  size: { width: 1400, height: 900 },
541
+ escapeCloses: false,
532
542
  });
533
543
  // Handle requests from WebView → dispatch to MailxService
544
+ // Pass server version to dispatch so getVersion returns it
545
+ const rootPkg = JSON.parse(fs.readFileSync(path.join(import.meta.dirname, "..", "package.json"), "utf-8"));
534
546
  handle.onRequest(async (req) => {
535
- const response = await dispatch(svc, req);
536
- handle.send(response);
547
+ if (verbose)
548
+ console.error(`[ipc] ← ${req._action} (${req._cbid})`);
549
+ req._version = rootPkg.version;
550
+ try {
551
+ const response = await dispatch(svc, req);
552
+ if (verbose)
553
+ console.error(`[ipc] → ${req._action} (${req._cbid}) ok`);
554
+ handle.send(response);
555
+ }
556
+ catch (e) {
557
+ console.error(`[ipc] → ${req._action} (${req._cbid}) error: ${e.message}`);
558
+ handle.send({ _cbid: req._cbid, error: e.message });
559
+ }
537
560
  });
538
561
  // Wire IMAP events → push to WebView
539
562
  imapManager.on("syncProgress", (accountId, phase, progress) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx",
3
- "version": "1.0.146",
3
+ "version": "1.0.147",
4
4
  "description": "Local-first email client with IMAP sync and standalone native app",
5
5
  "type": "module",
6
6
  "main": "bin/mailx.js",
@@ -23,7 +23,7 @@
23
23
  "@bobfrankston/iflow": "^1.0.52",
24
24
  "@bobfrankston/miscinfo": "^1.0.7",
25
25
  "@bobfrankston/oauthsupport": "^1.0.20",
26
- "@bobfrankston/msger": "^0.1.198",
26
+ "@bobfrankston/msger": "^0.1.199",
27
27
  "@capacitor/android": "^8.3.0",
28
28
  "@capacitor/cli": "^8.3.0",
29
29
  "@capacitor/core": "^8.3.0",
@@ -164,6 +164,7 @@ export declare class ImapManager extends EventEmitter {
164
164
  processOutbox(accountId: string): Promise<void>;
165
165
  /** Start background Outbox worker — runs immediately then every 10 seconds */
166
166
  private outboxBackoff;
167
+ private outboxBackoffDelay;
167
168
  startOutboxWorker(): void;
168
169
  /** Stop Outbox worker */
169
170
  stopOutboxWorker(): void;
@@ -1592,7 +1592,8 @@ export class ImapManager extends EventEmitter {
1592
1592
  }
1593
1593
  }
1594
1594
  /** Start background Outbox worker — runs immediately then every 10 seconds */
1595
- outboxBackoff = new Map(); // accountId → next retry time
1595
+ outboxBackoff = new Map(); // accountId → next retry timestamp
1596
+ outboxBackoffDelay = new Map(); // accountId → current delay ms
1596
1597
  startOutboxWorker() {
1597
1598
  if (this.outboxInterval)
1598
1599
  return;
@@ -1606,12 +1607,15 @@ export class ImapManager extends EventEmitter {
1606
1607
  try {
1607
1608
  await this.processLocalQueue(accountId);
1608
1609
  await this.processOutbox(accountId);
1609
- this.outboxBackoff.delete(accountId); // success — clear backoff
1610
+ // Success — clear backoff
1611
+ this.outboxBackoff.delete(accountId);
1612
+ this.outboxBackoffDelay.delete(accountId);
1610
1613
  }
1611
1614
  catch (e) {
1612
- // Exponential backoff: 30s, 60s, 120s, max 5min
1613
- const prev = this.outboxBackoff.get(accountId);
1614
- const delay = prev ? Math.min((now - prev + 30000) * 2, 300000) : 30000;
1615
+ // Exponential backoff: 30s 60s 120s → 300s (max 5min)
1616
+ const prevDelay = this.outboxBackoffDelay.get(accountId) || 0;
1617
+ const delay = prevDelay ? Math.min(prevDelay * 2, 300000) : 30000;
1618
+ this.outboxBackoffDelay.set(accountId, delay);
1615
1619
  this.outboxBackoff.set(accountId, now + delay);
1616
1620
  console.error(` [outbox] Error for ${accountId}: ${imapError(e)} (retry in ${Math.round(delay / 1000)}s)`);
1617
1621
  }
@@ -93,8 +93,11 @@ async function dispatchAction(svc, action, p) {
93
93
  case "allowRemoteContent":
94
94
  svc.allowRemoteContent(p.type, p.value);
95
95
  return { ok: true };
96
- case "getVersion":
97
- return svc.getStorageInfo();
96
+ case "getVersion": {
97
+ const settings = svc.getSettings();
98
+ const storage = svc.getStorageInfo();
99
+ return { version: p._version || "dev", theme: settings.ui?.theme || "system", storage };
100
+ }
98
101
  // Autocomplete
99
102
  case "autocomplete":
100
103
  return svc.autocomplete(p);