@bobfrankston/mailx 1.0.450 → 1.0.452
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.map +1 -0
- package/bin/mailx.ts +1498 -0
- package/bin/postinstall.js.map +1 -0
- package/bin/postinstall.ts +41 -0
- package/bin/tsconfig.json +10 -0
- package/client/.gitattributes +10 -0
- package/client/app.js +51 -2
- package/client/app.js.map +1 -0
- package/client/app.ts +3112 -0
- package/client/components/address-book.js.map +1 -0
- package/client/components/address-book.ts +204 -0
- package/client/components/alarms.js.map +1 -0
- package/client/components/alarms.ts +276 -0
- package/client/components/calendar-sidebar.js.map +1 -0
- package/client/components/calendar-sidebar.ts +474 -0
- package/client/components/calendar.js.map +1 -0
- package/client/components/calendar.ts +211 -0
- package/client/components/context-menu.js.map +1 -0
- package/client/components/context-menu.ts +95 -0
- package/client/components/folder-picker.js.map +1 -0
- package/client/components/folder-picker.ts +127 -0
- package/client/components/folder-tree.js.map +1 -0
- package/client/components/folder-tree.ts +1069 -0
- package/client/components/message-list.js.map +1 -0
- package/client/components/message-list.ts +1129 -0
- package/client/components/message-viewer.js.map +1 -0
- package/client/components/message-viewer.ts +1257 -0
- package/client/components/outbox-view.js.map +1 -0
- package/client/components/outbox-view.ts +102 -0
- package/client/components/tasks.js.map +1 -0
- package/client/components/tasks.ts +234 -0
- package/client/compose/compose.js.map +1 -0
- package/client/compose/compose.ts +1231 -0
- package/client/compose/editor.js.map +1 -0
- package/client/compose/editor.ts +599 -0
- package/client/compose/ghost-text.js.map +1 -0
- package/client/compose/ghost-text.ts +140 -0
- package/client/index.html +1 -0
- package/client/lib/android-bootstrap.js.map +1 -0
- package/client/lib/android-bootstrap.ts +9 -0
- package/client/lib/api-client.js.map +1 -0
- package/client/lib/api-client.ts +439 -0
- package/client/lib/local-service.js.map +1 -0
- package/client/lib/local-service.ts +646 -0
- package/client/lib/local-store.js.map +1 -0
- package/client/lib/local-store.ts +283 -0
- package/client/lib/message-state.js.map +1 -0
- package/client/lib/message-state.ts +140 -0
- package/client/tsconfig.json +19 -0
- package/package.json +15 -15
- package/packages/mailx-api/.gitattributes +10 -0
- package/packages/mailx-api/index.d.ts.map +1 -0
- package/packages/mailx-api/index.js.map +1 -0
- package/packages/mailx-api/index.ts +283 -0
- package/packages/mailx-api/tsconfig.json +9 -0
- package/packages/mailx-compose/.gitattributes +10 -0
- package/packages/mailx-compose/index.d.ts.map +1 -0
- package/packages/mailx-compose/index.js.map +1 -0
- package/packages/mailx-compose/index.ts +85 -0
- package/packages/mailx-compose/tsconfig.json +9 -0
- package/packages/mailx-core/index.d.ts.map +1 -0
- package/packages/mailx-core/index.js.map +1 -0
- package/packages/mailx-core/index.ts +424 -0
- package/packages/mailx-core/ipc.d.ts.map +1 -0
- package/packages/mailx-core/ipc.js.map +1 -0
- package/packages/mailx-core/ipc.ts +62 -0
- package/packages/mailx-core/tsconfig.json +9 -0
- package/packages/mailx-host/.gitattributes +10 -0
- package/packages/mailx-host/index.d.ts.map +1 -0
- package/packages/mailx-host/index.js.map +1 -0
- package/packages/mailx-host/index.ts +38 -0
- package/packages/mailx-host/package.json +10 -2
- package/packages/mailx-host/tsconfig.json +9 -0
- package/packages/mailx-send/.gitattributes +10 -0
- package/packages/mailx-send/cli-queue.d.ts.map +1 -0
- package/packages/mailx-send/cli-queue.js.map +1 -0
- package/packages/mailx-send/cli-queue.ts +62 -0
- package/packages/mailx-send/cli-send.d.ts.map +1 -0
- package/packages/mailx-send/cli-send.js.map +1 -0
- package/packages/mailx-send/cli-send.ts +83 -0
- package/packages/mailx-send/cli.d.ts.map +1 -0
- package/packages/mailx-send/cli.js.map +1 -0
- package/packages/mailx-send/cli.ts +126 -0
- package/packages/mailx-send/index.d.ts.map +1 -0
- package/packages/mailx-send/index.js.map +1 -0
- package/packages/mailx-send/index.ts +333 -0
- package/packages/mailx-send/mailsend/cli.d.ts.map +1 -0
- package/packages/mailx-send/mailsend/cli.js.map +1 -0
- package/packages/mailx-send/mailsend/cli.ts +81 -0
- package/packages/mailx-send/mailsend/index.d.ts.map +1 -0
- package/packages/mailx-send/mailsend/index.js.map +1 -0
- package/packages/mailx-send/mailsend/index.ts +333 -0
- package/packages/mailx-send/mailsend/package-lock.json +65 -0
- package/packages/mailx-send/mailsend/tsconfig.json +21 -0
- package/packages/mailx-send/package-lock.json +65 -0
- package/packages/mailx-send/package.json +1 -1
- package/packages/mailx-send/tsconfig.json +21 -0
- package/packages/mailx-server/.gitattributes +10 -0
- package/packages/mailx-server/index.d.ts.map +1 -0
- package/packages/mailx-server/index.js.map +1 -0
- package/packages/mailx-server/index.ts +429 -0
- package/packages/mailx-server/tsconfig.json +9 -0
- package/packages/mailx-service/google-sync.d.ts.map +1 -0
- package/packages/mailx-service/google-sync.js.map +1 -0
- package/packages/mailx-service/google-sync.ts +238 -0
- package/packages/mailx-service/index.d.ts.map +1 -0
- package/packages/mailx-service/index.js.map +1 -0
- package/packages/mailx-service/index.ts +2461 -0
- package/packages/mailx-service/jsonrpc.d.ts.map +1 -0
- package/packages/mailx-service/jsonrpc.js.map +1 -0
- package/packages/mailx-service/jsonrpc.ts +268 -0
- package/packages/mailx-service/tsconfig.json +9 -0
- package/packages/mailx-settings/.gitattributes +10 -0
- package/packages/mailx-settings/cloud.d.ts.map +1 -0
- package/packages/mailx-settings/cloud.js.map +1 -0
- package/packages/mailx-settings/cloud.ts +388 -0
- package/packages/mailx-settings/index.d.ts.map +1 -0
- package/packages/mailx-settings/index.js.map +1 -0
- package/packages/mailx-settings/index.ts +892 -0
- package/packages/mailx-settings/tsconfig.json +9 -0
- package/packages/mailx-store/.gitattributes +10 -0
- package/packages/mailx-store/db.d.ts.map +1 -0
- package/packages/mailx-store/db.js.map +1 -0
- package/packages/mailx-store/db.ts +2007 -0
- package/packages/mailx-store/file-store.d.ts.map +1 -0
- package/packages/mailx-store/file-store.js.map +1 -0
- package/packages/mailx-store/file-store.ts +82 -0
- package/packages/mailx-store/index.d.ts.map +1 -0
- package/packages/mailx-store/index.js.map +1 -0
- package/packages/mailx-store/index.ts +7 -0
- package/packages/mailx-store/tsconfig.json +9 -0
- package/packages/mailx-store-web/android-bootstrap.d.ts.map +1 -0
- package/packages/mailx-store-web/android-bootstrap.js.map +1 -0
- package/packages/mailx-store-web/android-bootstrap.ts +1262 -0
- package/packages/mailx-store-web/db.d.ts.map +1 -0
- package/packages/mailx-store-web/db.js.map +1 -0
- package/packages/mailx-store-web/db.ts +756 -0
- package/packages/mailx-store-web/gmail-api-web.d.ts.map +1 -0
- package/packages/mailx-store-web/gmail-api-web.js.map +1 -0
- package/packages/mailx-store-web/gmail-api-web.ts +11 -0
- package/packages/mailx-store-web/imap-web-provider.d.ts.map +1 -0
- package/packages/mailx-store-web/imap-web-provider.js.map +1 -0
- package/packages/mailx-store-web/imap-web-provider.ts +156 -0
- package/packages/mailx-store-web/index.d.ts.map +1 -0
- package/packages/mailx-store-web/index.js.map +1 -0
- package/packages/mailx-store-web/index.ts +10 -0
- package/packages/mailx-store-web/main-thread-host.d.ts.map +1 -0
- package/packages/mailx-store-web/main-thread-host.js.map +1 -0
- package/packages/mailx-store-web/main-thread-host.ts +322 -0
- package/packages/mailx-store-web/package.json +4 -4
- package/packages/mailx-store-web/provider-types.d.ts.map +1 -0
- package/packages/mailx-store-web/provider-types.js.map +1 -0
- package/packages/mailx-store-web/provider-types.ts +7 -0
- package/packages/mailx-store-web/sync-manager.d.ts.map +1 -0
- package/packages/mailx-store-web/sync-manager.js.map +1 -0
- package/packages/mailx-store-web/sync-manager.ts +508 -0
- package/packages/mailx-store-web/tsconfig.json +10 -0
- package/packages/mailx-store-web/web-jsonrpc.d.ts.map +1 -0
- package/packages/mailx-store-web/web-jsonrpc.js.map +1 -0
- package/packages/mailx-store-web/web-jsonrpc.ts +116 -0
- package/packages/mailx-store-web/web-message-store.d.ts.map +1 -0
- package/packages/mailx-store-web/web-message-store.js.map +1 -0
- package/packages/mailx-store-web/web-message-store.ts +97 -0
- package/packages/mailx-store-web/web-service.d.ts.map +1 -0
- package/packages/mailx-store-web/web-service.js.map +1 -0
- package/packages/mailx-store-web/web-service.ts +616 -0
- package/packages/mailx-store-web/web-settings.d.ts.map +1 -0
- package/packages/mailx-store-web/web-settings.js.map +1 -0
- package/packages/mailx-store-web/web-settings.ts +522 -0
- package/packages/mailx-store-web/worker-entry.d.ts.map +1 -0
- package/packages/mailx-store-web/worker-entry.js.map +1 -0
- package/packages/mailx-store-web/worker-entry.ts +215 -0
- package/packages/mailx-store-web/worker-tcp-transport.d.ts.map +1 -0
- package/packages/mailx-store-web/worker-tcp-transport.js.map +1 -0
- package/packages/mailx-store-web/worker-tcp-transport.ts +101 -0
- package/packages/mailx-types/.gitattributes +10 -0
- package/packages/mailx-types/index.d.ts.map +1 -0
- package/packages/mailx-types/index.js.map +1 -0
- package/packages/mailx-types/index.ts +498 -0
- package/packages/mailx-types/tsconfig.json +9 -0
- package/tsconfig.base.json +2 -1
- package/tsconfig.json +9 -0
- package/build-apk.cmd +0 -3
- package/npmg.bat +0 -6
- package/packages/mailx-imap/index.d.ts +0 -442
- package/packages/mailx-imap/index.js +0 -3669
- package/packages/mailx-imap/package.json +0 -25
- package/packages/mailx-imap/providers/gmail-api.d.ts +0 -8
- package/packages/mailx-imap/providers/gmail-api.js +0 -8
- package/packages/mailx-imap/providers/types.d.ts +0 -9
- package/packages/mailx-imap/providers/types.js +0 -9
- package/packages/mailx-imap/tsconfig.tsbuildinfo +0 -1
- package/rebuild.cmd +0 -23
- package/tdview.cmd +0 -2
- package/temp.ps1 +0 -10
- package/test-smtp-direct.mjs +0 -4
- package/unbash.cmd +0 -55
- package/unwedge.cmd +0 -1
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local mail service — runs entirely in the WebView.
|
|
3
|
+
* Uses mailxapi.tcp bridge for IMAP, IndexedDB for storage.
|
|
4
|
+
* Implements the same MailxTransport interface so the client doesn't know the difference.
|
|
5
|
+
*
|
|
6
|
+
* This is the Android equivalent of the Express server + MailxService.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as store from "./local-store.js";
|
|
10
|
+
|
|
11
|
+
declare const mailxapi: {
|
|
12
|
+
isApp: boolean;
|
|
13
|
+
platform: string;
|
|
14
|
+
tcp: {
|
|
15
|
+
connect(host: string, port: number, tls: boolean): Promise<number>;
|
|
16
|
+
write(streamId: number, data: string): Promise<void>;
|
|
17
|
+
onData(streamId: number, callback: (data: string) => void): void;
|
|
18
|
+
onClose(streamId: number, callback: (hadError: boolean) => void): void;
|
|
19
|
+
onError(streamId: number, callback: (message: string) => void): void;
|
|
20
|
+
upgradeTLS(streamId: number, servername: string): Promise<void>;
|
|
21
|
+
close(streamId: number): void;
|
|
22
|
+
};
|
|
23
|
+
fs: {
|
|
24
|
+
read(path: string, options?: any): Promise<string>;
|
|
25
|
+
write(path: string, content: string): Promise<void>;
|
|
26
|
+
exists(path: string): Promise<boolean>;
|
|
27
|
+
list(path: string): Promise<string[]>;
|
|
28
|
+
delete(path: string): Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
http: {
|
|
31
|
+
fetch(url: string, init?: any): Promise<string>;
|
|
32
|
+
};
|
|
33
|
+
info: { platform: string; version: string };
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// ── IMAP Parsing Helpers ──
|
|
37
|
+
|
|
38
|
+
/** Extract a balanced parenthesized group starting at position */
|
|
39
|
+
function extractParenGroup(s: string, start: number): string {
|
|
40
|
+
if (s[start] !== "(") return "";
|
|
41
|
+
let depth = 0;
|
|
42
|
+
for (let i = start; i < s.length; i++) {
|
|
43
|
+
if (s[i] === "(") depth++;
|
|
44
|
+
else if (s[i] === ")") { depth--; if (depth === 0) return s.substring(start, i + 1); }
|
|
45
|
+
}
|
|
46
|
+
return s.substring(start);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Tokenize top-level items in a parenthesized IMAP list */
|
|
50
|
+
function tokenizeEnvelope(s: string): string[] {
|
|
51
|
+
const tokens: string[] = [];
|
|
52
|
+
const str = s.trim();
|
|
53
|
+
const start = str.startsWith("(") ? 1 : 0;
|
|
54
|
+
const end = str.endsWith(")") ? str.length - 1 : str.length;
|
|
55
|
+
let i = start;
|
|
56
|
+
while (i < end) {
|
|
57
|
+
while (i < end && str[i] === " ") i++;
|
|
58
|
+
if (i >= end) break;
|
|
59
|
+
if (str[i] === "(") {
|
|
60
|
+
let depth = 1, j = i + 1;
|
|
61
|
+
while (j < end && depth > 0) { if (str[j] === "(") depth++; else if (str[j] === ")") depth--; j++; }
|
|
62
|
+
tokens.push(str.substring(i, j));
|
|
63
|
+
i = j;
|
|
64
|
+
} else if (str[i] === '"') {
|
|
65
|
+
let j = i + 1;
|
|
66
|
+
while (j < end) { if (str[j] === "\\" && j + 1 < end) { j += 2; continue; } if (str[j] === '"') { j++; break; } j++; }
|
|
67
|
+
tokens.push(str.substring(i, j));
|
|
68
|
+
i = j;
|
|
69
|
+
} else if (str.substring(i, i + 3).toUpperCase() === "NIL") {
|
|
70
|
+
tokens.push("NIL"); i += 3;
|
|
71
|
+
} else {
|
|
72
|
+
let j = i;
|
|
73
|
+
while (j < end && str[j] !== " " && str[j] !== ")" && str[j] !== "(") j++;
|
|
74
|
+
tokens.push(str.substring(i, j));
|
|
75
|
+
i = j;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return tokens;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Remove quotes from an IMAP string */
|
|
82
|
+
function unquoteImap(s: string): string {
|
|
83
|
+
if (!s || s === "NIL") return "";
|
|
84
|
+
if (s.startsWith('"') && s.endsWith('"')) return s.slice(1, -1).replace(/\\(.)/g, "$1");
|
|
85
|
+
return s;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Parse IMAP address list: ((name NIL mailbox host)...) */
|
|
89
|
+
function parseImapAddressList(token: string): { name: string; address: string }[] {
|
|
90
|
+
if (!token || token === "NIL") return [];
|
|
91
|
+
const addrs: { name: string; address: string }[] = [];
|
|
92
|
+
const re = /\(([^)]*)\)/g;
|
|
93
|
+
let m;
|
|
94
|
+
while ((m = re.exec(token)) !== null) {
|
|
95
|
+
const parts = tokenizeEnvelope(m[1]);
|
|
96
|
+
if (parts.length >= 4) {
|
|
97
|
+
const name = unquoteImap(parts[0]);
|
|
98
|
+
const mailbox = unquoteImap(parts[2]);
|
|
99
|
+
const host = unquoteImap(parts[3]);
|
|
100
|
+
addrs.push({ name, address: mailbox && host ? `${mailbox}@${host}` : mailbox || "" });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return addrs;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Decode =?charset?encoding?text?= encoded words */
|
|
107
|
+
function decodeEncodedWords(s: string): string {
|
|
108
|
+
if (!s) return "";
|
|
109
|
+
return s.replace(/=\?([^?]+)\?([BQ])\?([^?]+)\?=/gi, (_match, _charset, encoding, text) => {
|
|
110
|
+
try {
|
|
111
|
+
if (encoding.toUpperCase() === "B") return atob(text);
|
|
112
|
+
return text.replace(/=([0-9A-F]{2})/gi, (_: string, hex: string) =>
|
|
113
|
+
String.fromCharCode(parseInt(hex, 16))).replace(/_/g, " ");
|
|
114
|
+
} catch { return text; }
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── Settings ──
|
|
119
|
+
|
|
120
|
+
interface AccountConfig {
|
|
121
|
+
id: string;
|
|
122
|
+
name: string;
|
|
123
|
+
email: string;
|
|
124
|
+
imap: { host: string; port: number; user: string; password?: string; auth?: string };
|
|
125
|
+
smtp: { host: string; port: number; user: string; password?: string; auth?: string };
|
|
126
|
+
enabled?: boolean;
|
|
127
|
+
defaultSend?: boolean;
|
|
128
|
+
label?: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
interface Settings {
|
|
132
|
+
name?: string;
|
|
133
|
+
accounts: AccountConfig[];
|
|
134
|
+
ui?: { theme?: string; editor?: string };
|
|
135
|
+
sync?: { intervalMinutes?: number; historyDays?: number };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let settings: Settings | null = null;
|
|
139
|
+
const eventHandlers: ((event: any) => void)[] = [];
|
|
140
|
+
|
|
141
|
+
function emit(event: any): void {
|
|
142
|
+
for (const h of eventHandlers) {
|
|
143
|
+
try { h(event); } catch { /* ignore */ }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── IMAP via Bridge ──
|
|
148
|
+
|
|
149
|
+
/** Minimal IMAP client that works through the mailxapi.tcp bridge */
|
|
150
|
+
class BridgeImapClient {
|
|
151
|
+
private streamId: number | null = null;
|
|
152
|
+
private buffer = "";
|
|
153
|
+
private dataResolve: ((data: string) => void) | null = null;
|
|
154
|
+
private tagCounter = 0;
|
|
155
|
+
|
|
156
|
+
async connect(host: string, port: number, tls: boolean): Promise<string> {
|
|
157
|
+
this.streamId = await mailxapi.tcp.connect(host, port, tls);
|
|
158
|
+
mailxapi.tcp.onData(this.streamId, (data: string) => {
|
|
159
|
+
this.buffer += data;
|
|
160
|
+
if (this.dataResolve && this.buffer.includes("\r\n")) {
|
|
161
|
+
const resolve = this.dataResolve;
|
|
162
|
+
this.dataResolve = null;
|
|
163
|
+
resolve(this.buffer);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
// Read greeting
|
|
167
|
+
return this.readLine();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private nextTag(): string {
|
|
171
|
+
return `A${++this.tagCounter}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private async readLine(): Promise<string> {
|
|
175
|
+
// Check buffer first
|
|
176
|
+
const idx = this.buffer.indexOf("\r\n");
|
|
177
|
+
if (idx >= 0) {
|
|
178
|
+
const line = this.buffer.substring(0, idx);
|
|
179
|
+
this.buffer = this.buffer.substring(idx + 2);
|
|
180
|
+
return line;
|
|
181
|
+
}
|
|
182
|
+
// Wait for data
|
|
183
|
+
return new Promise(resolve => {
|
|
184
|
+
this.dataResolve = () => {
|
|
185
|
+
const idx = this.buffer.indexOf("\r\n");
|
|
186
|
+
if (idx >= 0) {
|
|
187
|
+
const line = this.buffer.substring(0, idx);
|
|
188
|
+
this.buffer = this.buffer.substring(idx + 2);
|
|
189
|
+
resolve(line);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Read all responses until we get the tagged response */
|
|
196
|
+
private async readUntilTag(tag: string): Promise<string[]> {
|
|
197
|
+
const lines: string[] = [];
|
|
198
|
+
const timeout = setTimeout(() => {
|
|
199
|
+
// Force resolve on timeout
|
|
200
|
+
if (this.dataResolve) {
|
|
201
|
+
this.dataResolve("");
|
|
202
|
+
this.dataResolve = null;
|
|
203
|
+
}
|
|
204
|
+
}, 30000);
|
|
205
|
+
|
|
206
|
+
while (true) {
|
|
207
|
+
const line = await this.readLine();
|
|
208
|
+
lines.push(line);
|
|
209
|
+
if (line.startsWith(tag + " ")) {
|
|
210
|
+
clearTimeout(timeout);
|
|
211
|
+
return lines;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async command(cmd: string): Promise<string[]> {
|
|
217
|
+
const tag = this.nextTag();
|
|
218
|
+
const full = `${tag} ${cmd}\r\n`;
|
|
219
|
+
if (this.streamId == null) throw new Error("Not connected");
|
|
220
|
+
await mailxapi.tcp.write(this.streamId, full);
|
|
221
|
+
return this.readUntilTag(tag);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async login(user: string, pass: string): Promise<boolean> {
|
|
225
|
+
const resp = await this.command(`LOGIN "${user}" "${pass}"`);
|
|
226
|
+
return resp.some(l => l.includes(" OK "));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async xoauth2(user: string, token: string): Promise<boolean> {
|
|
230
|
+
const authStr = btoa(`user=${user}\x01auth=Bearer ${token}\x01\x01`);
|
|
231
|
+
const resp = await this.command(`AUTHENTICATE XOAUTH2 ${authStr}`);
|
|
232
|
+
return resp.some(l => l.includes(" OK "));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async list(): Promise<{ path: string; delimiter: string; flags: string[] }[]> {
|
|
236
|
+
const resp = await this.command('LIST "" "*"');
|
|
237
|
+
const folders: { path: string; delimiter: string; flags: string[] }[] = [];
|
|
238
|
+
for (const line of resp) {
|
|
239
|
+
const m = line.match(/^\* LIST \(([^)]*)\) "([^"]*)" (?:"([^"]+)"|(\S+))$/);
|
|
240
|
+
if (m) {
|
|
241
|
+
folders.push({
|
|
242
|
+
flags: m[1] ? m[1].split(/\s+/).filter(Boolean) : [],
|
|
243
|
+
delimiter: m[2] || ".",
|
|
244
|
+
path: m[3] || m[4] || "",
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return folders;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async select(mailbox: string): Promise<{ exists: number }> {
|
|
252
|
+
const resp = await this.command(`SELECT "${mailbox}"`);
|
|
253
|
+
let exists = 0;
|
|
254
|
+
for (const line of resp) {
|
|
255
|
+
const m = line.match(/^\* (\d+) EXISTS/);
|
|
256
|
+
if (m) exists = parseInt(m[1]);
|
|
257
|
+
}
|
|
258
|
+
return { exists };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async status(mailbox: string): Promise<{ messages: number }> {
|
|
262
|
+
const resp = await this.command(`STATUS "${mailbox}" (MESSAGES)`);
|
|
263
|
+
for (const line of resp) {
|
|
264
|
+
const m = line.match(/MESSAGES\s+(\d+)/);
|
|
265
|
+
if (m) return { messages: parseInt(m[1]) };
|
|
266
|
+
}
|
|
267
|
+
return { messages: 0 };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async fetchHeaders(range: string): Promise<any[]> {
|
|
271
|
+
const resp = await this.command(`UID FETCH ${range} (UID FLAGS ENVELOPE RFC822.SIZE INTERNALDATE)`);
|
|
272
|
+
const messages: any[] = [];
|
|
273
|
+
// Join all response lines to handle multi-line FETCH responses
|
|
274
|
+
const fullResp = resp.join("\r\n");
|
|
275
|
+
// Split on "* N FETCH" boundaries
|
|
276
|
+
const fetchBlocks = fullResp.split(/(?=\* \d+ FETCH)/);
|
|
277
|
+
for (const block of fetchBlocks) {
|
|
278
|
+
if (!block.includes("FETCH")) continue;
|
|
279
|
+
const uid = block.match(/UID\s+(\d+)/)?.[1];
|
|
280
|
+
if (!uid) continue;
|
|
281
|
+
const flags = block.match(/FLAGS\s+\(([^)]*)\)/)?.[1] || "";
|
|
282
|
+
const size = block.match(/RFC822\.SIZE\s+(\d+)/)?.[1] || "0";
|
|
283
|
+
const dateMatch = block.match(/INTERNALDATE\s+"([^"]+)"/);
|
|
284
|
+
|
|
285
|
+
// Parse ENVELOPE: (date subject from sender reply-to to cc bcc in-reply-to message-id)
|
|
286
|
+
let subject = "", fromName = "", fromAddr = "", messageId = "";
|
|
287
|
+
let toAddrs: { name: string; address: string }[] = [];
|
|
288
|
+
let ccAddrs: { name: string; address: string }[] = [];
|
|
289
|
+
const envStart = block.indexOf("ENVELOPE (");
|
|
290
|
+
if (envStart >= 0) {
|
|
291
|
+
const envStr = extractParenGroup(block, envStart + 9);
|
|
292
|
+
const tokens = tokenizeEnvelope(envStr);
|
|
293
|
+
// tokens[0]=date, [1]=subject, [2]=from, [3]=sender, [4]=reply-to, [5]=to, [6]=cc, [7]=bcc, [8]=in-reply-to, [9]=message-id
|
|
294
|
+
if (tokens.length >= 10) {
|
|
295
|
+
subject = unquoteImap(tokens[1]);
|
|
296
|
+
messageId = unquoteImap(tokens[9]);
|
|
297
|
+
const fromList = parseImapAddressList(tokens[2]);
|
|
298
|
+
if (fromList.length > 0) { fromName = fromList[0].name; fromAddr = fromList[0].address; }
|
|
299
|
+
toAddrs = parseImapAddressList(tokens[5]);
|
|
300
|
+
ccAddrs = parseImapAddressList(tokens[6]);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
messages.push({
|
|
305
|
+
uid: parseInt(uid),
|
|
306
|
+
flags: flags.split(/\s+/).filter(Boolean),
|
|
307
|
+
size: parseInt(size),
|
|
308
|
+
date: dateMatch ? new Date(dateMatch[1]).getTime() : Date.now(),
|
|
309
|
+
subject: decodeEncodedWords(subject),
|
|
310
|
+
fromName: decodeEncodedWords(fromName),
|
|
311
|
+
fromAddr,
|
|
312
|
+
messageId,
|
|
313
|
+
to: toAddrs,
|
|
314
|
+
cc: ccAddrs,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
return messages;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async fetchBody(uid: number): Promise<string> {
|
|
321
|
+
const tag = this.nextTag();
|
|
322
|
+
const cmd = `${tag} UID FETCH ${uid} (BODY.PEEK[])\r\n`;
|
|
323
|
+
if (this.streamId == null) throw new Error("Not connected");
|
|
324
|
+
await mailxapi.tcp.write(this.streamId, cmd);
|
|
325
|
+
|
|
326
|
+
// Read response including literal data
|
|
327
|
+
let allData = "";
|
|
328
|
+
const deadline = Date.now() + 30000;
|
|
329
|
+
while (Date.now() < deadline) {
|
|
330
|
+
const chunk = await this.readLine();
|
|
331
|
+
allData += chunk + "\r\n";
|
|
332
|
+
if (chunk.startsWith(tag + " ")) break;
|
|
333
|
+
}
|
|
334
|
+
return allData;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async logout(): Promise<void> {
|
|
338
|
+
try { await this.command("LOGOUT"); } catch { /* ignore */ }
|
|
339
|
+
if (this.streamId != null) {
|
|
340
|
+
mailxapi.tcp.close(this.streamId);
|
|
341
|
+
this.streamId = null;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async close(): Promise<void> {
|
|
346
|
+
try { await this.command("CLOSE"); } catch { /* ignore */ }
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async storeFlags(uid: number, action: string, flags: string[]): Promise<void> {
|
|
350
|
+
await this.command(`UID STORE ${uid} ${action} (${flags.join(" ")})`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async copy(uid: number, dest: string): Promise<void> {
|
|
354
|
+
await this.command(`UID COPY ${uid} "${dest}"`);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async expunge(): Promise<void> {
|
|
358
|
+
await this.command("EXPUNGE");
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async search(criteria: string): Promise<number[]> {
|
|
362
|
+
const resp = await this.command(`UID SEARCH ${criteria}`);
|
|
363
|
+
for (const line of resp) {
|
|
364
|
+
if (line.startsWith("* SEARCH")) {
|
|
365
|
+
return line.substring(9).trim().split(/\s+/).map(Number).filter(n => !isNaN(n));
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return [];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// ── Service Methods ──
|
|
373
|
+
|
|
374
|
+
async function loadSettingsFromStorage(): Promise<Settings> {
|
|
375
|
+
// Try IndexedDB meta first
|
|
376
|
+
const saved = await store.getMeta("settings");
|
|
377
|
+
if (saved) return saved;
|
|
378
|
+
// Default empty
|
|
379
|
+
return { accounts: [] };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async function saveSettingsToStorage(s: Settings): Promise<void> {
|
|
383
|
+
settings = s;
|
|
384
|
+
await store.setMeta("settings", s);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async function syncAccount(account: AccountConfig): Promise<void> {
|
|
388
|
+
const client = new BridgeImapClient();
|
|
389
|
+
try {
|
|
390
|
+
const greeting = await client.connect(
|
|
391
|
+
account.imap.host,
|
|
392
|
+
account.imap.port || 993,
|
|
393
|
+
(account.imap.port || 993) === 993
|
|
394
|
+
);
|
|
395
|
+
console.log(`[local-service] Connected to ${account.imap.host}: ${greeting}`);
|
|
396
|
+
|
|
397
|
+
// Authenticate
|
|
398
|
+
let authOk: boolean;
|
|
399
|
+
if (account.imap.auth === "oauth2") {
|
|
400
|
+
// TODO: OAuth token via bridge — for now skip
|
|
401
|
+
console.log("[local-service] OAuth not yet supported in bridge mode");
|
|
402
|
+
await client.logout();
|
|
403
|
+
return;
|
|
404
|
+
} else {
|
|
405
|
+
authOk = await client.login(account.imap.user, account.imap.password || "");
|
|
406
|
+
}
|
|
407
|
+
if (!authOk) { console.error("[local-service] Auth failed"); await client.logout(); return; }
|
|
408
|
+
|
|
409
|
+
emit({ type: "syncProgress", accountId: account.id, phase: "folders", progress: 0 });
|
|
410
|
+
|
|
411
|
+
// Get folder list
|
|
412
|
+
const folders = await client.list();
|
|
413
|
+
let nextFolderId = (await store.getMeta("nextFolderId")) || 1;
|
|
414
|
+
|
|
415
|
+
for (const f of folders) {
|
|
416
|
+
const existing = (await store.getFolders(account.id)).find(ef => ef.path === f.path);
|
|
417
|
+
if (!existing) {
|
|
418
|
+
const flags = f.flags.map(fl => fl.toLowerCase());
|
|
419
|
+
let specialUse = "";
|
|
420
|
+
if (flags.includes("\\inbox") || f.path.toLowerCase() === "inbox") specialUse = "inbox";
|
|
421
|
+
else if (flags.includes("\\sent")) specialUse = "sent";
|
|
422
|
+
else if (flags.includes("\\trash")) specialUse = "trash";
|
|
423
|
+
else if (flags.includes("\\drafts")) specialUse = "drafts";
|
|
424
|
+
else if (flags.includes("\\junk")) specialUse = "junk";
|
|
425
|
+
else if (flags.includes("\\archive")) specialUse = "archive";
|
|
426
|
+
|
|
427
|
+
await store.upsertFolder({
|
|
428
|
+
id: nextFolderId++,
|
|
429
|
+
accountId: account.id,
|
|
430
|
+
path: f.path,
|
|
431
|
+
delimiter: f.delimiter,
|
|
432
|
+
specialUse,
|
|
433
|
+
totalCount: 0,
|
|
434
|
+
unreadCount: 0,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
await store.setMeta("nextFolderId", nextFolderId);
|
|
439
|
+
|
|
440
|
+
emit({ type: "syncProgress", accountId: account.id, phase: "folders", progress: 100 });
|
|
441
|
+
|
|
442
|
+
// Sync inbox
|
|
443
|
+
const allFolders = await store.getFolders(account.id);
|
|
444
|
+
const inbox = allFolders.find(f => f.specialUse === "inbox");
|
|
445
|
+
if (inbox) {
|
|
446
|
+
await syncFolder(client, account.id, inbox);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Emit folder counts
|
|
450
|
+
const updatedFolders = await store.getFolders(account.id);
|
|
451
|
+
const counts: Record<number, { total: number; unread: number }> = {};
|
|
452
|
+
for (const f of updatedFolders) {
|
|
453
|
+
counts[f.id] = { total: f.totalCount, unread: f.unreadCount };
|
|
454
|
+
}
|
|
455
|
+
emit({ type: "folderCountsChanged", accountId: account.id, counts });
|
|
456
|
+
|
|
457
|
+
await client.logout();
|
|
458
|
+
} catch (e: any) {
|
|
459
|
+
console.error(`[local-service] Sync error: ${e.message}`);
|
|
460
|
+
emit({ type: "error", message: `${account.id}: ${e.message}` });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function syncFolder(client: BridgeImapClient, accountId: string, folder: { id: number; path: string }): Promise<void> {
|
|
465
|
+
emit({ type: "syncProgress", accountId, phase: `sync:${folder.path}`, progress: 0 });
|
|
466
|
+
|
|
467
|
+
const { exists } = await client.select(folder.path);
|
|
468
|
+
const highestUid = await store.getHighestUid(accountId, folder.id);
|
|
469
|
+
|
|
470
|
+
let msgs: any[];
|
|
471
|
+
if (highestUid > 0) {
|
|
472
|
+
msgs = await client.fetchHeaders(`${highestUid + 1}:*`);
|
|
473
|
+
msgs = msgs.filter(m => m.uid > highestUid);
|
|
474
|
+
} else {
|
|
475
|
+
// First sync — get recent messages
|
|
476
|
+
const uids = await client.search("ALL");
|
|
477
|
+
// Take last 200 UIDs (most recent)
|
|
478
|
+
const recent = uids.slice(-200);
|
|
479
|
+
if (recent.length > 0) {
|
|
480
|
+
msgs = await client.fetchHeaders(recent.join(","));
|
|
481
|
+
} else {
|
|
482
|
+
msgs = [];
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Store messages
|
|
487
|
+
let newCount = 0;
|
|
488
|
+
for (const msg of msgs) {
|
|
489
|
+
await store.upsertMessage({
|
|
490
|
+
key: `${accountId}:${folder.id}:${msg.uid}`,
|
|
491
|
+
accountId,
|
|
492
|
+
folderId: folder.id,
|
|
493
|
+
uid: msg.uid,
|
|
494
|
+
messageId: msg.messageId || "",
|
|
495
|
+
date: msg.date,
|
|
496
|
+
subject: msg.subject || "(no subject)",
|
|
497
|
+
fromName: msg.fromName || "",
|
|
498
|
+
fromAddress: msg.fromAddr || "",
|
|
499
|
+
toJson: JSON.stringify(msg.to || []),
|
|
500
|
+
ccJson: JSON.stringify(msg.cc || []),
|
|
501
|
+
flags: msg.flags.join(","),
|
|
502
|
+
size: msg.size,
|
|
503
|
+
hasAttachments: false,
|
|
504
|
+
preview: "",
|
|
505
|
+
bodyPath: "",
|
|
506
|
+
});
|
|
507
|
+
newCount++;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Update folder counts
|
|
511
|
+
const allMsgs = await store.getMessages(accountId, folder.id, 1, 999999);
|
|
512
|
+
const total = allMsgs.total;
|
|
513
|
+
const unread = allMsgs.items.filter((m: any) => !m.flags.includes("\\Seen")).length;
|
|
514
|
+
await store.updateFolderCounts(folder.id, total, unread);
|
|
515
|
+
|
|
516
|
+
emit({ type: "syncProgress", accountId, phase: `sync:${folder.path}`, progress: 100 });
|
|
517
|
+
|
|
518
|
+
if (newCount > 0) {
|
|
519
|
+
console.log(`[local-service] ${folder.path}: ${newCount} new messages`);
|
|
520
|
+
emit({ type: "folderCountsChanged", accountId, counts: { [folder.id]: { total, unread } } });
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
await client.close();
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// ── Transport Implementation ──
|
|
527
|
+
|
|
528
|
+
export interface LocalTransportCall {
|
|
529
|
+
method: string;
|
|
530
|
+
params: any;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export async function handleCall(method: string, params: any = {}): Promise<any> {
|
|
534
|
+
switch (method) {
|
|
535
|
+
case "getAccounts": {
|
|
536
|
+
const accounts = await store.getAccounts();
|
|
537
|
+
if (!settings) settings = await loadSettingsFromStorage();
|
|
538
|
+
return accounts.map(a => {
|
|
539
|
+
const cfg = settings!.accounts.find(s => s.id === a.id);
|
|
540
|
+
return { ...a, label: cfg?.label, defaultSend: cfg?.defaultSend || false };
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
case "getFolders":
|
|
544
|
+
return store.getFolders(params.accountId);
|
|
545
|
+
case "getMessages":
|
|
546
|
+
return store.getMessages(params.accountId, params.folderId, params.page, params.pageSize);
|
|
547
|
+
case "getUnifiedInbox":
|
|
548
|
+
return store.getUnifiedInbox(params.page, params.pageSize);
|
|
549
|
+
case "getMessage": {
|
|
550
|
+
const env = await store.getMessageByUid(params.accountId, params.uid, params.folderId);
|
|
551
|
+
if (!env) return { error: "not found" };
|
|
552
|
+
// Try to get body from IndexedDB
|
|
553
|
+
const body = await store.getBody(env.accountId, env.folderId, env.uid);
|
|
554
|
+
return { ...env, bodyHtml: "", bodyText: body || "[Body not yet downloaded]", hasRemoteContent: false, remoteAllowed: false, attachments: [], emlPath: "", deliveredTo: "", returnPath: "", listUnsubscribe: "" };
|
|
555
|
+
}
|
|
556
|
+
case "searchContacts":
|
|
557
|
+
return store.searchContacts(params.query || "");
|
|
558
|
+
case "getSyncPending":
|
|
559
|
+
return { pending: 0 };
|
|
560
|
+
case "triggerSync": {
|
|
561
|
+
if (!settings) settings = await loadSettingsFromStorage();
|
|
562
|
+
for (const account of settings.accounts) {
|
|
563
|
+
if (account.enabled !== false) {
|
|
564
|
+
syncAccount(account).catch(e => console.error(`[local-service] ${e.message}`));
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return { ok: true };
|
|
568
|
+
}
|
|
569
|
+
case "updateFlags":
|
|
570
|
+
// TODO: queue IMAP flag update
|
|
571
|
+
return { ok: true };
|
|
572
|
+
case "deleteMessage":
|
|
573
|
+
await store.deleteMessage(params.accountId, params.uid);
|
|
574
|
+
return { ok: true };
|
|
575
|
+
case "moveMessage":
|
|
576
|
+
// TODO: queue IMAP move
|
|
577
|
+
return { ok: true };
|
|
578
|
+
case "undeleteMessage":
|
|
579
|
+
return { ok: true };
|
|
580
|
+
case "searchMessages":
|
|
581
|
+
// Simple local search
|
|
582
|
+
return { items: [], total: 0, page: 1, pageSize: 50 };
|
|
583
|
+
case "allowRemoteContent":
|
|
584
|
+
return { ok: true };
|
|
585
|
+
case "markFolderRead":
|
|
586
|
+
return { ok: true };
|
|
587
|
+
case "restartServer":
|
|
588
|
+
case "rebuildServer":
|
|
589
|
+
return { ok: true };
|
|
590
|
+
case "createFolder":
|
|
591
|
+
case "renameFolder":
|
|
592
|
+
case "deleteFolder":
|
|
593
|
+
case "emptyFolder":
|
|
594
|
+
return { ok: true };
|
|
595
|
+
case "sendMessage":
|
|
596
|
+
case "saveDraft":
|
|
597
|
+
return { ok: true };
|
|
598
|
+
default:
|
|
599
|
+
console.warn(`[local-service] Unknown method: ${method}`);
|
|
600
|
+
return { error: `Unknown: ${method}` };
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
export function onEvent(handler: (event: any) => void): void {
|
|
605
|
+
eventHandlers.push(handler);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/** Initialize the local service — load settings, start sync */
|
|
609
|
+
export async function initialize(initialSettings?: Settings): Promise<void> {
|
|
610
|
+
await store.init();
|
|
611
|
+
|
|
612
|
+
if (initialSettings) {
|
|
613
|
+
await saveSettingsToStorage(initialSettings);
|
|
614
|
+
settings = initialSettings;
|
|
615
|
+
} else {
|
|
616
|
+
settings = await loadSettingsFromStorage();
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Register accounts in IndexedDB
|
|
620
|
+
for (const account of settings.accounts) {
|
|
621
|
+
await store.upsertAccount(account.id, account.name, account.email, JSON.stringify(account));
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
emit({ type: "connected" });
|
|
625
|
+
|
|
626
|
+
// Start initial sync
|
|
627
|
+
for (const account of settings.accounts) {
|
|
628
|
+
if (account.enabled !== false) {
|
|
629
|
+
syncAccount(account).catch(e => console.error(`[local-service] ${e.message}`));
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Periodic re-sync every 30 seconds
|
|
634
|
+
setInterval(async () => {
|
|
635
|
+
if (!settings) return;
|
|
636
|
+
for (const account of settings.accounts) {
|
|
637
|
+
if (account.enabled !== false) {
|
|
638
|
+
syncAccount(account).catch(e => console.error(`[local-service] periodic: ${e.message}`));
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}, 30000);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
export function getSettings(): Settings | null {
|
|
645
|
+
return settings;
|
|
646
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-store.js","sourceRoot":"","sources":["local-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,OAAO,GAAG,OAAO,CAAC;AACxB,MAAM,UAAU,GAAG,CAAC,CAAC;AAoDrB,IAAI,EAAE,GAAuB,IAAI,CAAC;AAElC,KAAK,UAAU,MAAM;IACjB,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAClB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnC,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAChD,GAAG,CAAC,eAAe,GAAG,GAAG,EAAE;YACvB,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;YACrB,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3C,CAAC,CAAC,iBAAiB,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACvD,CAAC;YACD,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC1C,MAAM,EAAE,GAAG,CAAC,CAAC,iBAAiB,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClF,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YAC7C,CAAC;YACD,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3C,MAAM,EAAE,GAAG,CAAC,CAAC,iBAAiB,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC/D,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;gBACzC,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;gBACvC,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;gBACpD,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACnC,CAAC;YACD,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACzC,CAAC,CAAC,iBAAiB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YACtD,CAAC;YACD,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3C,MAAM,EAAE,GAAG,CAAC,CAAC,iBAAiB,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;gBACjE,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACnC,CAAC;YACD,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvC,CAAC,CAAC,iBAAiB,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YACpD,CAAC;QACL,CAAC,CAAC;QACF,GAAG,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,GAAG,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,EAAE,CAAC,MAAyB,EAAE,OAA2B,UAAU;IACxE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO,EAAG,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,MAAM,CAAI,KAAa,EAAE,KAAc,EAAE,GAA+B;IAC7E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACvD,GAAG,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,GAAG,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,GAAG,CAAC,KAAa,EAAE,KAAU;IAClC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QACjC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;QAC/B,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,GAAG,CAAC,KAAa,EAAE,GAAgB;IACxC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QACjC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;QAC/B,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACP,CAAC;AAED,wEAAwE;AAExE,MAAM,CAAC,KAAK,UAAU,IAAI;IACtB,MAAM,MAAM,EAAE,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC7B,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAU,EAAE,IAAY,EAAE,KAAa,EAAE,MAAc;IACvF,MAAM,GAAG,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,SAAiB;IAC9C,OAAO,MAAM,CAAC,SAAS,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAoB;IACnD,MAAM,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,SAAiB,EAAE,QAAgB,EAAE,IAAI,GAAG,CAAC,EAAE,QAAQ,GAAG,EAAE;IAC1F,MAAM,GAAG,GAAG,MAAM,MAAM,CAAgB,UAAU,EAAE,QAAQ,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IACrF,0BAA0B;IAC1B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC;IACzB,MAAM,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IACpC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACjE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAI,GAAG,CAAC,EAAE,QAAQ,GAAG,EAAE;IACzD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAe,SAAS,CAAC,CAAC;IACtD,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC;IACnE,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAgB,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACpF,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC;IAC7B,MAAM,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACrE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,SAAiB,EAAE,GAAW,EAAE,QAAiB;IACnF,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACnB,MAAM,GAAG,GAAG,GAAG,SAAS,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC;QAC9C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnC,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC;YACzB,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC/C,GAAG,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC1E,GAAG,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;IACP,CAAC;IACD,qBAAqB;IACrB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAgB,UAAU,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;IAC5E,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IACzC,OAAO,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAkB;IAClD,MAAM,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB,EAAE,GAAW;IAC9D,MAAM,GAAG,GAAG,MAAM,MAAM,CAAgB,UAAU,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;IAC5E,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IACzC,IAAI,GAAG;QAAE,MAAM,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB,EAAE,QAAgB;IACnE,MAAM,IAAI,GAAG,MAAM,MAAM,CAAgB,UAAU,EAAE,QAAQ,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IACtF,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,SAAiB,EAAE,QAAgB,EAAE,GAAW,EAAE,IAAY;IAC1F,MAAM,GAAG,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,SAAS,IAAI,QAAQ,IAAI,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,SAAiB,EAAE,QAAgB,EAAE,GAAW;IAC1E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnC,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC;QACvB,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC,CAAC;QAC3E,GAAG,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC;QACxD,GAAG,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACP,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,QAAgB,EAAE,KAAa,EAAE,MAAc;IACpF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAe,SAAS,CAAC,CAAC;IACtD,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IAC/C,IAAI,CAAC,EAAE,CAAC;QAAC,CAAC,CAAC,UAAU,GAAG,KAAK,CAAC;QAAC,CAAC,CAAC,WAAW,GAAG,MAAM,CAAC;QAAC,MAAM,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAAC,CAAC;AACrF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAa;IAC9C,MAAM,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAgB,UAAU,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC/G,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAa,EAAE,IAAY,EAAE,MAAc;IAC3E,MAAM,QAAQ,GAAG,MAAM,IAAI,OAAO,CAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC9E,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC;QACzB,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACjD,GAAG,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,GAAG,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IACH,IAAI,QAAQ,EAAE,CAAC;QACX,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACpB,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/B,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI;YAAE,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;QACjD,MAAM,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;SAAM,CAAC;QACJ,MAAM,GAAG,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACtF,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAW;IACrC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnC,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3C,GAAG,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACjD,GAAG,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACP,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAW,EAAE,KAAU;IACjD,MAAM,GAAG,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,gBAAgB;AAEhB,SAAS,UAAU,CAAC,CAAgB;IAChC,OAAO;QACH,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,WAAW,EAAE;QAClD,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC;QAChC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC;QAChC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;QACxD,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,cAAc,EAAE,CAAC,CAAC,cAAc;QAChC,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;KACvB,CAAC;AACN,CAAC"}
|