@remit/web-client 0.0.1

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 (298) hide show
  1. package/harness/build.mjs +84 -0
  2. package/harness/index.html +37 -0
  3. package/harness/vite-preset.ts +29 -0
  4. package/index.html +47 -0
  5. package/package.json +147 -0
  6. package/public/config.js +5 -0
  7. package/public/locales/de/common.json +1 -0
  8. package/public/locales/de/errors.json +1 -0
  9. package/public/locales/de/mail.json +13 -0
  10. package/public/locales/de/settings.json +1 -0
  11. package/public/locales/en/common.json +26 -0
  12. package/public/locales/en/errors.json +1 -0
  13. package/public/locales/en/mail.json +69 -0
  14. package/public/locales/en/settings.json +1 -0
  15. package/public/locales/fr/common.json +1 -0
  16. package/public/locales/fr/errors.json +1 -0
  17. package/public/locales/fr/mail.json +13 -0
  18. package/public/locales/fr/settings.json +1 -0
  19. package/public/locales/nl/common.json +1 -0
  20. package/public/locales/nl/errors.json +1 -0
  21. package/public/locales/nl/mail.json +13 -0
  22. package/public/locales/nl/settings.json +1 -0
  23. package/src/auth/AccountMenu.tsx +58 -0
  24. package/src/auth/AccountSession.tsx +19 -0
  25. package/src/auth/BetterAuthShell.tsx +198 -0
  26. package/src/auth/account-menu-mode.test.ts +64 -0
  27. package/src/auth/account-menu-mode.ts +28 -0
  28. package/src/auth/amplify-config.test.ts +158 -0
  29. package/src/auth/amplify-config.ts +60 -0
  30. package/src/auth/auth-error.test.ts +109 -0
  31. package/src/auth/auth-error.ts +107 -0
  32. package/src/auth/auth-interceptor.test.ts +86 -0
  33. package/src/auth/auth-interceptor.ts +23 -0
  34. package/src/auth/auth.css +334 -0
  35. package/src/auth/better-auth/BetterAuthAccount.tsx +25 -0
  36. package/src/auth/better-auth-config.ts +85 -0
  37. package/src/auth/better-auth-provider.tsx +20 -0
  38. package/src/auth/cognito/CognitoAccount.tsx +48 -0
  39. package/src/auth/cognito/CognitoShell.tsx +266 -0
  40. package/src/auth/cognito/cognito-token.test.ts +110 -0
  41. package/src/auth/cognito/cognito-token.ts +47 -0
  42. package/src/auth/cognito/cognito.css +21 -0
  43. package/src/auth/cognito-provider.tsx +22 -0
  44. package/src/auth/combined-provider.tsx +42 -0
  45. package/src/auth/composition.test.ts +136 -0
  46. package/src/auth/provider.tsx +63 -0
  47. package/src/auth/sign-out-visibility.test.ts +40 -0
  48. package/src/auth/sign-out-visibility.ts +10 -0
  49. package/src/auth/sign-up-disabled.test.ts +23 -0
  50. package/src/components/compose/AddressField.tsx +237 -0
  51. package/src/components/compose/ComposeBody.tsx +36 -0
  52. package/src/components/compose/ComposeForm.tsx +676 -0
  53. package/src/components/compose/ComposeProvider.tsx +133 -0
  54. package/src/components/compose/ComposeSmtpMissingBanner.tsx +49 -0
  55. package/src/components/compose/FromSelector.tsx +67 -0
  56. package/src/components/compose/FullCompose.tsx +82 -0
  57. package/src/components/compose/InlineCompose.tsx +29 -0
  58. package/src/components/compose/MobileComposeSheet.tsx +152 -0
  59. package/src/components/compose/PlateEditor.tsx +86 -0
  60. package/src/components/compose/PlateToolbar.tsx +110 -0
  61. package/src/components/compose/SubjectField.tsx +19 -0
  62. package/src/components/compose/draft-discard-banner.test.ts +76 -0
  63. package/src/components/compose/index.ts +6 -0
  64. package/src/components/compose/sanitize-quote-html.ts +23 -0
  65. package/src/components/layout/AppShellSkeleton.tsx +53 -0
  66. package/src/components/layout/ComposeFab.tsx +86 -0
  67. package/src/components/layout/Drawer.tsx +105 -0
  68. package/src/components/mail/AutoMovedIndicator.tsx +48 -0
  69. package/src/components/mail/BlockedImagesNotice.render.test.ts +65 -0
  70. package/src/components/mail/BlockedImagesNotice.tsx +41 -0
  71. package/src/components/mail/BriefPane.tsx +289 -0
  72. package/src/components/mail/ConversationView.tsx +462 -0
  73. package/src/components/mail/DailyBrief.tsx +451 -0
  74. package/src/components/mail/DraftsView.tsx +279 -0
  75. package/src/components/mail/FlaggedList.tsx +170 -0
  76. package/src/components/mail/FlaggedPane.tsx +217 -0
  77. package/src/components/mail/IntelligencePane.test.ts +123 -0
  78. package/src/components/mail/IntelligencePane.tsx +441 -0
  79. package/src/components/mail/MailListHeader.tsx +187 -0
  80. package/src/components/mail/MailNav.tsx +70 -0
  81. package/src/components/mail/MailSidebarAdapter.tsx +351 -0
  82. package/src/components/mail/MailViewChrome.tsx +95 -0
  83. package/src/components/mail/MailboxPane.tsx +1219 -0
  84. package/src/components/mail/MessageActionMenu.tsx +368 -0
  85. package/src/components/mail/MessageBody.tsx +207 -0
  86. package/src/components/mail/MessageBodyErrorBanner.render.test.ts +85 -0
  87. package/src/components/mail/MessageBodyErrorBanner.test.ts +86 -0
  88. package/src/components/mail/MessageBodyErrorBanner.tsx +90 -0
  89. package/src/components/mail/MessageCard.tsx +401 -0
  90. package/src/components/mail/MessageDetail.tsx +153 -0
  91. package/src/components/mail/MessageList.tsx +804 -0
  92. package/src/components/mail/MessageListItem.tsx +267 -0
  93. package/src/components/mail/MessageToolbar.render.test.ts +57 -0
  94. package/src/components/mail/MessageToolbar.tsx +166 -0
  95. package/src/components/mail/MobileMessageBar.tsx +142 -0
  96. package/src/components/mail/MoveToTrigger.tsx +250 -0
  97. package/src/components/mail/NavMenuButton.tsx +25 -0
  98. package/src/components/mail/OutboxPane.tsx +531 -0
  99. package/src/components/mail/PullToRefresh.tsx +24 -0
  100. package/src/components/mail/RawMessageView.tsx +84 -0
  101. package/src/components/mail/ReadingPaneEmpty.render.test.ts +42 -0
  102. package/src/components/mail/SelectionToolbar.tsx +139 -0
  103. package/src/components/mail/SpamRescue.render.test.ts +35 -0
  104. package/src/components/mail/SpamRescue.tsx +138 -0
  105. package/src/components/mail/SwipeableMessageRow.tsx +148 -0
  106. package/src/components/mail/message-body-error-banner-content.ts +87 -0
  107. package/src/components/mail/organize/OrganizeDialog.render.test.ts +37 -0
  108. package/src/components/mail/organize/OrganizeDialog.tsx +83 -0
  109. package/src/components/mail/organize/OrganizePanel.render.test.ts +102 -0
  110. package/src/components/mail/organize/OrganizePanel.tsx +344 -0
  111. package/src/components/onboarding/OnboardingWizard.tsx +1547 -0
  112. package/src/components/settings/AccountFormPanel.test.ts +72 -0
  113. package/src/components/settings/AccountFormPanel.tsx +942 -0
  114. package/src/components/settings/DangerZone.tsx +30 -0
  115. package/src/components/settings/DeleteAccountDialog.render.test.ts +47 -0
  116. package/src/components/settings/DeleteAccountDialog.tsx +224 -0
  117. package/src/components/settings/FiltersList.render.test.ts +71 -0
  118. package/src/components/settings/FiltersList.tsx +93 -0
  119. package/src/components/settings/account-form-helpers.test.ts +172 -0
  120. package/src/components/settings/account-form-helpers.ts +105 -0
  121. package/src/components/ui/AppVersion.tsx +50 -0
  122. package/src/components/ui/BugReportButton.tsx +45 -0
  123. package/src/components/ui/ConfirmDialog.tsx +125 -0
  124. package/src/components/ui/DropdownMenu.tsx +105 -0
  125. package/src/components/ui/EmptyState.tsx +11 -0
  126. package/src/components/ui/ErrorBanner.tsx +88 -0
  127. package/src/components/ui/ErrorBannerProvider.tsx +77 -0
  128. package/src/components/ui/ErrorBannerStack.tsx +34 -0
  129. package/src/components/ui/ErrorState.tsx +80 -0
  130. package/src/components/ui/FatalErrorOverlay.render.test.ts +66 -0
  131. package/src/components/ui/FatalErrorOverlay.tsx +151 -0
  132. package/src/components/ui/KeyboardShortcutsModal.tsx +120 -0
  133. package/src/components/ui/SlidePanel.tsx +72 -0
  134. package/src/components/ui/error-banners.test.ts +207 -0
  135. package/src/components/ui/error-banners.ts +111 -0
  136. package/src/hooks/queries/keys.ts +38 -0
  137. package/src/hooks/useArchiveMailbox.ts +104 -0
  138. package/src/hooks/useAutoMovedBadge.ts +77 -0
  139. package/src/hooks/useCurrentMailboxName.ts +99 -0
  140. package/src/hooks/useDebouncedValue.ts +12 -0
  141. package/src/hooks/useDeleteMessages.test.ts +103 -0
  142. package/src/hooks/useDeleteMessages.ts +226 -0
  143. package/src/hooks/useFilters.ts +112 -0
  144. package/src/hooks/useIntelligenceData.test.ts +269 -0
  145. package/src/hooks/useIntelligenceData.ts +284 -0
  146. package/src/hooks/useIsDark.ts +27 -0
  147. package/src/hooks/useKeyboardNavigation.ts +192 -0
  148. package/src/hooks/useLayoutTier.test.ts +47 -0
  149. package/src/hooks/useLayoutTier.ts +44 -0
  150. package/src/hooks/useLongPress.test.ts +291 -0
  151. package/src/hooks/useLongPress.ts +87 -0
  152. package/src/hooks/useMailboxAccount.ts +62 -0
  153. package/src/hooks/useMailboxNameIndex.ts +32 -0
  154. package/src/hooks/useMarkAsRead.test.ts +165 -0
  155. package/src/hooks/useMarkAsRead.ts +319 -0
  156. package/src/hooks/useMediaQuery.ts +39 -0
  157. package/src/hooks/useMessageBodyContent.test.ts +370 -0
  158. package/src/hooks/useMessageBodyContent.ts +380 -0
  159. package/src/hooks/useMoveMessages.test.ts +63 -0
  160. package/src/hooks/useMoveMessages.ts +221 -0
  161. package/src/hooks/useOrganizeJob.ts +90 -0
  162. package/src/hooks/useOrganizePreview.ts +40 -0
  163. package/src/hooks/useRescueCandidates.test.ts +217 -0
  164. package/src/hooks/useRescueCandidates.ts +48 -0
  165. package/src/hooks/useSaveDraft.ts +118 -0
  166. package/src/hooks/useSelection.test.ts +75 -0
  167. package/src/hooks/useSelection.ts +211 -0
  168. package/src/hooks/useSemanticSearch.ts +94 -0
  169. package/src/hooks/useSignature.ts +69 -0
  170. package/src/hooks/useStaleAccountSync.test.ts +211 -0
  171. package/src/hooks/useStaleAccountSync.ts +246 -0
  172. package/src/hooks/useSwipeNavigation.test.ts +35 -0
  173. package/src/hooks/useSwipeNavigation.ts +99 -0
  174. package/src/hooks/useToggleStar.ts +187 -0
  175. package/src/hooks/useToggleTrusted.test.ts +102 -0
  176. package/src/hooks/useToggleTrusted.ts +161 -0
  177. package/src/hooks/useTriageKeyboard.ts +103 -0
  178. package/src/hooks/useTriggerSync.test.ts +24 -0
  179. package/src/hooks/useTriggerSync.ts +58 -0
  180. package/src/hooks/useUpdateAddressFlags.ts +125 -0
  181. package/src/hooks/useVisualViewport.test.ts +127 -0
  182. package/src/hooks/useVisualViewport.ts +51 -0
  183. package/src/index.css +19 -0
  184. package/src/lib/account-order.test.ts +84 -0
  185. package/src/lib/account-order.ts +13 -0
  186. package/src/lib/adjacent-message.test.ts +43 -0
  187. package/src/lib/adjacent-message.ts +20 -0
  188. package/src/lib/api.ts +82 -0
  189. package/src/lib/app-info.ts +15 -0
  190. package/src/lib/auto-moved.test.ts +140 -0
  191. package/src/lib/auto-moved.ts +68 -0
  192. package/src/lib/autodiscovery.test.ts +94 -0
  193. package/src/lib/autodiscovery.ts +354 -0
  194. package/src/lib/brief.test.ts +366 -0
  195. package/src/lib/brief.ts +180 -0
  196. package/src/lib/bug-report.test.ts +213 -0
  197. package/src/lib/bug-report.ts +176 -0
  198. package/src/lib/client.ts +36 -0
  199. package/src/lib/console-errors.test.ts +64 -0
  200. package/src/lib/console-errors.ts +92 -0
  201. package/src/lib/conversation-target.test.ts +68 -0
  202. package/src/lib/conversation-target.ts +59 -0
  203. package/src/lib/display-category.ts +20 -0
  204. package/src/lib/drafts.test.ts +241 -0
  205. package/src/lib/drafts.ts +121 -0
  206. package/src/lib/error-classifier.test.ts +133 -0
  207. package/src/lib/error-classifier.ts +96 -0
  208. package/src/lib/fatal-error.test.ts +97 -0
  209. package/src/lib/fatal-error.ts +165 -0
  210. package/src/lib/folder-roles.test.ts +113 -0
  211. package/src/lib/folder-roles.ts +112 -0
  212. package/src/lib/format.test.ts +115 -0
  213. package/src/lib/format.ts +198 -0
  214. package/src/lib/i18n.ts +36 -0
  215. package/src/lib/intelligence-pref.test.ts +57 -0
  216. package/src/lib/intelligence-pref.ts +25 -0
  217. package/src/lib/keymap-dispatch.test.ts +178 -0
  218. package/src/lib/keymap-dispatch.ts +187 -0
  219. package/src/lib/keymap.test.ts +49 -0
  220. package/src/lib/keymap.ts +178 -0
  221. package/src/lib/mail-context.ts +64 -0
  222. package/src/lib/mail-route.test.ts +80 -0
  223. package/src/lib/mail-route.ts +44 -0
  224. package/src/lib/message-body-source.test.ts +98 -0
  225. package/src/lib/message-body-source.ts +48 -0
  226. package/src/lib/move-targets.test.ts +195 -0
  227. package/src/lib/move-targets.ts +74 -0
  228. package/src/lib/onboarding-completion.test.ts +64 -0
  229. package/src/lib/onboarding-completion.ts +23 -0
  230. package/src/lib/organize/filter-status.test.ts +99 -0
  231. package/src/lib/organize/filter-status.ts +86 -0
  232. package/src/lib/organize/organize-copy.test.ts +93 -0
  233. package/src/lib/organize/organize-copy.ts +61 -0
  234. package/src/lib/organize/organize-model.test.ts +104 -0
  235. package/src/lib/organize/organize-model.ts +105 -0
  236. package/src/lib/organize/organize-poll.test.ts +37 -0
  237. package/src/lib/organize/organize-poll.ts +28 -0
  238. package/src/lib/outbox-status.test.ts +64 -0
  239. package/src/lib/outbox-status.ts +75 -0
  240. package/src/lib/plate-serializer.ts +164 -0
  241. package/src/lib/provider-presets.test.ts +96 -0
  242. package/src/lib/provider-presets.ts +81 -0
  243. package/src/lib/query-error-handler.test.ts +144 -0
  244. package/src/lib/query-error-handler.ts +34 -0
  245. package/src/lib/query-escalation.integration.test.ts +138 -0
  246. package/src/lib/recent-searches.test.ts +53 -0
  247. package/src/lib/recent-searches.ts +34 -0
  248. package/src/lib/rescue-candidates.test.ts +94 -0
  249. package/src/lib/rescue-candidates.ts +35 -0
  250. package/src/lib/rescue-telemetry.test.ts +75 -0
  251. package/src/lib/rescue-telemetry.ts +55 -0
  252. package/src/lib/rum-adapter.test.ts +299 -0
  253. package/src/lib/rum-adapter.ts +107 -0
  254. package/src/lib/saved-searches.test.ts +80 -0
  255. package/src/lib/saved-searches.ts +52 -0
  256. package/src/lib/search-pending.test.ts +61 -0
  257. package/src/lib/search-pending.ts +24 -0
  258. package/src/lib/search-query.test.ts +35 -0
  259. package/src/lib/search-query.ts +11 -0
  260. package/src/lib/search-result.test.ts +160 -0
  261. package/src/lib/search-result.ts +102 -0
  262. package/src/lib/search-schemas.ts +34 -0
  263. package/src/lib/search-token-index.test.ts +112 -0
  264. package/src/lib/search-token-index.ts +56 -0
  265. package/src/lib/search-tokens.test.ts +218 -0
  266. package/src/lib/search-tokens.ts +133 -0
  267. package/src/lib/telemetry-context.tsx +9 -0
  268. package/src/lib/telemetry.ts +17 -0
  269. package/src/lib/theme-preference.ts +46 -0
  270. package/src/lib/theme.ts +33 -0
  271. package/src/lib/utils.ts +4 -0
  272. package/src/main.tsx +10 -0
  273. package/src/routeTree.gen.ts +392 -0
  274. package/src/router.test.ts +81 -0
  275. package/src/router.tsx +31 -0
  276. package/src/routes/__root.tsx +69 -0
  277. package/src/routes/index.tsx +7 -0
  278. package/src/routes/mail/$mailboxId.tsx +27 -0
  279. package/src/routes/mail/flagged.tsx +37 -0
  280. package/src/routes/mail/index.tsx +41 -0
  281. package/src/routes/mail/outbox.tsx +13 -0
  282. package/src/routes/mail.tsx +413 -0
  283. package/src/routes/onboarding.tsx +35 -0
  284. package/src/routes/settings/-accounts.test.ts +65 -0
  285. package/src/routes/settings/accounts.tsx +542 -0
  286. package/src/routes/settings/advanced.tsx +55 -0
  287. package/src/routes/settings/appearance.tsx +140 -0
  288. package/src/routes/settings/filters.tsx +141 -0
  289. package/src/routes/settings/folders.tsx +216 -0
  290. package/src/routes/settings/index.tsx +5 -0
  291. package/src/routes/settings/senders.tsx +466 -0
  292. package/src/routes/settings/suggested-vips.tsx +10 -0
  293. package/src/routes/settings.tsx +66 -0
  294. package/src/runtime-config.ts +75 -0
  295. package/src/shell/index.tsx +98 -0
  296. package/src/types/index.ts +73 -0
  297. package/src/vite-env.d.ts +4 -0
  298. package/vite.base.ts +43 -0
