@bobfrankston/rmfmail 1.0.467

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 (330) hide show
  1. package/README.md +429 -0
  2. package/bin/build-icon-ico.js +283 -0
  3. package/bin/lean-accounts.js +172 -0
  4. package/bin/mailx.js +1580 -0
  5. package/bin/mailx.js.map +1 -0
  6. package/bin/mailx.ts +1549 -0
  7. package/bin/postinstall.js +39 -0
  8. package/bin/postinstall.js.map +1 -0
  9. package/bin/postinstall.ts +41 -0
  10. package/bin/tsconfig.json +10 -0
  11. package/client/.gitattributes +10 -0
  12. package/client/.msger-window.json +1 -0
  13. package/client/android.html +226 -0
  14. package/client/app.js +3400 -0
  15. package/client/app.js.map +1 -0
  16. package/client/app.ts +3190 -0
  17. package/client/components/address-book.js +199 -0
  18. package/client/components/address-book.js.map +1 -0
  19. package/client/components/address-book.ts +204 -0
  20. package/client/components/alarms.js +299 -0
  21. package/client/components/alarms.js.map +1 -0
  22. package/client/components/alarms.ts +276 -0
  23. package/client/components/calendar-sidebar.js +508 -0
  24. package/client/components/calendar-sidebar.js.map +1 -0
  25. package/client/components/calendar-sidebar.ts +474 -0
  26. package/client/components/calendar.js +217 -0
  27. package/client/components/calendar.js.map +1 -0
  28. package/client/components/calendar.ts +211 -0
  29. package/client/components/context-menu.js +80 -0
  30. package/client/components/context-menu.js.map +1 -0
  31. package/client/components/context-menu.ts +95 -0
  32. package/client/components/folder-picker.js +119 -0
  33. package/client/components/folder-picker.js.map +1 -0
  34. package/client/components/folder-picker.ts +127 -0
  35. package/client/components/folder-tree.js +1093 -0
  36. package/client/components/folder-tree.js.map +1 -0
  37. package/client/components/folder-tree.ts +1069 -0
  38. package/client/components/message-list.js +1140 -0
  39. package/client/components/message-list.js.map +1 -0
  40. package/client/components/message-list.ts +1129 -0
  41. package/client/components/message-viewer.js +1291 -0
  42. package/client/components/message-viewer.js.map +1 -0
  43. package/client/components/message-viewer.ts +1257 -0
  44. package/client/components/outbox-view.js +104 -0
  45. package/client/components/outbox-view.js.map +1 -0
  46. package/client/components/outbox-view.ts +102 -0
  47. package/client/components/tasks.js +261 -0
  48. package/client/components/tasks.js.map +1 -0
  49. package/client/components/tasks.ts +234 -0
  50. package/client/compose/compose.css +560 -0
  51. package/client/compose/compose.html +52 -0
  52. package/client/compose/compose.js +1289 -0
  53. package/client/compose/compose.js.map +1 -0
  54. package/client/compose/compose.ts +1231 -0
  55. package/client/compose/editor.js +616 -0
  56. package/client/compose/editor.js.map +1 -0
  57. package/client/compose/editor.ts +599 -0
  58. package/client/compose/ghost-text.js +122 -0
  59. package/client/compose/ghost-text.js.map +1 -0
  60. package/client/compose/ghost-text.ts +140 -0
  61. package/client/favicon.svg +11 -0
  62. package/client/icon.ico +0 -0
  63. package/client/icon.png +0 -0
  64. package/client/index.html +230 -0
  65. package/client/lib/android-bootstrap.js +9 -0
  66. package/client/lib/android-bootstrap.js.map +1 -0
  67. package/client/lib/android-bootstrap.ts +9 -0
  68. package/client/lib/api-client.js +378 -0
  69. package/client/lib/api-client.js.map +1 -0
  70. package/client/lib/api-client.ts +439 -0
  71. package/client/lib/local-service.js +603 -0
  72. package/client/lib/local-service.js.map +1 -0
  73. package/client/lib/local-service.ts +646 -0
  74. package/client/lib/local-store.js +214 -0
  75. package/client/lib/local-store.js.map +1 -0
  76. package/client/lib/local-store.ts +283 -0
  77. package/client/lib/mailxapi.js +304 -0
  78. package/client/lib/message-state.js +132 -0
  79. package/client/lib/message-state.js.map +1 -0
  80. package/client/lib/message-state.ts +160 -0
  81. package/client/package.json +16 -0
  82. package/client/styles/components.css +2049 -0
  83. package/client/styles/layout.css +332 -0
  84. package/client/styles/variables.css +85 -0
  85. package/client/tsconfig.json +19 -0
  86. package/package.json +124 -0
  87. package/packages/mailx-api/.gitattributes +10 -0
  88. package/packages/mailx-api/index.d.ts +10 -0
  89. package/packages/mailx-api/index.d.ts.map +1 -0
  90. package/packages/mailx-api/index.js +322 -0
  91. package/packages/mailx-api/index.js.map +1 -0
  92. package/packages/mailx-api/index.ts +283 -0
  93. package/packages/mailx-api/package.json +27 -0
  94. package/packages/mailx-api/tsconfig.json +9 -0
  95. package/packages/mailx-api/tsconfig.tsbuildinfo +1 -0
  96. package/packages/mailx-compose/.gitattributes +10 -0
  97. package/packages/mailx-compose/index.d.ts +21 -0
  98. package/packages/mailx-compose/index.d.ts.map +1 -0
  99. package/packages/mailx-compose/index.js +61 -0
  100. package/packages/mailx-compose/index.js.map +1 -0
  101. package/packages/mailx-compose/index.ts +85 -0
  102. package/packages/mailx-compose/package.json +18 -0
  103. package/packages/mailx-compose/tsconfig.json +9 -0
  104. package/packages/mailx-core/index.d.ts +139 -0
  105. package/packages/mailx-core/index.d.ts.map +1 -0
  106. package/packages/mailx-core/index.js +403 -0
  107. package/packages/mailx-core/index.js.map +1 -0
  108. package/packages/mailx-core/index.ts +424 -0
  109. package/packages/mailx-core/ipc.d.ts +13 -0
  110. package/packages/mailx-core/ipc.d.ts.map +1 -0
  111. package/packages/mailx-core/ipc.js +56 -0
  112. package/packages/mailx-core/ipc.js.map +1 -0
  113. package/packages/mailx-core/ipc.ts +62 -0
  114. package/packages/mailx-core/package.json +18 -0
  115. package/packages/mailx-core/tsconfig.json +9 -0
  116. package/packages/mailx-host/.gitattributes +10 -0
  117. package/packages/mailx-host/index.d.ts +21 -0
  118. package/packages/mailx-host/index.d.ts.map +1 -0
  119. package/packages/mailx-host/index.js +29 -0
  120. package/packages/mailx-host/index.js.map +1 -0
  121. package/packages/mailx-host/index.ts +38 -0
  122. package/packages/mailx-host/package.json +23 -0
  123. package/packages/mailx-host/tsconfig.json +9 -0
  124. package/packages/mailx-host/types-shim.d.ts +14 -0
  125. package/packages/mailx-imap/.gitattributes +10 -0
  126. package/packages/mailx-imap/index.d.ts +442 -0
  127. package/packages/mailx-imap/index.d.ts.map +1 -0
  128. package/packages/mailx-imap/index.js +3684 -0
  129. package/packages/mailx-imap/index.js.map +1 -0
  130. package/packages/mailx-imap/index.ts +3652 -0
  131. package/packages/mailx-imap/package-lock.json +131 -0
  132. package/packages/mailx-imap/package.json +28 -0
  133. package/packages/mailx-imap/providers/gmail-api.d.ts +8 -0
  134. package/packages/mailx-imap/providers/gmail-api.d.ts.map +1 -0
  135. package/packages/mailx-imap/providers/gmail-api.js +8 -0
  136. package/packages/mailx-imap/providers/gmail-api.js.map +1 -0
  137. package/packages/mailx-imap/providers/gmail-api.ts +8 -0
  138. package/packages/mailx-imap/providers/outlook-api.ts +7 -0
  139. package/packages/mailx-imap/providers/types.d.ts +9 -0
  140. package/packages/mailx-imap/providers/types.d.ts.map +1 -0
  141. package/packages/mailx-imap/providers/types.js +9 -0
  142. package/packages/mailx-imap/providers/types.js.map +1 -0
  143. package/packages/mailx-imap/providers/types.ts +9 -0
  144. package/packages/mailx-imap/tsconfig.json +9 -0
  145. package/packages/mailx-imap/tsconfig.tsbuildinfo +1 -0
  146. package/packages/mailx-send/.gitattributes +10 -0
  147. package/packages/mailx-send/README.md +217 -0
  148. package/packages/mailx-send/cli-queue.d.ts +11 -0
  149. package/packages/mailx-send/cli-queue.d.ts.map +1 -0
  150. package/packages/mailx-send/cli-queue.js +59 -0
  151. package/packages/mailx-send/cli-queue.js.map +1 -0
  152. package/packages/mailx-send/cli-queue.ts +62 -0
  153. package/packages/mailx-send/cli-send.d.ts +17 -0
  154. package/packages/mailx-send/cli-send.d.ts.map +1 -0
  155. package/packages/mailx-send/cli-send.js +75 -0
  156. package/packages/mailx-send/cli-send.js.map +1 -0
  157. package/packages/mailx-send/cli-send.ts +83 -0
  158. package/packages/mailx-send/cli.d.ts +22 -0
  159. package/packages/mailx-send/cli.d.ts.map +1 -0
  160. package/packages/mailx-send/cli.js +115 -0
  161. package/packages/mailx-send/cli.js.map +1 -0
  162. package/packages/mailx-send/cli.ts +126 -0
  163. package/packages/mailx-send/index.d.ts +77 -0
  164. package/packages/mailx-send/index.d.ts.map +1 -0
  165. package/packages/mailx-send/index.js +264 -0
  166. package/packages/mailx-send/index.js.map +1 -0
  167. package/packages/mailx-send/index.ts +333 -0
  168. package/packages/mailx-send/mailsend/README.md +133 -0
  169. package/packages/mailx-send/mailsend/cli.d.ts +12 -0
  170. package/packages/mailx-send/mailsend/cli.d.ts.map +1 -0
  171. package/packages/mailx-send/mailsend/cli.js +70 -0
  172. package/packages/mailx-send/mailsend/cli.js.map +1 -0
  173. package/packages/mailx-send/mailsend/cli.ts +81 -0
  174. package/packages/mailx-send/mailsend/index.d.ts +77 -0
  175. package/packages/mailx-send/mailsend/index.d.ts.map +1 -0
  176. package/packages/mailx-send/mailsend/index.js +264 -0
  177. package/packages/mailx-send/mailsend/index.js.map +1 -0
  178. package/packages/mailx-send/mailsend/index.ts +333 -0
  179. package/packages/mailx-send/mailsend/package-lock.json +65 -0
  180. package/packages/mailx-send/mailsend/package.json +26 -0
  181. package/packages/mailx-send/mailsend/tsconfig.json +21 -0
  182. package/packages/mailx-send/package-lock.json +65 -0
  183. package/packages/mailx-send/package.json +37 -0
  184. package/packages/mailx-send/tsconfig.json +21 -0
  185. package/packages/mailx-server/.gitattributes +10 -0
  186. package/packages/mailx-server/index.d.ts +8 -0
  187. package/packages/mailx-server/index.d.ts.map +1 -0
  188. package/packages/mailx-server/index.js +397 -0
  189. package/packages/mailx-server/index.js.map +1 -0
  190. package/packages/mailx-server/index.ts +429 -0
  191. package/packages/mailx-server/package.json +28 -0
  192. package/packages/mailx-server/tsconfig.json +9 -0
  193. package/packages/mailx-server/tsconfig.tsbuildinfo +1 -0
  194. package/packages/mailx-service/google-sync.d.ts +96 -0
  195. package/packages/mailx-service/google-sync.d.ts.map +1 -0
  196. package/packages/mailx-service/google-sync.js +153 -0
  197. package/packages/mailx-service/google-sync.js.map +1 -0
  198. package/packages/mailx-service/google-sync.ts +238 -0
  199. package/packages/mailx-service/index.d.ts +366 -0
  200. package/packages/mailx-service/index.d.ts.map +1 -0
  201. package/packages/mailx-service/index.js +2589 -0
  202. package/packages/mailx-service/index.js.map +1 -0
  203. package/packages/mailx-service/index.ts +2512 -0
  204. package/packages/mailx-service/jsonrpc.d.ts +25 -0
  205. package/packages/mailx-service/jsonrpc.d.ts.map +1 -0
  206. package/packages/mailx-service/jsonrpc.js +238 -0
  207. package/packages/mailx-service/jsonrpc.js.map +1 -0
  208. package/packages/mailx-service/jsonrpc.ts +268 -0
  209. package/packages/mailx-service/package.json +22 -0
  210. package/packages/mailx-service/tsconfig.json +9 -0
  211. package/packages/mailx-settings/.gitattributes +10 -0
  212. package/packages/mailx-settings/cloud.d.ts +47 -0
  213. package/packages/mailx-settings/cloud.d.ts.map +1 -0
  214. package/packages/mailx-settings/cloud.js +408 -0
  215. package/packages/mailx-settings/cloud.js.map +1 -0
  216. package/packages/mailx-settings/cloud.ts +393 -0
  217. package/packages/mailx-settings/index.d.ts +140 -0
  218. package/packages/mailx-settings/index.d.ts.map +1 -0
  219. package/packages/mailx-settings/index.js +872 -0
  220. package/packages/mailx-settings/index.js.map +1 -0
  221. package/packages/mailx-settings/index.ts +908 -0
  222. package/packages/mailx-settings/package.json +20 -0
  223. package/packages/mailx-settings/tsconfig.json +9 -0
  224. package/packages/mailx-settings/tsconfig.tsbuildinfo +1 -0
  225. package/packages/mailx-store/.gitattributes +10 -0
  226. package/packages/mailx-store/db.d.ts +346 -0
  227. package/packages/mailx-store/db.d.ts.map +1 -0
  228. package/packages/mailx-store/db.js +1796 -0
  229. package/packages/mailx-store/db.js.map +1 -0
  230. package/packages/mailx-store/db.ts +2007 -0
  231. package/packages/mailx-store/file-store.d.ts +36 -0
  232. package/packages/mailx-store/file-store.d.ts.map +1 -0
  233. package/packages/mailx-store/file-store.js +80 -0
  234. package/packages/mailx-store/file-store.js.map +1 -0
  235. package/packages/mailx-store/file-store.ts +82 -0
  236. package/packages/mailx-store/index.d.ts +7 -0
  237. package/packages/mailx-store/index.d.ts.map +1 -0
  238. package/packages/mailx-store/index.js +7 -0
  239. package/packages/mailx-store/index.js.map +1 -0
  240. package/packages/mailx-store/index.ts +7 -0
  241. package/packages/mailx-store/package.json +19 -0
  242. package/packages/mailx-store/tsconfig.json +9 -0
  243. package/packages/mailx-store/tsconfig.tsbuildinfo +1 -0
  244. package/packages/mailx-store-web/android-bootstrap.d.ts +16 -0
  245. package/packages/mailx-store-web/android-bootstrap.d.ts.map +1 -0
  246. package/packages/mailx-store-web/android-bootstrap.js +1247 -0
  247. package/packages/mailx-store-web/android-bootstrap.js.map +1 -0
  248. package/packages/mailx-store-web/android-bootstrap.ts +1262 -0
  249. package/packages/mailx-store-web/db.d.ts +132 -0
  250. package/packages/mailx-store-web/db.d.ts.map +1 -0
  251. package/packages/mailx-store-web/db.js +642 -0
  252. package/packages/mailx-store-web/db.js.map +1 -0
  253. package/packages/mailx-store-web/db.ts +756 -0
  254. package/packages/mailx-store-web/gmail-api-web.d.ts +11 -0
  255. package/packages/mailx-store-web/gmail-api-web.d.ts.map +1 -0
  256. package/packages/mailx-store-web/gmail-api-web.js +11 -0
  257. package/packages/mailx-store-web/gmail-api-web.js.map +1 -0
  258. package/packages/mailx-store-web/gmail-api-web.ts +11 -0
  259. package/packages/mailx-store-web/imap-web-provider.d.ts +33 -0
  260. package/packages/mailx-store-web/imap-web-provider.d.ts.map +1 -0
  261. package/packages/mailx-store-web/imap-web-provider.js +140 -0
  262. package/packages/mailx-store-web/imap-web-provider.js.map +1 -0
  263. package/packages/mailx-store-web/imap-web-provider.ts +156 -0
  264. package/packages/mailx-store-web/index.d.ts +10 -0
  265. package/packages/mailx-store-web/index.d.ts.map +1 -0
  266. package/packages/mailx-store-web/index.js +10 -0
  267. package/packages/mailx-store-web/index.js.map +1 -0
  268. package/packages/mailx-store-web/index.ts +10 -0
  269. package/packages/mailx-store-web/main-thread-host.d.ts +15 -0
  270. package/packages/mailx-store-web/main-thread-host.d.ts.map +1 -0
  271. package/packages/mailx-store-web/main-thread-host.js +292 -0
  272. package/packages/mailx-store-web/main-thread-host.js.map +1 -0
  273. package/packages/mailx-store-web/main-thread-host.ts +322 -0
  274. package/packages/mailx-store-web/package.json +23 -0
  275. package/packages/mailx-store-web/provider-types.d.ts +7 -0
  276. package/packages/mailx-store-web/provider-types.d.ts.map +1 -0
  277. package/packages/mailx-store-web/provider-types.js +7 -0
  278. package/packages/mailx-store-web/provider-types.js.map +1 -0
  279. package/packages/mailx-store-web/provider-types.ts +7 -0
  280. package/packages/mailx-store-web/sql-wasm-esm.js +10 -0
  281. package/packages/mailx-store-web/sql.js.d.ts +29 -0
  282. package/packages/mailx-store-web/sync-manager.d.ts +68 -0
  283. package/packages/mailx-store-web/sync-manager.d.ts.map +1 -0
  284. package/packages/mailx-store-web/sync-manager.js +506 -0
  285. package/packages/mailx-store-web/sync-manager.js.map +1 -0
  286. package/packages/mailx-store-web/sync-manager.ts +508 -0
  287. package/packages/mailx-store-web/tsconfig.json +10 -0
  288. package/packages/mailx-store-web/web-jsonrpc.d.ts +20 -0
  289. package/packages/mailx-store-web/web-jsonrpc.d.ts.map +1 -0
  290. package/packages/mailx-store-web/web-jsonrpc.js +102 -0
  291. package/packages/mailx-store-web/web-jsonrpc.js.map +1 -0
  292. package/packages/mailx-store-web/web-jsonrpc.ts +116 -0
  293. package/packages/mailx-store-web/web-message-store.d.ts +16 -0
  294. package/packages/mailx-store-web/web-message-store.d.ts.map +1 -0
  295. package/packages/mailx-store-web/web-message-store.js +89 -0
  296. package/packages/mailx-store-web/web-message-store.js.map +1 -0
  297. package/packages/mailx-store-web/web-message-store.ts +97 -0
  298. package/packages/mailx-store-web/web-service.d.ts +111 -0
  299. package/packages/mailx-store-web/web-service.d.ts.map +1 -0
  300. package/packages/mailx-store-web/web-service.js +542 -0
  301. package/packages/mailx-store-web/web-service.js.map +1 -0
  302. package/packages/mailx-store-web/web-service.ts +616 -0
  303. package/packages/mailx-store-web/web-settings.d.ts +83 -0
  304. package/packages/mailx-store-web/web-settings.d.ts.map +1 -0
  305. package/packages/mailx-store-web/web-settings.js +491 -0
  306. package/packages/mailx-store-web/web-settings.js.map +1 -0
  307. package/packages/mailx-store-web/web-settings.ts +522 -0
  308. package/packages/mailx-store-web/worker-bundle.js +6838 -0
  309. package/packages/mailx-store-web/worker-entry.d.ts +8 -0
  310. package/packages/mailx-store-web/worker-entry.d.ts.map +1 -0
  311. package/packages/mailx-store-web/worker-entry.js +187 -0
  312. package/packages/mailx-store-web/worker-entry.js.map +1 -0
  313. package/packages/mailx-store-web/worker-entry.ts +215 -0
  314. package/packages/mailx-store-web/worker-tcp-transport.d.ts +28 -0
  315. package/packages/mailx-store-web/worker-tcp-transport.d.ts.map +1 -0
  316. package/packages/mailx-store-web/worker-tcp-transport.js +98 -0
  317. package/packages/mailx-store-web/worker-tcp-transport.js.map +1 -0
  318. package/packages/mailx-store-web/worker-tcp-transport.ts +101 -0
  319. package/packages/mailx-types/.gitattributes +10 -0
  320. package/packages/mailx-types/index.d.ts +319 -0
  321. package/packages/mailx-types/index.d.ts.map +1 -0
  322. package/packages/mailx-types/index.js +243 -0
  323. package/packages/mailx-types/index.js.map +1 -0
  324. package/packages/mailx-types/index.ts +498 -0
  325. package/packages/mailx-types/package.json +16 -0
  326. package/packages/mailx-types/tsconfig.json +9 -0
  327. package/packages/mailx-types/tsconfig.tsbuildinfo +1 -0
  328. package/todo.json +13 -0
  329. package/tsconfig.base.json +31 -0
  330. package/tsconfig.json +9 -0
