@bobfrankston/rmfmail 1.2.49 → 1.2.65
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/android-bootstrap.bundle.js +63 -2
- package/client/android-bootstrap.bundle.js.map +4 -4
- package/client/app.bundle.js +417 -63
- package/client/app.bundle.js.map +3 -3
- package/client/app.js +182 -24
- package/client/app.js.map +1 -1
- package/client/app.ts +166 -21
- package/client/components/calendar-sidebar.js +5 -5
- package/client/components/calendar-sidebar.js.map +1 -1
- package/client/components/calendar-sidebar.ts +5 -5
- package/client/components/folder-tree.js +81 -0
- package/client/components/folder-tree.js.map +1 -1
- package/client/components/folder-tree.ts +70 -0
- package/client/components/message-list.js +282 -38
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +264 -36
- package/client/compose/compose.bundle.js +70 -14
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/compose/compose.css +28 -7
- package/client/compose/compose.js +64 -4
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +66 -4
- package/client/compose/editor.js +23 -7
- package/client/compose/editor.js.map +1 -1
- package/client/compose/editor.ts +23 -7
- package/client/help/search-help.js +4 -3
- package/client/help/search-help.js.map +1 -1
- package/client/help/search-help.ts +4 -3
- package/client/index.html +5 -1
- package/client/lib/api-client.js +4 -2
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +4 -2
- package/client/lib/mailxapi.js +2 -2
- package/client/lib/rmf-tiny.js +39 -6
- package/client/styles/components.css +6 -0
- package/docs/azure.md +58 -0
- package/docs/config-help.md +131 -0
- package/docs/edit-in-word-docx.md +98 -0
- package/docs/host-abstraction-plan.md +169 -0
- package/docs/local-first-plan.md +303 -0
- package/docs/npmglobalize-transitive-workspace-deps.md +107 -0
- package/docs/outlook.md +80 -18
- package/docs/outlook.txt +35 -0
- package/docs/rules-design.md +172 -0
- package/package.json +7 -7
- package/packages/mailx-imap/index.d.ts +48 -1
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +332 -70
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +323 -67
- package/packages/mailx-imap/microsoft-credentials.json +8 -0
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-imap/providers/outlook-api.d.ts +7 -0
- package/packages/mailx-imap/providers/outlook-api.d.ts.map +1 -0
- package/packages/mailx-imap/providers/outlook-api.js +7 -0
- package/packages/mailx-imap/providers/outlook-api.js.map +1 -0
- package/packages/mailx-service/index.d.ts +7 -1
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +8 -8
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +8 -7
- package/packages/mailx-service/jsonrpc.js +1 -1
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +1 -1
- package/packages/mailx-settings/docs/azure.md +58 -0
- package/packages/mailx-settings/docs/config-help.md +131 -0
- package/packages/mailx-settings/docs/edit-in-word-docx.md +98 -0
- package/packages/mailx-settings/docs/host-abstraction-plan.md +169 -0
- package/packages/mailx-settings/docs/local-first-plan.md +303 -0
- package/packages/mailx-settings/docs/npmglobalize-transitive-workspace-deps.md +107 -0
- package/packages/mailx-settings/docs/outlook.md +80 -18
- package/packages/mailx-settings/docs/rules-design.md +172 -0
- package/packages/mailx-settings/index.d.ts.map +1 -1
- package/packages/mailx-settings/index.js +9 -8
- package/packages/mailx-settings/index.js.map +1 -1
- package/packages/mailx-settings/index.ts +9 -8
- package/packages/mailx-settings/package.json +1 -1
- package/packages/mailx-store/db.d.ts +50 -1
- package/packages/mailx-store/db.d.ts.map +1 -1
- package/packages/mailx-store/db.js +120 -6
- package/packages/mailx-store/db.js.map +1 -1
- package/packages/mailx-store/db.ts +126 -6
- package/packages/mailx-store/package.json +1 -1
- package/packages/mailx-types/contact-rules.d.ts +1 -1
- package/packages/mailx-types/contact-rules.js +1 -1
- package/packages/mailx-types/contact-rules.js.map +1 -1
- package/packages/mailx-types/contact-rules.jsonc +1 -1
- package/packages/mailx-types/contact-rules.ts +1 -1
- package/packages/mailx-types/package.json +1 -1
|
@@ -67,6 +67,24 @@ function isJunkContact(email: string, name: string): boolean {
|
|
|
67
67
|
for (const re of _denyPatterns) { if (re.test(lower)) return true; }
|
|
68
68
|
return false;
|
|
69
69
|
}
|
|
70
|
+
/** Sanitize a harvested display name. A real display name carries none of
|
|
71
|
+
* `@ < > |` — their presence means an address parser mis-split a header
|
|
72
|
+
* (e.g. a List-Id / "domain | Name@addr" fragment) and the "name" now
|
|
73
|
+
* embeds structural mail punctuation or a whole email address. The worst
|
|
74
|
+
* case is an embedded address that DIFFERS from this contact's own email:
|
|
75
|
+
* autocomplete then shows a TRUSTED name while sending ELSEWHERE. Live
|
|
76
|
+
* example (Bob 2026-06-22): a `discovered` row name `bob.ma | Bob@bob.ma`
|
|
77
|
+
* was wired to `miyakot@kt.rim.or.jp`, so test mail Bob addressed to
|
|
78
|
+
* himself silently went to a stranger in Japan ("MIA"). Drop the name;
|
|
79
|
+
* keep the contact (the email is still useful) — autocomplete falls back
|
|
80
|
+
* to showing the bare address, which the user won't mistake for someone. */
|
|
81
|
+
export function cleanContactName(name: string): string {
|
|
82
|
+
const n = (name || "").trim().replace(/^"(.*)"$/, "$1").trim();
|
|
83
|
+
if (!n) return "";
|
|
84
|
+
if (/[@<>|]/.test(n)) return "";
|
|
85
|
+
return n;
|
|
86
|
+
}
|
|
87
|
+
|
|
70
88
|
/** User-configured patterns from contacts.jsonc `denylistPatterns`. Compiled
|
|
71
89
|
* once on settings load; invalid regexes are skipped with a warning. */
|
|
72
90
|
export function setContactsDenyPatterns(patterns: string[]): void {
|
|
@@ -792,14 +810,21 @@ export class MailxDB {
|
|
|
792
810
|
const last = this.getKv("contacts", "purge_v");
|
|
793
811
|
if (last === TARGET) return;
|
|
794
812
|
let dropped = 0;
|
|
813
|
+
let renamed = 0;
|
|
795
814
|
try {
|
|
796
815
|
const rows = this.db.prepare("SELECT id, email, name FROM contacts WHERE source IN ('discovered','sent','received')").all() as { id: number; email: string; name: string }[];
|
|
797
816
|
const del = this.db.prepare("DELETE FROM contacts WHERE id = ?");
|
|
817
|
+
const blank = this.db.prepare("UPDATE contacts SET name = '' WHERE id = ?");
|
|
798
818
|
for (const r of rows) {
|
|
799
|
-
if (isJunkContact(r.email || "", r.name || "")) { del.run(r.id); dropped++; }
|
|
819
|
+
if (isJunkContact(r.email || "", r.name || "")) { del.run(r.id); dropped++; continue; }
|
|
820
|
+
// Blank mis-parsed display names that embed `@ < > |` — these
|
|
821
|
+
// can impersonate one identity while sending to another (the
|
|
822
|
+
// "bob.ma | Bob@bob.ma" → miyakot@kt.rim.or.jp cross-wire,
|
|
823
|
+
// Bob 2026-06-22). Keep the contact; drop only the unsafe name.
|
|
824
|
+
if (r.name && cleanContactName(r.name) !== r.name) { blank.run(r.id); renamed++; }
|
|
800
825
|
}
|
|
801
826
|
this.setKv("contacts", "purge_v", TARGET);
|
|
802
|
-
if (dropped > 0) console.log(` [contacts] one-shot purge: dropped ${dropped} junk rows (${TARGET})`);
|
|
827
|
+
if (dropped > 0 || renamed > 0) console.log(` [contacts] one-shot purge: dropped ${dropped} junk rows, blanked ${renamed} unsafe names (${TARGET})`);
|
|
803
828
|
} catch (e: any) {
|
|
804
829
|
console.warn(`[contacts] one-shot purge failed: ${e.message}`);
|
|
805
830
|
}
|
|
@@ -1379,6 +1404,55 @@ export class MailxDB {
|
|
|
1379
1404
|
return Number(result.lastInsertRowid);
|
|
1380
1405
|
}
|
|
1381
1406
|
|
|
1407
|
+
/** Rename a folder row's path (+leaf name) and rewrite every descendant
|
|
1408
|
+
* folder whose path lives under the old prefix. A single IMAP RENAME on the
|
|
1409
|
+
* server renames child mailboxes too (RFC 3501), and Gmail/Outlook rename
|
|
1410
|
+
* cascades the same way, so the local mirror must move the whole subtree in
|
|
1411
|
+
* lockstep. All in one transaction so a crash can't leave half the subtree
|
|
1412
|
+
* pointing at the old prefix.
|
|
1413
|
+
*
|
|
1414
|
+
* `newName` is the new LEAF name written to the renamed row's `name`
|
|
1415
|
+
* column; descendant rows keep their own leaf names (only the prefix
|
|
1416
|
+
* changes). The (account_id, path) UNIQUE constraint means the caller must
|
|
1417
|
+
* have already verified newPath is free. */
|
|
1418
|
+
renameFolderPath(accountId: string, oldPath: string, newPath: string, newName: string, delimiter: string): void {
|
|
1419
|
+
const childPrefix = oldPath + delimiter;
|
|
1420
|
+
this.db.exec("BEGIN");
|
|
1421
|
+
try {
|
|
1422
|
+
// The folder itself — path + leaf name both change.
|
|
1423
|
+
this.db.prepare(
|
|
1424
|
+
"UPDATE folders SET path = ?, name = ? WHERE account_id = ? AND path = ?"
|
|
1425
|
+
).run(newPath, newName, accountId, oldPath);
|
|
1426
|
+
// Descendants — rewrite only the path prefix, keep their leaf names.
|
|
1427
|
+
// SUBSTR(path, len(childPrefix)+1) is the part after the old prefix.
|
|
1428
|
+
const descendants = this.db.prepare(
|
|
1429
|
+
"SELECT id, path FROM folders WHERE account_id = ? AND path LIKE ? ESCAPE '\\'"
|
|
1430
|
+
).all(accountId, childPrefix.replace(/[\\%_]/g, "\\$&") + "%") as { id: number; path: string }[];
|
|
1431
|
+
const upd = this.db.prepare("UPDATE folders SET path = ? WHERE id = ?");
|
|
1432
|
+
for (const d of descendants) {
|
|
1433
|
+
const rest = d.path.slice(oldPath.length); // includes the leading delimiter
|
|
1434
|
+
upd.run(newPath + rest, d.id);
|
|
1435
|
+
}
|
|
1436
|
+
this.db.exec("COMMIT");
|
|
1437
|
+
} catch (e) {
|
|
1438
|
+
this.db.exec("ROLLBACK");
|
|
1439
|
+
throw e;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
/** Update only a folder's display `name`, leaving `path` untouched. For
|
|
1444
|
+
* providers whose `path` is an opaque, rename-stable id (Outlook/Graph:
|
|
1445
|
+
* path = folder id), a rename/reparent changes the display name but NOT
|
|
1446
|
+
* the id — so synthesizing a new hierarchical path (as renameFolderPath
|
|
1447
|
+
* does) would corrupt the id-as-path invariant every later Graph op relies
|
|
1448
|
+
* on. This updates the name in place. No descendant rewrite: opaque-id
|
|
1449
|
+
* children don't carry the parent in their path. */
|
|
1450
|
+
renameFolderName(accountId: string, folderId: number, newName: string): void {
|
|
1451
|
+
this.db.prepare(
|
|
1452
|
+
"UPDATE folders SET name = ? WHERE account_id = ? AND id = ?"
|
|
1453
|
+
).run(newName, accountId, folderId);
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1382
1456
|
getFolders(accountId: string): Folder[] {
|
|
1383
1457
|
const rows = this.db.prepare(
|
|
1384
1458
|
"SELECT * FROM folders WHERE account_id = ? ORDER BY path"
|
|
@@ -2630,6 +2704,32 @@ export class MailxDB {
|
|
|
2630
2704
|
return rows.map(r => r.uid);
|
|
2631
2705
|
}
|
|
2632
2706
|
|
|
2707
|
+
/** UIDs we actually have a stored MESSAGE ROW for in this folder — from the
|
|
2708
|
+
* `messages` table, NOT `message_folders`. The two can diverge: an "orphan
|
|
2709
|
+
* membership" (a message_folders row whose messages row was never written /
|
|
2710
|
+
* got deleted) makes getUidsForFolder report a UID we don't truly have.
|
|
2711
|
+
* The set-diff backfill must diff against THIS so it re-fetches the real
|
|
2712
|
+
* message data for orphans instead of treating them as present (Bob
|
|
2713
|
+
* 2026-06-20: ~1k old INBOX messages stuck missing — membership said
|
|
2714
|
+
* "have it", the row didn't exist). */
|
|
2715
|
+
getStoredUids(accountId: string, folderId: number): number[] {
|
|
2716
|
+
const rows = this.db.prepare(
|
|
2717
|
+
"SELECT uid FROM messages WHERE account_id = ? AND folder_id = ?"
|
|
2718
|
+
).all(accountId, folderId) as any[];
|
|
2719
|
+
return rows.map(r => r.uid);
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
/** Count of stored MESSAGE ROWS in a folder (messages table). The deficit
|
|
2723
|
+
* gate compares this to the server's count to decide whether to reconcile;
|
|
2724
|
+
* using the messages table (not message_folders) means orphan memberships
|
|
2725
|
+
* don't mask a real "rows are missing" deficit. */
|
|
2726
|
+
getStoredCount(accountId: string, folderId: number): number {
|
|
2727
|
+
const r = this.db.prepare(
|
|
2728
|
+
"SELECT count(*) as cnt FROM messages WHERE account_id = ? AND folder_id = ?"
|
|
2729
|
+
).get(accountId, folderId) as any;
|
|
2730
|
+
return r?.cnt || 0;
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2633
2733
|
/** List recent rows in a folder for a Sent-sweep / reconciliation pass.
|
|
2634
2734
|
* Returns only the columns the sweeper needs (uid, message_id, date,
|
|
2635
2735
|
* subject, size, has_attachments, preview, body_path) for every row in
|
|
@@ -2797,6 +2897,7 @@ export class MailxDB {
|
|
|
2797
2897
|
const lower = email.toLowerCase();
|
|
2798
2898
|
if (this.isAddressDenylisted(lower)) return;
|
|
2799
2899
|
if (isJunkContact(lower, name)) return;
|
|
2900
|
+
name = cleanContactName(name);
|
|
2800
2901
|
const now = Date.now();
|
|
2801
2902
|
// discovered tier holds one row per email — bump if present, else
|
|
2802
2903
|
// insert. Doesn't touch preferred or google rows for the same email;
|
|
@@ -2911,6 +3012,7 @@ export class MailxDB {
|
|
|
2911
3012
|
if (!email || !VALID.test(email)) return;
|
|
2912
3013
|
if (isJunkContact(email, name)) return;
|
|
2913
3014
|
if (this.isAddressDenylisted(email)) return;
|
|
3015
|
+
name = cleanContactName(name);
|
|
2914
3016
|
const e = agg.get(email);
|
|
2915
3017
|
if (e) {
|
|
2916
3018
|
e.cnt++;
|
|
@@ -3451,6 +3553,7 @@ export class MailxDB {
|
|
|
3451
3553
|
for (const part of parts) {
|
|
3452
3554
|
const fromMatch = part.match(/^from:(.+)$/i);
|
|
3453
3555
|
const toMatch = part.match(/^to:(.+)$/i);
|
|
3556
|
+
const ccMatch = part.match(/^cc:(.+)$/i);
|
|
3454
3557
|
const subjectMatch = part.match(/^subject:(.+)$/i);
|
|
3455
3558
|
const dateMatch = part.match(/^date:([><]?=?)(.+)$/i);
|
|
3456
3559
|
const afterMatch = part.match(/^after:(.+)$/i);
|
|
@@ -3465,8 +3568,15 @@ export class MailxDB {
|
|
|
3465
3568
|
const term = ftsClean(fromMatch[1]);
|
|
3466
3569
|
if (term) frags.push({ s: `{from_name from_address}:${term}*`, op: false });
|
|
3467
3570
|
} else if (toMatch) {
|
|
3571
|
+
// `to:` deliberately spans To AND Cc — when someone searches
|
|
3572
|
+
// "to:bob" they mean "addressed to bob", and being Cc'd counts.
|
|
3573
|
+
// Use `cc:` for a Cc-only match. (Bcc isn't indexed: it's absent
|
|
3574
|
+
// on received mail by design, and only meaningful in Sent.)
|
|
3468
3575
|
const term = ftsClean(toMatch[1]);
|
|
3469
3576
|
if (term) frags.push({ s: `{to_text cc_text}:${term}*`, op: false });
|
|
3577
|
+
} else if (ccMatch) {
|
|
3578
|
+
const term = ftsClean(ccMatch[1]);
|
|
3579
|
+
if (term) frags.push({ s: `cc_text:${term}*`, op: false });
|
|
3470
3580
|
} else if (subjectMatch) {
|
|
3471
3581
|
const term = ftsClean(subjectMatch[1]);
|
|
3472
3582
|
if (term) frags.push({ s: `subject:${term}*`, op: false });
|
|
@@ -3538,6 +3648,9 @@ export class MailxDB {
|
|
|
3538
3648
|
ftsQuery += sep + frags[i].s;
|
|
3539
3649
|
}
|
|
3540
3650
|
ftsQuery = ftsQuery.trim();
|
|
3651
|
+
// (Header-scoped search is reachable via the `subject:`/`from:`/`to:`
|
|
3652
|
+
// qualifiers — no separate "headers only" mode needed, and body search
|
|
3653
|
+
// isn't slow enough to warrant one.)
|
|
3541
3654
|
// No real FTS term — either qualifier-only ("is:flagged after:1w") or
|
|
3542
3655
|
// every term sanitized away (user typed only punctuation). There is NO
|
|
3543
3656
|
// valid FTS5 "match everything" string — `MATCH '*'` is itself an FTS5
|
|
@@ -3633,13 +3746,20 @@ export class MailxDB {
|
|
|
3633
3746
|
}
|
|
3634
3747
|
}
|
|
3635
3748
|
|
|
3636
|
-
/** Rebuild FTS index from existing messages
|
|
3749
|
+
/** Rebuild FTS index from existing messages.
|
|
3750
|
+
* body_text is seeded from `preview` here (all db.ts can reach); the full
|
|
3751
|
+
* message body is backfilled separately by mailx-imap's backfillFtsBodies()
|
|
3752
|
+
* pass, which can read the cached .eml. */
|
|
3637
3753
|
rebuildSearchIndex(): number {
|
|
3638
|
-
// Drop and recreate
|
|
3754
|
+
// Drop and recreate. MUST be the STANDALONE schema (no content=messages):
|
|
3755
|
+
// the external-content form indexes to_text/cc_text/body_text columns
|
|
3756
|
+
// that don't exist on `messages`, so content-dereferencing silently
|
|
3757
|
+
// drops those terms — the exact breakage migrateFtsSchema() repairs.
|
|
3758
|
+
// Recreating it here used to quietly reintroduce that bug on every
|
|
3759
|
+
// -reindex (Bob 2026-06-05 "search missed to/cc/body").
|
|
3639
3760
|
try { this.db.exec("DROP TABLE IF EXISTS messages_fts"); } catch { /* ignore */ }
|
|
3640
3761
|
this.db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
|
|
3641
|
-
subject, from_name, from_address, to_text, cc_text, body_text
|
|
3642
|
-
content=messages, content_rowid=id
|
|
3762
|
+
subject, from_name, from_address, to_text, cc_text, body_text
|
|
3643
3763
|
)`);
|
|
3644
3764
|
|
|
3645
3765
|
// Use a single transaction + prepared statement for speed (~50x faster than individual inserts)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const CONTACT_RULES: {
|
|
2
|
-
readonly rulesVersion: "
|
|
2
|
+
readonly rulesVersion: "v4-name-hygiene";
|
|
3
3
|
readonly junk: {
|
|
4
4
|
readonly localExact: "^(no-?reply|do-?not-?reply|noreply|mailer-daemon|postmaster|abuse|automated|bounce(s|d)?|list-?(server|admin|owner|manager)?|notification|notifications?|admin@.*automated|root|daemon|nobody|undisclosed)$";
|
|
5
5
|
readonly localSuffix: "(-bounces|\\+bounces|-noreply|-no-reply|-notifications?|-mailer)$";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// AUTO-GENERATED from contact-rules.jsonc — do not edit.
|
|
2
2
|
// Edit the .jsonc and run `node build-rules.js` (or `npm run build`).
|
|
3
3
|
export const CONTACT_RULES = {
|
|
4
|
-
"rulesVersion": "
|
|
4
|
+
"rulesVersion": "v4-name-hygiene",
|
|
5
5
|
"junk": {
|
|
6
6
|
"localExact": "^(no-?reply|do-?not-?reply|noreply|mailer-daemon|postmaster|abuse|automated|bounce(s|d)?|list-?(server|admin|owner|manager)?|notification|notifications?|admin@.*automated|root|daemon|nobody|undisclosed)$",
|
|
7
7
|
"localSuffix": "(-bounces|\\+bounces|-noreply|-no-reply|-notifications?|-mailer)$",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-rules.js","sourceRoot":"","sources":["contact-rules.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,sEAAsE;AAEtE,MAAM,CAAC,MAAM,aAAa,GAAG;IACzB,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"contact-rules.js","sourceRoot":"","sources":["contact-rules.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,sEAAsE;AAEtE,MAAM,CAAC,MAAM,aAAa,GAAG;IACzB,cAAc,EAAE,iBAAiB;IACjC,MAAM,EAAE;QACJ,YAAY,EAAE,6MAA6M;QAC3N,aAAa,EAAE,mEAAmE;QAClF,aAAa,EAAE,4FAA4F;QAC3G,aAAa,EAAE,8CAA8C;QAC7D,QAAQ,EAAE,8EAA8E;KAC3F;CACK,CAAC"}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// (e.g. "16179691997.15082868100.nfblcbll1x")
|
|
18
18
|
// domain — entire domain match (Google Voice SMS, reply.fb.com)
|
|
19
19
|
{
|
|
20
|
-
"rulesVersion": "
|
|
20
|
+
"rulesVersion": "v4-name-hygiene",
|
|
21
21
|
"junk": {
|
|
22
22
|
"localExact": "^(no-?reply|do-?not-?reply|noreply|mailer-daemon|postmaster|abuse|automated|bounce(s|d)?|list-?(server|admin|owner|manager)?|notification|notifications?|admin@.*automated|root|daemon|nobody|undisclosed)$",
|
|
23
23
|
"localSuffix": "(-bounces|\\+bounces|-noreply|-no-reply|-notifications?|-mailer)$",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Edit the .jsonc and run `node build-rules.js` (or `npm run build`).
|
|
3
3
|
|
|
4
4
|
export const CONTACT_RULES = {
|
|
5
|
-
"rulesVersion": "
|
|
5
|
+
"rulesVersion": "v4-name-hygiene",
|
|
6
6
|
"junk": {
|
|
7
7
|
"localExact": "^(no-?reply|do-?not-?reply|noreply|mailer-daemon|postmaster|abuse|automated|bounce(s|d)?|list-?(server|admin|owner|manager)?|notification|notifications?|admin@.*automated|root|daemon|nobody|undisclosed)$",
|
|
8
8
|
"localSuffix": "(-bounces|\\+bounces|-noreply|-no-reply|-notifications?|-mailer)$",
|