@bobfrankston/mailx-types 0.1.11 → 0.1.13
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/index.d.ts +25 -0
- package/index.js +44 -0
- package/mailx-api.d.ts +212 -0
- package/mailx-api.js +41 -0
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* This is the contract between client and server.
|
|
5
5
|
*/
|
|
6
6
|
export { CONTACT_RULES } from "./contact-rules.js";
|
|
7
|
+
export type { MailxApi } from "./mailx-api.js";
|
|
7
8
|
export { expandRecipients, splitRecipients, isAddressToken, extractAddress, } from "./groups.js";
|
|
8
9
|
export type { GroupMap, RecipientToken, ExpansionResult } from "./groups.js";
|
|
9
10
|
/** Supported authentication methods */
|
|
@@ -64,6 +65,30 @@ export interface Folder {
|
|
|
64
65
|
unreadCount: number;
|
|
65
66
|
children: Folder[]; /** Nested subfolders */
|
|
66
67
|
}
|
|
68
|
+
interface FlagBearing {
|
|
69
|
+
flags: string[];
|
|
70
|
+
}
|
|
71
|
+
export declare const seenOf: (m: {
|
|
72
|
+
flags?: readonly string[];
|
|
73
|
+
}) => boolean;
|
|
74
|
+
export declare const flaggedOf: (m: {
|
|
75
|
+
flags?: readonly string[];
|
|
76
|
+
}) => boolean;
|
|
77
|
+
export declare const answeredOf: (m: {
|
|
78
|
+
flags?: readonly string[];
|
|
79
|
+
}) => boolean;
|
|
80
|
+
export declare const draftOf: (m: {
|
|
81
|
+
flags?: readonly string[];
|
|
82
|
+
}) => boolean;
|
|
83
|
+
export declare const deletedOf: (m: {
|
|
84
|
+
flags?: readonly string[];
|
|
85
|
+
}) => boolean;
|
|
86
|
+
export declare const setSeen: (m: FlagBearing, state: boolean) => void;
|
|
87
|
+
export declare const setFlagged: (m: FlagBearing, state: boolean) => void;
|
|
88
|
+
export declare const setAnswered: (m: FlagBearing, state: boolean) => void;
|
|
89
|
+
export declare const setDraft: (m: FlagBearing, state: boolean) => void;
|
|
90
|
+
export declare const toggleSeen: (m: FlagBearing) => void;
|
|
91
|
+
export declare const toggleFlagged: (m: FlagBearing) => void;
|
|
67
92
|
/** Email address with optional display name */
|
|
68
93
|
export interface EmailAddress {
|
|
69
94
|
name: string; /** Display name, may be empty */
|
package/index.js
CHANGED
|
@@ -12,6 +12,50 @@ export { CONTACT_RULES } from "./contact-rules.js";
|
|
|
12
12
|
// send time. Both desktop and Android send paths consume this expander
|
|
13
13
|
// against contacts.jsonc → groups.
|
|
14
14
|
export { expandRecipients, splitRecipients, isAddressToken, extractAddress, } from "./groups.js";
|
|
15
|
+
// ── Message flag state ──
|
|
16
|
+
//
|
|
17
|
+
// External API surface for the IMAP system flags. The literal strings
|
|
18
|
+
// (`"\\Seen"`, `"\\Flagged"`, etc.) live ONLY inside this module — every
|
|
19
|
+
// caller speaks in terms of named predicates (`seenOf(msg)`) and verbs
|
|
20
|
+
// (`setSeen(msg, true)`, `toggleSeen(msg)`). That keeps a typo like
|
|
21
|
+
// `"\Seen"` (single backslash) from silently bypassing flag checks
|
|
22
|
+
// elsewhere, and removes the "two-API leak" of having both `FLAG.SEEN`
|
|
23
|
+
// constants AND verb helpers exposed simultaneously.
|
|
24
|
+
//
|
|
25
|
+
// These work against any object with a mutable `flags: string[]`
|
|
26
|
+
// property — `MessageEnvelope`, `Message`, and the row's `msg` reference
|
|
27
|
+
// all qualify. The verbs return `void` and mutate `msg.flags` in place
|
|
28
|
+
// (replacing the array with a fresh one so listeners observing
|
|
29
|
+
// reference identity, e.g. message-state, still re-render). Pure
|
|
30
|
+
// predicate calls (`seenOf`, `flaggedOf`) don't mutate.
|
|
31
|
+
const _SEEN = "\\Seen";
|
|
32
|
+
const _FLAGGED = "\\Flagged";
|
|
33
|
+
const _ANSWERED = "\\Answered";
|
|
34
|
+
const _DRAFT = "\\Draft";
|
|
35
|
+
const _DELETED = "\\Deleted";
|
|
36
|
+
function _has(msg, flag) {
|
|
37
|
+
return !!msg.flags && msg.flags.includes(flag);
|
|
38
|
+
}
|
|
39
|
+
function _set(msg, flag, state) {
|
|
40
|
+
const present = (msg.flags || []).includes(flag);
|
|
41
|
+
if (state === present)
|
|
42
|
+
return;
|
|
43
|
+
const next = state
|
|
44
|
+
? [...(msg.flags || []), flag]
|
|
45
|
+
: (msg.flags || []).filter(f => f !== flag);
|
|
46
|
+
msg.flags = next;
|
|
47
|
+
}
|
|
48
|
+
export const seenOf = (m) => _has(m, _SEEN);
|
|
49
|
+
export const flaggedOf = (m) => _has(m, _FLAGGED);
|
|
50
|
+
export const answeredOf = (m) => _has(m, _ANSWERED);
|
|
51
|
+
export const draftOf = (m) => _has(m, _DRAFT);
|
|
52
|
+
export const deletedOf = (m) => _has(m, _DELETED);
|
|
53
|
+
export const setSeen = (m, state) => _set(m, _SEEN, state);
|
|
54
|
+
export const setFlagged = (m, state) => _set(m, _FLAGGED, state);
|
|
55
|
+
export const setAnswered = (m, state) => _set(m, _ANSWERED, state);
|
|
56
|
+
export const setDraft = (m, state) => _set(m, _DRAFT, state);
|
|
57
|
+
export const toggleSeen = (m) => _set(m, _SEEN, !_has(m, _SEEN));
|
|
58
|
+
export const toggleFlagged = (m) => _set(m, _FLAGGED, !_has(m, _FLAGGED));
|
|
15
59
|
// ── Shared Utilities ──
|
|
16
60
|
// Pure functions used by both desktop (mailx-service) and Android (web-service).
|
|
17
61
|
// Kept here to avoid duplication — both platforms import from mailx-types.
|
package/mailx-api.d.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MailxApi — the single typed contract that every mailx service implementation
|
|
3
|
+
* must satisfy. Both the Node-side desktop service (`MailxService`) and the
|
|
4
|
+
* browser-side Android service (`WebMailxService`) declare
|
|
5
|
+
* `implements MailxApi`. Missing methods become build errors instead of
|
|
6
|
+
* silent runtime "parent bridge has no method X" failures.
|
|
7
|
+
*
|
|
8
|
+
* **Why this exists**: until now the two implementations drifted independently.
|
|
9
|
+
* A method added to MailxService for one platform release would be silently
|
|
10
|
+
* absent on the other for months. spellcheck-on-GDrive (2026-05-13) added
|
|
11
|
+
* `getUserDict` / `addUserDictWord` / `removeUserDictWord` to the desktop
|
|
12
|
+
* service; the Android service got none of them; the relay returned
|
|
13
|
+
* `parent bridge has no method`, the client `.catch(() => {})` swallowed the
|
|
14
|
+
* rejection, and the feature silently regressed on phone. With this contract
|
|
15
|
+
* any such addition is a compile-time failure on whichever side it's missing.
|
|
16
|
+
*
|
|
17
|
+
* **Surface**: the 83 method names dispatched by
|
|
18
|
+
* `mailx-service/jsonrpc.ts` plus a handful of internal helpers both
|
|
19
|
+
* implementations need. IPC-only names (`sendMessage`, `createCalendarEvent`,
|
|
20
|
+
* etc.) are listed under their dispatch names — desktop classes that today
|
|
21
|
+
* use a different internal name (e.g., `send`, `createCalendarEventLocal`)
|
|
22
|
+
* add a thin alias method so the contract holds without renaming call sites.
|
|
23
|
+
*
|
|
24
|
+
* **Optional methods**: a handful of methods require an OS popup or native
|
|
25
|
+
* filesystem hook and have no meaningful browser equivalent
|
|
26
|
+
* (`openInWord`, `closeWordEdit`, `showReminderPopup`, `openInTextEditor`,
|
|
27
|
+
* `openLocalPath`). They are declared `?:` so the browser service can
|
|
28
|
+
* legitimately omit them. The desktop service must implement them.
|
|
29
|
+
*
|
|
30
|
+
* **Types**: signatures use `any` where the shape genuinely varies across
|
|
31
|
+
* implementations (envelope rows, settings, AI payloads). The contract's
|
|
32
|
+
* job is name + arity enforcement, not deep schema typing — that lives in
|
|
33
|
+
* the relevant domain types (`Folder`, `AccountConfig`, etc.).
|
|
34
|
+
*
|
|
35
|
+
* **Where to add a new method**: add the method here first. Both
|
|
36
|
+
* implementations will fail to compile until they implement it. Don't add
|
|
37
|
+
* methods to a service class without the interface entry — that's how we
|
|
38
|
+
* got into the silent-drift hole.
|
|
39
|
+
*/
|
|
40
|
+
import type { AutocompleteSettings, AutocompleteRequest, AutocompleteResponse, AiTransformRequest, AiTransformResponse } from "./index.js";
|
|
41
|
+
import type { Folder } from "./index.js";
|
|
42
|
+
export interface MailxApi {
|
|
43
|
+
getAccounts(): any[] | Promise<any[]>;
|
|
44
|
+
getFolders(accountId: string): Folder[] | Promise<Folder[]>;
|
|
45
|
+
getMessages(accountId: string, folderId: number, page?: number, pageSize?: number, sort?: string, sortDir?: string, search?: string, flaggedOnly?: boolean): any | Promise<any>;
|
|
46
|
+
getUnifiedInbox(page?: number, pageSize?: number): any | Promise<any>;
|
|
47
|
+
getMessage(accountId: string, uid: number, allowRemote?: boolean, folderId?: number): Promise<any>;
|
|
48
|
+
getThreadMessages(accountId: string, threadId: string): any | Promise<any>;
|
|
49
|
+
getAttachment(accountId: string, uid: number, attachmentId: number, folderId?: number): Promise<{
|
|
50
|
+
content: any;
|
|
51
|
+
contentType: string;
|
|
52
|
+
filename: string;
|
|
53
|
+
}>;
|
|
54
|
+
updateFlags(accountId: string, uid: number, flags: string[]): Promise<void>;
|
|
55
|
+
deleteMessage(accountId: string, uid: number): Promise<void>;
|
|
56
|
+
deleteMessages(accountId: string, uids: number[]): Promise<void>;
|
|
57
|
+
undeleteMessage(accountId: string, uid: number, folderId: number): Promise<void>;
|
|
58
|
+
moveMessage(accountId: string, uid: number, targetFolderId: number, targetAccountId?: string): Promise<void>;
|
|
59
|
+
moveMessages(accountId: string, uids: number[], targetFolderId: number): Promise<void>;
|
|
60
|
+
markAsSpamMessages(accountId: string, uids: number[]): Promise<{
|
|
61
|
+
targetFolderId: number;
|
|
62
|
+
moved: number;
|
|
63
|
+
}>;
|
|
64
|
+
recordSpamReport(accountId: string, uid: number, folderId: number): Promise<any>;
|
|
65
|
+
markFolderRead(folderId: number): void | Promise<void>;
|
|
66
|
+
createFolder(accountId: string, parentPath: string, name: string): Promise<void>;
|
|
67
|
+
renameFolder(accountId: string, folderId: number, newName: string): Promise<void>;
|
|
68
|
+
deleteFolder(accountId: string, folderId: number): Promise<void>;
|
|
69
|
+
moveFolderToTrash(accountId: string, folderId: number): Promise<void>;
|
|
70
|
+
emptyFolder(accountId: string, folderId: number): Promise<void>;
|
|
71
|
+
sendMessage(msg: any): Promise<void>;
|
|
72
|
+
saveDraft(accountId: string, subject: string, bodyHtml: string, bodyText: string, to?: string, cc?: string, previousDraftUid?: number, draftId?: string): Promise<{
|
|
73
|
+
draftUid: number | null;
|
|
74
|
+
draftId: string;
|
|
75
|
+
}>;
|
|
76
|
+
deleteDraft(accountId: string, draftUid: number, draftId?: string): Promise<void>;
|
|
77
|
+
getOutboxStatus(): any | Promise<any>;
|
|
78
|
+
listQueuedOutgoing(): any[] | Promise<any[]>;
|
|
79
|
+
cancelQueuedOutgoing(filePath: string): {
|
|
80
|
+
ok: true;
|
|
81
|
+
} | Promise<{
|
|
82
|
+
ok: true;
|
|
83
|
+
}>;
|
|
84
|
+
syncAll(): Promise<void>;
|
|
85
|
+
syncAccount(accountId: string): Promise<void>;
|
|
86
|
+
getSyncPending(): {
|
|
87
|
+
pending: number;
|
|
88
|
+
} | Promise<{
|
|
89
|
+
pending: number;
|
|
90
|
+
}>;
|
|
91
|
+
drainStoreSync(): Promise<void>;
|
|
92
|
+
reauthenticate(accountId: string): Promise<boolean>;
|
|
93
|
+
reauthGoogleScopes(): {
|
|
94
|
+
cleared: number;
|
|
95
|
+
} | Promise<{
|
|
96
|
+
cleared: number;
|
|
97
|
+
}>;
|
|
98
|
+
searchMessages(query: string, page?: number, pageSize?: number, scope?: string, accountId?: string, folderId?: number, includeTrashSpam?: boolean): Promise<any>;
|
|
99
|
+
searchContacts(query: string): any[] | Promise<any[]>;
|
|
100
|
+
hasCcHistoryTo(email: string): boolean | Promise<boolean>;
|
|
101
|
+
hasBccHistoryTo(email: string): boolean | Promise<boolean>;
|
|
102
|
+
addContact(name: string, email: string): boolean | Promise<boolean>;
|
|
103
|
+
listContacts(query: string, page?: number, pageSize?: number): any | Promise<any>;
|
|
104
|
+
upsertContact(name: string, email: string): {
|
|
105
|
+
ok: true;
|
|
106
|
+
} | Promise<{
|
|
107
|
+
ok: true;
|
|
108
|
+
}>;
|
|
109
|
+
deleteContact(email: string): {
|
|
110
|
+
ok: true;
|
|
111
|
+
} | Promise<{
|
|
112
|
+
ok: true;
|
|
113
|
+
}>;
|
|
114
|
+
addPreferredContact(entry: {
|
|
115
|
+
name: string;
|
|
116
|
+
email: string;
|
|
117
|
+
source?: string;
|
|
118
|
+
organization?: string;
|
|
119
|
+
}): Promise<void>;
|
|
120
|
+
addToDenylist(email: string): Promise<void>;
|
|
121
|
+
loadContactsConfig(): Promise<any>;
|
|
122
|
+
getPriorityLists(): {
|
|
123
|
+
senders: string[];
|
|
124
|
+
domains: string[];
|
|
125
|
+
} | Promise<{
|
|
126
|
+
senders: string[];
|
|
127
|
+
domains: string[];
|
|
128
|
+
}>;
|
|
129
|
+
setPrioritySender(email: string, value: boolean, name?: string): Promise<void>;
|
|
130
|
+
setPriorityDomain(domain: string, value: boolean): Promise<void>;
|
|
131
|
+
flagSenderOrDomain(type: "sender" | "domain", value: string): Promise<{
|
|
132
|
+
flagged: boolean;
|
|
133
|
+
}>;
|
|
134
|
+
allowRemoteContent(type: "sender" | "domain" | "recipient", value: string): Promise<void>;
|
|
135
|
+
getCalendarEvents(fromMs: number, toMs: number): Promise<any[]>;
|
|
136
|
+
createCalendarEvent(ev: any): Promise<{
|
|
137
|
+
uuid: string;
|
|
138
|
+
}>;
|
|
139
|
+
updateCalendarEvent(uuid: string, patch: any): Promise<{
|
|
140
|
+
ok: true;
|
|
141
|
+
}>;
|
|
142
|
+
deleteCalendarEvent(uuid: string): Promise<{
|
|
143
|
+
ok: true;
|
|
144
|
+
}>;
|
|
145
|
+
getTasks(includeCompleted?: boolean): Promise<any[]>;
|
|
146
|
+
createTask(t: {
|
|
147
|
+
title: string;
|
|
148
|
+
notes?: string;
|
|
149
|
+
dueMs?: number;
|
|
150
|
+
}): Promise<{
|
|
151
|
+
uuid: string;
|
|
152
|
+
}>;
|
|
153
|
+
updateTask(uuid: string, patch: any): Promise<{
|
|
154
|
+
ok: true;
|
|
155
|
+
}>;
|
|
156
|
+
deleteTask(uuid: string): Promise<{
|
|
157
|
+
ok: true;
|
|
158
|
+
}>;
|
|
159
|
+
getUserDict(): Promise<string[]>;
|
|
160
|
+
addUserDictWord(word: string): Promise<string[]>;
|
|
161
|
+
removeUserDictWord(word: string): Promise<string[]>;
|
|
162
|
+
getSettings(): any | Promise<any>;
|
|
163
|
+
saveSettingsData(settings: any): Promise<void>;
|
|
164
|
+
getDiagnostics(): any | Promise<any>;
|
|
165
|
+
getPrimaryAccount(feature?: string): any | Promise<any>;
|
|
166
|
+
setupAccount(name: string, email: string, password?: string): Promise<{
|
|
167
|
+
ok: boolean;
|
|
168
|
+
error?: string;
|
|
169
|
+
message?: string;
|
|
170
|
+
}>;
|
|
171
|
+
repairAccounts(): Promise<{
|
|
172
|
+
ok: boolean;
|
|
173
|
+
error?: string;
|
|
174
|
+
message?: string;
|
|
175
|
+
}>;
|
|
176
|
+
getAutocompleteSettings(): AutocompleteSettings | Promise<AutocompleteSettings>;
|
|
177
|
+
saveAutocompleteSettings(settings: AutocompleteSettings): void | Promise<void>;
|
|
178
|
+
autocomplete(req: AutocompleteRequest): Promise<AutocompleteResponse>;
|
|
179
|
+
aiTransform(req: AiTransformRequest): Promise<AiTransformResponse>;
|
|
180
|
+
readJsoncFile(name: string): Promise<string | null>;
|
|
181
|
+
writeJsoncFile(name: string, content: string): Promise<void>;
|
|
182
|
+
formatJsonc(content: string): Promise<string>;
|
|
183
|
+
readConfigHelp(name: string): Promise<string>;
|
|
184
|
+
unsubscribeOneClick(url: string): Promise<{
|
|
185
|
+
ok: boolean;
|
|
186
|
+
status: number;
|
|
187
|
+
statusText: string;
|
|
188
|
+
}>;
|
|
189
|
+
consumePendingMailto(): any | Promise<any>;
|
|
190
|
+
logClientEvent?(...args: any[]): void | Promise<void>;
|
|
191
|
+
getVersion?(): any | Promise<any>;
|
|
192
|
+
openInWord?(editId: string, html: string): Promise<{
|
|
193
|
+
ok: boolean;
|
|
194
|
+
path: string;
|
|
195
|
+
opener: string;
|
|
196
|
+
}>;
|
|
197
|
+
closeWordEdit?(editId: string): Promise<void>;
|
|
198
|
+
showReminderPopup?(opts: any): Promise<{
|
|
199
|
+
button: string;
|
|
200
|
+
form?: any;
|
|
201
|
+
}>;
|
|
202
|
+
openInTextEditor?(filePath: string): Promise<{
|
|
203
|
+
ok: boolean;
|
|
204
|
+
opener: string;
|
|
205
|
+
reason?: string;
|
|
206
|
+
}>;
|
|
207
|
+
openLocalPath?(which: "config" | "log"): Promise<{
|
|
208
|
+
ok: true;
|
|
209
|
+
path: string;
|
|
210
|
+
}>;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=mailx-api.d.ts.map
|
package/mailx-api.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MailxApi — the single typed contract that every mailx service implementation
|
|
3
|
+
* must satisfy. Both the Node-side desktop service (`MailxService`) and the
|
|
4
|
+
* browser-side Android service (`WebMailxService`) declare
|
|
5
|
+
* `implements MailxApi`. Missing methods become build errors instead of
|
|
6
|
+
* silent runtime "parent bridge has no method X" failures.
|
|
7
|
+
*
|
|
8
|
+
* **Why this exists**: until now the two implementations drifted independently.
|
|
9
|
+
* A method added to MailxService for one platform release would be silently
|
|
10
|
+
* absent on the other for months. spellcheck-on-GDrive (2026-05-13) added
|
|
11
|
+
* `getUserDict` / `addUserDictWord` / `removeUserDictWord` to the desktop
|
|
12
|
+
* service; the Android service got none of them; the relay returned
|
|
13
|
+
* `parent bridge has no method`, the client `.catch(() => {})` swallowed the
|
|
14
|
+
* rejection, and the feature silently regressed on phone. With this contract
|
|
15
|
+
* any such addition is a compile-time failure on whichever side it's missing.
|
|
16
|
+
*
|
|
17
|
+
* **Surface**: the 83 method names dispatched by
|
|
18
|
+
* `mailx-service/jsonrpc.ts` plus a handful of internal helpers both
|
|
19
|
+
* implementations need. IPC-only names (`sendMessage`, `createCalendarEvent`,
|
|
20
|
+
* etc.) are listed under their dispatch names — desktop classes that today
|
|
21
|
+
* use a different internal name (e.g., `send`, `createCalendarEventLocal`)
|
|
22
|
+
* add a thin alias method so the contract holds without renaming call sites.
|
|
23
|
+
*
|
|
24
|
+
* **Optional methods**: a handful of methods require an OS popup or native
|
|
25
|
+
* filesystem hook and have no meaningful browser equivalent
|
|
26
|
+
* (`openInWord`, `closeWordEdit`, `showReminderPopup`, `openInTextEditor`,
|
|
27
|
+
* `openLocalPath`). They are declared `?:` so the browser service can
|
|
28
|
+
* legitimately omit them. The desktop service must implement them.
|
|
29
|
+
*
|
|
30
|
+
* **Types**: signatures use `any` where the shape genuinely varies across
|
|
31
|
+
* implementations (envelope rows, settings, AI payloads). The contract's
|
|
32
|
+
* job is name + arity enforcement, not deep schema typing — that lives in
|
|
33
|
+
* the relevant domain types (`Folder`, `AccountConfig`, etc.).
|
|
34
|
+
*
|
|
35
|
+
* **Where to add a new method**: add the method here first. Both
|
|
36
|
+
* implementations will fail to compile until they implement it. Don't add
|
|
37
|
+
* methods to a service class without the interface entry — that's how we
|
|
38
|
+
* got into the silent-drift hole.
|
|
39
|
+
*/
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=mailx-api.js.map
|