@bobfrankston/mailx 1.0.163 → 1.0.164
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
|
@@ -231,7 +231,7 @@ if (hasFlag("v") || hasFlag("version")) {
|
|
|
231
231
|
console.log("mailx (version unknown)");
|
|
232
232
|
}
|
|
233
233
|
console.log(` node ${process.version}`);
|
|
234
|
-
console.log(` iflow
|
|
234
|
+
console.log(` iflow-direct ${ver("@bobfrankston/iflow-direct")}`);
|
|
235
235
|
console.log(` miscinfo ${ver("@bobfrankston/miscinfo")}`);
|
|
236
236
|
console.log(` oauth ${ver("@bobfrankston/oauthsupport")}`);
|
|
237
237
|
console.log(` store ${ver("@bobfrankston/mailx-store")}`);
|
|
@@ -500,14 +500,15 @@ async function runTest() {
|
|
|
500
500
|
console.log(`Testing ${account.label || account.id} (${account.email}):`);
|
|
501
501
|
// Test IMAP
|
|
502
502
|
try {
|
|
503
|
-
const {
|
|
503
|
+
const { createAutoImapConfig, CompatImapClient } = await import("@bobfrankston/iflow-direct");
|
|
504
|
+
const { NodeTransport } = await import("@bobfrankston/iflow-node");
|
|
504
505
|
const config = createAutoImapConfig({
|
|
505
506
|
server: account.imap.host,
|
|
506
507
|
port: account.imap.port,
|
|
507
508
|
username: account.imap.user,
|
|
508
509
|
password: account.imap.password
|
|
509
510
|
});
|
|
510
|
-
const client = new
|
|
511
|
+
const client = new CompatImapClient(config, () => new NodeTransport());
|
|
511
512
|
const folders = await client.getFolderList();
|
|
512
513
|
await client.logout();
|
|
513
514
|
console.log(` IMAP: OK (${folders.length} folders)`);
|
|
@@ -524,7 +525,7 @@ async function runTest() {
|
|
|
524
525
|
}
|
|
525
526
|
else if (account.smtp.auth === "oauth2") {
|
|
526
527
|
// Try to get OAuth token
|
|
527
|
-
const { createAutoImapConfig } = await import("@bobfrankston/iflow");
|
|
528
|
+
const { createAutoImapConfig } = await import("@bobfrankston/iflow-direct");
|
|
528
529
|
const config = createAutoImapConfig({
|
|
529
530
|
server: account.imap.host,
|
|
530
531
|
port: account.imap.port,
|
|
@@ -599,11 +600,10 @@ async function main() {
|
|
|
599
600
|
if (setupMode || !hasConfig()) {
|
|
600
601
|
if (!setupMode)
|
|
601
602
|
console.log("No mailx configuration found.");
|
|
602
|
-
//
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|| (
|
|
606
|
-
|| (hasFlag("mail") ? args[args.indexOf("--mail") + 1] || args[args.indexOf("-mail") + 1] : undefined);
|
|
603
|
+
// -email or -mail flag skips the interactive prompt
|
|
604
|
+
const emailFlag = args.findIndex(a => a === "-email" || a === "--email" || a === "-mail" || a === "--mail");
|
|
605
|
+
const emailArg = args.find(a => a.startsWith("-email=") || a.startsWith("--email=") || a.startsWith("-mail=") || a.startsWith("--mail="))?.split("=")[1]
|
|
606
|
+
|| (emailFlag >= 0 ? args[emailFlag + 1] : undefined);
|
|
607
607
|
await runSetup(emailArg);
|
|
608
608
|
}
|
|
609
609
|
// Redirect console to log file — keep terminal clean
|
|
@@ -646,7 +646,7 @@ async function main() {
|
|
|
646
646
|
}
|
|
647
647
|
const db = new MailxDB(getConfigDir());
|
|
648
648
|
const imapManager = new ImapManager(db);
|
|
649
|
-
|
|
649
|
+
// Native client is the only option (iflow-direct)
|
|
650
650
|
const svc = new MailxService(db, imapManager);
|
|
651
651
|
// Open msger in service mode — custom protocol serves files from client dir
|
|
652
652
|
const clientDir = path.join(import.meta.dirname, "..", "client");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.164",
|
|
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,11 @@
|
|
|
20
20
|
"postinstall": "node bin/postinstall.js"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@bobfrankston/iflow": "^1.0
|
|
23
|
+
"@bobfrankston/iflow-direct": "^0.1.0",
|
|
24
|
+
"@bobfrankston/iflow-node": "^0.1.0",
|
|
24
25
|
"@bobfrankston/miscinfo": "^1.0.7",
|
|
25
26
|
"@bobfrankston/oauthsupport": "^1.0.20",
|
|
26
|
-
"@bobfrankston/msger": "^0.1.
|
|
27
|
+
"@bobfrankston/msger": "^0.1.214",
|
|
27
28
|
"@capacitor/android": "^8.3.0",
|
|
28
29
|
"@capacitor/cli": "^8.3.0",
|
|
29
30
|
"@capacitor/core": "^8.3.0",
|
|
@@ -54,7 +55,8 @@
|
|
|
54
55
|
"url": "https://github.com/BobFrankston/mailx.git"
|
|
55
56
|
},
|
|
56
57
|
".dependencies": {
|
|
57
|
-
"@bobfrankston/iflow": "file:../MailApps/iflow",
|
|
58
|
+
"@bobfrankston/iflow-direct": "file:../MailApps/iflow-direct",
|
|
59
|
+
"@bobfrankston/iflow-node": "file:../MailApps/iflow-node",
|
|
58
60
|
"@bobfrankston/miscinfo": "file:../../projects/npm/miscinfo",
|
|
59
61
|
"@bobfrankston/oauthsupport": "file:../../projects/oauth/oauthsupport",
|
|
60
62
|
"@bobfrankston/msger": "file:../../utils/msgx/msger",
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Multi-account IMAP management wrapping iflow.
|
|
4
4
|
* Syncs messages to local store, emits events for new mail.
|
|
5
5
|
*/
|
|
6
|
-
import { ImapClient } from "@bobfrankston/iflow";
|
|
7
6
|
import { MailxDB, FileMessageStore } from "@bobfrankston/mailx-store";
|
|
8
7
|
import type { AccountConfig, MessageEnvelope, Folder } from "@bobfrankston/mailx-types";
|
|
9
8
|
import { EventEmitter } from "node:events";
|
|
@@ -73,11 +72,11 @@ export declare class ImapManager extends EventEmitter {
|
|
|
73
72
|
/** Register an account */
|
|
74
73
|
addAccount(account: AccountConfig): Promise<void>;
|
|
75
74
|
/** Sync folder list for an account */
|
|
76
|
-
syncFolders(accountId: string, client?:
|
|
75
|
+
syncFolders(accountId: string, client?: any): Promise<Folder[]>;
|
|
77
76
|
/** Store a batch of messages to DB immediately — used by onChunk for incremental sync */
|
|
78
77
|
private storeMessages;
|
|
79
78
|
/** Sync messages for a specific folder */
|
|
80
|
-
syncFolder(accountId: string, folderId: number, client?:
|
|
79
|
+
syncFolder(accountId: string, folderId: number, client?: any): Promise<number>;
|
|
81
80
|
/** Sync all folders for all accounts */
|
|
82
81
|
syncAll(): Promise<void>;
|
|
83
82
|
private _syncAll;
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Multi-account IMAP management wrapping iflow.
|
|
4
4
|
* Syncs messages to local store, emits events for new mail.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { createAutoImapConfig, CompatImapClient } from "@bobfrankston/iflow-direct";
|
|
7
|
+
import { NodeTransport } from "@bobfrankston/iflow-node";
|
|
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";
|
|
@@ -280,10 +281,7 @@ export class ImapManager extends EventEmitter {
|
|
|
280
281
|
const config = this.configs.get(accountId);
|
|
281
282
|
if (!config)
|
|
282
283
|
throw new Error(`No config for account ${accountId}`);
|
|
283
|
-
|
|
284
|
-
return new CompatImapClient(config, () => new NodeTransport({ rejectUnauthorized: config.rejectUnauthorized !== false }));
|
|
285
|
-
}
|
|
286
|
-
return new ImapClient(config);
|
|
284
|
+
return new CompatImapClient(config, () => new NodeTransport({ rejectUnauthorized: config.rejectUnauthorized !== false }));
|
|
287
285
|
}
|
|
288
286
|
/** Disconnect the persistent operational connection for an account */
|
|
289
287
|
async disconnectOps(accountId) {
|
|
@@ -321,7 +319,7 @@ export class ImapManager extends EventEmitter {
|
|
|
321
319
|
port: account.imap.port,
|
|
322
320
|
username: account.imap.user,
|
|
323
321
|
password: account.imap.password,
|
|
324
|
-
tokenDirectory
|
|
322
|
+
// tokenDirectory is handled by oauthsupport, not iflow-direct
|
|
325
323
|
});
|
|
326
324
|
this.configs.set(account.id, config);
|
|
327
325
|
// Register account in DB
|
|
@@ -447,7 +445,7 @@ export class ImapManager extends EventEmitter {
|
|
|
447
445
|
// Incremental: fetch new messages — metadata only for speed, bodies on demand
|
|
448
446
|
const fetched = await client.fetchMessagesSinceUid(folder.path, highestUid, { source: false });
|
|
449
447
|
// Filter out the last known message (IMAP * always returns at least one)
|
|
450
|
-
messages = fetched.filter(m => m.uid > highestUid);
|
|
448
|
+
messages = fetched.filter((m) => m.uid > highestUid);
|
|
451
449
|
// Gap detection: check for missing UIDs within the range we've already synced
|
|
452
450
|
// Only reconcile between our lowest and highest UID — don't try to fetch the entire folder history
|
|
453
451
|
const existingUids = this.db.getUidsForFolder(accountId, folderId);
|
|
@@ -483,7 +481,7 @@ export class ImapManager extends EventEmitter {
|
|
|
483
481
|
if (oldestDate > 0 && startDate.getTime() < oldestDate) {
|
|
484
482
|
const existingUids = new Set(this.db.getUidsForFolder(accountId, folderId));
|
|
485
483
|
const backfill = await client.fetchMessageByDate(folder.path, startDate, new Date(oldestDate), { source: false });
|
|
486
|
-
const newBackfill = backfill.filter(m => !existingUids.has(m.uid));
|
|
484
|
+
const newBackfill = backfill.filter((m) => !existingUids.has(m.uid));
|
|
487
485
|
if (newBackfill.length > 0) {
|
|
488
486
|
console.log(` ${folder.path}: backfilling ${newBackfill.length} older messages`);
|
|
489
487
|
messages.push(...newBackfill);
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@bobfrankston/mailx-types": "file:../mailx-types",
|
|
13
13
|
"@bobfrankston/mailx-settings": "file:../mailx-settings",
|
|
14
14
|
"@bobfrankston/mailx-store": "file:../mailx-store",
|
|
15
|
-
"@bobfrankston/iflow": "file:../../../MailApps/iflow",
|
|
15
|
+
"@bobfrankston/iflow-direct": "file:../../../MailApps/iflow-direct",
|
|
16
16
|
"@bobfrankston/oauthsupport": "file:../../../../projects/oauth/oauthsupport",
|
|
17
17
|
"nodemailer": "^7.0.0"
|
|
18
18
|
},
|
|
@@ -30,10 +30,12 @@ function findGoogleCredentials() {
|
|
|
30
30
|
try {
|
|
31
31
|
let dir = import.meta.dirname;
|
|
32
32
|
for (let i = 0; i < 5; i++) {
|
|
33
|
-
for (const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
for (const pkg of ["iflow-direct", "iflow"]) {
|
|
34
|
+
for (const name of ["iflow-credentials.json", "credentials.json"]) {
|
|
35
|
+
const p = path.join(dir, "node_modules", "@bobfrankston", pkg, name);
|
|
36
|
+
if (fs.existsSync(p))
|
|
37
|
+
return p;
|
|
38
|
+
}
|
|
37
39
|
}
|
|
38
40
|
const parent = path.dirname(dir);
|
|
39
41
|
if (parent === dir)
|