@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.
Files changed (75) hide show
  1. package/android-bootstrap.d.ts +16 -0
  2. package/android-bootstrap.d.ts.map +1 -0
  3. package/android-bootstrap.js +1438 -0
  4. package/android-bootstrap.js.map +1 -0
  5. package/android-bootstrap.ts +1450 -0
  6. package/db.d.ts +146 -0
  7. package/db.d.ts.map +1 -0
  8. package/db.js +725 -0
  9. package/db.js.map +1 -0
  10. package/db.ts +831 -0
  11. package/gmail-api-web.d.ts +11 -0
  12. package/gmail-api-web.d.ts.map +1 -0
  13. package/gmail-api-web.js +11 -0
  14. package/gmail-api-web.js.map +1 -0
  15. package/gmail-api-web.ts +11 -0
  16. package/imap-web-provider.d.ts +33 -0
  17. package/imap-web-provider.d.ts.map +1 -0
  18. package/imap-web-provider.js +140 -0
  19. package/imap-web-provider.js.map +1 -0
  20. package/imap-web-provider.ts +156 -0
  21. package/index.d.ts +10 -0
  22. package/index.d.ts.map +1 -0
  23. package/index.js +10 -0
  24. package/index.js.map +1 -0
  25. package/index.ts +10 -0
  26. package/main-thread-host.d.ts +15 -0
  27. package/main-thread-host.d.ts.map +1 -0
  28. package/main-thread-host.js +292 -0
  29. package/main-thread-host.js.map +1 -0
  30. package/main-thread-host.ts +322 -0
  31. package/package.json +41 -0
  32. package/provider-types.d.ts +7 -0
  33. package/provider-types.d.ts.map +1 -0
  34. package/provider-types.js +7 -0
  35. package/provider-types.js.map +1 -0
  36. package/provider-types.ts +7 -0
  37. package/sql-wasm-esm.js +10 -0
  38. package/sql.js.d.ts +29 -0
  39. package/sync-manager.d.ts +68 -0
  40. package/sync-manager.d.ts.map +1 -0
  41. package/sync-manager.js +506 -0
  42. package/sync-manager.js.map +1 -0
  43. package/sync-manager.ts +508 -0
  44. package/tsconfig.json +10 -0
  45. package/web-jsonrpc.d.ts +20 -0
  46. package/web-jsonrpc.d.ts.map +1 -0
  47. package/web-jsonrpc.js +112 -0
  48. package/web-jsonrpc.js.map +1 -0
  49. package/web-jsonrpc.ts +126 -0
  50. package/web-message-store.d.ts +16 -0
  51. package/web-message-store.d.ts.map +1 -0
  52. package/web-message-store.js +89 -0
  53. package/web-message-store.js.map +1 -0
  54. package/web-message-store.ts +97 -0
  55. package/web-service.d.ts +136 -0
  56. package/web-service.d.ts.map +1 -0
  57. package/web-service.js +687 -0
  58. package/web-service.js.map +1 -0
  59. package/web-service.ts +754 -0
  60. package/web-settings.d.ts +91 -0
  61. package/web-settings.d.ts.map +1 -0
  62. package/web-settings.js +518 -0
  63. package/web-settings.js.map +1 -0
  64. package/web-settings.ts +547 -0
  65. package/worker-bundle.js +6838 -0
  66. package/worker-entry.d.ts +8 -0
  67. package/worker-entry.d.ts.map +1 -0
  68. package/worker-entry.js +218 -0
  69. package/worker-entry.js.map +1 -0
  70. package/worker-entry.ts +245 -0
  71. package/worker-tcp-transport.d.ts +28 -0
  72. package/worker-tcp-transport.d.ts.map +1 -0
  73. package/worker-tcp-transport.js +98 -0
  74. package/worker-tcp-transport.js.map +1 -0
  75. package/worker-tcp-transport.ts +101 -0
