@bobfrankston/mailx-store 0.1.47 → 0.1.48
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/db.d.ts +7 -1
- package/db.js +35 -1
- package/package.json +1 -1
package/db.d.ts
CHANGED
|
@@ -9,7 +9,13 @@ import type { MessageEnvelope, Folder, EmailAddress, PagedResult, MessageQuery }
|
|
|
9
9
|
export declare function setContactsDenyPatterns(patterns: string[]): void;
|
|
10
10
|
export declare class MailxDB {
|
|
11
11
|
private db;
|
|
12
|
-
|
|
12
|
+
/** True for the DB read-worker's connection. Read-only handles never run
|
|
13
|
+
* DDL / migrations / backfills (the main writer already did, before this
|
|
14
|
+
* handle opened) and reject any write at the SQLite level. */
|
|
15
|
+
readonly readOnly: boolean;
|
|
16
|
+
constructor(dbDir: string, opts?: {
|
|
17
|
+
readOnly?: boolean;
|
|
18
|
+
});
|
|
13
19
|
/** Fail loud + early if expected columns are missing. Cheap (PRAGMA only
|
|
14
20
|
* runs at startup). The user-facing message names the recovery command. */
|
|
15
21
|
private verifySchema;
|
package/db.js
CHANGED
|
@@ -403,10 +403,35 @@ const SCHEMA = `
|
|
|
403
403
|
`;
|
|
404
404
|
export class MailxDB {
|
|
405
405
|
db;
|
|
406
|
-
|
|
406
|
+
/** True for the DB read-worker's connection. Read-only handles never run
|
|
407
|
+
* DDL / migrations / backfills (the main writer already did, before this
|
|
408
|
+
* handle opened) and reject any write at the SQLite level. */
|
|
409
|
+
readOnly;
|
|
410
|
+
constructor(dbDir, opts = {}) {
|
|
407
411
|
fs.mkdirSync(dbDir, { recursive: true });
|
|
408
412
|
const dbPath = path.join(dbDir, "mailx.db");
|
|
413
|
+
this.readOnly = !!opts.readOnly;
|
|
409
414
|
this.db = new DatabaseSync(dbPath);
|
|
415
|
+
if (this.readOnly) {
|
|
416
|
+
// Read-worker connection (Phase 0 read isolation). The main-thread
|
|
417
|
+
// MailxDB has ALREADY created the schema and run every migration /
|
|
418
|
+
// backfill before this worker is spawned, so we must NOT touch any
|
|
419
|
+
// of that here — two threads racing writes on one WAL file means
|
|
420
|
+
// SQLITE_BUSY and corruption risk. Strategy:
|
|
421
|
+
// • Do NOT pass {readOnly:true} to DatabaseSync. An OS-read-only
|
|
422
|
+
// handle on a WAL database can't maintain the -shm wal-index
|
|
423
|
+
// and fails to read at all. We open read-WRITE at the OS level
|
|
424
|
+
// so WAL reads work, then forbid writes LOGICALLY below.
|
|
425
|
+
// • PRAGMA query_only = ON makes every statement on this handle
|
|
426
|
+
// read-only — INSERT/UPDATE/DELETE/DDL all throw. That is the
|
|
427
|
+
// guard that lets us reuse the full MailxDB read surface here
|
|
428
|
+
// without any chance of a stray write.
|
|
429
|
+
// WAL mode is a persisted property of the file (the writer set it),
|
|
430
|
+
// so readers see committed snapshots with no journal_mode call.
|
|
431
|
+
this.db.exec("PRAGMA foreign_keys = ON");
|
|
432
|
+
this.db.exec("PRAGMA query_only = ON");
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
410
435
|
this.db.exec("PRAGMA journal_mode = WAL");
|
|
411
436
|
this.db.exec("PRAGMA foreign_keys = ON");
|
|
412
437
|
this.db.exec(SCHEMA);
|
|
@@ -1180,6 +1205,7 @@ export class MailxDB {
|
|
|
1180
1205
|
accountId: r.account_id,
|
|
1181
1206
|
folderId: r.folder_id,
|
|
1182
1207
|
uid: r.uid,
|
|
1208
|
+
uuid: r.uuid || "", // stable identity for row data-uuid / selection restore
|
|
1183
1209
|
messageId: r.message_id || "",
|
|
1184
1210
|
inReplyTo: r.in_reply_to || "",
|
|
1185
1211
|
references: JSON.parse(r.refs || "[]"),
|
|
@@ -1873,6 +1899,13 @@ export class MailxDB {
|
|
|
1873
1899
|
// Bcc). The unified-inbox UI shows a small ⇆ badge on these
|
|
1874
1900
|
// rows so the user knows "this is a copy of the same message".
|
|
1875
1901
|
dupeCount: r.dupeCount | 0,
|
|
1902
|
+
// uuid is the stable local identity each message-list row carries as
|
|
1903
|
+
// data-uuid. Without it, restoreSelection() / rememberPosition() /
|
|
1904
|
+
// the set-diff scroll anchor all match on empty string and fail, so
|
|
1905
|
+
// the selected-row highlight vanishes on every reload (Bob
|
|
1906
|
+
// 2026-06-13: "no highlighting in the summary"). getMessages already
|
|
1907
|
+
// maps uuid; this unified-inbox map dropped it. SELECT m.* exposes it.
|
|
1908
|
+
uuid: r.uuid || "",
|
|
1876
1909
|
}));
|
|
1877
1910
|
return { items, total, page, pageSize };
|
|
1878
1911
|
}
|
|
@@ -3132,6 +3165,7 @@ export class MailxDB {
|
|
|
3132
3165
|
folderId: r.folder_id,
|
|
3133
3166
|
folderName: r.folder_name || "",
|
|
3134
3167
|
uid: r.uid,
|
|
3168
|
+
uuid: r.uuid || "", // stable identity for row data-uuid / selection restore
|
|
3135
3169
|
messageId: r.message_id || "",
|
|
3136
3170
|
inReplyTo: r.in_reply_to || "",
|
|
3137
3171
|
references: JSON.parse(r.refs || "[]"),
|