@bobfrankston/mailx-types 0.1.49 → 0.1.55

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.
Files changed (3) hide show
  1. package/index.d.ts +59 -1
  2. package/index.js +72 -0
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -253,10 +253,17 @@ export interface MailxSettings {
253
253
  accounts: AccountConfig[];
254
254
  ui: {
255
255
  theme: "system" | "dark" | "light";
256
- editor: "quill" | "tiptap";
256
+ editor: "quill" | "tiptap" | "tinymce";
257
257
  folderWidth: number;
258
258
  listViewerSplit: number; /** Percentage for message list height */
259
+ /** Root font-size in px — scales the whole app chrome (rem-based CSS). */
259
260
  fontSize: number;
261
+ /** Editor body font-size in px. DISPLAY ONLY — it reaches the editors as
262
+ * a content stylesheet / CSS var and is never serialized into outgoing
263
+ * mail, so changing it cannot affect recipients. Distinct from
264
+ * `fontSize` above, which is the app chrome. Was read by app.ts and
265
+ * compose.ts but never declared here (Bob 2026-08-19). */
266
+ composeFontSize: number;
260
267
  };
261
268
  sync: {
262
269
  intervalMinutes: number;
@@ -439,4 +446,55 @@ export declare function parseSearchQuery(query: string): {
439
446
  conditions: string[];
440
447
  params: (string | number)[];
441
448
  };
449
+ /**
450
+ * Is this text a JS "missing value" that leaked into a message body?
451
+ *
452
+ * Marketing mail whose `text/plain` alternative is generated in JavaScript
453
+ * sometimes stringifies an absent value straight into the part, so the entire
454
+ * plain-text body is the word `undefined`. Adobe Firefly / Creative Cloud and
455
+ * Xfinity do this — 40 messages in Bob's bobma store on 2026-08-19, every one
456
+ * with a perfectly good HTML part sitting next to the junk text part.
457
+ *
458
+ * Used in two places that must agree:
459
+ * - `extractPreview` (mailx-imap) — skip the text part and summarize the
460
+ * HTML instead, so newly-synced mail never indexes the junk.
461
+ * - the message-list row renderer — suppress the snippet for rows that were
462
+ * already indexed with it, so old rows heal without a re-sync.
463
+ *
464
+ * Deliberately narrow: only values that mean "a value was missing". `false`,
465
+ * `0` and `no` are excluded — those are things a human might actually write.
466
+ */
467
+ export declare function isJunkPreviewText(s: string | null | undefined): boolean;
468
+ /**
469
+ * Does a display name assert an email address other than the real one?
470
+ *
471
+ * Display-name spoofing is the cheapest phish there is: put
472
+ * `security@paypal.com` in the From *name* and point the real address at
473
+ * anywhere. The message list shows only the display name, so nothing on
474
+ * screen contradicts the claim.
475
+ *
476
+ * Returns the claimed address when the name contains an email-like token that
477
+ * differs from `address`, otherwise null. A name that merely repeats the real
478
+ * address is not spoofing — see formatSender, which collapses that case.
479
+ */
480
+ export declare function displayNameClaimsOtherAddress(name: string, address: string): string | null;
481
+ /**
482
+ * How a sender should read in the UI.
483
+ *
484
+ * `text` is what to show. `claimed` is set when the display name asserts a
485
+ * different address than the envelope's — callers must make the real address
486
+ * visible in that case rather than trusting `text` alone.
487
+ *
488
+ * Collapses the redundant `"x@y.com" <x@y.com>` form that Microsoft 365
489
+ * quarantine notices and plenty of bulk senders emit — showing the same
490
+ * address twice reads as if two different parties were involved
491
+ * (Bob 2026-08-19).
492
+ */
493
+ export declare function formatSender(addr: {
494
+ name?: string;
495
+ address: string;
496
+ } | null | undefined): {
497
+ text: string;
498
+ claimed: string | null;
499
+ };
442
500
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -434,4 +434,76 @@ export function parseSearchQuery(query) {
434
434
  }
435
435
  return { conditions, params };
436
436
  }
437
+ /**
438
+ * Is this text a JS "missing value" that leaked into a message body?
439
+ *
440
+ * Marketing mail whose `text/plain` alternative is generated in JavaScript
441
+ * sometimes stringifies an absent value straight into the part, so the entire
442
+ * plain-text body is the word `undefined`. Adobe Firefly / Creative Cloud and
443
+ * Xfinity do this — 40 messages in Bob's bobma store on 2026-08-19, every one
444
+ * with a perfectly good HTML part sitting next to the junk text part.
445
+ *
446
+ * Used in two places that must agree:
447
+ * - `extractPreview` (mailx-imap) — skip the text part and summarize the
448
+ * HTML instead, so newly-synced mail never indexes the junk.
449
+ * - the message-list row renderer — suppress the snippet for rows that were
450
+ * already indexed with it, so old rows heal without a re-sync.
451
+ *
452
+ * Deliberately narrow: only values that mean "a value was missing". `false`,
453
+ * `0` and `no` are excluded — those are things a human might actually write.
454
+ */
455
+ export function isJunkPreviewText(s) {
456
+ const t = (s || "").replace(/\s+/g, " ").trim().toLowerCase();
457
+ if (!t)
458
+ return true;
459
+ return t === "undefined" || t === "null" || t === "nan" || t === "[object object]";
460
+ }
461
+ /**
462
+ * Does a display name assert an email address other than the real one?
463
+ *
464
+ * Display-name spoofing is the cheapest phish there is: put
465
+ * `security@paypal.com` in the From *name* and point the real address at
466
+ * anywhere. The message list shows only the display name, so nothing on
467
+ * screen contradicts the claim.
468
+ *
469
+ * Returns the claimed address when the name contains an email-like token that
470
+ * differs from `address`, otherwise null. A name that merely repeats the real
471
+ * address is not spoofing — see formatSender, which collapses that case.
472
+ */
473
+ export function displayNameClaimsOtherAddress(name, address) {
474
+ const n = (name || "").trim();
475
+ if (!n)
476
+ return null;
477
+ const real = (address || "").trim().toLowerCase();
478
+ // Deliberately loose: anything shaped like local@domain.tld inside the name.
479
+ const m = n.match(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g);
480
+ if (!m)
481
+ return null;
482
+ for (const claimed of m) {
483
+ if (claimed.toLowerCase() !== real)
484
+ return claimed;
485
+ }
486
+ return null;
487
+ }
488
+ /**
489
+ * How a sender should read in the UI.
490
+ *
491
+ * `text` is what to show. `claimed` is set when the display name asserts a
492
+ * different address than the envelope's — callers must make the real address
493
+ * visible in that case rather than trusting `text` alone.
494
+ *
495
+ * Collapses the redundant `"x@y.com" <x@y.com>` form that Microsoft 365
496
+ * quarantine notices and plenty of bulk senders emit — showing the same
497
+ * address twice reads as if two different parties were involved
498
+ * (Bob 2026-08-19).
499
+ */
500
+ export function formatSender(addr) {
501
+ const address = (addr?.address || "").trim();
502
+ const name = (addr?.name || "").trim();
503
+ if (!name)
504
+ return { text: address, claimed: null };
505
+ if (name.toLowerCase() === address.toLowerCase())
506
+ return { text: address, claimed: null };
507
+ return { text: `${name} <${address}>`, claimed: displayNameClaimsOtherAddress(name, address) };
508
+ }
437
509
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-types",
3
- "version": "0.1.49",
3
+ "version": "0.1.55",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",