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