@bobfrankston/mailx-store-web 0.1.5
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/android-bootstrap.d.ts +16 -0
- package/android-bootstrap.d.ts.map +1 -0
- package/android-bootstrap.js +1438 -0
- package/android-bootstrap.js.map +1 -0
- package/android-bootstrap.ts +1450 -0
- package/db.d.ts +146 -0
- package/db.d.ts.map +1 -0
- package/db.js +725 -0
- package/db.js.map +1 -0
- package/db.ts +831 -0
- package/gmail-api-web.d.ts +11 -0
- package/gmail-api-web.d.ts.map +1 -0
- package/gmail-api-web.js +11 -0
- package/gmail-api-web.js.map +1 -0
- package/gmail-api-web.ts +11 -0
- package/imap-web-provider.d.ts +33 -0
- package/imap-web-provider.d.ts.map +1 -0
- package/imap-web-provider.js +140 -0
- package/imap-web-provider.js.map +1 -0
- package/imap-web-provider.ts +156 -0
- package/index.d.ts +10 -0
- package/index.d.ts.map +1 -0
- package/index.js +10 -0
- package/index.js.map +1 -0
- package/index.ts +10 -0
- package/main-thread-host.d.ts +15 -0
- package/main-thread-host.d.ts.map +1 -0
- package/main-thread-host.js +292 -0
- package/main-thread-host.js.map +1 -0
- package/main-thread-host.ts +322 -0
- package/package.json +41 -0
- package/provider-types.d.ts +7 -0
- package/provider-types.d.ts.map +1 -0
- package/provider-types.js +7 -0
- package/provider-types.js.map +1 -0
- package/provider-types.ts +7 -0
- package/sql-wasm-esm.js +10 -0
- package/sql.js.d.ts +29 -0
- package/sync-manager.d.ts +68 -0
- package/sync-manager.d.ts.map +1 -0
- package/sync-manager.js +506 -0
- package/sync-manager.js.map +1 -0
- package/sync-manager.ts +508 -0
- package/tsconfig.json +10 -0
- package/web-jsonrpc.d.ts +20 -0
- package/web-jsonrpc.d.ts.map +1 -0
- package/web-jsonrpc.js +112 -0
- package/web-jsonrpc.js.map +1 -0
- package/web-jsonrpc.ts +126 -0
- package/web-message-store.d.ts +16 -0
- package/web-message-store.d.ts.map +1 -0
- package/web-message-store.js +89 -0
- package/web-message-store.js.map +1 -0
- package/web-message-store.ts +97 -0
- package/web-service.d.ts +136 -0
- package/web-service.d.ts.map +1 -0
- package/web-service.js +687 -0
- package/web-service.js.map +1 -0
- package/web-service.ts +754 -0
- package/web-settings.d.ts +91 -0
- package/web-settings.d.ts.map +1 -0
- package/web-settings.js +518 -0
- package/web-settings.js.map +1 -0
- package/web-settings.ts +547 -0
- package/worker-bundle.js +6838 -0
- package/worker-entry.d.ts +8 -0
- package/worker-entry.d.ts.map +1 -0
- package/worker-entry.js +218 -0
- package/worker-entry.js.map +1 -0
- package/worker-entry.ts +245 -0
- package/worker-tcp-transport.d.ts +28 -0
- package/worker-tcp-transport.d.ts.map +1 -0
- package/worker-tcp-transport.js +98 -0
- package/worker-tcp-transport.js.map +1 -0
- package/worker-tcp-transport.ts +101 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Back-compat re-export. The Gmail provider is now a single implementation
|
|
3
|
+
* in @bobfrankston/mailx-sync, used by both desktop (mailx-imap) and
|
|
4
|
+
* Android (this package). Bug fixes (e.g. 403-quota retry, bounded
|
|
5
|
+
* concurrency) land in one place and apply to both platforms.
|
|
6
|
+
*
|
|
7
|
+
* `GmailApiWebProvider` is kept as an alias for back-compat with existing
|
|
8
|
+
* Android wiring; it's the same class as `GmailApiProvider`.
|
|
9
|
+
*/
|
|
10
|
+
export { GmailApiProvider as GmailApiWebProvider } from "@bobfrankston/mailx-sync";
|
|
11
|
+
//# sourceMappingURL=gmail-api-web.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gmail-api-web.d.ts","sourceRoot":"","sources":["gmail-api-web.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,gBAAgB,IAAI,mBAAmB,EAAE,MAAM,0BAA0B,CAAC"}
|
package/gmail-api-web.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Back-compat re-export. The Gmail provider is now a single implementation
|
|
3
|
+
* in @bobfrankston/mailx-sync, used by both desktop (mailx-imap) and
|
|
4
|
+
* Android (this package). Bug fixes (e.g. 403-quota retry, bounded
|
|
5
|
+
* concurrency) land in one place and apply to both platforms.
|
|
6
|
+
*
|
|
7
|
+
* `GmailApiWebProvider` is kept as an alias for back-compat with existing
|
|
8
|
+
* Android wiring; it's the same class as `GmailApiProvider`.
|
|
9
|
+
*/
|
|
10
|
+
export { GmailApiProvider as GmailApiWebProvider } from "@bobfrankston/mailx-sync";
|
|
11
|
+
//# sourceMappingURL=gmail-api-web.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gmail-api-web.js","sourceRoot":"","sources":["gmail-api-web.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,gBAAgB,IAAI,mBAAmB,EAAE,MAAM,0BAA0B,CAAC"}
|
package/gmail-api-web.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Back-compat re-export. The Gmail provider is now a single implementation
|
|
3
|
+
* in @bobfrankston/mailx-sync, used by both desktop (mailx-imap) and
|
|
4
|
+
* Android (this package). Bug fixes (e.g. 403-quota retry, bounded
|
|
5
|
+
* concurrency) land in one place and apply to both platforms.
|
|
6
|
+
*
|
|
7
|
+
* `GmailApiWebProvider` is kept as an alias for back-compat with existing
|
|
8
|
+
* Android wiring; it's the same class as `GmailApiProvider`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export { GmailApiProvider as GmailApiWebProvider } from "@bobfrankston/mailx-sync";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IMAP web provider — implements MailProvider using CompatImapClient + BridgeTransport.
|
|
3
|
+
* Used for non-Gmail accounts on Android/WebView where Node.js isn't available.
|
|
4
|
+
*
|
|
5
|
+
* The native shell (MAUI) exposes TCP via window._nativeBridge.tcp.*; we alias it
|
|
6
|
+
* to window.msgapi in initAndroid() because iflow-direct's BridgeTransport expects
|
|
7
|
+
* that global name.
|
|
8
|
+
*
|
|
9
|
+
* Includes automatic retry on broken pipe / connection errors: if an operation fails
|
|
10
|
+
* with a connection-related error, we create a fresh client and retry once.
|
|
11
|
+
*/
|
|
12
|
+
import { type ImapClientConfig } from "@bobfrankston/iflow-direct";
|
|
13
|
+
import type { MailProvider, ProviderFolder, ProviderMessage, FetchOptions } from "./provider-types.js";
|
|
14
|
+
export declare class ImapWebProvider implements MailProvider {
|
|
15
|
+
private client;
|
|
16
|
+
private config;
|
|
17
|
+
private transportFactory;
|
|
18
|
+
private specialFolders;
|
|
19
|
+
private folderListCache;
|
|
20
|
+
constructor(config: ImapClientConfig, transportFactory?: () => any);
|
|
21
|
+
/** Create a fresh client (after broken pipe / connection error) */
|
|
22
|
+
private reconnect;
|
|
23
|
+
/** Run an operation with one retry on connection error */
|
|
24
|
+
private withRetry;
|
|
25
|
+
listFolders(): Promise<ProviderFolder[]>;
|
|
26
|
+
fetchSince(folder: string, sinceUid: number, options?: FetchOptions): Promise<ProviderMessage[]>;
|
|
27
|
+
fetchByDate(folder: string, since: Date, before: Date, options?: FetchOptions, onChunk?: (msgs: ProviderMessage[]) => void): Promise<ProviderMessage[]>;
|
|
28
|
+
fetchByUids(folder: string, uids: number[], options?: FetchOptions): Promise<ProviderMessage[]>;
|
|
29
|
+
fetchOne(folder: string, uid: number, options?: FetchOptions): Promise<ProviderMessage | null>;
|
|
30
|
+
getUids(folder: string): Promise<number[]>;
|
|
31
|
+
close(): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=imap-web-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imap-web-provider.d.ts","sourceRoot":"","sources":["imap-web-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAqC,KAAK,gBAAgB,EAAyB,MAAM,4BAA4B,CAAC;AAC7H,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAwDvG,qBAAa,eAAgB,YAAW,YAAY;IAChD,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,cAAc,CAA0C;IAChE,OAAO,CAAC,eAAe,CAAiC;gBAE5C,MAAM,EAAE,gBAAgB,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG;IAMlE,mEAAmE;IACnE,OAAO,CAAC,SAAS;IAMjB,0DAA0D;YAC5C,SAAS;IAajB,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IASxC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAQhG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,YAAY,EAC/E,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAStE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAU/F,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAQ9F,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAI1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IMAP web provider — implements MailProvider using CompatImapClient + BridgeTransport.
|
|
3
|
+
* Used for non-Gmail accounts on Android/WebView where Node.js isn't available.
|
|
4
|
+
*
|
|
5
|
+
* The native shell (MAUI) exposes TCP via window._nativeBridge.tcp.*; we alias it
|
|
6
|
+
* to window.msgapi in initAndroid() because iflow-direct's BridgeTransport expects
|
|
7
|
+
* that global name.
|
|
8
|
+
*
|
|
9
|
+
* Includes automatic retry on broken pipe / connection errors: if an operation fails
|
|
10
|
+
* with a connection-related error, we create a fresh client and retry once.
|
|
11
|
+
*/
|
|
12
|
+
import { CompatImapClient, BridgeTransport } from "@bobfrankston/iflow-direct";
|
|
13
|
+
/**
|
|
14
|
+
* Convert a NativeFolder (from iflow-direct) into a ProviderFolder,
|
|
15
|
+
* detecting special-use from the IMAP flags.
|
|
16
|
+
*/
|
|
17
|
+
function toProviderFolder(f, special) {
|
|
18
|
+
const flagsLower = (f.flags || []).map(x => x.toLowerCase());
|
|
19
|
+
let specialUse = "";
|
|
20
|
+
if (f.path === special.inbox || flagsLower.includes("\\inbox") || f.path.toUpperCase() === "INBOX")
|
|
21
|
+
specialUse = "inbox";
|
|
22
|
+
else if (f.path === special.sent || flagsLower.includes("\\sent"))
|
|
23
|
+
specialUse = "sent";
|
|
24
|
+
else if (f.path === special.trash || flagsLower.includes("\\trash"))
|
|
25
|
+
specialUse = "trash";
|
|
26
|
+
else if (f.path === special.drafts || flagsLower.includes("\\drafts"))
|
|
27
|
+
specialUse = "drafts";
|
|
28
|
+
else if (f.path === special.spam || f.path === special.junk || flagsLower.includes("\\junk"))
|
|
29
|
+
specialUse = "junk";
|
|
30
|
+
else if (f.path === special.archive || flagsLower.includes("\\archive"))
|
|
31
|
+
specialUse = "archive";
|
|
32
|
+
// Leaf name = last path segment after delimiter
|
|
33
|
+
const leaf = f.delimiter ? f.path.split(f.delimiter).pop() || f.path : f.path;
|
|
34
|
+
return {
|
|
35
|
+
path: f.path,
|
|
36
|
+
name: leaf,
|
|
37
|
+
delimiter: f.delimiter || "/",
|
|
38
|
+
specialUse,
|
|
39
|
+
flags: f.flags || [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function toProviderMessage(m) {
|
|
43
|
+
return {
|
|
44
|
+
uid: m.uid,
|
|
45
|
+
messageId: m.messageId || "",
|
|
46
|
+
providerId: "",
|
|
47
|
+
date: m.date || null,
|
|
48
|
+
subject: m.subject || "",
|
|
49
|
+
from: m.from || [],
|
|
50
|
+
to: m.to || [],
|
|
51
|
+
cc: m.cc || [],
|
|
52
|
+
seen: !!m.seen,
|
|
53
|
+
flagged: !!m.flagged,
|
|
54
|
+
answered: !!m.answered,
|
|
55
|
+
draft: !!m.draft,
|
|
56
|
+
size: m.size || 0,
|
|
57
|
+
source: m.source || "",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** Check if an error is a connection/broken-pipe error worth retrying */
|
|
61
|
+
function isConnectionError(e) {
|
|
62
|
+
const msg = (e?.message || "").toLowerCase();
|
|
63
|
+
return msg.includes("broken pipe") || msg.includes("not connected") ||
|
|
64
|
+
msg.includes("connection") || msg.includes("socket") ||
|
|
65
|
+
msg.includes("timeout") || msg.includes("econnreset") ||
|
|
66
|
+
msg.includes("epipe") || msg.includes("closed");
|
|
67
|
+
}
|
|
68
|
+
export class ImapWebProvider {
|
|
69
|
+
client;
|
|
70
|
+
config;
|
|
71
|
+
transportFactory;
|
|
72
|
+
specialFolders = {};
|
|
73
|
+
folderListCache = null;
|
|
74
|
+
constructor(config, transportFactory) {
|
|
75
|
+
this.config = config;
|
|
76
|
+
this.transportFactory = transportFactory || (() => new BridgeTransport());
|
|
77
|
+
this.client = new CompatImapClient(config, this.transportFactory);
|
|
78
|
+
}
|
|
79
|
+
/** Create a fresh client (after broken pipe / connection error) */
|
|
80
|
+
reconnect() {
|
|
81
|
+
console.log("[imap-web] reconnecting after connection error");
|
|
82
|
+
try {
|
|
83
|
+
this.client.logout();
|
|
84
|
+
}
|
|
85
|
+
catch { /* ignore */ }
|
|
86
|
+
this.client = new CompatImapClient(this.config, this.transportFactory);
|
|
87
|
+
}
|
|
88
|
+
/** Run an operation with one retry on connection error */
|
|
89
|
+
async withRetry(op, label) {
|
|
90
|
+
try {
|
|
91
|
+
return await op();
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
if (isConnectionError(e)) {
|
|
95
|
+
console.warn(`[imap-web] ${label}: ${e.message} — reconnecting and retrying`);
|
|
96
|
+
this.reconnect();
|
|
97
|
+
return await op();
|
|
98
|
+
}
|
|
99
|
+
throw e;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async listFolders() {
|
|
103
|
+
const native = await this.withRetry(() => this.client.getFolderList(), "listFolders");
|
|
104
|
+
const special = this.client.getSpecialFolders(native);
|
|
105
|
+
this.specialFolders = special;
|
|
106
|
+
const result = native.map(f => toProviderFolder(f, this.specialFolders));
|
|
107
|
+
this.folderListCache = result;
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
async fetchSince(folder, sinceUid, options) {
|
|
111
|
+
const msgs = await this.withRetry(() => this.client.fetchMessagesSinceUid(folder, sinceUid, { source: !!options?.source }), `fetchSince(${folder})`);
|
|
112
|
+
return msgs.map(toProviderMessage);
|
|
113
|
+
}
|
|
114
|
+
async fetchByDate(folder, since, before, options, onChunk) {
|
|
115
|
+
const wrappedChunk = onChunk ? (raw) => onChunk(raw.map(toProviderMessage)) : undefined;
|
|
116
|
+
const msgs = await this.withRetry(() => this.client.fetchMessageByDate(folder, since, before, { source: !!options?.source }, wrappedChunk), `fetchByDate(${folder})`);
|
|
117
|
+
return msgs.map(toProviderMessage);
|
|
118
|
+
}
|
|
119
|
+
async fetchByUids(folder, uids, options) {
|
|
120
|
+
if (!uids.length)
|
|
121
|
+
return [];
|
|
122
|
+
const range = uids.join(",");
|
|
123
|
+
const msgs = await this.withRetry(() => this.client.fetchMessages(folder, range, { source: !!options?.source }), `fetchByUids(${folder})`);
|
|
124
|
+
return msgs.map(toProviderMessage);
|
|
125
|
+
}
|
|
126
|
+
async fetchOne(folder, uid, options) {
|
|
127
|
+
const msg = await this.withRetry(() => this.client.fetchMessageByUid(folder, uid, { source: !!options?.source }), `fetchOne(${folder}/${uid})`);
|
|
128
|
+
return msg ? toProviderMessage(msg) : null;
|
|
129
|
+
}
|
|
130
|
+
async getUids(folder) {
|
|
131
|
+
return this.withRetry(() => this.client.getUids(folder), `getUids(${folder})`);
|
|
132
|
+
}
|
|
133
|
+
async close() {
|
|
134
|
+
try {
|
|
135
|
+
await this.client.logout();
|
|
136
|
+
}
|
|
137
|
+
catch { /* ignore */ }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=imap-web-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imap-web-provider.js","sourceRoot":"","sources":["imap-web-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IMAP web provider — implements MailProvider using CompatImapClient + BridgeTransport.
|
|
3
|
+
* Used for non-Gmail accounts on Android/WebView where Node.js isn't available.
|
|
4
|
+
*
|
|
5
|
+
* The native shell (MAUI) exposes TCP via window._nativeBridge.tcp.*; we alias it
|
|
6
|
+
* to window.msgapi in initAndroid() because iflow-direct's BridgeTransport expects
|
|
7
|
+
* that global name.
|
|
8
|
+
*
|
|
9
|
+
* Includes automatic retry on broken pipe / connection errors: if an operation fails
|
|
10
|
+
* with a connection-related error, we create a fresh client and retry once.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { CompatImapClient, BridgeTransport, type ImapClientConfig, type TransportFactory } from "@bobfrankston/iflow-direct";
|
|
14
|
+
import type { MailProvider, ProviderFolder, ProviderMessage, FetchOptions } from "./provider-types.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Convert a NativeFolder (from iflow-direct) into a ProviderFolder,
|
|
18
|
+
* detecting special-use from the IMAP flags.
|
|
19
|
+
*/
|
|
20
|
+
function toProviderFolder(f: { path: string; delimiter: string; flags: string[] }, special: Record<string, string | undefined>): ProviderFolder {
|
|
21
|
+
const flagsLower = (f.flags || []).map(x => x.toLowerCase());
|
|
22
|
+
let specialUse = "";
|
|
23
|
+
if (f.path === special.inbox || flagsLower.includes("\\inbox") || f.path.toUpperCase() === "INBOX") specialUse = "inbox";
|
|
24
|
+
else if (f.path === special.sent || flagsLower.includes("\\sent")) specialUse = "sent";
|
|
25
|
+
else if (f.path === special.trash || flagsLower.includes("\\trash")) specialUse = "trash";
|
|
26
|
+
else if (f.path === special.drafts || flagsLower.includes("\\drafts")) specialUse = "drafts";
|
|
27
|
+
else if (f.path === special.spam || f.path === special.junk || flagsLower.includes("\\junk")) specialUse = "junk";
|
|
28
|
+
else if (f.path === special.archive || flagsLower.includes("\\archive")) specialUse = "archive";
|
|
29
|
+
|
|
30
|
+
// Leaf name = last path segment after delimiter
|
|
31
|
+
const leaf = f.delimiter ? f.path.split(f.delimiter).pop() || f.path : f.path;
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
path: f.path,
|
|
35
|
+
name: leaf,
|
|
36
|
+
delimiter: f.delimiter || "/",
|
|
37
|
+
specialUse,
|
|
38
|
+
flags: f.flags || [],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function toProviderMessage(m: any): ProviderMessage {
|
|
43
|
+
return {
|
|
44
|
+
uid: m.uid,
|
|
45
|
+
messageId: m.messageId || "",
|
|
46
|
+
providerId: "",
|
|
47
|
+
date: m.date || null,
|
|
48
|
+
subject: m.subject || "",
|
|
49
|
+
from: m.from || [],
|
|
50
|
+
to: m.to || [],
|
|
51
|
+
cc: m.cc || [],
|
|
52
|
+
seen: !!m.seen,
|
|
53
|
+
flagged: !!m.flagged,
|
|
54
|
+
answered: !!m.answered,
|
|
55
|
+
draft: !!m.draft,
|
|
56
|
+
size: m.size || 0,
|
|
57
|
+
source: m.source || "",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Check if an error is a connection/broken-pipe error worth retrying */
|
|
62
|
+
function isConnectionError(e: any): boolean {
|
|
63
|
+
const msg = (e?.message || "").toLowerCase();
|
|
64
|
+
return msg.includes("broken pipe") || msg.includes("not connected") ||
|
|
65
|
+
msg.includes("connection") || msg.includes("socket") ||
|
|
66
|
+
msg.includes("timeout") || msg.includes("econnreset") ||
|
|
67
|
+
msg.includes("epipe") || msg.includes("closed");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class ImapWebProvider implements MailProvider {
|
|
71
|
+
private client: CompatImapClient;
|
|
72
|
+
private config: ImapClientConfig;
|
|
73
|
+
private transportFactory: TransportFactory;
|
|
74
|
+
private specialFolders: Record<string, string | undefined> = {};
|
|
75
|
+
private folderListCache: ProviderFolder[] | null = null;
|
|
76
|
+
|
|
77
|
+
constructor(config: ImapClientConfig, transportFactory?: () => any) {
|
|
78
|
+
this.config = config;
|
|
79
|
+
this.transportFactory = transportFactory || (() => new BridgeTransport());
|
|
80
|
+
this.client = new CompatImapClient(config, this.transportFactory);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Create a fresh client (after broken pipe / connection error) */
|
|
84
|
+
private reconnect(): void {
|
|
85
|
+
console.log("[imap-web] reconnecting after connection error");
|
|
86
|
+
try { this.client.logout(); } catch { /* ignore */ }
|
|
87
|
+
this.client = new CompatImapClient(this.config, this.transportFactory);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Run an operation with one retry on connection error */
|
|
91
|
+
private async withRetry<T>(op: () => Promise<T>, label: string): Promise<T> {
|
|
92
|
+
try {
|
|
93
|
+
return await op();
|
|
94
|
+
} catch (e: any) {
|
|
95
|
+
if (isConnectionError(e)) {
|
|
96
|
+
console.warn(`[imap-web] ${label}: ${e.message} — reconnecting and retrying`);
|
|
97
|
+
this.reconnect();
|
|
98
|
+
return await op();
|
|
99
|
+
}
|
|
100
|
+
throw e;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async listFolders(): Promise<ProviderFolder[]> {
|
|
105
|
+
const native = await this.withRetry(() => this.client.getFolderList(), "listFolders");
|
|
106
|
+
const special = this.client.getSpecialFolders(native);
|
|
107
|
+
this.specialFolders = special as any;
|
|
108
|
+
const result = native.map(f => toProviderFolder(f, this.specialFolders));
|
|
109
|
+
this.folderListCache = result;
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async fetchSince(folder: string, sinceUid: number, options?: FetchOptions): Promise<ProviderMessage[]> {
|
|
114
|
+
const msgs = await this.withRetry(
|
|
115
|
+
() => this.client.fetchMessagesSinceUid(folder, sinceUid, { source: !!options?.source }),
|
|
116
|
+
`fetchSince(${folder})`
|
|
117
|
+
);
|
|
118
|
+
return msgs.map(toProviderMessage);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async fetchByDate(folder: string, since: Date, before: Date, options?: FetchOptions,
|
|
122
|
+
onChunk?: (msgs: ProviderMessage[]) => void): Promise<ProviderMessage[]> {
|
|
123
|
+
const wrappedChunk = onChunk ? (raw: any[]) => onChunk(raw.map(toProviderMessage)) : undefined;
|
|
124
|
+
const msgs = await this.withRetry(
|
|
125
|
+
() => this.client.fetchMessageByDate(folder, since, before, { source: !!options?.source }, wrappedChunk),
|
|
126
|
+
`fetchByDate(${folder})`
|
|
127
|
+
);
|
|
128
|
+
return msgs.map(toProviderMessage);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async fetchByUids(folder: string, uids: number[], options?: FetchOptions): Promise<ProviderMessage[]> {
|
|
132
|
+
if (!uids.length) return [];
|
|
133
|
+
const range = uids.join(",");
|
|
134
|
+
const msgs = await this.withRetry(
|
|
135
|
+
() => this.client.fetchMessages(folder, range, { source: !!options?.source }),
|
|
136
|
+
`fetchByUids(${folder})`
|
|
137
|
+
);
|
|
138
|
+
return msgs.map(toProviderMessage);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async fetchOne(folder: string, uid: number, options?: FetchOptions): Promise<ProviderMessage | null> {
|
|
142
|
+
const msg = await this.withRetry(
|
|
143
|
+
() => this.client.fetchMessageByUid(folder, uid, { source: !!options?.source }),
|
|
144
|
+
`fetchOne(${folder}/${uid})`
|
|
145
|
+
);
|
|
146
|
+
return msg ? toProviderMessage(msg) : null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async getUids(folder: string): Promise<number[]> {
|
|
150
|
+
return this.withRetry(() => this.client.getUids(folder), `getUids(${folder})`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async close(): Promise<void> {
|
|
154
|
+
try { await this.client.logout(); } catch { /* ignore */ }
|
|
155
|
+
}
|
|
156
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bobfrankston/mailx-store-web
|
|
3
|
+
* WebAssembly SQLite + IndexedDB storage for Android/browser.
|
|
4
|
+
* Drop-in replacement for @bobfrankston/mailx-store in non-Node environments.
|
|
5
|
+
*/
|
|
6
|
+
export { WebMailxDB } from "./db.js";
|
|
7
|
+
export { WebMessageStore } from "./web-message-store.js";
|
|
8
|
+
export { WebMailxService } from "./web-service.js";
|
|
9
|
+
export { dispatch } from "./web-jsonrpc.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC"}
|
package/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bobfrankston/mailx-store-web
|
|
3
|
+
* WebAssembly SQLite + IndexedDB storage for Android/browser.
|
|
4
|
+
* Drop-in replacement for @bobfrankston/mailx-store in non-Node environments.
|
|
5
|
+
*/
|
|
6
|
+
export { WebMailxDB } from "./db.js";
|
|
7
|
+
export { WebMessageStore } from "./web-message-store.js";
|
|
8
|
+
export { WebMailxService } from "./web-service.js";
|
|
9
|
+
export { dispatch } from "./web-jsonrpc.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC"}
|
package/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bobfrankston/mailx-store-web
|
|
3
|
+
* WebAssembly SQLite + IndexedDB storage for Android/browser.
|
|
4
|
+
* Drop-in replacement for @bobfrankston/mailx-store in non-Node environments.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { WebMailxDB } from "./db.js";
|
|
8
|
+
export { WebMessageStore } from "./web-message-store.js";
|
|
9
|
+
export { WebMailxService } from "./web-service.js";
|
|
10
|
+
export { dispatch } from "./web-jsonrpc.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main thread Worker host — thin shim that keeps UI responsive.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* 1. Create Worker and proxy mailxapi calls to it
|
|
6
|
+
* 2. Proxy TCP calls from Worker to msgapi.tcp (C# native bridge)
|
|
7
|
+
* 3. Proxy native bridge calls (OAuth, device info)
|
|
8
|
+
* 4. Forward events from Worker to UI event handlers
|
|
9
|
+
* 5. Set up visibilitychange/periodic sync triggers
|
|
10
|
+
*
|
|
11
|
+
* The UI code (api-client.ts) calls window.mailxapi.* exactly as before —
|
|
12
|
+
* this host just routes those calls to the Worker instead of running them inline.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createWorkerHost(): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=main-thread-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main-thread-host.d.ts","sourceRoot":"","sources":["main-thread-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA4QH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAyCtD"}
|