@@ -0,0 +1,354 @@
1
+ /**
2
+ * Client-side autodiscovery for IMAP/SMTP server settings.
3
+ *
4
+ * Strategy (stop at first hit):
5
+ * 1. Curated provider table (top ~30 providers)
6
+ * 2. Mozilla Autoconfig: GET https://autoconfig.thunderbird.net/v1.1/{domain}
7
+ * 3. Domain-hosted autoconfig: GET https://autoconfig.{domain}/mail/config-v1.1.xml
8
+ * 4. Fallback heuristics: imap.{domain}:993/tls, smtp.{domain}:587/starttls
9
+ *
10
+ * NOTE: A future backend endpoint `GET /autodiscovery?email=…` (TypeSpec-first)
11
+ * is planned to handle DNS SRV lookups and CORS-blocked domains server-side.
12
+ * Until it exists, this client-side implementation covers the majority of providers.
13
+ */
14
+
15
+ export type SecurityMode = "tls" | "starttls" | "none";
16
+
17
+ export interface ServerSettings {
18
+ host: string;
19
+ port: number;
20
+ security: SecurityMode;
21
+ }
22
+
23
+ export interface DiscoveryResult {
24
+ imap: ServerSettings;
25
+ smtp: ServerSettings;
26
+ /** Source of the discovery result */
27
+ source:
28
+ | "provider-table"
29
+ | "mozilla-autoconfig"
30
+ | "domain-autoconfig"
31
+ | "heuristic";
32
+ }
33
+
34
+ /** Static curated provider table — top providers with known-good settings */
35
+ const PROVIDER_TABLE: Record<string, DiscoveryResult> = {
36
+ "gmail.com": {
37
+ imap: { host: "imap.gmail.com", port: 993, security: "tls" },
38
+ smtp: { host: "smtp.gmail.com", port: 587, security: "starttls" },
39
+ source: "provider-table",
40
+ },
41
+ "googlemail.com": {
42
+ imap: { host: "imap.gmail.com", port: 993, security: "tls" },
43
+ smtp: { host: "smtp.gmail.com", port: 587, security: "starttls" },
44
+ source: "provider-table",
45
+ },
46
+ "outlook.com": {
47
+ imap: { host: "outlook.office365.com", port: 993, security: "tls" },
48
+ smtp: { host: "smtp.office365.com", port: 587, security: "starttls" },
49
+ source: "provider-table",
50
+ },
51
+ "hotmail.com": {
52
+ imap: { host: "outlook.office365.com", port: 993, security: "tls" },
53
+ smtp: { host: "smtp.office365.com", port: 587, security: "starttls" },
54
+ source: "provider-table",
55
+ },
56
+ "live.com": {
57
+ imap: { host: "outlook.office365.com", port: 993, security: "tls" },
58
+ smtp: { host: "smtp.office365.com", port: 587, security: "starttls" },
59
+ source: "provider-table",
60
+ },
61
+ "msn.com": {
62
+ imap: { host: "outlook.office365.com", port: 993, security: "tls" },
63
+ smtp: { host: "smtp.office365.com", port: 587, security: "starttls" },
64
+ source: "provider-table",
65
+ },
66
+ "icloud.com": {
67
+ imap: { host: "imap.mail.me.com", port: 993, security: "tls" },
68
+ smtp: { host: "smtp.mail.me.com", port: 587, security: "starttls" },
69
+ source: "provider-table",
70
+ },
71
+ "me.com": {
72
+ imap: { host: "imap.mail.me.com", port: 993, security: "tls" },
73
+ smtp: { host: "smtp.mail.me.com", port: 587, security: "starttls" },
74
+ source: "provider-table",
75
+ },
76
+ "mac.com": {
77
+ imap: { host: "imap.mail.me.com", port: 993, security: "tls" },
78
+ smtp: { host: "smtp.mail.me.com", port: 587, security: "starttls" },
79
+ source: "provider-table",
80
+ },
81
+ "fastmail.com": {
82
+ imap: { host: "imap.fastmail.com", port: 993, security: "tls" },
83
+ smtp: { host: "smtp.fastmail.com", port: 465, security: "tls" },
84
+ source: "provider-table",
85
+ },
86
+ "fastmail.fm": {
87
+ imap: { host: "imap.fastmail.com", port: 993, security: "tls" },
88
+ smtp: { host: "smtp.fastmail.com", port: 465, security: "tls" },
89
+ source: "provider-table",
90
+ },
91
+ "yahoo.com": {
92
+ imap: { host: "imap.mail.yahoo.com", port: 993, security: "tls" },
93
+ smtp: { host: "smtp.mail.yahoo.com", port: 465, security: "tls" },
94
+ source: "provider-table",
95
+ },
96
+ "yahoo.co.uk": {
97
+ imap: { host: "imap.mail.yahoo.com", port: 993, security: "tls" },
98
+ smtp: { host: "smtp.mail.yahoo.com", port: 465, security: "tls" },
99
+ source: "provider-table",
100
+ },
101
+ "ymail.com": {
102
+ imap: { host: "imap.mail.yahoo.com", port: 993, security: "tls" },
103
+ smtp: { host: "smtp.mail.yahoo.com", port: 465, security: "tls" },
104
+ source: "provider-table",
105
+ },
106
+ // NOTE: ProtonMail is intentionally omitted from the provider table.
107
+ // Proton has no direct IMAP/SMTP for hosted clients — it requires the
108
+ // locally-run Proton Bridge (127.0.0.1). Remit syncs server-side, so a
109
+ // loopback host would resolve to the sync worker, never the user's
110
+ // machine. Proton accounts fall through to manual entry, where the user
111
+ // can point at their own Bridge/relay if they run one.
112
+ "aol.com": {
113
+ imap: { host: "imap.aol.com", port: 993, security: "tls" },
114
+ smtp: { host: "smtp.aol.com", port: 465, security: "tls" },
115
+ source: "provider-table",
116
+ },
117
+ "zoho.com": {
118
+ imap: { host: "imap.zoho.com", port: 993, security: "tls" },
119
+ smtp: { host: "smtp.zoho.com", port: 465, security: "tls" },
120
+ source: "provider-table",
121
+ },
122
+ "gmx.com": {
123
+ imap: { host: "imap.gmx.com", port: 993, security: "tls" },
124
+ smtp: { host: "smtp.gmx.com", port: 587, security: "starttls" },
125
+ source: "provider-table",
126
+ },
127
+ "gmx.net": {
128
+ imap: { host: "imap.gmx.net", port: 993, security: "tls" },
129
+ smtp: { host: "mail.gmx.net", port: 587, security: "starttls" },
130
+ source: "provider-table",
131
+ },
132
+ "gmx.de": {
133
+ imap: { host: "imap.gmx.net", port: 993, security: "tls" },
134
+ smtp: { host: "mail.gmx.net", port: 587, security: "starttls" },
135
+ source: "provider-table",
136
+ },
137
+ "web.de": {
138
+ imap: { host: "imap.web.de", port: 993, security: "tls" },
139
+ smtp: { host: "smtp.web.de", port: 587, security: "starttls" },
140
+ source: "provider-table",
141
+ },
142
+ "mail.com": {
143
+ imap: { host: "imap.mail.com", port: 993, security: "tls" },
144
+ smtp: { host: "smtp.mail.com", port: 465, security: "tls" },
145
+ source: "provider-table",
146
+ },
147
+ "hey.com": {
148
+ imap: { host: "imap.hey.com", port: 993, security: "tls" },
149
+ smtp: { host: "smtp.hey.com", port: 465, security: "tls" },
150
+ source: "provider-table",
151
+ },
152
+ "tutanota.com": {
153
+ imap: { host: "mail.tutanota.com", port: 993, security: "tls" },
154
+ smtp: { host: "mail.tutanota.com", port: 465, security: "tls" },
155
+ source: "provider-table",
156
+ },
157
+ "tuta.io": {
158
+ imap: { host: "mail.tutanota.com", port: 993, security: "tls" },
159
+ smtp: { host: "mail.tutanota.com", port: 465, security: "tls" },
160
+ source: "provider-table",
161
+ },
162
+ "posteo.de": {
163
+ imap: { host: "posteo.de", port: 993, security: "tls" },
164
+ smtp: { host: "posteo.de", port: 465, security: "tls" },
165
+ source: "provider-table",
166
+ },
167
+ "runbox.com": {
168
+ imap: { host: "imap.runbox.com", port: 993, security: "tls" },
169
+ smtp: { host: "smtp.runbox.com", port: 587, security: "starttls" },
170
+ source: "provider-table",
171
+ },
172
+ "mailbox.org": {
173
+ imap: { host: "imap.mailbox.org", port: 993, security: "tls" },
174
+ smtp: { host: "smtp.mailbox.org", port: 465, security: "tls" },
175
+ source: "provider-table",
176
+ },
177
+ "hushmail.com": {
178
+ imap: { host: "imap.hushmail.com", port: 993, security: "tls" },
179
+ smtp: { host: "smtp.hushmail.com", port: 465, security: "tls" },
180
+ source: "provider-table",
181
+ },
182
+ };
183
+
184
+ /** App-password doc links per domain (static lookup) */
185
+ export const APP_PASSWORD_URLS: Record<string, string> = {
186
+ "gmail.com": "https://support.google.com/accounts/answer/185833",
187
+ "googlemail.com": "https://support.google.com/accounts/answer/185833",
188
+ "yahoo.com":
189
+ "https://help.yahoo.com/kb/generate-manage-third-party-passwords-sln15241.html",
190
+ "icloud.com": "https://support.apple.com/en-us/102654",
191
+ "me.com": "https://support.apple.com/en-us/102654",
192
+ "mac.com": "https://support.apple.com/en-us/102654",
193
+ "fastmail.com": "https://www.fastmail.help/hc/en-us/articles/360058752854",
194
+ "fastmail.fm": "https://www.fastmail.help/hc/en-us/articles/360058752854",
195
+ "outlook.com":
196
+ "https://support.microsoft.com/en-us/account-billing/manage-app-passwords-for-two-step-verification-d6dc8c6d-4bf7-4851-ad95-6d07799387e9",
197
+ "hotmail.com":
198
+ "https://support.microsoft.com/en-us/account-billing/manage-app-passwords-for-two-step-verification-d6dc8c6d-4bf7-4851-ad95-6d07799387e9",
199
+ "zoho.com":
200
+ "https://help.zoho.com/portal/en/kb/zoho-mail/client-configuration/imap-access/articles/zoho-mail-app-passwords",
201
+ };
202
+
203
+ function parseMozillaAutoconfigXml(xml: string): DiscoveryResult | null {
204
+ try {
205
+ const parser = new DOMParser();
206
+ const doc = parser.parseFromString(xml, "application/xml");
207
+ if (doc.querySelector("parsererror")) return null;
208
+
209
+ const incomingServers = doc.querySelectorAll("incomingServer[type='imap']");
210
+ const outgoingServers = doc.querySelectorAll("outgoingServer[type='smtp']");
211
+
212
+ if (!incomingServers.length || !outgoingServers.length) return null;
213
+
214
+ const imap = incomingServers[0];
215
+ const smtp = outgoingServers[0];
216
+
217
+ const imapHost = imap.querySelector("hostname")?.textContent?.trim();
218
+ const imapPortStr = imap.querySelector("port")?.textContent?.trim();
219
+ const imapSocketType = imap
220
+ .querySelector("socketType")
221
+ ?.textContent?.trim()
222
+ .toUpperCase();
223
+
224
+ const smtpHost = smtp.querySelector("hostname")?.textContent?.trim();
225
+ const smtpPortStr = smtp.querySelector("port")?.textContent?.trim();
226
+ const smtpSocketType = smtp
227
+ .querySelector("socketType")
228
+ ?.textContent?.trim()
229
+ .toUpperCase();
230
+
231
+ if (!imapHost || !imapPortStr || !smtpHost || !smtpPortStr) return null;
232
+
233
+ const socketToSecurity = (socket: string | undefined): SecurityMode => {
234
+ if (socket === "SSL" || socket === "TLS" || socket === "SSL/TLS")
235
+ return "tls";
236
+ if (socket === "STARTTLS") return "starttls";
237
+ return "none";
238
+ };
239
+
240
+ return {
241
+ imap: {
242
+ host: imapHost,
243
+ port: Number.parseInt(imapPortStr, 10),
244
+ security: socketToSecurity(imapSocketType),
245
+ },
246
+ smtp: {
247
+ host: smtpHost,
248
+ port: Number.parseInt(smtpPortStr, 10),
249
+ security: socketToSecurity(smtpSocketType),
250
+ },
251
+ source: "mozilla-autoconfig",
252
+ };
253
+ } catch {
254
+ return null;
255
+ }
256
+ }
257
+
258
+ async function fetchMozillaAutoconfig(
259
+ domain: string,
260
+ signal: AbortSignal,
261
+ ): Promise<DiscoveryResult | null> {
262
+ try {
263
+ const url = `https://autoconfig.thunderbird.net/v1.1/${domain}`;
264
+ const res = await fetch(url, { signal });
265
+ if (!res.ok) return null;
266
+ const xml = await res.text();
267
+ const result = parseMozillaAutoconfigXml(xml);
268
+ if (result) return result;
269
+ return null;
270
+ } catch {
271
+ return null;
272
+ }
273
+ }
274
+
275
+ async function fetchDomainAutoconfig(
276
+ domain: string,
277
+ signal: AbortSignal,
278
+ ): Promise<DiscoveryResult | null> {
279
+ try {
280
+ const url = `https://autoconfig.${domain}/mail/config-v1.1.xml`;
281
+ const res = await fetch(url, { signal });
282
+ if (!res.ok) return null;
283
+ const xml = await res.text();
284
+ const result = parseMozillaAutoconfigXml(xml);
285
+ if (result) return { ...result, source: "domain-autoconfig" };
286
+ return null;
287
+ } catch {
288
+ return null;
289
+ }
290
+ }
291
+
292
+ function heuristicResult(domain: string): DiscoveryResult {
293
+ return {
294
+ imap: { host: `imap.${domain}`, port: 993, security: "tls" },
295
+ smtp: { host: `smtp.${domain}`, port: 587, security: "starttls" },
296
+ source: "heuristic",
297
+ };
298
+ }
299
+
300
+ /**
301
+ * Run autodiscovery for the given email address.
302
+ * Returns null if discovery fails after all strategies; caller should show manual-entry UI.
303
+ *
304
+ * Timeout defaults to 1.5s (not the spec's 5s): the browser autoconfig
305
+ * fetches (Mozilla ISPDB + autoconfig.{domain}) almost always reject on CORS,
306
+ * so for any non-table domain we'd otherwise burn ~5s on two doomed requests
307
+ * before the heuristic fallback. A short budget keeps the address step snappy.
308
+ * The real fix is the backend `/autodiscovery` endpoint (see module header).
309
+ */
310
+ export async function discoverSettings(
311
+ email: string,
312
+ timeoutMs = 1500,
313
+ ): Promise<DiscoveryResult | null> {
314
+ const atIdx = email.indexOf("@");
315
+ if (atIdx === -1) return null;
316
+ const domain = email.slice(atIdx + 1).toLowerCase();
317
+ if (!domain) return null;
318
+
319
+ // 1. Curated table — instant, no network
320
+ const tableResult = PROVIDER_TABLE[domain];
321
+ if (tableResult) return tableResult;
322
+
323
+ // 2+3. Network lookups with timeout
324
+ const controller = new AbortController();
325
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
326
+ try {
327
+ const mozilla = await fetchMozillaAutoconfig(domain, controller.signal);
328
+ if (mozilla) return mozilla;
329
+
330
+ const domainConfig = await fetchDomainAutoconfig(domain, controller.signal);
331
+ if (domainConfig) return domainConfig;
332
+ } finally {
333
+ clearTimeout(timer);
334
+ }
335
+
336
+ // 4. Heuristic fallback — caller decides whether to prefill or show manual
337
+ return heuristicResult(domain);
338
+ }
339
+
340
+ /** Get a human-readable "looking up …" message shown during autodiscovery */
341
+ export function getDiscoveryStatusMessage(email: string): string {
342
+ const atIdx = email.indexOf("@");
343
+ if (atIdx === -1) return "Looking up server settings…";
344
+ const domain = email.slice(atIdx + 1);
345
+ return `Looking up settings for ${domain}…`;
346
+ }
347
+
348
+ /** Get app-password URL for the email's domain, if known */
349
+ export function getAppPasswordUrl(email: string): string | undefined {
350
+ const atIdx = email.indexOf("@");
351
+ if (atIdx === -1) return undefined;
352
+ const domain = email.slice(atIdx + 1).toLowerCase();
353
+ return APP_PASSWORD_URLS[domain];
354
+ }