@bobfrankston/mailx-types 0.1.14 → 0.1.16

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.
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared `contacts.jsonc` mutations — denylist + preferred.
3
+ *
4
+ * Pure logic with cloud I/O injected, so the desktop service
5
+ * (`mailx-service`, Node) and the Android service (`mailx-store-web`, in the
6
+ * WebView) run ONE implementation instead of each keeping its own copy.
7
+ * `web-service.ts` used to stub `addToDenylist` / `addPreferredContact` as
8
+ * `notImpl()`, which is why the phone's ⊘ / ★ buttons did nothing.
9
+ *
10
+ * Nothing here is platform-specific: it's a `cloudRead` → mutate → `cloudWrite`
11
+ * round-trip. Each caller passes its own platform's cloud functions.
12
+ */
13
+ export type CloudReadFn = (filename: string) => Promise<string | null>;
14
+ export type CloudWriteFn = (filename: string, content: string) => Promise<unknown>;
15
+ export interface PreferredContactEntry {
16
+ name: string;
17
+ email: string;
18
+ source?: string;
19
+ organization?: string;
20
+ }
21
+ /** Append an email to `contacts.jsonc#denylist[]` — idempotent and
22
+ * case-insensitive. Writes only when the entry is genuinely new. */
23
+ export declare function addContactsDenylistEntry(email: string, cloudRead: CloudReadFn, cloudWrite: CloudWriteFn): Promise<void>;
24
+ /** Append a preferred contact to `contacts.jsonc#preferred[]` — dedup by
25
+ * source|email|name. Writes only when the entry is genuinely new. */
26
+ export declare function addContactsPreferredEntry(entry: PreferredContactEntry, cloudRead: CloudReadFn, cloudWrite: CloudWriteFn): Promise<void>;
27
+ //# sourceMappingURL=contacts-config.d.ts.map
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Shared `contacts.jsonc` mutations — denylist + preferred.
3
+ *
4
+ * Pure logic with cloud I/O injected, so the desktop service
5
+ * (`mailx-service`, Node) and the Android service (`mailx-store-web`, in the
6
+ * WebView) run ONE implementation instead of each keeping its own copy.
7
+ * `web-service.ts` used to stub `addToDenylist` / `addPreferredContact` as
8
+ * `notImpl()`, which is why the phone's ⊘ / ★ buttons did nothing.
9
+ *
10
+ * Nothing here is platform-specific: it's a `cloudRead` → mutate → `cloudWrite`
11
+ * round-trip. Each caller passes its own platform's cloud functions.
12
+ */
13
+ /** Loose-parse a JSONC blob. The file mailx writes is plain JSON
14
+ * (`JSON.stringify`), but a hand-edit may add `//` comments or trailing
15
+ * commas — strip those and retry rather than losing the whole file. */
16
+ function parseContactsConfig(raw) {
17
+ if (!raw)
18
+ return {};
19
+ try {
20
+ return JSON.parse(raw) || {};
21
+ }
22
+ catch { /* fall through to loose */ }
23
+ try {
24
+ const stripped = raw
25
+ .replace(/^\s*\/\/.*$/gm, "") // line comments
26
+ .replace(/,(\s*[}\]])/g, "$1"); // trailing commas
27
+ return JSON.parse(stripped) || {};
28
+ }
29
+ catch {
30
+ return {};
31
+ }
32
+ }
33
+ /** Append an email to `contacts.jsonc#denylist[]` — idempotent and
34
+ * case-insensitive. Writes only when the entry is genuinely new. */
35
+ export async function addContactsDenylistEntry(email, cloudRead, cloudWrite) {
36
+ const lower = (email || "").trim().toLowerCase();
37
+ if (!lower)
38
+ return;
39
+ const cfg = parseContactsConfig(await cloudRead("contacts.jsonc"));
40
+ if (!Array.isArray(cfg.denylist))
41
+ cfg.denylist = [];
42
+ if (cfg.denylist.some((e) => (e || "").toLowerCase() === lower))
43
+ return;
44
+ cfg.denylist.push(email);
45
+ await cloudWrite("contacts.jsonc", JSON.stringify(cfg, null, 2));
46
+ }
47
+ /** Append a preferred contact to `contacts.jsonc#preferred[]` — dedup by
48
+ * source|email|name. Writes only when the entry is genuinely new. */
49
+ export async function addContactsPreferredEntry(entry, cloudRead, cloudWrite) {
50
+ if (!entry?.email)
51
+ return;
52
+ const cfg = parseContactsConfig(await cloudRead("contacts.jsonc"));
53
+ if (!Array.isArray(cfg.preferred))
54
+ cfg.preferred = [];
55
+ const key = (s, e, n) => `${(s || "preferred").toLowerCase()}|${(e || "").toLowerCase()}|${(n || "").toLowerCase()}`;
56
+ const dupKey = key(entry.source || "", entry.email, entry.name || "");
57
+ if (cfg.preferred.some((e) => key(e?.source || "", e?.email || "", e?.name || "") === dupKey))
58
+ return;
59
+ const row = { name: entry.name || "", email: entry.email };
60
+ if (entry.source)
61
+ row.source = entry.source;
62
+ if (entry.organization)
63
+ row.organization = entry.organization;
64
+ cfg.preferred.push(row);
65
+ await cloudWrite("contacts.jsonc", JSON.stringify(cfg, null, 2));
66
+ }
67
+ //# sourceMappingURL=contacts-config.js.map
package/index.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export { CONTACT_RULES } from "./contact-rules.js";
7
7
  export type { MailxApi } from "./mailx-api.js";
8
+ export { addContactsDenylistEntry, addContactsPreferredEntry, type PreferredContactEntry, type CloudReadFn, type CloudWriteFn, } from "./contacts-config.js";
8
9
  export { expandRecipients, splitRecipients, isAddressToken, extractAddress, } from "./groups.js";
9
10
  export type { GroupMap, RecipientToken, ExpansionResult } from "./groups.js";
10
11
  /** Supported authentication methods */
package/index.js CHANGED
@@ -7,6 +7,8 @@
7
7
  // this barrel so a single source-of-truth (contact-rules.jsonc) drives
8
8
  // junk-contact filtering on every platform.
9
9
  export { CONTACT_RULES } from "./contact-rules.js";
10
+ // Shared contacts.jsonc mutations — one implementation for desktop + Android.
11
+ export { addContactsDenylistEntry, addContactsPreferredEntry, } from "./contacts-config.js";
10
12
  // Group-name expansion for recipient fields. Lets users type a group name
11
13
  // (e.g. "family") in To/Cc/Bcc and have it expand to the address list at
12
14
  // send time. Both desktop and Android send paths consume this expander
package/mailx-api.d.ts CHANGED
@@ -158,6 +158,7 @@ export interface MailxApi {
158
158
  }>;
159
159
  getUserDict(): Promise<string[]>;
160
160
  addUserDictWord(word: string): Promise<string[]>;
161
+ addUserDictWords(words: string[]): Promise<string[]>;
161
162
  removeUserDictWord(word: string): Promise<string[]>;
162
163
  getSettings(): any | Promise<any>;
163
164
  saveSettingsData(settings: any): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-types",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",