proton-mail-bridge-client 2.2.3 → 2.2.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"local-index-service.d.ts","sourceRoot":"","sources":["../../src/services/local-index-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local-index-service.d.ts","sourceRoot":"","sources":["../../src/services/local-index-service.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { chmodSync } from "node:fs";
3
3
  import { mkdir, readFile, rm } from "node:fs/promises";
4
4
  import { dirname, join } from "node:path";
5
5
  import { ensureAccountIdentityMatches } from "../utils/account-identity.js";
6
- import { createEmailId, dedupeEmails, extractDomain, extractMessageIdList, lowerCaseAddress, nextDay, normalizeMailboxLabel, normalizeMessageId, normalizeSubjectForThread, sortEmailsByNewest, } from "../utils/helpers.js";
6
+ import { createEmailId, dedupeEmails, extractDomain, extractMessageIdList, foldSearchText, lowerCaseAddress, nextDay, normalizeMailboxLabel, normalizeMessageId, normalizeSubjectForThread, sortEmailsByNewest, } from "../utils/helpers.js";
7
7
  import { logger } from "../utils/logger.js";
8
8
  const DB_SCHEMA_VERSION = 3;
9
9
  const STALE_THRESHOLD_MINUTES = 60;
@@ -100,9 +100,9 @@ function matchesIndexedSearch(email, filters) {
100
100
  return false;
101
101
  }
102
102
  if (normalizedFilters.label) {
103
- const labelNeedle = normalizedFilters.label.toLowerCase();
104
- const folderMatch = normalizeMailboxLabel(email.folder)?.toLowerCase() === labelNeedle;
105
- const labelMatch = email.labels.some((label) => normalizeMailboxLabel(label)?.toLowerCase() === labelNeedle);
103
+ const labelNeedle = foldSearchText(normalizedFilters.label);
104
+ const folderMatch = foldSearchText(normalizeMailboxLabel(email.folder) ?? "") === labelNeedle;
105
+ const labelMatch = email.labels.some((label) => foldSearchText(normalizeMailboxLabel(label) ?? "") === labelNeedle);
106
106
  if (!folderMatch && !labelMatch) {
107
107
  return false;
108
108
  }
@@ -131,8 +131,8 @@ function matchesIndexedSearch(email, filters) {
131
131
  return false;
132
132
  }
133
133
  if (normalizedFilters.attachmentName) {
134
- const attachmentNeedle = normalizedFilters.attachmentName.toLowerCase();
135
- const match = email.attachments.some((attachment) => (attachment.filename || "").toLowerCase().includes(attachmentNeedle));
134
+ const attachmentNeedle = foldSearchText(normalizedFilters.attachmentName);
135
+ const match = email.attachments.some((attachment) => foldSearchText(attachment.filename || "").includes(attachmentNeedle));
136
136
  if (!match) {
137
137
  return false;
138
138
  }
@@ -152,35 +152,34 @@ function matchesIndexedSearch(email, filters) {
152
152
  ...email.from.map((value) => `${value.name ?? ""} ${value.address ?? ""}`),
153
153
  ...email.to.map((value) => `${value.name ?? ""} ${value.address ?? ""}`),
154
154
  ...email.cc.map((value) => `${value.name ?? ""} ${value.address ?? ""}`),
155
- ]
156
- .join("\n")
157
- .toLowerCase();
155
+ ].join("\n");
156
+ const foldedHaystacks = foldSearchText(haystacks);
158
157
  // searchFtsIds() ANDs every whitespace-separated query token as its own quoted FTS5 term
159
158
  // (so "invoice payment" matches a preview like "payment for the invoice is overdue"), but
160
159
  // this post-filter used to require the ENTIRE joined query as one literal substring —
161
160
  // rejecting anything FTS already matched whose words were merely out of order or separated
162
161
  // by other words. Match FTS's own AND-of-terms semantics instead of a single-phrase substring.
163
162
  if (normalizedFilters.query) {
164
- const terms = normalizedFilters.query.toLowerCase().split(/\s+/).filter(Boolean);
165
- if (terms.length > 0 && !terms.every((term) => haystacks.includes(term))) {
163
+ const terms = foldSearchText(normalizedFilters.query).split(/\s+/).filter(Boolean);
164
+ if (terms.length > 0 && !terms.every((term) => foldedHaystacks.includes(term))) {
166
165
  return false;
167
166
  }
168
167
  }
169
168
  if (normalizedFilters.subject &&
170
- !email.subject.toLowerCase().includes(normalizedFilters.subject.toLowerCase())) {
169
+ !foldSearchText(email.subject).includes(foldSearchText(normalizedFilters.subject))) {
171
170
  return false;
172
171
  }
173
172
  if (normalizedFilters.from) {
174
- const fromNeedle = normalizedFilters.from.toLowerCase();
175
- const match = email.from.some((value) => `${value.name ?? ""} ${value.address ?? ""}`.toLowerCase().includes(fromNeedle));
173
+ const fromNeedle = foldSearchText(normalizedFilters.from);
174
+ const match = email.from.some((value) => foldSearchText(`${value.name ?? ""} ${value.address ?? ""}`).includes(fromNeedle));
176
175
  if (!match) {
177
176
  return false;
178
177
  }
179
178
  }
180
179
  if (normalizedFilters.to) {
181
- const toNeedle = normalizedFilters.to.toLowerCase();
180
+ const toNeedle = foldSearchText(normalizedFilters.to);
182
181
  const recipients = [...email.to, ...email.cc, ...email.bcc];
183
- const match = recipients.some((value) => `${value.name ?? ""} ${value.address ?? ""}`.toLowerCase().includes(toNeedle));
182
+ const match = recipients.some((value) => foldSearchText(`${value.name ?? ""} ${value.address ?? ""}`).includes(toNeedle));
184
183
  if (!match) {
185
184
  return false;
186
185
  }
@@ -763,20 +762,18 @@ export class LocalIndexService {
763
762
  // get plain ThreadSummary shapes, not the full per-message detail.
764
763
  const threads = this.buildThreads(snapshot, true).filter((thread) => {
765
764
  if (input.label) {
766
- const labelNeedle = input.label.toLowerCase();
767
- if (!thread.normalizedLabels.some((label) => label.toLowerCase() === labelNeedle)) {
765
+ const labelNeedle = foldSearchText(input.label);
766
+ if (!thread.normalizedLabels.some((label) => foldSearchText(label) === labelNeedle)) {
768
767
  return false;
769
768
  }
770
769
  }
771
770
  if (input.query) {
772
- const queryNeedle = input.query.toLowerCase();
773
- const threadLevelHaystack = [
771
+ const queryNeedle = foldSearchText(input.query);
772
+ const threadLevelHaystack = foldSearchText([
774
773
  ...thread.participants.map((participant) => `${participant.name ?? ""} ${participant.address ?? ""}`),
775
774
  ...thread.normalizedLabels,
776
- ]
777
- .join("\n")
778
- .toLowerCase();
779
- const matchesAnyMessage = thread.messages.some((message) => message.subject.toLowerCase().includes(queryNeedle));
775
+ ].join("\n"));
776
+ const matchesAnyMessage = thread.messages.some((message) => foldSearchText(message.subject).includes(queryNeedle));
780
777
  if (!matchesAnyMessage && !threadLevelHaystack.includes(queryNeedle)) {
781
778
  return false;
782
779
  }
@@ -868,13 +865,13 @@ export class LocalIndexService {
868
865
  conditions.push(`is_read = 0`);
869
866
  }
870
867
  if (input.label) {
871
- const labelNeedle = escapeLike(input.label.toLowerCase());
872
- conditions.push(`(LOWER(folder) LIKE ? ESCAPE '\\' OR LOWER(labels_json) LIKE ? ESCAPE '\\')`);
868
+ const labelNeedle = escapeLike(foldSearchText(input.label));
869
+ conditions.push(`(FOLD(folder) LIKE ? ESCAPE '\\' OR FOLD(labels_json) LIKE ? ESCAPE '\\')`);
873
870
  params.push(`%${labelNeedle}%`, `%${labelNeedle}%`);
874
871
  }
875
872
  if (input.query) {
876
- const needle = `%${escapeLike(input.query.toLowerCase())}%`;
877
- conditions.push(`(LOWER(subject) LIKE ? ESCAPE '\\' OR LOWER(preview) LIKE ? ESCAPE '\\' OR LOWER(from_json) LIKE ? ESCAPE '\\' OR LOWER(labels_json) LIKE ? ESCAPE '\\')`);
873
+ const needle = `%${escapeLike(foldSearchText(input.query))}%`;
874
+ conditions.push(`(FOLD(subject) LIKE ? ESCAPE '\\' OR FOLD(preview) LIKE ? ESCAPE '\\' OR FOLD(from_json) LIKE ? ESCAPE '\\' OR FOLD(labels_json) LIKE ? ESCAPE '\\')`);
878
875
  params.push(needle, needle, needle, needle);
879
876
  }
880
877
  const sql = `SELECT * FROM messages${conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : ""}`;
@@ -916,21 +913,19 @@ export class LocalIndexService {
916
913
  return false;
917
914
  }
918
915
  if (input.label) {
919
- const labelNeedle = input.label.toLowerCase();
920
- if (!thread.normalizedLabels.some((label) => label.toLowerCase() === labelNeedle)) {
916
+ const labelNeedle = foldSearchText(input.label);
917
+ if (!thread.normalizedLabels.some((label) => foldSearchText(label) === labelNeedle)) {
921
918
  return false;
922
919
  }
923
920
  }
924
921
  if (input.query) {
925
- const haystack = [
922
+ const haystack = foldSearchText([
926
923
  thread.subject,
927
924
  thread.latestPreview || "",
928
925
  ...thread.latestFrom.map((value) => `${value.name ?? ""} ${value.address ?? ""}`),
929
926
  ...thread.normalizedLabels,
930
- ]
931
- .join("\n")
932
- .toLowerCase();
933
- if (!haystack.includes(input.query.toLowerCase())) {
927
+ ].join("\n"));
928
+ if (!haystack.includes(foldSearchText(input.query))) {
934
929
  return false;
935
930
  }
936
931
  }
@@ -1151,7 +1146,7 @@ export class LocalIndexService {
1151
1146
  // membership so a matching document thread outside DEFAULT_SNAPSHOT_LIMIT is never
1152
1147
  // silently missed. A message with no attachments can never contribute a document
1153
1148
  // match regardless of its text, so has_attachments = 1 narrows this further.
1154
- const keywordConditions = keywords.map(() => `(LOWER(attachments_json) LIKE ? ESCAPE '\\' OR LOWER(subject) LIKE ? ESCAPE '\\' OR LOWER(preview) LIKE ? ESCAPE '\\' OR LOWER(attachment_text) LIKE ? ESCAPE '\\')`);
1149
+ const keywordConditions = keywords.map(() => `(FOLD(attachments_json) LIKE ? ESCAPE '\\' OR FOLD(subject) LIKE ? ESCAPE '\\' OR FOLD(preview) LIKE ? ESCAPE '\\' OR FOLD(attachment_text) LIKE ? ESCAPE '\\')`);
1155
1150
  const keywordParams = [];
1156
1151
  for (const keyword of keywords) {
1157
1152
  const needle = `%${escapeLike(keyword)}%`;
@@ -1225,19 +1220,19 @@ export class LocalIndexService {
1225
1220
  const db = await this.ensureDb();
1226
1221
  const { ownerEmail, updatedAt, folders, indexedFolders } = this.loadFoldersAndMetadata(db);
1227
1222
  const syncCheckpoints = this.loadCheckpointsSync(db);
1228
- const personNeedle = input.person?.toLowerCase();
1223
+ const personNeedle = input.person ? foldSearchText(input.person) : undefined;
1229
1224
  const domainNeedle = input.domain?.toLowerCase();
1230
1225
  const limit = input.limit ?? 10;
1231
1226
  const conditions = [];
1232
1227
  const params = [];
1233
1228
  if (personNeedle) {
1234
1229
  const needle = `%${escapeLike(personNeedle)}%`;
1235
- conditions.push(`(LOWER(from_json) LIKE ? ESCAPE '\\' OR LOWER(to_json) LIKE ? ESCAPE '\\' OR LOWER(cc_json) LIKE ? ESCAPE '\\')`);
1230
+ conditions.push(`(FOLD(from_json) LIKE ? ESCAPE '\\' OR FOLD(to_json) LIKE ? ESCAPE '\\' OR FOLD(cc_json) LIKE ? ESCAPE '\\')`);
1236
1231
  params.push(needle, needle, needle);
1237
1232
  }
1238
1233
  if (domainNeedle) {
1239
1234
  const needle = `%@${escapeLike(domainNeedle)}%`;
1240
- conditions.push(`(LOWER(from_json) LIKE ? ESCAPE '\\' OR LOWER(to_json) LIKE ? ESCAPE '\\' OR LOWER(cc_json) LIKE ? ESCAPE '\\')`);
1235
+ conditions.push(`(FOLD(from_json) LIKE ? ESCAPE '\\' OR FOLD(to_json) LIKE ? ESCAPE '\\' OR FOLD(cc_json) LIKE ? ESCAPE '\\')`);
1241
1236
  params.push(needle, needle, needle);
1242
1237
  }
1243
1238
  let messages;
@@ -1263,7 +1258,7 @@ export class LocalIndexService {
1263
1258
  const snapshot = { ownerEmail, updatedAt, folders, indexedFolders, syncCheckpoints, messages };
1264
1259
  const threads = this.buildThreads(snapshot, true)
1265
1260
  .filter((thread) => thread.participants.some((participant) => {
1266
- const participantText = `${participant.name ?? ""} ${participant.address ?? ""}`.toLowerCase();
1261
+ const participantText = foldSearchText(`${participant.name ?? ""} ${participant.address ?? ""}`);
1267
1262
  const participantDomain = extractDomain(participant.address || "");
1268
1263
  if (personNeedle && participantText.includes(personNeedle)) {
1269
1264
  return true;
@@ -1327,6 +1322,9 @@ export class LocalIndexService {
1327
1322
  const db = this.db ?? new Database(this.dbPath);
1328
1323
  if (isFirstOpen) {
1329
1324
  this.chmodDbFiles();
1325
+ // SQLite's LOWER() only folds ASCII and LIKE does not ignore accents, so "łódź" could not find
1326
+ // "Łódź" and "pelcova" could not find "Pelcová". Compare folded text on both sides instead.
1327
+ db.function("FOLD", { deterministic: true }, (value) => (value == null ? null : foldSearchText(String(value))));
1330
1328
  }
1331
1329
  // auto_vacuum only takes effect on a brand-new/empty database (page_count 0) —
1332
1330
  // setting the pragma alone does NOT retroactively enable incremental vacuuming
@@ -1963,12 +1961,12 @@ export class LocalIndexService {
1963
1961
  params.push(filters.hasAttachment ? 1 : 0);
1964
1962
  }
1965
1963
  if (filters.subject) {
1966
- conditions.push(`LOWER(subject) LIKE ? ESCAPE '\\'`);
1967
- params.push(`%${escapeLike(filters.subject.toLowerCase())}%`);
1964
+ conditions.push(`FOLD(subject) LIKE ? ESCAPE '\\'`);
1965
+ params.push(`%${escapeLike(foldSearchText(filters.subject))}%`);
1968
1966
  }
1969
1967
  if (filters.senderDomain) {
1970
- conditions.push(`LOWER(from_json) LIKE ? ESCAPE '\\'`);
1971
- params.push(`%${escapeLike(filters.senderDomain.toLowerCase())}%`);
1968
+ conditions.push(`FOLD(from_json) LIKE ? ESCAPE '\\'`);
1969
+ params.push(`%${escapeLike(foldSearchText(filters.senderDomain))}%`);
1972
1970
  }
1973
1971
  // from/to/messageId were accepted by search_indexed_emails but never
1974
1972
  // narrowed the SQL candidate scan — only applied afterward by
@@ -1978,12 +1976,12 @@ export class LocalIndexService {
1978
1976
  // genuine match older than the window instead of finding it. Mirror the
1979
1977
  // existing senderDomain LIKE pattern (a safe superset pre-filter).
1980
1978
  if (filters.from) {
1981
- conditions.push(`LOWER(from_json) LIKE ? ESCAPE '\\'`);
1982
- params.push(`%${escapeLike(filters.from.toLowerCase())}%`);
1979
+ conditions.push(`FOLD(from_json) LIKE ? ESCAPE '\\'`);
1980
+ params.push(`%${escapeLike(foldSearchText(filters.from))}%`);
1983
1981
  }
1984
1982
  if (filters.to) {
1985
- conditions.push(`(LOWER(to_json) LIKE ? ESCAPE '\\' OR LOWER(cc_json) LIKE ? ESCAPE '\\' OR LOWER(bcc_json) LIKE ? ESCAPE '\\')`);
1986
- const toNeedle = `%${escapeLike(filters.to.toLowerCase())}%`;
1983
+ conditions.push(`(FOLD(to_json) LIKE ? ESCAPE '\\' OR FOLD(cc_json) LIKE ? ESCAPE '\\' OR FOLD(bcc_json) LIKE ? ESCAPE '\\')`);
1984
+ const toNeedle = `%${escapeLike(foldSearchText(filters.to))}%`;
1987
1985
  params.push(toNeedle, toNeedle, toNeedle);
1988
1986
  }
1989
1987
  if (filters.messageId) {
@@ -2148,8 +2146,8 @@ export class LocalIndexService {
2148
2146
  messageParams.push(options.folder);
2149
2147
  }
2150
2148
  if (options.label) {
2151
- const labelNeedle = escapeLike(options.label.toLowerCase());
2152
- messageConditions.push(`(LOWER(folder) LIKE ? ESCAPE '\\' OR LOWER(labels_json) LIKE ? ESCAPE '\\')`);
2149
+ const labelNeedle = escapeLike(foldSearchText(options.label));
2150
+ messageConditions.push(`(FOLD(folder) LIKE ? ESCAPE '\\' OR FOLD(labels_json) LIKE ? ESCAPE '\\')`);
2153
2151
  messageParams.push(`%${labelNeedle}%`, `%${labelNeedle}%`);
2154
2152
  }
2155
2153
  if (typeof options.isRead === "boolean") {
@@ -2190,13 +2188,13 @@ export class LocalIndexService {
2190
2188
  params.push(input.folder);
2191
2189
  }
2192
2190
  if (input.label) {
2193
- const labelNeedle = escapeLike(input.label.toLowerCase());
2194
- conditions.push(`(LOWER(folder) LIKE ? ESCAPE '\\' OR LOWER(labels_json) LIKE ? ESCAPE '\\')`);
2191
+ const labelNeedle = escapeLike(foldSearchText(input.label));
2192
+ conditions.push(`(FOLD(folder) LIKE ? ESCAPE '\\' OR FOLD(labels_json) LIKE ? ESCAPE '\\')`);
2195
2193
  params.push(`%${labelNeedle}%`, `%${labelNeedle}%`);
2196
2194
  }
2197
2195
  if (input.query) {
2198
- const needle = `%${escapeLike(input.query.toLowerCase())}%`;
2199
- conditions.push(`(LOWER(subject) LIKE ? ESCAPE '\\' OR LOWER(from_json) LIKE ? ESCAPE '\\' OR LOWER(to_json) LIKE ? ESCAPE '\\' OR LOWER(cc_json) LIKE ? ESCAPE '\\' OR LOWER(bcc_json) LIKE ? ESCAPE '\\' OR LOWER(labels_json) LIKE ? ESCAPE '\\')`);
2196
+ const needle = `%${escapeLike(foldSearchText(input.query))}%`;
2197
+ conditions.push(`(FOLD(subject) LIKE ? ESCAPE '\\' OR FOLD(from_json) LIKE ? ESCAPE '\\' OR FOLD(to_json) LIKE ? ESCAPE '\\' OR FOLD(cc_json) LIKE ? ESCAPE '\\' OR FOLD(bcc_json) LIKE ? ESCAPE '\\' OR FOLD(labels_json) LIKE ? ESCAPE '\\')`);
2200
2198
  params.push(needle, needle, needle, needle, needle, needle);
2201
2199
  }
2202
2200
  const sql = `SELECT * FROM messages${conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : ""}`;