@@ -0,0 +1,429 @@
1
+ /**
2
+ * @bobfrankston/mailx-server
3
+ * Express server: serves client, mounts API, manages WebSocket push.
4
+ */
5
+
6
+ import express from "express";
7
+ import { WebSocketServer, WebSocket } from "ws";
8
+ import * as path from "node:path";
9
+ import * as fs from "node:fs";
10
+ import { MailxDB } from "@bobfrankston/mailx-store";
11
+ import { ImapManager } from "@bobfrankston/mailx-imap";
12
+ import { createApiRouter } from "@bobfrankston/mailx-api";
13
+ import { loadSettings, loadAccountsAsync, getConfigDir, getStorePath, getSharedDir, getStorageInfo } from "@bobfrankston/mailx-settings";
14
+ import type { WsEvent } from "@bobfrankston/mailx-types";
15
+ import { ports } from "@bobfrankston/miscinfo";
16
+ import { createServer } from "node:http";
17
+
18
+ const PORT = ports.mailx;
19
+
20
+ // ── File logging ──
21
+ const logDir = path.join(process.env.USERPROFILE || process.env.HOME || ".", ".rmfmail", "logs");
22
+ fs.mkdirSync(logDir, { recursive: true });
23
+ // Rotate: delete logs older than 7 days
24
+ try {
25
+ for (const f of fs.readdirSync(logDir).filter(f => f.startsWith("mailx-") && f.endsWith(".log"))) {
26
+ const stat = fs.statSync(path.join(logDir, f));
27
+ if (Date.now() - stat.mtimeMs > 7 * 86400000) fs.unlinkSync(path.join(logDir, f));
28
+ }
29
+ } catch { /* ignore cleanup errors */ }
30
+ const logDate = new Date().toISOString().slice(0, 10);
31
+ const logPath = path.join(logDir, `mailx-${logDate}.log`);
32
+ const logStream = fs.createWriteStream(logPath, { flags: "a" });
33
+ const origLog = console.log;
34
+ const origErr = console.error;
35
+ function logTs(): string { return new Date().toLocaleString("se", { hour12: false }); }
36
+ console.log = (...args: any[]) => {
37
+ const msg = `${logTs()} ${args.join(" ")}`;
38
+ origLog(msg);
39
+ logStream.write(msg + "\n");
40
+ };
41
+ console.error = (...args: any[]) => {
42
+ const msg = `${logTs()} ERROR ${args.join(" ")}`;
43
+ origErr(msg);
44
+ logStream.write(msg + "\n");
45
+ };
46
+ // Read version from root package.json (the published version)
47
+ const rootPkg = JSON.parse(fs.readFileSync(path.join(import.meta.dirname, "..", "..", "package.json"), "utf-8"));
48
+ const SERVER_VERSION = rootPkg.version;
49
+
50
+ // ── Initialize ──
51
+ // No initLocalConfig() here — config is created when user adds an account (initCloudConfig).
52
+ // On fresh install, loadSettings() returns defaults and the setup form handles configuration.
53
+
54
+ let settings = loadSettings();
55
+ if (settings.accounts.length === 0) {
56
+ // Try cloud API fallback (Google Drive, OneDrive) if filesystem mount not available
57
+ const cloudAccounts = await loadAccountsAsync();
58
+ if (cloudAccounts.length > 0) {
59
+ settings = { ...settings, accounts: cloudAccounts };
60
+ console.log(` Loaded ${cloudAccounts.length} account(s) from cloud API`);
61
+ } else {
62
+ console.log(" No accounts configured. Open http://127.0.0.1:9333 to add your email account.");
63
+ }
64
+ }
65
+ const dbDir = getConfigDir();
66
+ const db = new MailxDB(dbDir);
67
+ import { NodeTransport } from "@bobfrankston/iflow-node";
68
+ const imapManager = new ImapManager(db, () => new NodeTransport());
69
+
70
+ // ── Express App ──
71
+
72
+ const app = express();
73
+ app.use(express.json({ limit: "Infinity" }));
74
+
75
+ // Optional token gate — required whenever the server binds to anything other
76
+ // than a loopback address. Set via `MAILX_SERVER_TOKEN=<secret>` (or the
77
+ // shorter `MAILX_TOKEN`). When the server is loopback-only (the default), the
78
+ // gate is a no-op since nothing outside the machine can reach it. When bound
79
+ // externally (`MAILX_SERVER_HOST=0.0.0.0` or `--external`), connections must
80
+ // present the token in either `?t=<token>` or the `x-mailx-token` header. No
81
+ // token configured + external bind → server refuses to start (safer than
82
+ // serving open).
83
+ const SERVER_TOKEN = process.env.MAILX_SERVER_TOKEN || process.env.MAILX_TOKEN || "";
84
+ const SERVER_HOST = process.env.MAILX_SERVER_HOST || "";
85
+ const IS_EXTERNAL_BIND = SERVER_HOST && SERVER_HOST !== "127.0.0.1" && SERVER_HOST !== "localhost" && SERVER_HOST !== "::1";
86
+ if (IS_EXTERNAL_BIND && !SERVER_TOKEN) {
87
+ console.error("[server] Refusing to bind externally without MAILX_SERVER_TOKEN. Set the env var or drop the external host.");
88
+ process.exit(2);
89
+ }
90
+ app.use((req, res, next) => {
91
+ if (!IS_EXTERNAL_BIND) return next();
92
+ // Allow the bare static file fetch so the login page can render; every
93
+ // /api/* path requires the token.
94
+ if (!req.path.startsWith("/api/")) return next();
95
+ const provided = (req.query.t as string) || req.header("x-mailx-token") || "";
96
+ if (provided !== SERVER_TOKEN) {
97
+ res.status(401).json({ error: "missing or invalid token" });
98
+ return;
99
+ }
100
+ next();
101
+ });
102
+
103
+ // Request logging
104
+ app.use((req, res, next) => {
105
+ const start = Date.now();
106
+ res.on("finish", () => {
107
+ const ms = Date.now() - start;
108
+ // Skip noisy polling endpoints
109
+ if (req.path.endsWith("/sync/pending")) return;
110
+ console.log(` ${req.method} ${req.path} ${res.statusCode} ${ms}ms`);
111
+ });
112
+ next();
113
+ });
114
+
115
+ // Serve client static files
116
+ const clientDir = path.join(import.meta.dirname, "..", "..", "client");
117
+ const rootDir = path.join(import.meta.dirname, "..", "..");
118
+ app.use(express.static(clientDir, { etag: false, lastModified: false, maxAge: 0 }));
119
+ app.use("/node_modules", express.static(path.join(rootDir, "node_modules"), { etag: false, lastModified: false, maxAge: 0 }));
120
+
121
+ // Mount API
122
+ const apiRouter = createApiRouter(db, imapManager);
123
+ app.use("/api", apiRouter);
124
+
125
+ app.get("/api/version", (req, res) => {
126
+ const storage = getStorageInfo();
127
+ const imapAccounts = imapManager.getAccountCount();
128
+ const dbAccounts = db.getAccounts().length;
129
+ // Warn if DB has accounts but IMAP has none — stale DB, settings missing
130
+ const settingsError = (dbAccounts > 0 && imapAccounts === 0)
131
+ ? "No accounts loaded from settings — showing stale data. Check accounts.jsonc on your cloud drive."
132
+ : undefined;
133
+ res.json({ version: SERVER_VERSION, theme: settings.ui?.theme || "system", storage, imapAccounts, hasAccounts: imapAccounts > 0 || dbAccounts > 0, settingsError });
134
+ });
135
+
136
+ app.all("/info", (req, res) => {
137
+ res.json({ version: SERVER_VERSION, uptime: Math.round(process.uptime()), port: PORT, imap: imapManager.useNativeClient ? "native" : "imapflow" });
138
+ });
139
+
140
+ app.get("/status", (req, res) => {
141
+ const accounts = db.getAccounts();
142
+ const pendingSync = db.getTotalPendingSyncCount();
143
+ const uptime = process.uptime();
144
+ const mem = process.memoryUsage();
145
+
146
+ const accountInfo = accounts.map((a: any) => {
147
+ const folders = db.getFolders(a.id);
148
+ const inbox = folders.find((f: any) => f.specialUse === "inbox");
149
+ return {
150
+ id: a.id, name: a.name, folders: folders.length,
151
+ inboxTotal: inbox?.totalCount || 0,
152
+ inboxUnread: inbox?.unreadCount || 0,
153
+ lastSync: a.lastSync ? new Date(a.lastSync).toLocaleString() : "never",
154
+ };
155
+ });
156
+
157
+ res.type("html").send(`<!DOCTYPE html>
158
+ <html><head><meta charset="UTF-8"><title>mailx status</title>
159
+ <meta http-equiv="refresh" content="10">
160
+ <style>body{font-family:system-ui;padding:2rem;max-width:600px;color:#cdd6f4;background:#1e1e2e}
161
+ table{border-collapse:collapse;width:100%}td,th{text-align:left;padding:4px 12px;border-bottom:1px solid #333}
162
+ h1{font-size:1.2rem}h2{font-size:1rem;margin-top:1.5rem}.ok{color:#a6e3a1}.warn{color:#f9e2af}
163
+ a{color:#89b4fa}</style></head>
164
+ <body>
165
+ <h1>mailx status</h1>
166
+ <p>mailx v${SERVER_VERSION}</p>
167
+ <p>Uptime: ${Math.floor(uptime / 3600)}h ${Math.floor((uptime % 3600) / 60)}m | Memory: ${Math.round(mem.rss / 1048576)} MB</p>
168
+ <p>Pending sync: <span class="${pendingSync > 0 ? "warn" : "ok"}">${pendingSync}</span></p>
169
+ <h2>Accounts</h2>
170
+ <table><tr><th>Account</th><th>Folders</th><th>Inbox</th><th>Unread</th><th>Last Sync</th></tr>
171
+ ${accountInfo.map((a: any) => `<tr><td>${a.name}</td><td>${a.folders}</td><td>${a.inboxTotal}</td><td>${a.inboxUnread}</td><td>${a.lastSync}</td></tr>`).join("")}
172
+ </table>
173
+ <p style="margin-top:2rem;font-size:0.8rem"><a href="/">Open mailx</a> | Auto-refreshes every 10s</p>
174
+ </body></html>`);
175
+ });
176
+
177
+ // Graceful exit — close IMAP, DB, HTTP then exit
178
+ app.all("/api/exit", (req, res) => {
179
+ res.json({ ok: true });
180
+ setTimeout(() => shutdown(), 100);
181
+ });
182
+
183
+ // Restart server + reload clients
184
+ app.post("/api/restart", (req, res) => {
185
+ res.json({ ok: true });
186
+ broadcast({ type: "reload" });
187
+ setTimeout(async () => {
188
+ console.log(" Restart requested via API");
189
+ await shutdown();
190
+ }, 500);
191
+ });
192
+
193
+ // Rebuild: wipe DB + message store, keep accounts/settings, restart
194
+ app.post("/api/rebuild", (req, res) => {
195
+ res.json({ ok: true });
196
+ broadcast({ type: "reload" });
197
+ setTimeout(async () => {
198
+ console.log(" Rebuild requested — wiping DB and message store...");
199
+ imapManager.stopPeriodicSync();
200
+ try { await imapManager.shutdown(); } catch { /* proceed */ }
201
+ db.close();
202
+
203
+ // Remove DB files
204
+ const dbDir = getConfigDir();
205
+ for (const f of ["mailx.db", "mailx.db-wal", "mailx.db-shm"]) {
206
+ const p = path.join(dbDir, f);
207
+ if (fs.existsSync(p)) { fs.unlinkSync(p); console.log(` Deleted ${f}`); }
208
+ }
209
+
210
+ // Remove message store
211
+ const storePath = getStorePath();
212
+ if (fs.existsSync(storePath)) {
213
+ fs.rmSync(storePath, { recursive: true });
214
+ console.log(` Deleted ${storePath}`);
215
+ }
216
+
217
+ console.log(" Rebuild complete — restarting...");
218
+ server?.close();
219
+ process.exit(0);
220
+ }, 500);
221
+ });
222
+
223
+ // SPA fallback
224
+ app.get("*", (req, res) => {
225
+ if (!req.path.startsWith("/api"))
226
+ res.sendFile(path.join(clientDir, "index.html"));
227
+ });
228
+
229
+ // JSON error handler — all errors return JSON, never HTML
230
+ app.use((err: any, _req: any, res: any, _next: any) => {
231
+ console.error(`ERROR ${err.message}`);
232
+ const status = err.status || err.statusCode || 500;
233
+ res.status(status).json({ error: err.message || "Internal server error" });
234
+ });
235
+
236
+ // ── HTTP Server ──
237
+
238
+ let server: ReturnType<typeof createServer>;
239
+ let wss: WebSocketServer;
240
+
241
+ const clients = new Set<WebSocket>();
242
+
243
+ function wireWebSocket(): void {
244
+ wss.on("connection", (ws) => {
245
+ clients.add(ws);
246
+ (ws as any).__alive = true;
247
+ const connected: WsEvent = { type: "connected" };
248
+ ws.send(JSON.stringify(connected));
249
+
250
+ ws.on("pong", () => { (ws as any).__alive = true; });
251
+ ws.on("close", () => clients.delete(ws));
252
+ });
253
+
254
+ // Heartbeat — detect dead connections, prevent CLOSE_WAIT zombie accumulation
255
+ setInterval(() => {
256
+ for (const ws of clients) {
257
+ if ((ws as any).__alive === false) {
258
+ clients.delete(ws);
259
+ ws.terminate();
260
+ continue;
261
+ }
262
+ (ws as any).__alive = false;
263
+ ws.ping();
264
+ }
265
+ }, 30000);
266
+ }
267
+
268
+ function broadcast(event: WsEvent): void {
269
+ const data = JSON.stringify(event);
270
+ for (const ws of clients) {
271
+ if (ws.readyState === WebSocket.OPEN)
272
+ ws.send(data);
273
+ }
274
+ }
275
+
276
+ // Wire IMAP events to WebSocket broadcasts
277
+ imapManager.on("syncProgress", (accountId: string, phase: string, progress: number) => {
278
+ broadcast({ type: "syncProgress", accountId, phase, progress });
279
+ });
280
+
281
+ imapManager.on("folderCountsChanged", (accountId: string, counts: Record<number, { total: number; unread: number }>) => {
282
+ broadcast({ type: "folderCountsChanged", accountId, counts });
283
+ });
284
+
285
+ imapManager.on("folderSynced", (accountId: string, folderId: number, syncedAt: number) => {
286
+ broadcast({ type: "folderSynced", accountId, entries: [{ folderId, syncedAt }] });
287
+ });
288
+
289
+ imapManager.on("syncError", (accountId: string, error: string) => {
290
+ broadcast({ type: "error", message: `${accountId}: ${error}` });
291
+ });
292
+
293
+ imapManager.on("accountError", (accountId: string, error: string, hint: string, isOAuth: boolean) => {
294
+ broadcast({ type: "accountError", accountId, error, hint, isOAuth });
295
+ });
296
+
297
+ // ── Startup ──
298
+
299
+ async function start(): Promise<void> {
300
+ console.log("mailx server starting...");
301
+
302
+ // Start HTTP server FIRST so UI is always reachable (even during IMAP startup)
303
+ const externalAccess = process.argv.includes("--external");
304
+ const hostname = externalAccess ? "0.0.0.0" : "127.0.0.1";
305
+
306
+ // Retry listen with backoff — Windows CLOSE_WAIT zombies can hold the port for minutes after a crash
307
+ for (let attempt = 0; attempt < 30; attempt++) {
308
+ server = createServer(app);
309
+ // Prevent CLOSE_WAIT accumulation: short keepAlive timeout + connection close headers
310
+ server.keepAliveTimeout = 5000; // close idle keep-alive connections after 5s
311
+ server.headersTimeout = 10000; // kill connections with no headers after 10s
312
+ // Track connections for clean shutdown (prevents CLOSE_WAIT zombies on Windows)
313
+ server.on("connection", (conn) => {
314
+ openConnections.add(conn);
315
+ conn.on("close", () => openConnections.delete(conn));
316
+ });
317
+ // Suppress EADDRINUSE from bubbling to uncaughtException — we handle it here
318
+ server.on("error", () => {}); // will be replaced by listen handler below
319
+ wss = new WebSocketServer({ server });
320
+ wireWebSocket();
321
+
322
+ const listenResult = await new Promise<"ok" | string>((resolve) => {
323
+ server.removeAllListeners("error");
324
+ server.once("error", (e: any) => { resolve(e.code || e.message); });
325
+ server.listen({ port: PORT, host: hostname, exclusive: false }, () => {
326
+ server.removeAllListeners("error");
327
+ resolve("ok");
328
+ });
329
+ });
330
+ if (listenResult === "ok") break;
331
+ if (listenResult === "EADDRINUSE" && attempt < 29) {
332
+ const wait = Math.min(2000 + attempt * 1000, 10000);
333
+ console.log(` Port ${PORT} in use (CLOSE_WAIT zombies?) — retry ${attempt + 1}/30 in ${wait / 1000}s...`);
334
+ server.close();
335
+ await new Promise(r => setTimeout(r, wait));
336
+ } else {
337
+ throw new Error(`Cannot bind port ${PORT}: ${listenResult}`);
338
+ }
339
+ }
340
+ console.log(`mailx server running on http://${hostname}:${PORT}`);
341
+
342
+ // Seed contacts (fast — skips existing)
343
+ const seeded = db.seedContactsFromMessages();
344
+ if (seeded > 0) console.log(` Seeded ${seeded} contacts`);
345
+
346
+ // Search index — rebuild in background (non-blocking)
347
+ setTimeout(() => {
348
+ let ftsCount = 0;
349
+ try { ftsCount = ((db as any).db.prepare("SELECT COUNT(*) as cnt FROM messages_fts").get() as any)?.cnt || 0; } catch { /* */ }
350
+ if (ftsCount === 0) {
351
+ const indexed = db.rebuildSearchIndex();
352
+ if (indexed > 0) console.log(` Search index: ${indexed} messages`);
353
+ }
354
+ }, 5000);
355
+
356
+ // Add configured accounts
357
+ for (const account of settings.accounts) {
358
+ if (!account.enabled && !(account as any).syncContacts) continue;
359
+ try {
360
+ await imapManager.addAccount(account);
361
+ console.log(` Account added: ${account.name} (${account.id})${!account.enabled ? " [contacts only]" : ""}`);
362
+ } catch (e: any) {
363
+ console.error(` Failed to add account ${account.id}: ${e.message}`);
364
+ }
365
+ }
366
+
367
+ // Initial sync, then start IDLE watchers
368
+ if (settings.accounts.some(a => a.enabled)) {
369
+ console.log(" Starting initial sync...");
370
+ imapManager.syncAll().then(async () => {
371
+ console.log(" Initial sync complete");
372
+ // Start IDLE watchers for instant new-mail notifications
373
+ await imapManager.startWatching();
374
+ // Sync Google Contacts for OAuth accounts
375
+ imapManager.syncAllContacts().catch(e =>
376
+ console.error(` Google Contacts sync error: ${e.message}`)
377
+ );
378
+ }).catch(e => {
379
+ console.error(` Initial sync error: ${e.message}`);
380
+ });
381
+ }
382
+
383
+ // Periodic sync as fallback (IDLE handles most updates)
384
+ imapManager.startPeriodicSync(settings.sync.intervalMinutes);
385
+
386
+ // Outbox worker — processes queued outgoing messages
387
+ imapManager.startOutboxWorker();
388
+ }
389
+
390
+ // ── Graceful Shutdown ──
391
+
392
+ /** Track all open connections so we can destroy them on shutdown (prevents CLOSE_WAIT zombies) */
393
+ const openConnections = new Set<import("node:net").Socket>();
394
+
395
+ async function shutdown(): Promise<void> {
396
+ console.log("\nShutting down...");
397
+ const forceExit = setTimeout(() => { console.log("Force exit"); process.exit(1); }, 3000);
398
+ imapManager.stopPeriodicSync();
399
+ try { await imapManager.shutdown(); } catch { /* proceed */ }
400
+ db.close();
401
+ // Destroy all open connections immediately — prevents CLOSE_WAIT zombies on Windows
402
+ for (const conn of openConnections) {
403
+ conn.destroy();
404
+ }
405
+ openConnections.clear();
406
+ server?.close();
407
+ clearTimeout(forceExit);
408
+ process.exit(0);
409
+ }
410
+
411
+ process.on("SIGINT", shutdown);
412
+ process.on("SIGTERM", shutdown);
413
+ process.on("unhandledRejection", (err: any) => {
414
+ console.error("Unhandled rejection:", err?.message || err);
415
+ });
416
+ process.on("uncaughtException", (err) => {
417
+ console.error("FATAL uncaught exception:", err.message);
418
+ console.error(err.stack);
419
+ // Other exceptions: stay alive, let node --watch handle file-change restarts
420
+ });
421
+ process.on("exit", (code) => {
422
+ console.log(`Process exiting with code ${code}`);
423
+ });
424
+
425
+ // ── Entry Point ──
426
+
427
+ await start();
428
+
429
+ export { app, start };
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@bobfrankston/mailx-server",
3
+ "version": "1.0.9",
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": "file:../mailx-types",
13
+ "@bobfrankston/mailx-store": "file:../mailx-store",
14
+ "@bobfrankston/mailx-imap": "file:../mailx-imap",
15
+ "@bobfrankston/mailx-api": "file:../mailx-api",
16
+ "@bobfrankston/mailx-settings": "file:../mailx-settings",
17
+ "express": "^4.21.0",
18
+ "ws": "^8.18.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/express": "^5.0.0",
22
+ "@types/ws": "^8.5.13"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/BobFrankston/mailx-server.git"
27
+ }
28
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": ".",
5
+ "rootDir": "."
6
+ },
7
+ "include": ["*.ts"],
8
+ "exclude": ["node_modules", "prev", "cruft", "tests"]
9
+ }
@@ -0,0 +1 @@
1
+ {"root":["./index.ts"],"version":"5.9.2"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Google Calendar / Tasks / People (Contacts) two-way sync helpers.
3
+ *
4
+ * Used by MailxService to push local edits to Google and pull server
5
+ * changes into the local cache. All functions take a `getToken` function
6
+ * and a fetch implementation so they stay platform-agnostic (Node uses
7
+ * global `fetch` on Node 18+; browsers use window.fetch).
8
+ *
9
+ * Error handling: throws on network / HTTP errors. Caller catches and
10
+ * either retries via the store_sync drainer or surfaces to the UI.
11
+ */
12
+ type TokenProvider = () => Promise<string>;
13
+ export declare class GoogleHttpError extends Error {
14
+ status: number;
15
+ statusText: string;
16
+ body: string;
17
+ constructor(status: number, statusText: string, body: string);
18
+ }
19
+ export interface GCalEvent {
20
+ id: string;
21
+ summary: string;
22
+ start: {
23
+ dateTime?: string;
24
+ date?: string;
25
+ };
26
+ end: {
27
+ dateTime?: string;
28
+ date?: string;
29
+ };
30
+ location?: string;
31
+ description?: string;
32
+ etag?: string;
33
+ /** Set on instances expanded from a recurring series (singleEvents=true).
34
+ * Absent for singletons. Lets the UI filter out recurring expansions. */
35
+ recurringEventId?: string;
36
+ /** Link to open the event in Google Calendar web. */
37
+ htmlLink?: string;
38
+ }
39
+ export declare function listCalendarEvents(tokenProvider: TokenProvider, fromMs: number, toMs: number, calendarId?: string): Promise<GCalEvent[]>;
40
+ export declare function createCalendarEvent(tokenProvider: TokenProvider, event: any, calendarId?: string): Promise<GCalEvent>;
41
+ export declare function updateCalendarEvent(tokenProvider: TokenProvider, eventId: string, event: any, calendarId?: string): Promise<GCalEvent>;
42
+ export declare function deleteCalendarEvent(tokenProvider: TokenProvider, eventId: string, calendarId?: string): Promise<void>;
43
+ export interface GTask {
44
+ id: string;
45
+ title: string;
46
+ notes?: string;
47
+ due?: string;
48
+ completed?: string;
49
+ status?: "needsAction" | "completed";
50
+ etag?: string;
51
+ }
52
+ export declare function listTasks(tokenProvider: TokenProvider, listId?: string, showCompleted?: boolean): Promise<GTask[]>;
53
+ export declare function createTask(tokenProvider: TokenProvider, task: any, listId?: string): Promise<GTask>;
54
+ export declare function updateTask(tokenProvider: TokenProvider, taskId: string, task: any, listId?: string): Promise<GTask>;
55
+ export declare function deleteTask(tokenProvider: TokenProvider, taskId: string, listId?: string): Promise<void>;
56
+ export declare function createContact(tokenProvider: TokenProvider, person: any): Promise<any>;
57
+ export declare function updateContact(tokenProvider: TokenProvider, resourceName: string, updatePersonFields: string, person: any): Promise<any>;
58
+ export declare function deleteContact(tokenProvider: TokenProvider, resourceName: string): Promise<void>;
59
+ export declare function calendarEventToLocal(ev: GCalEvent, accountId: string): {
60
+ providerId: string;
61
+ accountId: string;
62
+ title: string;
63
+ startMs: number;
64
+ endMs: number;
65
+ allDay: boolean;
66
+ location: string;
67
+ notes: string;
68
+ etag: string;
69
+ recurringEventId: string | undefined;
70
+ htmlLink: string | undefined;
71
+ };
72
+ export declare function localToCalendarEvent(local: {
73
+ title: string;
74
+ startMs: number;
75
+ endMs: number;
76
+ allDay?: boolean;
77
+ location?: string;
78
+ notes?: string;
79
+ }): any;
80
+ export declare function taskToLocal(t: GTask, accountId: string): {
81
+ providerId: string;
82
+ accountId: string;
83
+ title: string;
84
+ notes: string;
85
+ dueMs: number | undefined;
86
+ completedMs: number | undefined;
87
+ etag: string;
88
+ };
89
+ export declare function localToTask(local: {
90
+ title: string;
91
+ notes?: string;
92
+ dueMs?: number;
93
+ completedMs?: number;
94
+ }): any;
95
+ export {};
96
+ //# sourceMappingURL=google-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-sync.d.ts","sourceRoot":"","sources":["google-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,KAAK,aAAa,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;AAE3C,qBAAa,eAAgB,SAAQ,KAAK;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;gBACD,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAM/D;AAmBD,MAAM,WAAW,SAAS;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,GAAG,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;8EAC0E;IAC1E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,kBAAkB,CACpC,aAAa,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,SAAY,GACnF,OAAO,CAAC,SAAS,EAAE,CAAC,CAgBtB;AAED,wBAAsB,mBAAmB,CACrC,aAAa,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,SAAY,GACjE,OAAO,CAAC,SAAS,CAAC,CAIpB;AAED,wBAAsB,mBAAmB,CACrC,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,SAAY,GAClF,OAAO,CAAC,SAAS,CAAC,CAIpB;AAED,wBAAsB,mBAAmB,CACrC,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,SAAY,GACtE,OAAO,CAAC,IAAI,CAAC,CAGf;AAID,MAAM,WAAW,KAAK;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,aAAa,GAAG,WAAW,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,SAAS,CAC3B,aAAa,EAAE,aAAa,EAAE,MAAM,SAAa,EAAE,aAAa,UAAQ,GACzE,OAAO,CAAC,KAAK,EAAE,CAAC,CAMlB;AAED,wBAAsB,UAAU,CAC5B,aAAa,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,SAAa,GAC7D,OAAO,CAAC,KAAK,CAAC,CAIhB;AAED,wBAAsB,UAAU,CAC5B,aAAa,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,SAAa,GAC7E,OAAO,CAAC,KAAK,CAAC,CAIhB;AAED,wBAAsB,UAAU,CAC5B,aAAa,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAa,GAClE,OAAO,CAAC,IAAI,CAAC,CAGf;AAID,wBAAsB,aAAa,CAC/B,aAAa,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,GAC1C,OAAO,CAAC,GAAG,CAAC,CAId;AAED,wBAAsB,aAAa,CAC/B,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAC5F,OAAO,CAAC,GAAG,CAAC,CAKd;AAED,wBAAsB,aAAa,CAC/B,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,GACnD,OAAO,CAAC,IAAI,CAAC,CAGf;AAID,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG;IACpE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IACrD,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAChD,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAC9C,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAiBA;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IACxC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACrC,GAAG,GAAG,CAUN;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,GAAG;IACtD,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IACpE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;CAC5E,CAQA;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAC/B,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CACvE,GAAG,GAAG,CAQN"}