@bobfrankston/rmfmail 1.1.103 → 1.1.105
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/client/app.bundle.js +11 -6
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +19 -18
- package/client/app.js.map +1 -1
- package/client/app.ts +20 -18
- package/client/components/message-list.js +12 -2
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +13 -2
- package/client/compose/compose.bundle.js +6 -2
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/compose/compose.js +17 -4
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +16 -2
- package/client/styles/components.css +12 -0
- package/package.json +3 -3
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +56 -15
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +61 -17
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-22936 → node_modules.npmglobalize-stash-18636}/.package-lock.json +0 -0
|
@@ -1028,26 +1028,63 @@ export class MailxService implements MailxApi {
|
|
|
1028
1028
|
const seen = new Set<string>();
|
|
1029
1029
|
const items: any[] = [];
|
|
1030
1030
|
let total = 0;
|
|
1031
|
+
// Partial-failure accounting. The old code did `if (r.status !==
|
|
1032
|
+
// "fulfilled") continue` — a folder whose SEARCH rejected
|
|
1033
|
+
// (connection discard, a genuinely slow huge folder hitting the
|
|
1034
|
+
// 90s cap) contributed zero results AND zero signal. The user saw
|
|
1035
|
+
// "No results" for a search that simply never ran in the folder
|
|
1036
|
+
// holding the match. Now we count failures and hand them back so
|
|
1037
|
+
// the UI can say "searched 88/93 — 5 folders failed, retry".
|
|
1038
|
+
let foldersSearched = 0;
|
|
1039
|
+
let foldersFailed = 0;
|
|
1040
|
+
const failedFolders: string[] = [];
|
|
1041
|
+
let droppedHits = 0;
|
|
1042
|
+
|
|
1043
|
+
// Bounded concurrency. The per-account ops queue already
|
|
1044
|
+
// serializes connection use, so this doesn't change IMAP
|
|
1045
|
+
// parallelism — but firing all ~93 folder promises at once
|
|
1046
|
+
// builds a 90+-deep pending-promise queue with 90+ live
|
|
1047
|
+
// setTimeout timers. Batching keeps that bounded and gives a
|
|
1048
|
+
// natural place to add early-abort later.
|
|
1049
|
+
const SERVER_SEARCH_BATCH = 8;
|
|
1031
1050
|
|
|
1032
1051
|
for (const acct of dbAccounts) {
|
|
1033
1052
|
const folders = this.db.getFolders(acct.id)
|
|
1034
1053
|
.filter((f: any) => !(f.flags || []).some((x: string) => /noselect/i.test(x)));
|
|
1035
|
-
|
|
1036
|
-
folders.
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
for (
|
|
1044
|
-
const
|
|
1045
|
-
if (
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1054
|
+
for (let i = 0; i < folders.length; i += SERVER_SEARCH_BATCH) {
|
|
1055
|
+
const batch = folders.slice(i, i + SERVER_SEARCH_BATCH);
|
|
1056
|
+
const results = await Promise.allSettled(
|
|
1057
|
+
batch.map(f =>
|
|
1058
|
+
this.imapManager.searchAndFetchOnServer(acct.id, f.id, f.path, criteria)
|
|
1059
|
+
.then(uids => ({ folder: f, uids }))
|
|
1060
|
+
)
|
|
1061
|
+
);
|
|
1062
|
+
for (let j = 0; j < results.length; j++) {
|
|
1063
|
+
const r = results[j];
|
|
1064
|
+
if (r.status !== "fulfilled") {
|
|
1065
|
+
foldersFailed++;
|
|
1066
|
+
failedFolders.push(`${acct.id}/${batch[j].path}`);
|
|
1067
|
+
console.error(` [server-search] ${acct.id}/${batch[j].path}: ${r.reason?.message || r.reason}`);
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
foldersSearched++;
|
|
1071
|
+
for (const uid of r.value.uids) {
|
|
1072
|
+
const msg = this.db.getMessageByUid(acct.id, uid, r.value.folder.id);
|
|
1073
|
+
if (!msg) {
|
|
1074
|
+
// SEARCH matched on the server but the
|
|
1075
|
+
// fetch-and-store in searchAndFetchOnServer
|
|
1076
|
+
// didn't land the row. Count it so the total
|
|
1077
|
+
// doesn't silently under-report.
|
|
1078
|
+
droppedHits++;
|
|
1079
|
+
console.error(` [server-search] ${acct.id}/${r.value.folder.path} uid ${uid}: SEARCH hit not in local DB after fetch — dropped`);
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
const key = msg.messageId || `${acct.id}:${r.value.folder.id}:${uid}`;
|
|
1083
|
+
if (seen.has(key)) continue;
|
|
1084
|
+
seen.add(key);
|
|
1085
|
+
items.push(msg);
|
|
1086
|
+
total++;
|
|
1087
|
+
}
|
|
1051
1088
|
}
|
|
1052
1089
|
}
|
|
1053
1090
|
}
|
|
@@ -1055,7 +1092,14 @@ export class MailxService implements MailxApi {
|
|
|
1055
1092
|
// Newest first, then paginate.
|
|
1056
1093
|
items.sort((a: any, b: any) => (b.date?.getTime?.() || 0) - (a.date?.getTime?.() || 0));
|
|
1057
1094
|
const sliced = items.slice((page - 1) * pageSize, page * pageSize);
|
|
1058
|
-
|
|
1095
|
+
if (foldersFailed > 0 || droppedHits > 0) {
|
|
1096
|
+
console.log(` [server-search] q="${q}" — ${total} hits across ${foldersSearched} folders; ${foldersFailed} folders failed, ${droppedHits} hits dropped`);
|
|
1097
|
+
}
|
|
1098
|
+
return {
|
|
1099
|
+
items: sliced, total, page, pageSize,
|
|
1100
|
+
partial: foldersFailed > 0 || droppedHits > 0,
|
|
1101
|
+
foldersSearched, foldersFailed, failedFolders, droppedHits,
|
|
1102
|
+
};
|
|
1059
1103
|
} else if (scope === "current" && accountId && folderId) {
|
|
1060
1104
|
// Per-folder search — folder scope is the user's explicit
|
|
1061
1105
|
// intent, including trash/spam if that's where they are.
|