@bobfrankston/mailx-types 0.1.14 → 0.1.15
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/contacts-config.d.ts +27 -0
- package/contacts-config.js +67 -0
- package/index.d.ts +1 -0
- package/index.js +2 -0
- package/package.json +1 -1
|
@@ -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
|