@bobfrankston/mailx 1.0.165 → 1.0.166

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.165",
3
+ "version": "1.0.166",
4
4
  "description": "Local-first email client with IMAP sync and standalone native app",
5
5
  "type": "module",
6
6
  "main": "bin/mailx.js",
@@ -24,7 +24,7 @@
24
24
  "@bobfrankston/iflow-node": "^0.1.1",
25
25
  "@bobfrankston/miscinfo": "^1.0.7",
26
26
  "@bobfrankston/oauthsupport": "^1.0.20",
27
- "@bobfrankston/msger": "^0.1.215",
27
+ "@bobfrankston/msger": "^0.1.216",
28
28
  "@capacitor/android": "^8.3.0",
29
29
  "@capacitor/cli": "^8.3.0",
30
30
  "@capacitor/core": "^8.3.0",
@@ -4,6 +4,7 @@
4
4
  * Syncs messages to local store, emits events for new mail.
5
5
  */
6
6
  import { createAutoImapConfig, CompatImapClient } from "@bobfrankston/iflow-direct";
7
+ import { authenticateOAuth } from "@bobfrankston/oauthsupport";
7
8
  import { FileMessageStore } from "@bobfrankston/mailx-store";
8
9
  import { loadSettings, getStorePath, getConfigDir, getHistoryDays } from "@bobfrankston/mailx-settings";
9
10
  import { EventEmitter } from "node:events";
@@ -314,13 +315,26 @@ export class ImapManager extends EventEmitter {
314
315
  if (this.configs.has(account.id))
315
316
  return;
316
317
  // createAutoImapConfig auto-detects Gmail from server/username and sets up OAuth
317
- // Token directory in ~/.mailx/ so tokens persist across npm reinstalls
318
+ // For OAuth accounts, provide a tokenProvider using oauthsupport
319
+ let tokenProvider;
320
+ if (account.imap.auth === "oauth2" || (!account.imap.password && account.imap.host?.includes("gmail"))) {
321
+ const credPath = path.join(getConfigDir(), "google-credentials.json");
322
+ const tokenDir = path.join(getConfigDir(), "tokens", account.imap.user.replace(/[@.]/g, "_"));
323
+ tokenProvider = async () => {
324
+ const result = await authenticateOAuth(credPath, {
325
+ scope: "https://mail.google.com/ https://www.googleapis.com/auth/contacts.readonly",
326
+ tokenDirectory: tokenDir,
327
+ loginHint: account.imap.user,
328
+ });
329
+ return result?.access_token || "";
330
+ };
331
+ }
318
332
  const config = createAutoImapConfig({
319
333
  server: account.imap.host,
320
334
  port: account.imap.port,
321
335
  username: account.imap.user,
322
336
  password: account.imap.password,
323
- // tokenDirectory is handled by oauthsupport, not iflow-direct
337
+ tokenProvider,
324
338
  });
325
339
  this.configs.set(account.id, config);
326
340
  // Register account in DB
@@ -388,6 +402,11 @@ export class ImapManager extends EventEmitter {
388
402
  this.db.beginTransaction();
389
403
  try {
390
404
  for (const msg of msgs) {
405
+ // Debug: log subjects with non-ASCII to trace encoding issues
406
+ if (msg.subject && /[^\x00-\x7F]/.test(msg.subject)) {
407
+ const hex = Buffer.from(msg.subject, "utf-8").subarray(0, 40).toString("hex");
408
+ console.log(` [encoding] subject: "${msg.subject.substring(0, 60)}" hex: ${hex}`);
409
+ }
391
410
  if (msg.uid <= highestUid)
392
411
  continue; // already have it
393
412
  const source = msg.source || "";
@@ -500,7 +519,8 @@ export class ImapManager extends EventEmitter {
500
519
  this.emit("folderCountsChanged", accountId, {});
501
520
  }
502
521
  };
503
- messages = await client.fetchMessageByDate(folder.path, startDate, new Date(), { source: false }, onChunk);
522
+ const tomorrow = new Date(Date.now() + 86400000); // IMAP BEFORE is exclusive
523
+ messages = await client.fetchMessageByDate(folder.path, startDate, tomorrow, { source: false }, onChunk);
504
524
  if (totalStored > 0) {
505
525
  console.log(` ${folder.path}: ${totalStored} messages (streamed)`);
506
526
  this.db.recalcFolderCounts(folderId);