@bobfrankston/mailx-store 0.1.112 → 0.1.116
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 +23 -0
- package/db.js +47 -0
- package/package.json +5 -5
- package/store.js +3 -0
package/db.d.ts
CHANGED
|
@@ -654,6 +654,29 @@ export declare class MailxDB {
|
|
|
654
654
|
runInTxn<T>(fn: () => T): T;
|
|
655
655
|
/** Record an address used in sent mail */
|
|
656
656
|
recordSentAddress(name: string, email: string): void;
|
|
657
|
+
/** Apply one email entry of a Google People contact.
|
|
658
|
+
* (Claude Code 2026-09-06, at Bob's direction)
|
|
659
|
+
*
|
|
660
|
+
* The `google` row for this address is the one Google owns: its name and
|
|
661
|
+
* card follow Google on every sync, so a contact renamed in Google
|
|
662
|
+
* Contacts is renamed here. Discovered rows for the same address are left
|
|
663
|
+
* alone — they hold the name the correspondent's own mail carried, and
|
|
664
|
+
* searchContacts folds the two into one line with the google row on top.
|
|
665
|
+
*
|
|
666
|
+
* Replaces the pre-2026-09-06 sequence recordSentAddress + a bare
|
|
667
|
+
* `UPDATE ... WHERE email = ?` in the People sync, which had two defects,
|
|
668
|
+
* both visible on one contact in Bob's DB: (1) it never wrote `name`, so
|
|
669
|
+
* a google row kept whatever name it was first created with — Tyler's
|
|
670
|
+
* Gmail address stayed "T Nichols" after the contact became "tyler K
|
|
671
|
+
* Nichols" in Google, and typing "Tyler Nichols" in compose found only
|
|
672
|
+
* his protonmail row; (2) the WHERE-by-email update also tried to turn
|
|
673
|
+
* the discovered duplicate into a second google row with the same
|
|
674
|
+
* (source, email, name) and hit the UNIQUE constraint — 4,505
|
|
675
|
+
* "[contacts] field update failed" lines in one day's log, and none of
|
|
676
|
+
* those contacts ever received a card update either.
|
|
677
|
+
*
|
|
678
|
+
* Returns true when the google row was created. */
|
|
679
|
+
upsertGoogleContact(googleId: string, name: string, email: string, card: Partial<ContactFields>): boolean;
|
|
657
680
|
/** True if `email` (lowercased) appears in the active denylist. Cached
|
|
658
681
|
* in-memory; refreshed on contacts.jsonc reload via setContactsDenylist. */
|
|
659
682
|
private _denylist;
|
package/db.js
CHANGED
|
@@ -3201,6 +3201,53 @@ export class MailxDB {
|
|
|
3201
3201
|
}
|
|
3202
3202
|
this.notifyContactsChanged();
|
|
3203
3203
|
}
|
|
3204
|
+
/** Apply one email entry of a Google People contact.
|
|
3205
|
+
* (Claude Code 2026-09-06, at Bob's direction)
|
|
3206
|
+
*
|
|
3207
|
+
* The `google` row for this address is the one Google owns: its name and
|
|
3208
|
+
* card follow Google on every sync, so a contact renamed in Google
|
|
3209
|
+
* Contacts is renamed here. Discovered rows for the same address are left
|
|
3210
|
+
* alone — they hold the name the correspondent's own mail carried, and
|
|
3211
|
+
* searchContacts folds the two into one line with the google row on top.
|
|
3212
|
+
*
|
|
3213
|
+
* Replaces the pre-2026-09-06 sequence recordSentAddress + a bare
|
|
3214
|
+
* `UPDATE ... WHERE email = ?` in the People sync, which had two defects,
|
|
3215
|
+
* both visible on one contact in Bob's DB: (1) it never wrote `name`, so
|
|
3216
|
+
* a google row kept whatever name it was first created with — Tyler's
|
|
3217
|
+
* Gmail address stayed "T Nichols" after the contact became "tyler K
|
|
3218
|
+
* Nichols" in Google, and typing "Tyler Nichols" in compose found only
|
|
3219
|
+
* his protonmail row; (2) the WHERE-by-email update also tried to turn
|
|
3220
|
+
* the discovered duplicate into a second google row with the same
|
|
3221
|
+
* (source, email, name) and hit the UNIQUE constraint — 4,505
|
|
3222
|
+
* "[contacts] field update failed" lines in one day's log, and none of
|
|
3223
|
+
* those contacts ever received a card update either.
|
|
3224
|
+
*
|
|
3225
|
+
* Returns true when the google row was created. */
|
|
3226
|
+
upsertGoogleContact(googleId, name, email, card) {
|
|
3227
|
+
const lower = (email || "").toLowerCase();
|
|
3228
|
+
if (!lower || !/^[^\s<>@]+@[^\s<>@]+\.[^\s<>@]+$/.test(lower))
|
|
3229
|
+
return false;
|
|
3230
|
+
name = cleanContactName(name);
|
|
3231
|
+
const now = Date.now();
|
|
3232
|
+
const rows = this.db.prepare("SELECT id FROM contacts WHERE source = 'google' AND lower(email) = ? ORDER BY use_count DESC, id ASC").all(lower);
|
|
3233
|
+
// Older syncs could leave two google rows for one address (the
|
|
3234
|
+
// WHERE-by-email update converted discovered rows). Keep the
|
|
3235
|
+
// most-used one; the rest would collide with the rename below.
|
|
3236
|
+
for (const extra of rows.slice(1))
|
|
3237
|
+
this.db.prepare("DELETE FROM contacts WHERE id = ?").run(extra.id);
|
|
3238
|
+
const cardVals = CONTACT_FIELD_COLUMNS.map(c => card?.[c] ?? "");
|
|
3239
|
+
if (rows.length) {
|
|
3240
|
+
this.db.prepare(`UPDATE contacts SET name = CASE WHEN ? != '' THEN ? ELSE name END, google_id = ?,
|
|
3241
|
+
${CONTACT_FIELD_COLUMNS.map(c => `${c} = ?`).join(", ")}, updated_at = ?
|
|
3242
|
+
WHERE id = ?`).run(name, name, googleId, ...cardVals, now, rows[0].id);
|
|
3243
|
+
this.notifyContactsChanged();
|
|
3244
|
+
return false;
|
|
3245
|
+
}
|
|
3246
|
+
this.db.prepare(`INSERT INTO contacts (source, google_id, name, email, ${CONTACT_FIELD_COLUMNS.join(", ")}, last_used, use_count, updated_at)
|
|
3247
|
+
VALUES ('google', ?, ?, ?, ${CONTACT_FIELD_COLUMNS.map(() => "?").join(", ")}, 0, 0, ?)`).run(googleId, name, lower, ...cardVals, now);
|
|
3248
|
+
this.notifyContactsChanged();
|
|
3249
|
+
return true;
|
|
3250
|
+
}
|
|
3204
3251
|
/** True if `email` (lowercased) appears in the active denylist. Cached
|
|
3205
3252
|
* in-memory; refreshed on contacts.jsonc reload via setContactsDenylist. */
|
|
3206
3253
|
_denylist = new Set();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx-store",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.116",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@bobfrankston/mailx-types": "^0.1.
|
|
13
|
-
"@bobfrankston/mailx-settings": "^0.1.
|
|
12
|
+
"@bobfrankston/mailx-types": "^0.1.84",
|
|
13
|
+
"@bobfrankston/mailx-settings": "^0.1.78",
|
|
14
14
|
"@bobfrankston/mailx-bus": "^0.1.2",
|
|
15
15
|
"mailparser": "^3.7.2"
|
|
16
16
|
},
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
".transformedSnapshot": {
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@bobfrankston/mailx-types": "^0.1.
|
|
33
|
-
"@bobfrankston/mailx-settings": "^0.1.
|
|
32
|
+
"@bobfrankston/mailx-types": "^0.1.84",
|
|
33
|
+
"@bobfrankston/mailx-settings": "^0.1.78",
|
|
34
34
|
"@bobfrankston/mailx-bus": "^0.1.2",
|
|
35
35
|
"mailparser": "^3.7.2"
|
|
36
36
|
}
|
package/store.js
CHANGED
|
@@ -575,6 +575,9 @@ export class Store {
|
|
|
575
575
|
senderTrusted,
|
|
576
576
|
bodyHtml,
|
|
577
577
|
bodyText,
|
|
578
|
+
// Services the reader trusts to relay on others' behalf; counts
|
|
579
|
+
// only when the service provably handled the message (2026-09-08).
|
|
580
|
+
trustedIntermediaries: (allowList.trustedIntermediaries || []).map((d) => (d || "").toLowerCase()),
|
|
578
581
|
};
|
|
579
582
|
const trust = assessMessageTrust(trustInput);
|
|
580
583
|
// The server's score travels separately from the findings: a finding is
|