@bobfrankston/rmfmail 1.0.502 → 1.0.504
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/bin/mailx.js +9 -1
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +9 -1
- package/client/app.js +82 -32
- package/client/app.js.map +1 -1
- package/client/app.ts +88 -32
- package/client/components/message-viewer.js +51 -21
- package/client/components/message-viewer.js.map +1 -1
- package/client/components/message-viewer.ts +38 -19
- package/client/compose/compose.css +14 -0
- package/client/compose/compose.html +4 -3
- package/client/compose/compose.js +288 -36
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +270 -40
- package/client/compose/editor-help.js +107 -0
- package/client/compose/editor-help.js.map +1 -0
- package/client/compose/editor-help.ts +106 -0
- package/client/compose/editor.js +43 -0
- package/client/compose/editor.js.map +1 -1
- package/client/compose/editor.ts +22 -0
- package/client/index.html +1 -4
- package/docs/editor.md +92 -0
- package/package.json +10 -4
- package/packages/mailx-service/html-to-docx.d.ts +1 -0
- package/packages/mailx-service/index.d.ts +10 -2
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +143 -21
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +114 -21
- package/packages/mailx-service/package.json +5 -3
- package/packages/mailx-settings/docs/accounts.md +41 -0
- package/packages/mailx-settings/docs/allowlist.md +27 -0
- package/packages/mailx-settings/docs/clients.md +24 -0
- package/packages/mailx-settings/docs/config.md +32 -0
- package/packages/mailx-settings/docs/contact-rules.md +40 -0
- package/packages/mailx-settings/docs/contacts.md +48 -0
- package/packages/mailx-settings/docs/editor.md +92 -0
- package/packages/mailx-settings/docs/preferences.md +43 -0
- package/packages/mailx-settings/index.d.ts +2 -2
- package/packages/mailx-settings/index.d.ts.map +1 -1
- package/packages/mailx-settings/index.js +120 -23
- package/packages/mailx-settings/index.js.map +1 -1
- package/packages/mailx-settings/index.ts +98 -23
- package/packages/mailx-store/db.d.ts +8 -0
- package/packages/mailx-store/db.d.ts.map +1 -1
- package/packages/mailx-store/db.js +22 -0
- package/packages/mailx-store/db.js.map +1 -1
- package/packages/mailx-store/db.ts +22 -0
- package/packages/mailx-store/file-store.d.ts +31 -13
- package/packages/mailx-store/file-store.d.ts.map +1 -1
- package/packages/mailx-store/file-store.js +75 -31
- package/packages/mailx-store/file-store.js.map +1 -1
- package/packages/mailx-store/file-store.ts +72 -27
- package/packages/mailx-types/groups.d.ts +38 -0
- package/packages/mailx-types/groups.d.ts.map +1 -0
- package/packages/mailx-types/groups.js +99 -0
- package/packages/mailx-types/groups.js.map +1 -0
- package/packages/mailx-types/groups.ts +112 -0
- package/packages/mailx-types/index.d.ts +2 -0
- package/packages/mailx-types/index.d.ts.map +1 -1
- package/packages/mailx-types/index.js +5 -0
- package/packages/mailx-types/index.js.map +1 -1
- package/packages/mailx-types/index.ts +9 -0
|
@@ -21,46 +21,91 @@ export class FileMessageStore implements MessageStore {
|
|
|
21
21
|
fs.mkdirSync(basePath, { recursive: true });
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/** Fresh opaque path per call
|
|
25
|
-
|
|
24
|
+
/** Fresh opaque path per call, returned RELATIVE to `basePath`. The DB
|
|
25
|
+
* stores this relative form so the body store can be relocated (or
|
|
26
|
+
* the user folder renamed — e.g. `.mailx → .rmfmail`) without the
|
|
27
|
+
* body_path entries breaking. */
|
|
28
|
+
private newRelativePath(accountId: string): string {
|
|
26
29
|
const uuid = randomUUID().replace(/-/g, "");
|
|
27
30
|
const prefix = uuid.slice(0, 2);
|
|
28
|
-
return path.join(
|
|
31
|
+
return path.join(accountId, prefix, `${uuid}.eml`);
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
/**
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
/** Resolve a stored path. Accepts either a relative path (post-2026-05
|
|
35
|
+
* format) or an absolute path (legacy entries until next-launch
|
|
36
|
+
* migration rewrites them). Either way, refuse anything outside the
|
|
37
|
+
* store as a directory-traversal guard. */
|
|
38
|
+
private resolveStored(p: string): string {
|
|
39
|
+
if (!p) return "";
|
|
40
|
+
const abs = path.isAbsolute(p) ? p : path.resolve(this.basePath, p);
|
|
41
|
+
const rel = path.relative(path.resolve(this.basePath), abs);
|
|
42
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) return "";
|
|
43
|
+
return abs;
|
|
37
44
|
}
|
|
38
45
|
|
|
39
|
-
/** Write a new body.
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* NOT affect the filename. */
|
|
46
|
+
/** Write a new body. Returns a path RELATIVE to basePath; caller stores
|
|
47
|
+
* it in `body_path`. The (folderId, uid) args are kept for interface
|
|
48
|
+
* compatibility; they do NOT affect the filename. */
|
|
43
49
|
async putMessage(accountId: string, _folderId: number, _uid: number, raw: Buffer): Promise<string> {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
fs.
|
|
47
|
-
|
|
50
|
+
const rel = this.newRelativePath(accountId);
|
|
51
|
+
const abs = path.join(this.basePath, rel);
|
|
52
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
53
|
+
fs.writeFileSync(abs, raw);
|
|
54
|
+
return rel;
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
/** Read by
|
|
51
|
-
async readByPath(
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
/** Read by stored path (relative or absolute). */
|
|
58
|
+
async readByPath(stored: string): Promise<Buffer> {
|
|
59
|
+
const abs = this.resolveStored(stored);
|
|
60
|
+
if (!abs) throw new Error(`refusing to read outside store: ${stored}`);
|
|
61
|
+
return fs.readFileSync(abs);
|
|
54
62
|
}
|
|
55
63
|
|
|
56
|
-
async hasByPath(
|
|
57
|
-
|
|
58
|
-
return fs.existsSync(
|
|
64
|
+
async hasByPath(stored: string): Promise<boolean> {
|
|
65
|
+
const abs = this.resolveStored(stored);
|
|
66
|
+
return !!abs && fs.existsSync(abs);
|
|
59
67
|
}
|
|
60
68
|
|
|
61
|
-
async unlinkByPath(
|
|
62
|
-
|
|
63
|
-
if (
|
|
69
|
+
async unlinkByPath(stored: string): Promise<void> {
|
|
70
|
+
const abs = this.resolveStored(stored);
|
|
71
|
+
if (!abs) return;
|
|
72
|
+
if (fs.existsSync(abs)) fs.unlinkSync(abs);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** One-shot migration: rewrite absolute body_path values that point at
|
|
76
|
+
* this store's basePath into relative form. Called once from db.ts on
|
|
77
|
+
* startup (gated by a kv flag); silently skipped after first run.
|
|
78
|
+
* Returns the count of rewritten paths.
|
|
79
|
+
*
|
|
80
|
+
* Bodies whose absolute path is OUTSIDE basePath (the legacy
|
|
81
|
+
* `~/.mailx/mailxstore/...` entries on a machine where basePath is
|
|
82
|
+
* now `~/.rmfmail/mailxstore`) are left alone — the migration in
|
|
83
|
+
* mailx-settings already moves the files; this rewrite is purely
|
|
84
|
+
* about getting body_path to a relative shape. */
|
|
85
|
+
rewriteAbsoluteToRelative(rows: Array<{ id: number; body_path: string }>,
|
|
86
|
+
update: (id: number, newPath: string) => void): number {
|
|
87
|
+
let n = 0;
|
|
88
|
+
const baseAbs = path.resolve(this.basePath);
|
|
89
|
+
for (const r of rows) {
|
|
90
|
+
if (!r.body_path || !path.isAbsolute(r.body_path)) continue;
|
|
91
|
+
const rel = path.relative(baseAbs, path.resolve(r.body_path));
|
|
92
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
93
|
+
// Outside this store — try mapping `.mailx → .rmfmail` if the
|
|
94
|
+
// legacy dir was the source. Then re-test.
|
|
95
|
+
const remapped = r.body_path.replace(/[\\/]\.mailx[\\/]mailxstore[\\/]/i, `${path.sep}.rmfmail${path.sep}mailxstore${path.sep}`);
|
|
96
|
+
if (remapped !== r.body_path) {
|
|
97
|
+
const rel2 = path.relative(baseAbs, path.resolve(remapped));
|
|
98
|
+
if (!rel2.startsWith("..") && !path.isAbsolute(rel2)) {
|
|
99
|
+
update(r.id, rel2.replace(/\\/g, "/"));
|
|
100
|
+
n++;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
update(r.id, rel.replace(/\\/g, "/"));
|
|
106
|
+
n++;
|
|
107
|
+
}
|
|
108
|
+
return n;
|
|
64
109
|
}
|
|
65
110
|
|
|
66
111
|
// MessageStore interface compatibility (unused once all callers migrate to
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Group expansion — turns a recipient string with group names into a flat
|
|
3
|
+
* deduplicated address list. Shared between desktop (mailx-service) and
|
|
4
|
+
* Android (mailx-store-web) so send paths produce the same result.
|
|
5
|
+
*
|
|
6
|
+
* Group source: contacts.jsonc → groups: Record<string, string[]>. Each
|
|
7
|
+
* value is an array of either email addresses ("a@b" or "Name <a@b>") or
|
|
8
|
+
* names of other groups (recursive aliasing). Cycles are detected; depth
|
|
9
|
+
* is capped at 10 nested levels.
|
|
10
|
+
*/
|
|
11
|
+
/** Raw recipient string from a To/Cc/Bcc field; may contain group names. */
|
|
12
|
+
export type RecipientToken = string;
|
|
13
|
+
/** Map of group name → member list (each entry: address or another group name). */
|
|
14
|
+
export type GroupMap = Record<string, string[]>;
|
|
15
|
+
/** Result of expanding a recipient string. */
|
|
16
|
+
export interface ExpansionResult {
|
|
17
|
+
/** Flat unique address list (preserving order of first occurrence). */
|
|
18
|
+
addresses: string[];
|
|
19
|
+
/** Group names that couldn't be resolved (typo / cycle) — surface in UI. */
|
|
20
|
+
unresolved: string[];
|
|
21
|
+
}
|
|
22
|
+
/** Split a comma-or-semicolon recipient string into tokens. Respects
|
|
23
|
+
* angle-bracket address blocks ("Name, Suffix <a@b>") so a comma inside
|
|
24
|
+
* the display name doesn't split the entry. */
|
|
25
|
+
export declare function splitRecipients(raw: string): RecipientToken[];
|
|
26
|
+
/** Quick test: does the token look like an email address (with or without
|
|
27
|
+
* a display name)? Anything containing "@" with non-whitespace on both
|
|
28
|
+
* sides counts. */
|
|
29
|
+
export declare function isAddressToken(token: string): boolean;
|
|
30
|
+
/** Extract the bare email address from a token. Returns the original token
|
|
31
|
+
* lowercased if it has no angle-bracket form. */
|
|
32
|
+
export declare function extractAddress(token: string): string;
|
|
33
|
+
/** Expand a recipient string by resolving group names against `groups`.
|
|
34
|
+
* Group names take precedence: if a token matches a group name AND looks
|
|
35
|
+
* like an address, it's treated as a group. (In practice no one names a
|
|
36
|
+
* group "x@y.com", so this is rarely a real conflict.) */
|
|
37
|
+
export declare function expandRecipients(raw: string, groups: GroupMap): ExpansionResult;
|
|
38
|
+
//# sourceMappingURL=groups.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["groups.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,mFAAmF;AACnF,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAEhD,8CAA8C;AAC9C,MAAM,WAAW,eAAe;IAC5B,uEAAuE;IACvE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,4EAA4E;IAC5E,UAAU,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;gDAEgD;AAChD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CAmB7D;AAED;;oBAEoB;AACpB,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;kDACkD;AAClD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD;AAED;;;2DAG2D;AAC3D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,eAAe,CA2C/E"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Group expansion — turns a recipient string with group names into a flat
|
|
3
|
+
* deduplicated address list. Shared between desktop (mailx-service) and
|
|
4
|
+
* Android (mailx-store-web) so send paths produce the same result.
|
|
5
|
+
*
|
|
6
|
+
* Group source: contacts.jsonc → groups: Record<string, string[]>. Each
|
|
7
|
+
* value is an array of either email addresses ("a@b" or "Name <a@b>") or
|
|
8
|
+
* names of other groups (recursive aliasing). Cycles are detected; depth
|
|
9
|
+
* is capped at 10 nested levels.
|
|
10
|
+
*/
|
|
11
|
+
/** Split a comma-or-semicolon recipient string into tokens. Respects
|
|
12
|
+
* angle-bracket address blocks ("Name, Suffix <a@b>") so a comma inside
|
|
13
|
+
* the display name doesn't split the entry. */
|
|
14
|
+
export function splitRecipients(raw) {
|
|
15
|
+
const out = [];
|
|
16
|
+
let buf = "";
|
|
17
|
+
let depth = 0;
|
|
18
|
+
for (let i = 0; i < raw.length; i++) {
|
|
19
|
+
const c = raw[i];
|
|
20
|
+
if (c === "<")
|
|
21
|
+
depth++;
|
|
22
|
+
else if (c === ">")
|
|
23
|
+
depth = Math.max(0, depth - 1);
|
|
24
|
+
if ((c === "," || c === ";") && depth === 0) {
|
|
25
|
+
const t = buf.trim();
|
|
26
|
+
if (t)
|
|
27
|
+
out.push(t);
|
|
28
|
+
buf = "";
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
buf += c;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const t = buf.trim();
|
|
35
|
+
if (t)
|
|
36
|
+
out.push(t);
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
/** Quick test: does the token look like an email address (with or without
|
|
40
|
+
* a display name)? Anything containing "@" with non-whitespace on both
|
|
41
|
+
* sides counts. */
|
|
42
|
+
export function isAddressToken(token) {
|
|
43
|
+
return /^[^@\s][^@]*@[^@\s]+(\s|$)|<[^>]+@[^>]+>/.test(token) || /^[^\s<>@]+@[^\s<>@]+$/.test(token);
|
|
44
|
+
}
|
|
45
|
+
/** Extract the bare email address from a token. Returns the original token
|
|
46
|
+
* lowercased if it has no angle-bracket form. */
|
|
47
|
+
export function extractAddress(token) {
|
|
48
|
+
const m = token.match(/<([^>]+)>/);
|
|
49
|
+
if (m)
|
|
50
|
+
return m[1].trim().toLowerCase();
|
|
51
|
+
return token.trim().toLowerCase();
|
|
52
|
+
}
|
|
53
|
+
/** Expand a recipient string by resolving group names against `groups`.
|
|
54
|
+
* Group names take precedence: if a token matches a group name AND looks
|
|
55
|
+
* like an address, it's treated as a group. (In practice no one names a
|
|
56
|
+
* group "x@y.com", so this is rarely a real conflict.) */
|
|
57
|
+
export function expandRecipients(raw, groups) {
|
|
58
|
+
const tokens = splitRecipients(raw);
|
|
59
|
+
const seen = new Set();
|
|
60
|
+
const addresses = [];
|
|
61
|
+
const unresolved = [];
|
|
62
|
+
const visit = (token, depth, visited) => {
|
|
63
|
+
if (depth > 10) {
|
|
64
|
+
unresolved.push(`${token} (depth limit)`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// Group name match — case-insensitive
|
|
68
|
+
const groupKey = Object.keys(groups).find(k => k.toLowerCase() === token.trim().toLowerCase());
|
|
69
|
+
if (groupKey) {
|
|
70
|
+
if (visited.has(groupKey.toLowerCase())) {
|
|
71
|
+
unresolved.push(`${groupKey} (cycle)`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const next = new Set(visited);
|
|
75
|
+
next.add(groupKey.toLowerCase());
|
|
76
|
+
for (const member of groups[groupKey] || []) {
|
|
77
|
+
visit(member, depth + 1, next);
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Address — keep the original (with display name if present), but
|
|
82
|
+
// dedupe by lowercased bare address.
|
|
83
|
+
if (isAddressToken(token)) {
|
|
84
|
+
const key = extractAddress(token);
|
|
85
|
+
if (!seen.has(key)) {
|
|
86
|
+
seen.add(key);
|
|
87
|
+
addresses.push(token.trim());
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Neither group nor address — flag.
|
|
92
|
+
unresolved.push(token);
|
|
93
|
+
};
|
|
94
|
+
for (const t of tokens) {
|
|
95
|
+
visit(t, 0, new Set());
|
|
96
|
+
}
|
|
97
|
+
return { addresses, unresolved };
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=groups.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groups.js","sourceRoot":"","sources":["groups.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Group expansion — turns a recipient string with group names into a flat
|
|
3
|
+
* deduplicated address list. Shared between desktop (mailx-service) and
|
|
4
|
+
* Android (mailx-store-web) so send paths produce the same result.
|
|
5
|
+
*
|
|
6
|
+
* Group source: contacts.jsonc → groups: Record<string, string[]>. Each
|
|
7
|
+
* value is an array of either email addresses ("a@b" or "Name <a@b>") or
|
|
8
|
+
* names of other groups (recursive aliasing). Cycles are detected; depth
|
|
9
|
+
* is capped at 10 nested levels.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Raw recipient string from a To/Cc/Bcc field; may contain group names. */
|
|
13
|
+
export type RecipientToken = string;
|
|
14
|
+
|
|
15
|
+
/** Map of group name → member list (each entry: address or another group name). */
|
|
16
|
+
export type GroupMap = Record<string, string[]>;
|
|
17
|
+
|
|
18
|
+
/** Result of expanding a recipient string. */
|
|
19
|
+
export interface ExpansionResult {
|
|
20
|
+
/** Flat unique address list (preserving order of first occurrence). */
|
|
21
|
+
addresses: string[];
|
|
22
|
+
/** Group names that couldn't be resolved (typo / cycle) — surface in UI. */
|
|
23
|
+
unresolved: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Split a comma-or-semicolon recipient string into tokens. Respects
|
|
27
|
+
* angle-bracket address blocks ("Name, Suffix <a@b>") so a comma inside
|
|
28
|
+
* the display name doesn't split the entry. */
|
|
29
|
+
export function splitRecipients(raw: string): RecipientToken[] {
|
|
30
|
+
const out: string[] = [];
|
|
31
|
+
let buf = "";
|
|
32
|
+
let depth = 0;
|
|
33
|
+
for (let i = 0; i < raw.length; i++) {
|
|
34
|
+
const c = raw[i];
|
|
35
|
+
if (c === "<") depth++;
|
|
36
|
+
else if (c === ">") depth = Math.max(0, depth - 1);
|
|
37
|
+
if ((c === "," || c === ";") && depth === 0) {
|
|
38
|
+
const t = buf.trim();
|
|
39
|
+
if (t) out.push(t);
|
|
40
|
+
buf = "";
|
|
41
|
+
} else {
|
|
42
|
+
buf += c;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const t = buf.trim();
|
|
46
|
+
if (t) out.push(t);
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Quick test: does the token look like an email address (with or without
|
|
51
|
+
* a display name)? Anything containing "@" with non-whitespace on both
|
|
52
|
+
* sides counts. */
|
|
53
|
+
export function isAddressToken(token: string): boolean {
|
|
54
|
+
return /^[^@\s][^@]*@[^@\s]+(\s|$)|<[^>]+@[^>]+>/.test(token) || /^[^\s<>@]+@[^\s<>@]+$/.test(token);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Extract the bare email address from a token. Returns the original token
|
|
58
|
+
* lowercased if it has no angle-bracket form. */
|
|
59
|
+
export function extractAddress(token: string): string {
|
|
60
|
+
const m = token.match(/<([^>]+)>/);
|
|
61
|
+
if (m) return m[1].trim().toLowerCase();
|
|
62
|
+
return token.trim().toLowerCase();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Expand a recipient string by resolving group names against `groups`.
|
|
66
|
+
* Group names take precedence: if a token matches a group name AND looks
|
|
67
|
+
* like an address, it's treated as a group. (In practice no one names a
|
|
68
|
+
* group "x@y.com", so this is rarely a real conflict.) */
|
|
69
|
+
export function expandRecipients(raw: string, groups: GroupMap): ExpansionResult {
|
|
70
|
+
const tokens = splitRecipients(raw);
|
|
71
|
+
const seen = new Set<string>();
|
|
72
|
+
const addresses: string[] = [];
|
|
73
|
+
const unresolved: string[] = [];
|
|
74
|
+
|
|
75
|
+
const visit = (token: string, depth: number, visited: Set<string>): void => {
|
|
76
|
+
if (depth > 10) {
|
|
77
|
+
unresolved.push(`${token} (depth limit)`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Group name match — case-insensitive
|
|
81
|
+
const groupKey = Object.keys(groups).find(k => k.toLowerCase() === token.trim().toLowerCase());
|
|
82
|
+
if (groupKey) {
|
|
83
|
+
if (visited.has(groupKey.toLowerCase())) {
|
|
84
|
+
unresolved.push(`${groupKey} (cycle)`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const next = new Set(visited);
|
|
88
|
+
next.add(groupKey.toLowerCase());
|
|
89
|
+
for (const member of groups[groupKey] || []) {
|
|
90
|
+
visit(member, depth + 1, next);
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// Address — keep the original (with display name if present), but
|
|
95
|
+
// dedupe by lowercased bare address.
|
|
96
|
+
if (isAddressToken(token)) {
|
|
97
|
+
const key = extractAddress(token);
|
|
98
|
+
if (!seen.has(key)) {
|
|
99
|
+
seen.add(key);
|
|
100
|
+
addresses.push(token.trim());
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Neither group nor address — flag.
|
|
105
|
+
unresolved.push(token);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
for (const t of tokens) {
|
|
109
|
+
visit(t, 0, new Set());
|
|
110
|
+
}
|
|
111
|
+
return { addresses, unresolved };
|
|
112
|
+
}
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* This is the contract between client and server.
|
|
5
5
|
*/
|
|
6
6
|
export { CONTACT_RULES } from "./contact-rules.js";
|
|
7
|
+
export { expandRecipients, splitRecipients, isAddressToken, extractAddress, } from "./groups.js";
|
|
8
|
+
export type { GroupMap, RecipientToken, ExpansionResult } from "./groups.js";
|
|
7
9
|
/** Supported authentication methods */
|
|
8
10
|
export type AuthMethod = "password" | "oauth2";
|
|
9
11
|
/** Mail account configuration */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,11 @@
|
|
|
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
|
+
// Group-name expansion for recipient fields. Lets users type a group name
|
|
11
|
+
// (e.g. "family") in To/Cc/Bcc and have it expand to the address list at
|
|
12
|
+
// send time. Both desktop and Android send paths consume this expander
|
|
13
|
+
// against contacts.jsonc → groups.
|
|
14
|
+
export { expandRecipients, splitRecipients, isAddressToken, extractAddress, } from "./groups.js";
|
|
10
15
|
// ── Shared Utilities ──
|
|
11
16
|
// Pure functions used by both desktop (mailx-service) and Android (web-service).
|
|
12
17
|
// Kept here to avoid duplication — both platforms import from mailx-types.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wEAAwE;AACxE,uEAAuE;AACvE,4CAA4C;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
// junk-contact filtering on every platform.
|
|
10
10
|
export { CONTACT_RULES } from "./contact-rules.js";
|
|
11
11
|
|
|
12
|
+
// Group-name expansion for recipient fields. Lets users type a group name
|
|
13
|
+
// (e.g. "family") in To/Cc/Bcc and have it expand to the address list at
|
|
14
|
+
// send time. Both desktop and Android send paths consume this expander
|
|
15
|
+
// against contacts.jsonc → groups.
|
|
16
|
+
export {
|
|
17
|
+
expandRecipients, splitRecipients, isAddressToken, extractAddress,
|
|
18
|
+
} from "./groups.js";
|
|
19
|
+
export type { GroupMap, RecipientToken, ExpansionResult } from "./groups.js";
|
|
20
|
+
|
|
12
21
|
// ── Account Configuration ──
|
|
13
22
|
|
|
14
23
|
/** Supported authentication methods */
|