@@ -0,0 +1,292 @@
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
+ const eventHandlers = [];
15
+ let worker;
16
+ let rpcCounter = 0;
17
+ const pendingRpc = new Map();
18
+ // ── Worker communication ──
19
+ function sendRpc(action, params = {}) {
20
+ const id = ++rpcCounter;
21
+ return new Promise((resolve, reject) => {
22
+ pendingRpc.set(id, { resolve, reject });
23
+ worker.postMessage({ type: "rpc", id, action, params });
24
+ // 5-minute timeout for long operations like syncAll
25
+ setTimeout(() => {
26
+ if (pendingRpc.has(id)) {
27
+ pendingRpc.delete(id);
28
+ reject(new Error(`RPC ${action} timeout`));
29
+ }
30
+ }, 300000);
31
+ });
32
+ }
33
+ // ── TCP proxy: relay Worker TCP requests to msgapi.tcp ──
34
+ function handleTcpRequest(msg) {
35
+ const msgapi = window.msgapi;
36
+ if (!msgapi?.tcp) {
37
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, error: "TCP bridge not available" });
38
+ return;
39
+ }
40
+ switch (msg.op) {
41
+ case "connect": {
42
+ msgapi.tcp.connect(msg.host, msg.port, msg.tls)
43
+ .then((streamId) => {
44
+ const sid = Number(streamId);
45
+ // Register event forwarding for this stream
46
+ msgapi.tcp.onData(sid, (data) => {
47
+ worker.postMessage({ type: "tcp-data", streamId: sid, data });
48
+ });
49
+ msgapi.tcp.onClose(sid, (hadError) => {
50
+ worker.postMessage({ type: "tcp-close", streamId: sid, hadError });
51
+ });
52
+ msgapi.tcp.onError(sid, (message) => {
53
+ worker.postMessage({ type: "tcp-error", streamId: sid, message });
54
+ });
55
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, result: String(sid) });
56
+ })
57
+ .catch((e) => {
58
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, error: e.message });
59
+ });
60
+ break;
61
+ }
62
+ case "write": {
63
+ const data = msg.data;
64
+ // btoa for the bridge (C# expects base64)
65
+ msgapi.tcp.write(msg.streamId, data)
66
+ .then(() => {
67
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, result: "ok" });
68
+ })
69
+ .catch((e) => {
70
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, error: e.message });
71
+ });
72
+ break;
73
+ }
74
+ case "upgradeTLS": {
75
+ msgapi.tcp.upgradeTLS(msg.streamId, msg.servername)
76
+ .then(() => {
77
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, result: "ok" });
78
+ })
79
+ .catch((e) => {
80
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, error: e.message });
81
+ });
82
+ break;
83
+ }
84
+ case "close": {
85
+ try {
86
+ msgapi.tcp.close(msg.streamId);
87
+ }
88
+ catch { /* ignore */ }
89
+ break;
90
+ }
91
+ }
92
+ }
93
+ // ── Native bridge proxy: relay Worker native requests ──
94
+ function handleNativeRequest(msg) {
95
+ const bridge = window._nativeBridge;
96
+ switch (msg.op) {
97
+ case "refreshToken": {
98
+ const email = msg.args?.[0];
99
+ if (bridge?.app?.startOAuth) {
100
+ // Use the existing OAuth flow
101
+ // For now, use the cached token or refresh
102
+ bridge.app.refreshToken?.(email)
103
+ .then((token) => {
104
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, result: token });
105
+ })
106
+ .catch((e) => {
107
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, error: e.message });
108
+ });
109
+ }
110
+ else {
111
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, error: "No OAuth bridge" });
112
+ }
113
+ break;
114
+ }
115
+ case "vlog": {
116
+ // Verbose logging via logit
117
+ const text = msg.args?.[0] || "";
118
+ fetch(`https://rmf39.aaz.lt/logit/${encodeURIComponent("V/" + text)}?log=mailx-android`).catch(() => { });
119
+ break;
120
+ }
121
+ case "getAndroidId": {
122
+ bridge?.app?.getAndroidId?.()
123
+ .then((id) => {
124
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, result: id });
125
+ })
126
+ .catch((e) => {
127
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, error: e.message });
128
+ });
129
+ break;
130
+ }
131
+ case "getDeviceAccounts": {
132
+ bridge?.app?.getDeviceAccounts?.()
133
+ .then((accounts) => {
134
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, result: accounts });
135
+ })
136
+ .catch((e) => {
137
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, error: e.message });
138
+ });
139
+ break;
140
+ }
141
+ case "localStorageGet": {
142
+ const key = msg.args?.[0];
143
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, result: localStorage.getItem(key) });
144
+ break;
145
+ }
146
+ case "localStorageSet": {
147
+ const [key, value] = msg.args || [];
148
+ localStorage.setItem(key, value);
149
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, result: "ok" });
150
+ break;
151
+ }
152
+ default: {
153
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, error: `Unknown native op: ${msg.op}` });
154
+ }
155
+ }
156
+ }
157
+ // ── Event handling ──
158
+ function handleWorkerEvent(event) {
159
+ for (const handler of eventHandlers) {
160
+ try {
161
+ handler(event);
162
+ }
163
+ catch { /* ignore */ }
164
+ }
165
+ // Also dispatch to the global host service-channel event callback
166
+ const cb = window._msgapiServiceEvent;
167
+ if (cb)
168
+ try {
169
+ cb(event);
170
+ }
171
+ catch { /* ignore */ }
172
+ }
173
+ // ── Worker message router ──
174
+ function onWorkerMessage(e) {
175
+ const msg = e.data;
176
+ if (msg.type === "rpc-response") {
177
+ const pending = pendingRpc.get(msg.id);
178
+ if (pending) {
179
+ pendingRpc.delete(msg.id);
180
+ if (msg.error)
181
+ pending.reject(new Error(msg.error));
182
+ else
183
+ pending.resolve(msg.result);
184
+ }
185
+ return;
186
+ }
187
+ if (msg.type === "event") {
188
+ handleWorkerEvent(msg.event);
189
+ return;
190
+ }
191
+ if (msg.type === "tcp") {
192
+ handleTcpRequest(msg);
193
+ return;
194
+ }
195
+ if (msg.type === "native") {
196
+ handleNativeRequest(msg);
197
+ return;
198
+ }
199
+ if (msg.type === "init-complete") {
200
+ console.log("[host] Worker initialization complete");
201
+ return;
202
+ }
203
+ if (msg.type === "init-error") {
204
+ console.error(`[host] Worker init failed: ${msg.error}`);
205
+ return;
206
+ }
207
+ }
208
+ // ── Install mailxapi bridge (same interface as before, routes to Worker) ──
209
+ function installBridge() {
210
+ window.mailxapi = {
211
+ isApp: true,
212
+ platform: "android",
213
+ // All service methods proxy to the Worker
214
+ getAccounts: () => sendRpc("getAccounts"),
215
+ getFolders: (accountId) => sendRpc("getFolders", { accountId }),
216
+ getMessages: (accountId, folderId, page, pageSize) => sendRpc("getMessages", { accountId, folderId, page, pageSize }),
217
+ getUnifiedInbox: (page, pageSize) => sendRpc("getUnifiedInbox", { page, pageSize }),
218
+ getMessage: (accountId, uid, allowRemote, folderId) => sendRpc("getMessage", { accountId, uid, allowRemote, folderId }),
219
+ getAttachment: (accountId, uid, index, folderId) => sendRpc("getAttachment", { accountId, uid, index, folderId }),
220
+ updateFlags: (accountId, uid, flags) => sendRpc("updateFlags", { accountId, uid, flags }),
221
+ deleteMessage: (accountId, uid) => sendRpc("deleteMessage", { accountId, uid }),
222
+ deleteMessages: (accountId, uids) => sendRpc("deleteMessages", { accountId, uids }),
223
+ undeleteMessage: (accountId, uid, folderId) => sendRpc("undeleteMessage", { accountId, uid, folderId }),
224
+ moveMessage: (accountId, uid, targetFolderId, targetAccountId) => sendRpc("moveMessage", { accountId, uid, targetFolderId, targetAccountId }),
225
+ moveMessages: (accountId, uids, targetFolderId) => sendRpc("moveMessages", { accountId, uids, targetFolderId }),
226
+ markFolderRead: (folderId) => sendRpc("markFolderRead", { folderId }),
227
+ sendMessage: async (msg) => { await sendRpc("sendMessage", msg); return { ok: true }; },
228
+ saveDraft: (p) => sendRpc("saveDraft", p),
229
+ deleteDraft: (accountId, draftUid) => sendRpc("deleteDraft", { accountId, draftUid }),
230
+ syncAll: () => sendRpc("syncAll"),
231
+ syncAccount: (accountId) => sendRpc("syncAccount", { accountId }),
232
+ triggerSync: () => sendRpc("syncAll"),
233
+ getSyncPending: () => sendRpc("getSyncPending"),
234
+ reauthenticate: (accountId) => sendRpc("reauthenticate", { accountId }),
235
+ searchMessages: (query, page, pageSize) => sendRpc("searchMessages", { query, page, pageSize }),
236
+ searchContacts: (query) => sendRpc("searchContacts", { query }),
237
+ listContacts: (query, page = 1, pageSize = 100) => sendRpc("listContacts", { query, page, pageSize }),
238
+ upsertContact: (name, email) => sendRpc("upsertContact", { name, email }),
239
+ deleteContact: (email) => sendRpc("deleteContact", { email }),
240
+ addContact: (name, email) => sendRpc("addContact", { name, email }),
241
+ getSettings: () => sendRpc("getSettings"),
242
+ saveSettings: (data) => sendRpc("saveSettingsData", data),
243
+ allowRemoteContent: (type, value) => sendRpc("allowRemoteContent", { type, value }),
244
+ getVersion: () => sendRpc("getVersion"),
245
+ getAutocompleteSettings: () => sendRpc("getAutocompleteSettings"),
246
+ saveAutocompleteSettings: (data) => sendRpc("saveAutocompleteSettings", data),
247
+ emptyFolder: (accountId, folderId) => sendRpc("emptyFolder", { accountId, folderId }),
248
+ resetAll: () => sendRpc("resetStore"),
249
+ // Event registration
250
+ onEvent: (handler) => { eventHandlers.push(handler); },
251
+ };
252
+ }
253
+ // ── Public entry point ──
254
+ export async function createWorkerHost() {
255
+ console.log("[host] Creating service worker...");
256
+ // Wait for native bridge
257
+ await new Promise((resolve) => {
258
+ const check = () => {
259
+ if (window._nativeBridge) {
260
+ resolve();
261
+ return;
262
+ }
263
+ setTimeout(check, 50);
264
+ };
265
+ window.addEventListener("nativebridgeready", () => resolve(), { once: true });
266
+ check();
267
+ });
268
+ // Alias for TCP bridge
269
+ if (window._nativeBridge && !window.msgapi) {
270
+ window.msgapi = window._nativeBridge;
271
+ }
272
+ // Create Worker
273
+ // Note: Worker URL must point to the bundled worker entry.
274
+ // For now, use the same-origin asset path.
275
+ // Use bundled worker (esbuild inlines all dependencies — no import map needed)
276
+ worker = new Worker("../packages/mailx-store-web/worker-bundle.js");
277
+ worker.onmessage = onWorkerMessage;
278
+ worker.onerror = (e) => console.error("[host] Worker error:", e.message);
279
+ // Install the mailxapi bridge
280
+ installBridge();
281
+ // Tell Worker to initialize
282
+ worker.postMessage({ type: "init", config: {} });
283
+ // Sync on resume
284
+ document.addEventListener("visibilitychange", () => {
285
+ if (document.visibilityState === "visible") {
286
+ console.log("[sync] resume poll");
287
+ sendRpc("syncAll").catch(() => { });
288
+ }
289
+ });
290
+ console.log("[host] Worker host ready");
291
+ }
292
+ //# sourceMappingURL=main-thread-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main-thread-host.js","sourceRoot":"","sources":["main-thread-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,322 @@
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
+
15
+ const eventHandlers: ((event: any) => void)[] = [];
16
+ let worker: Worker;
17
+ let rpcCounter = 0;
18
+ const pendingRpc = new Map<number, { resolve: (v: any) => void; reject: (e: Error) => void }>();
19
+
20
+ // ── Worker communication ──
21
+
22
+ function sendRpc(action: string, params: any = {}): Promise<any> {
23
+ const id = ++rpcCounter;
24
+ return new Promise((resolve, reject) => {
25
+ pendingRpc.set(id, { resolve, reject });
26
+ worker.postMessage({ type: "rpc", id, action, params });
27
+ // 5-minute timeout for long operations like syncAll
28
+ setTimeout(() => {
29
+ if (pendingRpc.has(id)) {
30
+ pendingRpc.delete(id);
31
+ reject(new Error(`RPC ${action} timeout`));
32
+ }
33
+ }, 300000);
34
+ });
35
+ }
36
+
37
+ // ── TCP proxy: relay Worker TCP requests to msgapi.tcp ──
38
+
39
+ function handleTcpRequest(msg: any): void {
40
+ const msgapi = (window as any).msgapi;
41
+ if (!msgapi?.tcp) {
42
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, error: "TCP bridge not available" });
43
+ return;
44
+ }
45
+
46
+ switch (msg.op) {
47
+ case "connect": {
48
+ msgapi.tcp.connect(msg.host, msg.port, msg.tls)
49
+ .then((streamId: string) => {
50
+ const sid = Number(streamId);
51
+ // Register event forwarding for this stream
52
+ msgapi.tcp.onData(sid, (data: string) => {
53
+ worker.postMessage({ type: "tcp-data", streamId: sid, data });
54
+ });
55
+ msgapi.tcp.onClose(sid, (hadError: boolean) => {
56
+ worker.postMessage({ type: "tcp-close", streamId: sid, hadError });
57
+ });
58
+ msgapi.tcp.onError(sid, (message: string) => {
59
+ worker.postMessage({ type: "tcp-error", streamId: sid, message });
60
+ });
61
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, result: String(sid) });
62
+ })
63
+ .catch((e: any) => {
64
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, error: e.message });
65
+ });
66
+ break;
67
+ }
68
+ case "write": {
69
+ const data = msg.data;
70
+ // btoa for the bridge (C# expects base64)
71
+ msgapi.tcp.write(msg.streamId, data)
72
+ .then(() => {
73
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, result: "ok" });
74
+ })
75
+ .catch((e: any) => {
76
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, error: e.message });
77
+ });
78
+ break;
79
+ }
80
+ case "upgradeTLS": {
81
+ msgapi.tcp.upgradeTLS(msg.streamId, msg.servername)
82
+ .then(() => {
83
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, result: "ok" });
84
+ })
85
+ .catch((e: any) => {
86
+ worker.postMessage({ type: "tcp-response", reqId: msg.reqId, error: e.message });
87
+ });
88
+ break;
89
+ }
90
+ case "close": {
91
+ try { msgapi.tcp.close(msg.streamId); } catch { /* ignore */ }
92
+ break;
93
+ }
94
+ }
95
+ }
96
+
97
+ // ── Native bridge proxy: relay Worker native requests ──
98
+
99
+ function handleNativeRequest(msg: any): void {
100
+ const bridge = (window as any)._nativeBridge;
101
+
102
+ switch (msg.op) {
103
+ case "refreshToken": {
104
+ const email = msg.args?.[0];
105
+ if (bridge?.app?.startOAuth) {
106
+ // Use the existing OAuth flow
107
+ // For now, use the cached token or refresh
108
+ bridge.app.refreshToken?.(email)
109
+ .then((token: string) => {
110
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, result: token });
111
+ })
112
+ .catch((e: any) => {
113
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, error: e.message });
114
+ });
115
+ } else {
116
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, error: "No OAuth bridge" });
117
+ }
118
+ break;
119
+ }
120
+ case "vlog": {
121
+ // Verbose logging via logit
122
+ const text = msg.args?.[0] || "";
123
+ fetch(`https://rmf39.aaz.lt/logit/${encodeURIComponent("V/" + text)}?log=mailx-android`).catch(() => {});
124
+ break;
125
+ }
126
+ case "getAndroidId": {
127
+ bridge?.app?.getAndroidId?.()
128
+ .then((id: string) => {
129
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, result: id });
130
+ })
131
+ .catch((e: any) => {
132
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, error: e.message });
133
+ });
134
+ break;
135
+ }
136
+ case "getDeviceAccounts": {
137
+ bridge?.app?.getDeviceAccounts?.()
138
+ .then((accounts: any) => {
139
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, result: accounts });
140
+ })
141
+ .catch((e: any) => {
142
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, error: e.message });
143
+ });
144
+ break;
145
+ }
146
+ case "localStorageGet": {
147
+ const key = msg.args?.[0];
148
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, result: localStorage.getItem(key) });
149
+ break;
150
+ }
151
+ case "localStorageSet": {
152
+ const [key, value] = msg.args || [];
153
+ localStorage.setItem(key, value);
154
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, result: "ok" });
155
+ break;
156
+ }
157
+ default: {
158
+ worker.postMessage({ type: "native-response", reqId: msg.reqId, error: `Unknown native op: ${msg.op}` });
159
+ }
160
+ }
161
+ }
162
+
163
+ // ── Event handling ──
164
+
165
+ function handleWorkerEvent(event: any): void {
166
+ for (const handler of eventHandlers) {
167
+ try { handler(event); } catch { /* ignore */ }
168
+ }
169
+ // Also dispatch to the global host service-channel event callback
170
+ const cb = (window as any)._msgapiServiceEvent;
171
+ if (cb) try { cb(event); } catch { /* ignore */ }
172
+ }
173
+
174
+ // ── Worker message router ──
175
+
176
+ function onWorkerMessage(e: MessageEvent): void {
177
+ const msg = e.data;
178
+
179
+ if (msg.type === "rpc-response") {
180
+ const pending = pendingRpc.get(msg.id);
181
+ if (pending) {
182
+ pendingRpc.delete(msg.id);
183
+ if (msg.error) pending.reject(new Error(msg.error));
184
+ else pending.resolve(msg.result);
185
+ }
186
+ return;
187
+ }
188
+
189
+ if (msg.type === "event") {
190
+ handleWorkerEvent(msg.event);
191
+ return;
192
+ }
193
+
194
+ if (msg.type === "tcp") {
195
+ handleTcpRequest(msg);
196
+ return;
197
+ }
198
+
199
+ if (msg.type === "native") {
200
+ handleNativeRequest(msg);
201
+ return;
202
+ }
203
+
204
+ if (msg.type === "init-complete") {
205
+ console.log("[host] Worker initialization complete");
206
+ return;
207
+ }
208
+
209
+ if (msg.type === "init-error") {
210
+ console.error(`[host] Worker init failed: ${msg.error}`);
211
+ return;
212
+ }
213
+ }
214
+
215
+ // ── Install mailxapi bridge (same interface as before, routes to Worker) ──
216
+
217
+ function installBridge(): void {
218
+ (window as any).mailxapi = {
219
+ isApp: true,
220
+ platform: "android",
221
+
222
+ // All service methods proxy to the Worker
223
+ getAccounts: () => sendRpc("getAccounts"),
224
+ getFolders: (accountId: string) => sendRpc("getFolders", { accountId }),
225
+ getMessages: (accountId: string, folderId: number, page?: number, pageSize?: number) =>
226
+ sendRpc("getMessages", { accountId, folderId, page, pageSize }),
227
+ getUnifiedInbox: (page?: number, pageSize?: number) =>
228
+ sendRpc("getUnifiedInbox", { page, pageSize }),
229
+ getMessage: (accountId: string, uid: number, allowRemote?: boolean, folderId?: number) =>
230
+ sendRpc("getMessage", { accountId, uid, allowRemote, folderId }),
231
+ getAttachment: (accountId: string, uid: number, index: number, folderId?: number) =>
232
+ sendRpc("getAttachment", { accountId, uid, index, folderId }),
233
+ updateFlags: (accountId: string, uid: number, flags: any) =>
234
+ sendRpc("updateFlags", { accountId, uid, flags }),
235
+ deleteMessage: (accountId: string, uid: number) =>
236
+ sendRpc("deleteMessage", { accountId, uid }),
237
+ deleteMessages: (accountId: string, uids: number[]) =>
238
+ sendRpc("deleteMessages", { accountId, uids }),
239
+ undeleteMessage: (accountId: string, uid: number, folderId: number) =>
240
+ sendRpc("undeleteMessage", { accountId, uid, folderId }),
241
+ moveMessage: (accountId: string, uid: number, targetFolderId: number, targetAccountId?: string) =>
242
+ sendRpc("moveMessage", { accountId, uid, targetFolderId, targetAccountId }),
243
+ moveMessages: (accountId: string, uids: number[], targetFolderId: number) =>
244
+ sendRpc("moveMessages", { accountId, uids, targetFolderId }),
245
+ markFolderRead: (folderId: number) =>
246
+ sendRpc("markFolderRead", { folderId }),
247
+ sendMessage: async (msg: any) => { await sendRpc("sendMessage", msg); return { ok: true }; },
248
+ saveDraft: (p: any) => sendRpc("saveDraft", p),
249
+ deleteDraft: (accountId: string, draftUid: number) =>
250
+ sendRpc("deleteDraft", { accountId, draftUid }),
251
+ syncAll: () => sendRpc("syncAll"),
252
+ syncAccount: (accountId: string) => sendRpc("syncAccount", { accountId }),
253
+ triggerSync: () => sendRpc("syncAll"),
254
+ getSyncPending: () => sendRpc("getSyncPending"),
255
+ reauthenticate: (accountId: string) => sendRpc("reauthenticate", { accountId }),
256
+ searchMessages: (query: string, page?: number, pageSize?: number) =>
257
+ sendRpc("searchMessages", { query, page, pageSize }),
258
+ searchContacts: (query: string) => sendRpc("searchContacts", { query }),
259
+ listContacts: (query: string, page = 1, pageSize = 100) => sendRpc("listContacts", { query, page, pageSize }),
260
+ upsertContact: (name: string, email: string) => sendRpc("upsertContact", { name, email }),
261
+ deleteContact: (email: string) => sendRpc("deleteContact", { email }),
262
+ addContact: (name: string, email: string) => sendRpc("addContact", { name, email }),
263
+ getSettings: () => sendRpc("getSettings"),
264
+ saveSettings: (data: any) => sendRpc("saveSettingsData", data),
265
+ allowRemoteContent: (type: string, value: string) =>
266
+ sendRpc("allowRemoteContent", { type, value }),
267
+ getVersion: () => sendRpc("getVersion"),
268
+ getAutocompleteSettings: () => sendRpc("getAutocompleteSettings"),
269
+ saveAutocompleteSettings: (data: any) => sendRpc("saveAutocompleteSettings", data),
270
+ emptyFolder: (accountId: string, folderId: number) =>
271
+ sendRpc("emptyFolder", { accountId, folderId }),
272
+ resetAll: () => sendRpc("resetStore"),
273
+
274
+ // Event registration
275
+ onEvent: (handler: (event: any) => void) => { eventHandlers.push(handler); },
276
+ };
277
+ }
278
+
279
+ // ── Public entry point ──
280
+
281
+ export async function createWorkerHost(): Promise<void> {
282
+ console.log("[host] Creating service worker...");
283
+
284
+ // Wait for native bridge
285
+ await new Promise<void>((resolve) => {
286
+ const check = () => {
287
+ if ((window as any)._nativeBridge) { resolve(); return; }
288
+ setTimeout(check, 50);
289
+ };
290
+ window.addEventListener("nativebridgeready", () => resolve(), { once: true });
291
+ check();
292
+ });
293
+
294
+ // Alias for TCP bridge
295
+ if ((window as any)._nativeBridge && !(window as any).msgapi) {
296
+ (window as any).msgapi = (window as any)._nativeBridge;
297
+ }
298
+
299
+ // Create Worker
300
+ // Note: Worker URL must point to the bundled worker entry.
301
+ // For now, use the same-origin asset path.
302
+ // Use bundled worker (esbuild inlines all dependencies — no import map needed)
303
+ worker = new Worker("../packages/mailx-store-web/worker-bundle.js");
304
+ worker.onmessage = onWorkerMessage;
305
+ worker.onerror = (e) => console.error("[host] Worker error:", e.message);
306
+
307
+ // Install the mailxapi bridge
308
+ installBridge();
309
+
310
+ // Tell Worker to initialize
311
+ worker.postMessage({ type: "init", config: {} });
312
+
313
+ // Sync on resume
314
+ document.addEventListener("visibilitychange", () => {
315
+ if (document.visibilityState === "visible") {
316
+ console.log("[sync] resume poll");
317
+ sendRpc("syncAll").catch(() => {});
318
+ }
319
+ });
320
+
321
+ console.log("[host] Worker host ready");
322
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@bobfrankston/mailx-store-web",
3
+ "version": "0.1.5",
4
+ "type": "module",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc"
9
+ },
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "@bobfrankston/mailx-types": "^0.1.10",
13
+ "@bobfrankston/iflow-direct": "^0.1.39",
14
+ "@bobfrankston/tcp-transport": "^0.1.6",
15
+ "@bobfrankston/smtp-direct": "^0.1.8",
16
+ "@bobfrankston/mailx-sync": "^0.1.16",
17
+ "sql.js": "^1.14.1"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/BobFrankston/mailx-store-web.git"
22
+ },
23
+ ".dependencies": {
24
+ "@bobfrankston/mailx-types": "file:../mailx-types",
25
+ "@bobfrankston/iflow-direct": "file:../../../../MailApps/iflow-direct",
26
+ "@bobfrankston/tcp-transport": "file:../../../../MailApps/tcp-transport",
27
+ "@bobfrankston/smtp-direct": "file:../../../../MailApps/smtp-direct",
28
+ "@bobfrankston/mailx-sync": "file:../../../../MailApps/mailx-sync",
29
+ "sql.js": "^1.14.1"
30
+ },
31
+ ".transformedSnapshot": {
32
+ "dependencies": {
33
+ "@bobfrankston/mailx-types": "^0.1.10",
34
+ "@bobfrankston/iflow-direct": "^0.1.39",
35
+ "@bobfrankston/tcp-transport": "^0.1.6",
36
+ "@bobfrankston/smtp-direct": "^0.1.8",
37
+ "@bobfrankston/mailx-sync": "^0.1.16",
38
+ "sql.js": "^1.14.1"
39
+ }
40
+ }
41
+ }