@bobfrankston/mailx 1.0.158 → 1.0.159
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 +11 -6
- package/package.json +2 -2
- package/packages/mailx-imap/index.d.ts +2 -0
- package/packages/mailx-imap/index.js +59 -9
package/bin/mailx.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* mailx --server Start Express HTTP server (dev/remote)
|
|
8
8
|
* mailx --no-browser Start server only (headless)
|
|
9
9
|
* mailx --verbose Show console output (default: log file only)
|
|
10
|
+
* mailx --email <addr> First-time setup with email (skips prompt)
|
|
10
11
|
* mailx --import <file> Import accounts.jsonc into GDrive and merge
|
|
11
12
|
* mailx -v / --version Show version and exit
|
|
12
13
|
* mailx -kill Kill running mailx processes
|
|
@@ -34,7 +35,7 @@ const rebuildMode = hasFlag("rebuild");
|
|
|
34
35
|
const repairMode = hasFlag("repair");
|
|
35
36
|
const importMode = hasFlag("import");
|
|
36
37
|
// Validate arguments
|
|
37
|
-
const knownFlags = ["server", "no-browser", "verbose", "external", "kill", "v", "version", "setup", "add", "test", "rebuild", "repair", "native-imap", "log", "import"];
|
|
38
|
+
const knownFlags = ["server", "no-browser", "verbose", "external", "kill", "v", "version", "setup", "add", "test", "rebuild", "repair", "native-imap", "log", "import", "email"];
|
|
38
39
|
for (const arg of args) {
|
|
39
40
|
const flag = arg.replace(/^--?/, "");
|
|
40
41
|
if (arg.startsWith("-") && !knownFlags.includes(flag)) {
|
|
@@ -364,18 +365,19 @@ async function promptForAccount(intro) {
|
|
|
364
365
|
};
|
|
365
366
|
}
|
|
366
367
|
/** Interactive first-time setup — GDrive API for cloud storage */
|
|
367
|
-
async function runSetup() {
|
|
368
|
+
async function runSetup(providedEmail) {
|
|
368
369
|
console.log("\nmailx — first-time setup\n");
|
|
369
370
|
const home = process.env.USERPROFILE || process.env.HOME || "";
|
|
370
371
|
const mailxDir = path.join(home, ".mailx");
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
const email = await prompt("Email address: ");
|
|
372
|
+
// Use --email flag or prompt interactively
|
|
373
|
+
const email = providedEmail || await prompt("Email address (Gmail recommended): ");
|
|
374
374
|
if (!email || !email.includes("@")) {
|
|
375
375
|
console.log(`\nNo account added. The UI will show a setup form.`);
|
|
376
376
|
fs.mkdirSync(mailxDir, { recursive: true });
|
|
377
377
|
return false;
|
|
378
378
|
}
|
|
379
|
+
if (providedEmail)
|
|
380
|
+
console.log(`Using email: ${email}`);
|
|
379
381
|
const domain = email.split("@")[1]?.toLowerCase() || "";
|
|
380
382
|
let isGoogle = ["gmail.com", "googlemail.com"].includes(domain);
|
|
381
383
|
if (!isGoogle) {
|
|
@@ -597,7 +599,10 @@ async function main() {
|
|
|
597
599
|
if (setupMode || !hasConfig()) {
|
|
598
600
|
if (!setupMode)
|
|
599
601
|
console.log("No mailx configuration found.");
|
|
600
|
-
|
|
602
|
+
// --email flag skips the interactive prompt
|
|
603
|
+
const emailArg = args.find(a => a.startsWith("--email="))?.split("=")[1]
|
|
604
|
+
|| (hasFlag("email") ? args[args.indexOf("--email") + 1] || args[args.indexOf("-email") + 1] : undefined);
|
|
605
|
+
await runSetup(emailArg);
|
|
601
606
|
}
|
|
602
607
|
// Redirect console to log file — keep terminal clean
|
|
603
608
|
if (!verbose) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.159",
|
|
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.54",
|
|
24
24
|
"@bobfrankston/miscinfo": "^1.0.7",
|
|
25
25
|
"@bobfrankston/oauthsupport": "^1.0.20",
|
|
26
|
-
"@bobfrankston/msger": "^0.1.
|
|
26
|
+
"@bobfrankston/msger": "^0.1.209",
|
|
27
27
|
"@capacitor/android": "^8.3.0",
|
|
28
28
|
"@capacitor/cli": "^8.3.0",
|
|
29
29
|
"@capacitor/core": "^8.3.0",
|
|
@@ -74,6 +74,8 @@ export declare class ImapManager extends EventEmitter {
|
|
|
74
74
|
addAccount(account: AccountConfig): Promise<void>;
|
|
75
75
|
/** Sync folder list for an account */
|
|
76
76
|
syncFolders(accountId: string, client?: ImapClient): Promise<Folder[]>;
|
|
77
|
+
/** Store a batch of messages to DB immediately — used by onChunk for incremental sync */
|
|
78
|
+
private storeMessages;
|
|
77
79
|
/** Sync messages for a specific folder */
|
|
78
80
|
syncFolder(accountId: string, folderId: number, client?: ImapClient): Promise<number>;
|
|
79
81
|
/** Sync all folders for all accounts */
|
|
@@ -383,6 +383,46 @@ export class ImapManager extends EventEmitter {
|
|
|
383
383
|
this.emit("folderCountsChanged", accountId, {});
|
|
384
384
|
return dbFolders;
|
|
385
385
|
}
|
|
386
|
+
/** Store a batch of messages to DB immediately — used by onChunk for incremental sync */
|
|
387
|
+
storeMessages(accountId, folderId, folder, msgs, highestUid) {
|
|
388
|
+
let stored = 0;
|
|
389
|
+
this.db.beginTransaction();
|
|
390
|
+
try {
|
|
391
|
+
for (const msg of msgs) {
|
|
392
|
+
if (msg.uid <= highestUid)
|
|
393
|
+
continue; // already have it
|
|
394
|
+
const source = msg.source || "";
|
|
395
|
+
let bodyPath = "";
|
|
396
|
+
// Skip body storage during sync — bodies fetched on demand
|
|
397
|
+
const flags = [];
|
|
398
|
+
if (msg.seen)
|
|
399
|
+
flags.push("\\Seen");
|
|
400
|
+
if (msg.flagged)
|
|
401
|
+
flags.push("\\Flagged");
|
|
402
|
+
if (msg.answered)
|
|
403
|
+
flags.push("\\Answered");
|
|
404
|
+
if (msg.draft)
|
|
405
|
+
flags.push("\\Draft");
|
|
406
|
+
this.db.upsertMessage({
|
|
407
|
+
accountId, folderId, uid: msg.uid,
|
|
408
|
+
messageId: msg.messageId || "", inReplyTo: "", references: [],
|
|
409
|
+
date: msg.date instanceof Date ? msg.date.getTime() : (typeof msg.date === "number" ? msg.date : Date.now()),
|
|
410
|
+
subject: msg.subject || "",
|
|
411
|
+
from: toEmailAddress(msg.from?.[0] || {}),
|
|
412
|
+
to: toEmailAddresses(msg.to || []),
|
|
413
|
+
cc: toEmailAddresses(msg.cc || []),
|
|
414
|
+
flags, size: msg.size || 0, hasAttachments: false, preview: "", bodyPath
|
|
415
|
+
});
|
|
416
|
+
stored++;
|
|
417
|
+
}
|
|
418
|
+
this.db.commitTransaction();
|
|
419
|
+
}
|
|
420
|
+
catch (e) {
|
|
421
|
+
this.db.rollbackTransaction();
|
|
422
|
+
console.error(` storeMessages error: ${e.message}`);
|
|
423
|
+
}
|
|
424
|
+
return stored;
|
|
425
|
+
}
|
|
386
426
|
/** Sync messages for a specific folder */
|
|
387
427
|
async syncFolder(accountId, folderId, client) {
|
|
388
428
|
if (!client)
|
|
@@ -451,15 +491,25 @@ export class ImapManager extends EventEmitter {
|
|
|
451
491
|
}
|
|
452
492
|
}
|
|
453
493
|
else {
|
|
454
|
-
// First sync:
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
494
|
+
// First sync: fetch in chunks, store each chunk immediately for instant UI
|
|
495
|
+
let totalStored = 0;
|
|
496
|
+
const onChunk = (chunk) => {
|
|
497
|
+
const stored = this.storeMessages(accountId, folderId, folder, chunk, highestUid);
|
|
498
|
+
totalStored += stored;
|
|
499
|
+
if (stored > 0) {
|
|
500
|
+
this.db.recalcFolderCounts(folderId);
|
|
501
|
+
this.emit("folderCountsChanged", accountId, {});
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
messages = await client.fetchMessageByDate(folder.path, startDate, new Date(), { source: false }, onChunk);
|
|
505
|
+
if (totalStored > 0) {
|
|
506
|
+
console.log(` ${folder.path}: ${totalStored} messages (streamed)`);
|
|
507
|
+
this.db.recalcFolderCounts(folderId);
|
|
508
|
+
this.emit("folderCountsChanged", accountId, {});
|
|
509
|
+
this.emit("syncProgress", accountId, `sync:${folder.path}`, 100);
|
|
510
|
+
return totalStored;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
463
513
|
if (messages.length > 0)
|
|
464
514
|
console.log(` ${folder.path}: ${messages.length} new messages`);
|
|
465
515
|
let newCount = 0;
|