@nocobase/app-plugin-mail 0.0.0-stage → 1.0.0-beta.10

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 (831) hide show
  1. package/CHANGELOG.md +309 -0
  2. package/README.md +55 -2
  3. package/dist/client/components/index.d.ts +19 -0
  4. package/dist/client/components/index.d.ts.map +1 -0
  5. package/dist/client/components/index.js +19 -0
  6. package/dist/client/components/index.js.map +1 -0
  7. package/dist/client/components/mail-account-card.d.ts +14 -0
  8. package/dist/client/components/mail-account-card.d.ts.map +1 -0
  9. package/dist/client/components/mail-account-card.js +18 -0
  10. package/dist/client/components/mail-account-card.js.map +1 -0
  11. package/dist/client/components/mail-account-connector.d.ts +32 -0
  12. package/dist/client/components/mail-account-connector.d.ts.map +1 -0
  13. package/dist/client/components/mail-account-connector.js +59 -0
  14. package/dist/client/components/mail-account-connector.js.map +1 -0
  15. package/dist/client/components/mail-composer.d.ts +4 -0
  16. package/dist/client/components/mail-composer.d.ts.map +1 -0
  17. package/dist/client/components/mail-composer.js +312 -0
  18. package/dist/client/components/mail-composer.js.map +1 -0
  19. package/dist/client/components/mail-conversation-view.d.ts +64 -0
  20. package/dist/client/components/mail-conversation-view.d.ts.map +1 -0
  21. package/dist/client/components/mail-conversation-view.js +196 -0
  22. package/dist/client/components/mail-conversation-view.js.map +1 -0
  23. package/dist/client/components/mail-dev-page-shell.d.ts +10 -0
  24. package/dist/client/components/mail-dev-page-shell.d.ts.map +1 -0
  25. package/dist/client/components/mail-dev-page-shell.js +7 -0
  26. package/dist/client/components/mail-dev-page-shell.js.map +1 -0
  27. package/dist/client/components/mail-html-body.d.ts +9 -0
  28. package/dist/client/components/mail-html-body.d.ts.map +1 -0
  29. package/dist/client/components/mail-html-body.js +60 -0
  30. package/dist/client/components/mail-html-body.js.map +1 -0
  31. package/dist/client/components/mail-image-resizer.d.ts +10 -0
  32. package/dist/client/components/mail-image-resizer.d.ts.map +1 -0
  33. package/dist/client/components/mail-image-resizer.js +168 -0
  34. package/dist/client/components/mail-image-resizer.js.map +1 -0
  35. package/dist/client/components/mail-label-manager.d.ts +3 -0
  36. package/dist/client/components/mail-label-manager.d.ts.map +1 -0
  37. package/dist/client/components/mail-label-manager.js +145 -0
  38. package/dist/client/components/mail-label-manager.js.map +1 -0
  39. package/dist/client/components/mail-label-tag.d.ts +15 -0
  40. package/dist/client/components/mail-label-tag.d.ts.map +1 -0
  41. package/dist/client/components/mail-label-tag.js +12 -0
  42. package/dist/client/components/mail-label-tag.js.map +1 -0
  43. package/dist/client/components/mail-management-form-actions.d.ts +14 -0
  44. package/dist/client/components/mail-management-form-actions.d.ts.map +1 -0
  45. package/dist/client/components/mail-management-form-actions.js +8 -0
  46. package/dist/client/components/mail-management-form-actions.js.map +1 -0
  47. package/dist/client/components/mail-management-list-item.d.ts +11 -0
  48. package/dist/client/components/mail-management-list-item.d.ts.map +1 -0
  49. package/dist/client/components/mail-management-list-item.js +8 -0
  50. package/dist/client/components/mail-management-list-item.js.map +1 -0
  51. package/dist/client/components/mail-message-content.d.ts +9 -0
  52. package/dist/client/components/mail-message-content.d.ts.map +1 -0
  53. package/dist/client/components/mail-message-content.js +44 -0
  54. package/dist/client/components/mail-message-content.js.map +1 -0
  55. package/dist/client/components/mail-message-list.d.ts +25 -0
  56. package/dist/client/components/mail-message-list.d.ts.map +1 -0
  57. package/dist/client/components/mail-message-list.js +77 -0
  58. package/dist/client/components/mail-message-list.js.map +1 -0
  59. package/dist/client/components/mail-navigation-icon.d.ts +5 -0
  60. package/dist/client/components/mail-navigation-icon.d.ts.map +1 -0
  61. package/dist/client/components/mail-navigation-icon.js +63 -0
  62. package/dist/client/components/mail-navigation-icon.js.map +1 -0
  63. package/dist/client/components/mail-page-header.d.ts +9 -0
  64. package/dist/client/components/mail-page-header.d.ts.map +1 -0
  65. package/dist/client/components/mail-page-header.js +6 -0
  66. package/dist/client/components/mail-page-header.js.map +1 -0
  67. package/dist/client/components/mail-pagination.d.ts +12 -0
  68. package/dist/client/components/mail-pagination.d.ts.map +1 -0
  69. package/dist/client/components/mail-pagination.js +62 -0
  70. package/dist/client/components/mail-pagination.js.map +1 -0
  71. package/dist/client/components/mail-provider-card.d.ts +13 -0
  72. package/dist/client/components/mail-provider-card.d.ts.map +1 -0
  73. package/dist/client/components/mail-provider-card.js +11 -0
  74. package/dist/client/components/mail-provider-card.js.map +1 -0
  75. package/dist/client/components/mail-rich-text-editor.d.ts +64 -0
  76. package/dist/client/components/mail-rich-text-editor.d.ts.map +1 -0
  77. package/dist/client/components/mail-rich-text-editor.js +199 -0
  78. package/dist/client/components/mail-rich-text-editor.js.map +1 -0
  79. package/dist/client/components/mail-scheduled-status.d.ts +8 -0
  80. package/dist/client/components/mail-scheduled-status.d.ts.map +1 -0
  81. package/dist/client/components/mail-scheduled-status.js +52 -0
  82. package/dist/client/components/mail-scheduled-status.js.map +1 -0
  83. package/dist/client/components/mail-signature-manager.d.ts +8 -0
  84. package/dist/client/components/mail-signature-manager.d.ts.map +1 -0
  85. package/dist/client/components/mail-signature-manager.js +255 -0
  86. package/dist/client/components/mail-signature-manager.js.map +1 -0
  87. package/dist/client/components/mail-status-badge.d.ts +8 -0
  88. package/dist/client/components/mail-status-badge.d.ts.map +1 -0
  89. package/dist/client/components/mail-status-badge.js +12 -0
  90. package/dist/client/components/mail-status-badge.js.map +1 -0
  91. package/dist/client/components/mail-sync-policy-fields.d.ts +14 -0
  92. package/dist/client/components/mail-sync-policy-fields.d.ts.map +1 -0
  93. package/dist/client/components/mail-sync-policy-fields.js +27 -0
  94. package/dist/client/components/mail-sync-policy-fields.js.map +1 -0
  95. package/dist/client/components/mail-template-manager.d.ts +3 -0
  96. package/dist/client/components/mail-template-manager.d.ts.map +1 -0
  97. package/dist/client/components/mail-template-manager.js +135 -0
  98. package/dist/client/components/mail-template-manager.js.map +1 -0
  99. package/dist/client/components/mail-text-body.d.ts +5 -0
  100. package/dist/client/components/mail-text-body.d.ts.map +1 -0
  101. package/dist/client/components/mail-text-body.js +11 -0
  102. package/dist/client/components/mail-text-body.js.map +1 -0
  103. package/dist/client/components/mail-workspace-composer.d.ts +4 -0
  104. package/dist/client/components/mail-workspace-composer.d.ts.map +1 -0
  105. package/dist/client/components/mail-workspace-composer.js +74 -0
  106. package/dist/client/components/mail-workspace-composer.js.map +1 -0
  107. package/dist/client/components/mailbox-sidebar.d.ts +38 -0
  108. package/dist/client/components/mailbox-sidebar.d.ts.map +1 -0
  109. package/dist/client/components/mailbox-sidebar.js +71 -0
  110. package/dist/client/components/mailbox-sidebar.js.map +1 -0
  111. package/dist/client/components/page-container.d.ts +4 -0
  112. package/dist/client/components/page-container.d.ts.map +1 -0
  113. package/dist/client/components/page-container.js +6 -0
  114. package/dist/client/components/page-container.js.map +1 -0
  115. package/dist/client/components/page-header.d.ts +8 -0
  116. package/dist/client/components/page-header.d.ts.map +1 -0
  117. package/dist/client/components/page-header.js +5 -0
  118. package/dist/client/components/page-header.js.map +1 -0
  119. package/dist/client/components/public.d.ts +19 -0
  120. package/dist/client/components/public.d.ts.map +1 -0
  121. package/dist/client/components/public.js +18 -0
  122. package/dist/client/components/public.js.map +1 -0
  123. package/dist/client/components/ui/button.d.ts +7 -0
  124. package/dist/client/components/ui/button.d.ts.map +1 -0
  125. package/dist/client/components/ui/button.js +19 -0
  126. package/dist/client/components/ui/button.js.map +1 -0
  127. package/dist/client/components/ui/calendar.d.ts +9 -0
  128. package/dist/client/components/ui/calendar.d.ts.map +1 -0
  129. package/dist/client/components/ui/calendar.js +23 -0
  130. package/dist/client/components/ui/calendar.js.map +1 -0
  131. package/dist/client/components/ui/card.d.ts +3 -0
  132. package/dist/client/components/ui/card.d.ts.map +1 -0
  133. package/dist/client/components/ui/card.js +6 -0
  134. package/dist/client/components/ui/card.js.map +1 -0
  135. package/dist/client/components/ui/checkbox.d.ts +5 -0
  136. package/dist/client/components/ui/checkbox.d.ts.map +1 -0
  137. package/dist/client/components/ui/checkbox.js +8 -0
  138. package/dist/client/components/ui/checkbox.js.map +1 -0
  139. package/dist/client/components/ui/collapsible.d.ts +6 -0
  140. package/dist/client/components/ui/collapsible.d.ts.map +1 -0
  141. package/dist/client/components/ui/collapsible.js +12 -0
  142. package/dist/client/components/ui/collapsible.js.map +1 -0
  143. package/dist/client/components/ui/dialog.d.ts +14 -0
  144. package/dist/client/components/ui/dialog.d.ts.map +1 -0
  145. package/dist/client/components/ui/dialog.js +36 -0
  146. package/dist/client/components/ui/dialog.js.map +1 -0
  147. package/dist/client/components/ui/dropdown-menu.d.ts +8 -0
  148. package/dist/client/components/ui/dropdown-menu.d.ts.map +1 -0
  149. package/dist/client/components/ui/dropdown-menu.js +21 -0
  150. package/dist/client/components/ui/dropdown-menu.js.map +1 -0
  151. package/dist/client/components/ui/input.d.ts +4 -0
  152. package/dist/client/components/ui/input.d.ts.map +1 -0
  153. package/dist/client/components/ui/input.js +7 -0
  154. package/dist/client/components/ui/input.js.map +1 -0
  155. package/dist/client/components/ui/native-select.d.ts +4 -0
  156. package/dist/client/components/ui/native-select.d.ts.map +1 -0
  157. package/dist/client/components/ui/native-select.js +7 -0
  158. package/dist/client/components/ui/native-select.js.map +1 -0
  159. package/dist/client/components/ui/popover.d.ts +10 -0
  160. package/dist/client/components/ui/popover.d.ts.map +1 -0
  161. package/dist/client/components/ui/popover.js +23 -0
  162. package/dist/client/components/ui/popover.js.map +1 -0
  163. package/dist/client/components/ui/sheet.d.ts +13 -0
  164. package/dist/client/components/ui/sheet.d.ts.map +1 -0
  165. package/dist/client/components/ui/sheet.js +28 -0
  166. package/dist/client/components/ui/sheet.js.map +1 -0
  167. package/dist/client/components/ui/textarea.d.ts +4 -0
  168. package/dist/client/components/ui/textarea.d.ts.map +1 -0
  169. package/dist/client/components/ui/textarea.js +6 -0
  170. package/dist/client/components/ui/textarea.js.map +1 -0
  171. package/dist/client/components/ui/tooltip.d.ts +7 -0
  172. package/dist/client/components/ui/tooltip.d.ts.map +1 -0
  173. package/dist/client/components/ui/tooltip.js +16 -0
  174. package/dist/client/components/ui/tooltip.js.map +1 -0
  175. package/dist/client/contracts/composer.d.ts +68 -0
  176. package/dist/client/contracts/composer.d.ts.map +1 -0
  177. package/dist/client/contracts/composer.js +12 -0
  178. package/dist/client/contracts/composer.js.map +1 -0
  179. package/dist/client/hooks/use-mail-composer.d.ts +52 -0
  180. package/dist/client/hooks/use-mail-composer.d.ts.map +1 -0
  181. package/dist/client/hooks/use-mail-composer.js +633 -0
  182. package/dist/client/hooks/use-mail-composer.js.map +1 -0
  183. package/dist/client/hooks/use-mail-invalidations.d.ts +3 -0
  184. package/dist/client/hooks/use-mail-invalidations.d.ts.map +1 -0
  185. package/dist/client/hooks/use-mail-invalidations.js +28 -0
  186. package/dist/client/hooks/use-mail-invalidations.js.map +1 -0
  187. package/dist/client/hooks/use-mail-table-page.d.ts +12 -0
  188. package/dist/client/hooks/use-mail-table-page.d.ts.map +1 -0
  189. package/dist/client/hooks/use-mail-table-page.js +22 -0
  190. package/dist/client/hooks/use-mail-table-page.js.map +1 -0
  191. package/dist/client/hooks/use-mail-workspace-data.d.ts +35 -0
  192. package/dist/client/hooks/use-mail-workspace-data.d.ts.map +1 -0
  193. package/dist/client/hooks/use-mail-workspace-data.js +409 -0
  194. package/dist/client/hooks/use-mail-workspace-data.js.map +1 -0
  195. package/dist/client/index.d.ts +14 -0
  196. package/dist/client/index.d.ts.map +1 -0
  197. package/dist/client/index.js +10 -0
  198. package/dist/client/index.js.map +1 -0
  199. package/dist/client/lib/mail-address.d.ts +5 -0
  200. package/dist/client/lib/mail-address.d.ts.map +1 -0
  201. package/dist/client/lib/mail-address.js +14 -0
  202. package/dist/client/lib/mail-address.js.map +1 -0
  203. package/dist/client/lib/mail-body-theme.d.ts +3 -0
  204. package/dist/client/lib/mail-body-theme.d.ts.map +1 -0
  205. package/dist/client/lib/mail-body-theme.js +69 -0
  206. package/dist/client/lib/mail-body-theme.js.map +1 -0
  207. package/dist/client/lib/mail-composer-state.d.ts +36 -0
  208. package/dist/client/lib/mail-composer-state.d.ts.map +1 -0
  209. package/dist/client/lib/mail-composer-state.js +212 -0
  210. package/dist/client/lib/mail-composer-state.js.map +1 -0
  211. package/dist/client/lib/mail-editor-headings.d.ts +3 -0
  212. package/dist/client/lib/mail-editor-headings.d.ts.map +1 -0
  213. package/dist/client/lib/mail-editor-headings.js +27 -0
  214. package/dist/client/lib/mail-editor-headings.js.map +1 -0
  215. package/dist/client/lib/mail-editor-images.d.ts +10 -0
  216. package/dist/client/lib/mail-editor-images.d.ts.map +1 -0
  217. package/dist/client/lib/mail-editor-images.js +82 -0
  218. package/dist/client/lib/mail-editor-images.js.map +1 -0
  219. package/dist/client/lib/mail-editor-labels.d.ts +3 -0
  220. package/dist/client/lib/mail-editor-labels.d.ts.map +1 -0
  221. package/dist/client/lib/mail-editor-labels.js +43 -0
  222. package/dist/client/lib/mail-editor-labels.js.map +1 -0
  223. package/dist/client/lib/mail-error-description.d.ts +7 -0
  224. package/dist/client/lib/mail-error-description.d.ts.map +1 -0
  225. package/dist/client/lib/mail-error-description.js +84 -0
  226. package/dist/client/lib/mail-error-description.js.map +1 -0
  227. package/dist/client/lib/mail-folders.d.ts +40 -0
  228. package/dist/client/lib/mail-folders.d.ts.map +1 -0
  229. package/dist/client/lib/mail-folders.js +215 -0
  230. package/dist/client/lib/mail-folders.js.map +1 -0
  231. package/dist/client/lib/mail-forward-content.d.ts +19 -0
  232. package/dist/client/lib/mail-forward-content.d.ts.map +1 -0
  233. package/dist/client/lib/mail-forward-content.js +120 -0
  234. package/dist/client/lib/mail-forward-content.js.map +1 -0
  235. package/dist/client/lib/mail-inline-images.d.ts +6 -0
  236. package/dist/client/lib/mail-inline-images.d.ts.map +1 -0
  237. package/dist/client/lib/mail-inline-images.js +41 -0
  238. package/dist/client/lib/mail-inline-images.js.map +1 -0
  239. package/dist/client/lib/mail-label.d.ts +10 -0
  240. package/dist/client/lib/mail-label.d.ts.map +1 -0
  241. package/dist/client/lib/mail-label.js +47 -0
  242. package/dist/client/lib/mail-label.js.map +1 -0
  243. package/dist/client/lib/mail-message-document.d.ts +6 -0
  244. package/dist/client/lib/mail-message-document.d.ts.map +1 -0
  245. package/dist/client/lib/mail-message-document.js +68 -0
  246. package/dist/client/lib/mail-message-document.js.map +1 -0
  247. package/dist/client/lib/mail-pending-delivery.d.ts +22 -0
  248. package/dist/client/lib/mail-pending-delivery.d.ts.map +1 -0
  249. package/dist/client/lib/mail-pending-delivery.js +81 -0
  250. package/dist/client/lib/mail-pending-delivery.js.map +1 -0
  251. package/dist/client/lib/mail-quoted-content.d.ts +8 -0
  252. package/dist/client/lib/mail-quoted-content.d.ts.map +1 -0
  253. package/dist/client/lib/mail-quoted-content.js +62 -0
  254. package/dist/client/lib/mail-quoted-content.js.map +1 -0
  255. package/dist/client/lib/mail-reply-content.d.ts +9 -0
  256. package/dist/client/lib/mail-reply-content.d.ts.map +1 -0
  257. package/dist/client/lib/mail-reply-content.js +50 -0
  258. package/dist/client/lib/mail-reply-content.js.map +1 -0
  259. package/dist/client/lib/mail-signature.d.ts +8 -0
  260. package/dist/client/lib/mail-signature.d.ts.map +1 -0
  261. package/dist/client/lib/mail-signature.js +40 -0
  262. package/dist/client/lib/mail-signature.js.map +1 -0
  263. package/dist/client/lib/mail-template.d.ts +14 -0
  264. package/dist/client/lib/mail-template.d.ts.map +1 -0
  265. package/dist/client/lib/mail-template.js +189 -0
  266. package/dist/client/lib/mail-template.js.map +1 -0
  267. package/dist/client/lib/utils.d.ts +2 -0
  268. package/dist/client/lib/utils.d.ts.map +1 -0
  269. package/dist/client/lib/utils.js +2 -0
  270. package/dist/client/lib/utils.js.map +1 -0
  271. package/dist/client/locales/en-US.d.ts +555 -0
  272. package/dist/client/locales/en-US.d.ts.map +1 -0
  273. package/dist/client/locales/en-US.js +553 -0
  274. package/dist/client/locales/en-US.js.map +1 -0
  275. package/dist/client/locales/index.d.ts +4 -0
  276. package/dist/client/locales/index.d.ts.map +1 -0
  277. package/dist/client/locales/index.js +6 -0
  278. package/dist/client/locales/index.js.map +1 -0
  279. package/dist/client/locales/zh-CN.d.ts +4 -0
  280. package/dist/client/locales/zh-CN.d.ts.map +1 -0
  281. package/dist/client/locales/zh-CN.js +553 -0
  282. package/dist/client/locales/zh-CN.js.map +1 -0
  283. package/dist/client/mail-client.d.ts +112 -0
  284. package/dist/client/mail-client.d.ts.map +1 -0
  285. package/dist/client/mail-client.js +412 -0
  286. package/dist/client/mail-client.js.map +1 -0
  287. package/dist/client/namespace.d.ts +4 -0
  288. package/dist/client/namespace.d.ts.map +1 -0
  289. package/dist/client/namespace.js +3 -0
  290. package/dist/client/namespace.js.map +1 -0
  291. package/dist/client/pages/mail-accounts-page.d.ts +8 -0
  292. package/dist/client/pages/mail-accounts-page.d.ts.map +1 -0
  293. package/dist/client/pages/mail-accounts-page.js +463 -0
  294. package/dist/client/pages/mail-accounts-page.js.map +1 -0
  295. package/dist/client/pages/mail-workspace-page.d.ts +15 -0
  296. package/dist/client/pages/mail-workspace-page.d.ts.map +1 -0
  297. package/dist/client/pages/mail-workspace-page.js +834 -0
  298. package/dist/client/pages/mail-workspace-page.js.map +1 -0
  299. package/dist/client/plugin.d.ts +7 -0
  300. package/dist/client/plugin.d.ts.map +1 -0
  301. package/dist/client/plugin.js +10 -0
  302. package/dist/client/plugin.js.map +1 -0
  303. package/dist/client/runtime.d.ts +5 -0
  304. package/dist/client/runtime.d.ts.map +1 -0
  305. package/dist/client/runtime.js +7 -0
  306. package/dist/client/runtime.js.map +1 -0
  307. package/dist/client/service-provider.d.ts +10 -0
  308. package/dist/client/service-provider.d.ts.map +1 -0
  309. package/dist/client/service-provider.js +15 -0
  310. package/dist/client/service-provider.js.map +1 -0
  311. package/dist/client/subscription.d.ts +8 -0
  312. package/dist/client/subscription.d.ts.map +1 -0
  313. package/dist/client/subscription.js +33 -0
  314. package/dist/client/subscription.js.map +1 -0
  315. package/dist/database/migrations/.manifest.json +21 -0
  316. package/dist/database/migrations/202609030001_create_mail_tables.d.ts +4 -0
  317. package/dist/database/migrations/202609030001_create_mail_tables.d.ts.map +1 -0
  318. package/dist/database/migrations/202609030001_create_mail_tables.js +536 -0
  319. package/dist/database/migrations/202609030001_create_mail_tables.js.map +1 -0
  320. package/dist/database/migrations/202609200001_backfill_mail_conversations.d.ts +4 -0
  321. package/dist/database/migrations/202609200001_backfill_mail_conversations.d.ts.map +1 -0
  322. package/dist/database/migrations/202609200001_backfill_mail_conversations.js +42 -0
  323. package/dist/database/migrations/202609200001_backfill_mail_conversations.js.map +1 -0
  324. package/dist/database/migrations/202609210001_normalize_mail_folders.d.ts +4 -0
  325. package/dist/database/migrations/202609210001_normalize_mail_folders.d.ts.map +1 -0
  326. package/dist/database/migrations/202609210001_normalize_mail_folders.js +42 -0
  327. package/dist/database/migrations/202609210001_normalize_mail_folders.js.map +1 -0
  328. package/dist/database/migrations/202609260001_add_mail_sync_retry_attempts.d.ts +4 -0
  329. package/dist/database/migrations/202609260001_add_mail_sync_retry_attempts.d.ts.map +1 -0
  330. package/dist/database/migrations/202609260001_add_mail_sync_retry_attempts.js +17 -0
  331. package/dist/database/migrations/202609260001_add_mail_sync_retry_attempts.js.map +1 -0
  332. package/dist/server/adapter-resolver.d.ts +14 -0
  333. package/dist/server/adapter-resolver.d.ts.map +1 -0
  334. package/dist/server/adapter-resolver.js +21 -0
  335. package/dist/server/adapter-resolver.js.map +1 -0
  336. package/dist/server/adapters/gmail/adapter.d.ts +42 -0
  337. package/dist/server/adapters/gmail/adapter.d.ts.map +1 -0
  338. package/dist/server/adapters/gmail/adapter.js +800 -0
  339. package/dist/server/adapters/gmail/adapter.js.map +1 -0
  340. package/dist/server/adapters/gmail/auth.d.ts +9 -0
  341. package/dist/server/adapters/gmail/auth.d.ts.map +1 -0
  342. package/dist/server/adapters/gmail/auth.js +178 -0
  343. package/dist/server/adapters/gmail/auth.js.map +1 -0
  344. package/dist/server/adapters/gmail/batch.d.ts +8 -0
  345. package/dist/server/adapters/gmail/batch.d.ts.map +1 -0
  346. package/dist/server/adapters/gmail/batch.js +116 -0
  347. package/dist/server/adapters/gmail/batch.js.map +1 -0
  348. package/dist/server/adapters/gmail/concurrency.d.ts +2 -0
  349. package/dist/server/adapters/gmail/concurrency.d.ts.map +1 -0
  350. package/dist/server/adapters/gmail/concurrency.js +12 -0
  351. package/dist/server/adapters/gmail/concurrency.js.map +1 -0
  352. package/dist/server/adapters/gmail/constants.d.ts +7 -0
  353. package/dist/server/adapters/gmail/constants.d.ts.map +1 -0
  354. package/dist/server/adapters/gmail/constants.js +19 -0
  355. package/dist/server/adapters/gmail/constants.js.map +1 -0
  356. package/dist/server/adapters/gmail/definition.d.ts +4 -0
  357. package/dist/server/adapters/gmail/definition.d.ts.map +1 -0
  358. package/dist/server/adapters/gmail/definition.js +58 -0
  359. package/dist/server/adapters/gmail/definition.js.map +1 -0
  360. package/dist/server/adapters/gmail/errors.d.ts +12 -0
  361. package/dist/server/adapters/gmail/errors.d.ts.map +1 -0
  362. package/dist/server/adapters/gmail/errors.js +58 -0
  363. package/dist/server/adapters/gmail/errors.js.map +1 -0
  364. package/dist/server/adapters/gmail/http.d.ts +11 -0
  365. package/dist/server/adapters/gmail/http.d.ts.map +1 -0
  366. package/dist/server/adapters/gmail/http.js +152 -0
  367. package/dist/server/adapters/gmail/http.js.map +1 -0
  368. package/dist/server/adapters/gmail/index.d.ts +4 -0
  369. package/dist/server/adapters/gmail/index.d.ts.map +1 -0
  370. package/dist/server/adapters/gmail/index.js +3 -0
  371. package/dist/server/adapters/gmail/index.js.map +1 -0
  372. package/dist/server/adapters/gmail/mime.d.ts +7 -0
  373. package/dist/server/adapters/gmail/mime.d.ts.map +1 -0
  374. package/dist/server/adapters/gmail/mime.js +189 -0
  375. package/dist/server/adapters/gmail/mime.js.map +1 -0
  376. package/dist/server/adapters/gmail/normalize.d.ts +7 -0
  377. package/dist/server/adapters/gmail/normalize.d.ts.map +1 -0
  378. package/dist/server/adapters/gmail/normalize.js +104 -0
  379. package/dist/server/adapters/gmail/normalize.js.map +1 -0
  380. package/dist/server/adapters/gmail/quota.d.ts +3 -0
  381. package/dist/server/adapters/gmail/quota.d.ts.map +1 -0
  382. package/dist/server/adapters/gmail/quota.js +112 -0
  383. package/dist/server/adapters/gmail/quota.js.map +1 -0
  384. package/dist/server/adapters/gmail/sync.d.ts +5 -0
  385. package/dist/server/adapters/gmail/sync.d.ts.map +1 -0
  386. package/dist/server/adapters/gmail/sync.js +32 -0
  387. package/dist/server/adapters/gmail/sync.js.map +1 -0
  388. package/dist/server/adapters/gmail/types.d.ts +141 -0
  389. package/dist/server/adapters/gmail/types.d.ts.map +1 -0
  390. package/dist/server/adapters/gmail/types.js +2 -0
  391. package/dist/server/adapters/gmail/types.js.map +1 -0
  392. package/dist/server/adapters/imap-smtp/adapter.d.ts +40 -0
  393. package/dist/server/adapters/imap-smtp/adapter.d.ts.map +1 -0
  394. package/dist/server/adapters/imap-smtp/adapter.js +721 -0
  395. package/dist/server/adapters/imap-smtp/adapter.js.map +1 -0
  396. package/dist/server/adapters/imap-smtp/config.d.ts +19 -0
  397. package/dist/server/adapters/imap-smtp/config.d.ts.map +1 -0
  398. package/dist/server/adapters/imap-smtp/config.js +2 -0
  399. package/dist/server/adapters/imap-smtp/config.js.map +1 -0
  400. package/dist/server/adapters/imap-smtp/connection.d.ts +9 -0
  401. package/dist/server/adapters/imap-smtp/connection.d.ts.map +1 -0
  402. package/dist/server/adapters/imap-smtp/connection.js +72 -0
  403. package/dist/server/adapters/imap-smtp/connection.js.map +1 -0
  404. package/dist/server/adapters/imap-smtp/constants.d.ts +7 -0
  405. package/dist/server/adapters/imap-smtp/constants.d.ts.map +1 -0
  406. package/dist/server/adapters/imap-smtp/constants.js +16 -0
  407. package/dist/server/adapters/imap-smtp/constants.js.map +1 -0
  408. package/dist/server/adapters/imap-smtp/content.d.ts +11 -0
  409. package/dist/server/adapters/imap-smtp/content.d.ts.map +1 -0
  410. package/dist/server/adapters/imap-smtp/content.js +157 -0
  411. package/dist/server/adapters/imap-smtp/content.js.map +1 -0
  412. package/dist/server/adapters/imap-smtp/definition.d.ts +4 -0
  413. package/dist/server/adapters/imap-smtp/definition.d.ts.map +1 -0
  414. package/dist/server/adapters/imap-smtp/definition.js +59 -0
  415. package/dist/server/adapters/imap-smtp/definition.js.map +1 -0
  416. package/dist/server/adapters/imap-smtp/errors.d.ts +10 -0
  417. package/dist/server/adapters/imap-smtp/errors.d.ts.map +1 -0
  418. package/dist/server/adapters/imap-smtp/errors.js +95 -0
  419. package/dist/server/adapters/imap-smtp/errors.js.map +1 -0
  420. package/dist/server/adapters/imap-smtp/folders.d.ts +7 -0
  421. package/dist/server/adapters/imap-smtp/folders.d.ts.map +1 -0
  422. package/dist/server/adapters/imap-smtp/folders.js +39 -0
  423. package/dist/server/adapters/imap-smtp/folders.js.map +1 -0
  424. package/dist/server/adapters/imap-smtp/index.d.ts +3 -0
  425. package/dist/server/adapters/imap-smtp/index.d.ts.map +1 -0
  426. package/dist/server/adapters/imap-smtp/index.js +2 -0
  427. package/dist/server/adapters/imap-smtp/index.js.map +1 -0
  428. package/dist/server/adapters/imap-smtp/locators.d.ts +23 -0
  429. package/dist/server/adapters/imap-smtp/locators.d.ts.map +1 -0
  430. package/dist/server/adapters/imap-smtp/locators.js +156 -0
  431. package/dist/server/adapters/imap-smtp/locators.js.map +1 -0
  432. package/dist/server/adapters/imap-smtp/normalize.d.ts +27 -0
  433. package/dist/server/adapters/imap-smtp/normalize.d.ts.map +1 -0
  434. package/dist/server/adapters/imap-smtp/normalize.js +64 -0
  435. package/dist/server/adapters/imap-smtp/normalize.js.map +1 -0
  436. package/dist/server/adapters/imap-smtp/types.d.ts +21 -0
  437. package/dist/server/adapters/imap-smtp/types.d.ts.map +1 -0
  438. package/dist/server/adapters/imap-smtp/types.js +2 -0
  439. package/dist/server/adapters/imap-smtp/types.js.map +1 -0
  440. package/dist/server/adapters/incomplete-message.d.ts +4 -0
  441. package/dist/server/adapters/incomplete-message.d.ts.map +1 -0
  442. package/dist/server/adapters/incomplete-message.js +21 -0
  443. package/dist/server/adapters/incomplete-message.js.map +1 -0
  444. package/dist/server/adapters/microsoft/adapter.d.ts +42 -0
  445. package/dist/server/adapters/microsoft/adapter.d.ts.map +1 -0
  446. package/dist/server/adapters/microsoft/adapter.js +1063 -0
  447. package/dist/server/adapters/microsoft/adapter.js.map +1 -0
  448. package/dist/server/adapters/microsoft/auth.d.ts +10 -0
  449. package/dist/server/adapters/microsoft/auth.d.ts.map +1 -0
  450. package/dist/server/adapters/microsoft/auth.js +153 -0
  451. package/dist/server/adapters/microsoft/auth.js.map +1 -0
  452. package/dist/server/adapters/microsoft/concurrency.d.ts +2 -0
  453. package/dist/server/adapters/microsoft/concurrency.d.ts.map +1 -0
  454. package/dist/server/adapters/microsoft/concurrency.js +15 -0
  455. package/dist/server/adapters/microsoft/concurrency.js.map +1 -0
  456. package/dist/server/adapters/microsoft/constants.d.ts +10 -0
  457. package/dist/server/adapters/microsoft/constants.d.ts.map +1 -0
  458. package/dist/server/adapters/microsoft/constants.js +46 -0
  459. package/dist/server/adapters/microsoft/constants.js.map +1 -0
  460. package/dist/server/adapters/microsoft/content.d.ts +9 -0
  461. package/dist/server/adapters/microsoft/content.d.ts.map +1 -0
  462. package/dist/server/adapters/microsoft/content.js +40 -0
  463. package/dist/server/adapters/microsoft/content.js.map +1 -0
  464. package/dist/server/adapters/microsoft/definition.d.ts +4 -0
  465. package/dist/server/adapters/microsoft/definition.d.ts.map +1 -0
  466. package/dist/server/adapters/microsoft/definition.js +48 -0
  467. package/dist/server/adapters/microsoft/definition.js.map +1 -0
  468. package/dist/server/adapters/microsoft/errors.d.ts +12 -0
  469. package/dist/server/adapters/microsoft/errors.d.ts.map +1 -0
  470. package/dist/server/adapters/microsoft/errors.js +51 -0
  471. package/dist/server/adapters/microsoft/errors.js.map +1 -0
  472. package/dist/server/adapters/microsoft/http.d.ts +13 -0
  473. package/dist/server/adapters/microsoft/http.d.ts.map +1 -0
  474. package/dist/server/adapters/microsoft/http.js +115 -0
  475. package/dist/server/adapters/microsoft/http.js.map +1 -0
  476. package/dist/server/adapters/microsoft/index.d.ts +4 -0
  477. package/dist/server/adapters/microsoft/index.d.ts.map +1 -0
  478. package/dist/server/adapters/microsoft/index.js +3 -0
  479. package/dist/server/adapters/microsoft/index.js.map +1 -0
  480. package/dist/server/adapters/microsoft/normalize.d.ts +14 -0
  481. package/dist/server/adapters/microsoft/normalize.d.ts.map +1 -0
  482. package/dist/server/adapters/microsoft/normalize.js +110 -0
  483. package/dist/server/adapters/microsoft/normalize.js.map +1 -0
  484. package/dist/server/adapters/microsoft/sync.d.ts +12 -0
  485. package/dist/server/adapters/microsoft/sync.d.ts.map +1 -0
  486. package/dist/server/adapters/microsoft/sync.js +111 -0
  487. package/dist/server/adapters/microsoft/sync.js.map +1 -0
  488. package/dist/server/adapters/microsoft/types.d.ts +128 -0
  489. package/dist/server/adapters/microsoft/types.d.ts.map +1 -0
  490. package/dist/server/adapters/microsoft/types.js +2 -0
  491. package/dist/server/adapters/microsoft/types.js.map +1 -0
  492. package/dist/server/config.d.ts +56 -0
  493. package/dist/server/config.d.ts.map +1 -0
  494. package/dist/server/config.js +253 -0
  495. package/dist/server/config.js.map +1 -0
  496. package/dist/server/contracts/persistence.d.ts +220 -0
  497. package/dist/server/contracts/persistence.d.ts.map +1 -0
  498. package/dist/server/contracts/persistence.js +2 -0
  499. package/dist/server/contracts/persistence.js.map +1 -0
  500. package/dist/server/contracts/provider.d.ts +303 -0
  501. package/dist/server/contracts/provider.d.ts.map +1 -0
  502. package/dist/server/contracts/provider.js +4 -0
  503. package/dist/server/contracts/provider.js.map +1 -0
  504. package/dist/server/contracts/service.d.ts +82 -0
  505. package/dist/server/contracts/service.d.ts.map +1 -0
  506. package/dist/server/contracts/service.js +2 -0
  507. package/dist/server/contracts/service.js.map +1 -0
  508. package/dist/server/credentials.d.ts +24 -0
  509. package/dist/server/credentials.d.ts.map +1 -0
  510. package/dist/server/credentials.js +263 -0
  511. package/dist/server/credentials.js.map +1 -0
  512. package/dist/server/index.d.ts +13 -0
  513. package/dist/server/index.d.ts.map +1 -0
  514. package/dist/server/index.js +7 -0
  515. package/dist/server/index.js.map +1 -0
  516. package/dist/server/jobs/send-scheduled-mail.d.ts +8 -0
  517. package/dist/server/jobs/send-scheduled-mail.d.ts.map +1 -0
  518. package/dist/server/jobs/send-scheduled-mail.js +18 -0
  519. package/dist/server/jobs/send-scheduled-mail.js.map +1 -0
  520. package/dist/server/jobs/sync-mailbox.d.ts +11 -0
  521. package/dist/server/jobs/sync-mailbox.d.ts.map +1 -0
  522. package/dist/server/jobs/sync-mailbox.js +23 -0
  523. package/dist/server/jobs/sync-mailbox.js.map +1 -0
  524. package/dist/server/locales/en-US.d.ts +18 -0
  525. package/dist/server/locales/en-US.d.ts.map +1 -0
  526. package/dist/server/locales/en-US.js +16 -0
  527. package/dist/server/locales/en-US.js.map +1 -0
  528. package/dist/server/locales/index.d.ts +4 -0
  529. package/dist/server/locales/index.d.ts.map +1 -0
  530. package/dist/server/locales/index.js +6 -0
  531. package/dist/server/locales/index.js.map +1 -0
  532. package/dist/server/locales/zh-CN.d.ts +4 -0
  533. package/dist/server/locales/zh-CN.d.ts.map +1 -0
  534. package/dist/server/locales/zh-CN.js +16 -0
  535. package/dist/server/locales/zh-CN.js.map +1 -0
  536. package/dist/server/log-pagination.d.ts +2 -0
  537. package/dist/server/log-pagination.d.ts.map +1 -0
  538. package/dist/server/log-pagination.js +8 -0
  539. package/dist/server/log-pagination.js.map +1 -0
  540. package/dist/server/logging.d.ts +16 -0
  541. package/dist/server/logging.d.ts.map +1 -0
  542. package/dist/server/logging.js +24 -0
  543. package/dist/server/logging.js.map +1 -0
  544. package/dist/server/operations/index.d.ts +3 -0
  545. package/dist/server/operations/index.d.ts.map +1 -0
  546. package/dist/server/operations/index.js +3 -0
  547. package/dist/server/operations/index.js.map +1 -0
  548. package/dist/server/operations/message-mutations.d.ts +16 -0
  549. package/dist/server/operations/message-mutations.d.ts.map +1 -0
  550. package/dist/server/operations/message-mutations.js +139 -0
  551. package/dist/server/operations/message-mutations.js.map +1 -0
  552. package/dist/server/operations/schedule-sync.d.ts +15 -0
  553. package/dist/server/operations/schedule-sync.d.ts.map +1 -0
  554. package/dist/server/operations/schedule-sync.js +44 -0
  555. package/dist/server/operations/schedule-sync.js.map +1 -0
  556. package/dist/server/operations/send-mail.d.ts +36 -0
  557. package/dist/server/operations/send-mail.d.ts.map +1 -0
  558. package/dist/server/operations/send-mail.js +691 -0
  559. package/dist/server/operations/send-mail.js.map +1 -0
  560. package/dist/server/operations/sync-mailbox.d.ts +29 -0
  561. package/dist/server/operations/sync-mailbox.d.ts.map +1 -0
  562. package/dist/server/operations/sync-mailbox.js +388 -0
  563. package/dist/server/operations/sync-mailbox.js.map +1 -0
  564. package/dist/server/outbound-attachments.d.ts +28 -0
  565. package/dist/server/outbound-attachments.d.ts.map +1 -0
  566. package/dist/server/outbound-attachments.js +91 -0
  567. package/dist/server/outbound-attachments.js.map +1 -0
  568. package/dist/server/plugin.d.ts +4 -0
  569. package/dist/server/plugin.d.ts.map +1 -0
  570. package/dist/server/plugin.js +16 -0
  571. package/dist/server/plugin.js.map +1 -0
  572. package/dist/server/providers/index.d.ts +4 -0
  573. package/dist/server/providers/index.d.ts.map +1 -0
  574. package/dist/server/providers/index.js +8 -0
  575. package/dist/server/providers/index.js.map +1 -0
  576. package/dist/server/providers/mail-builtins.d.ts +7 -0
  577. package/dist/server/providers/mail-builtins.d.ts.map +1 -0
  578. package/dist/server/providers/mail-builtins.js +17 -0
  579. package/dist/server/providers/mail-builtins.js.map +1 -0
  580. package/dist/server/providers/mail-core.d.ts +23 -0
  581. package/dist/server/providers/mail-core.d.ts.map +1 -0
  582. package/dist/server/providers/mail-core.js +150 -0
  583. package/dist/server/providers/mail-core.js.map +1 -0
  584. package/dist/server/public-types.d.ts +7 -0
  585. package/dist/server/public-types.d.ts.map +1 -0
  586. package/dist/server/public-types.js +2 -0
  587. package/dist/server/public-types.js.map +1 -0
  588. package/dist/server/realtime.d.ts +11 -0
  589. package/dist/server/realtime.d.ts.map +1 -0
  590. package/dist/server/realtime.js +20 -0
  591. package/dist/server/realtime.js.map +1 -0
  592. package/dist/server/registry.d.ts +10 -0
  593. package/dist/server/registry.d.ts.map +1 -0
  594. package/dist/server/registry.js +21 -0
  595. package/dist/server/registry.js.map +1 -0
  596. package/dist/server/routes/api.d.ts +4 -0
  597. package/dist/server/routes/api.d.ts.map +1 -0
  598. package/dist/server/routes/api.js +1025 -0
  599. package/dist/server/routes/api.js.map +1 -0
  600. package/dist/server/routes/index.d.ts +5 -0
  601. package/dist/server/routes/index.d.ts.map +1 -0
  602. package/dist/server/routes/index.js +10 -0
  603. package/dist/server/routes/index.js.map +1 -0
  604. package/dist/server/routes/oauth-callback.d.ts +5 -0
  605. package/dist/server/routes/oauth-callback.d.ts.map +1 -0
  606. package/dist/server/routes/oauth-callback.js +41 -0
  607. package/dist/server/routes/oauth-callback.js.map +1 -0
  608. package/dist/server/routes/push-webhook.d.ts +5 -0
  609. package/dist/server/routes/push-webhook.d.ts.map +1 -0
  610. package/dist/server/routes/push-webhook.js +95 -0
  611. package/dist/server/routes/push-webhook.js.map +1 -0
  612. package/dist/server/routes/schemas.d.ts +345 -0
  613. package/dist/server/routes/schemas.d.ts.map +1 -0
  614. package/dist/server/routes/schemas.js +649 -0
  615. package/dist/server/routes/schemas.js.map +1 -0
  616. package/dist/server/runtime/account-removals.d.ts +8 -0
  617. package/dist/server/runtime/account-removals.d.ts.map +1 -0
  618. package/dist/server/runtime/account-removals.js +84 -0
  619. package/dist/server/runtime/account-removals.js.map +1 -0
  620. package/dist/server/runtime/background-tasks.d.ts +8 -0
  621. package/dist/server/runtime/background-tasks.d.ts.map +1 -0
  622. package/dist/server/runtime/background-tasks.js +18 -0
  623. package/dist/server/runtime/background-tasks.js.map +1 -0
  624. package/dist/server/runtime/outbox-relay.d.ts +19 -0
  625. package/dist/server/runtime/outbox-relay.d.ts.map +1 -0
  626. package/dist/server/runtime/outbox-relay.js +41 -0
  627. package/dist/server/runtime/outbox-relay.js.map +1 -0
  628. package/dist/server/runtime/push-subscriptions.d.ts +15 -0
  629. package/dist/server/runtime/push-subscriptions.d.ts.map +1 -0
  630. package/dist/server/runtime/push-subscriptions.js +144 -0
  631. package/dist/server/runtime/push-subscriptions.js.map +1 -0
  632. package/dist/server/runtime.d.ts +67 -0
  633. package/dist/server/runtime.d.ts.map +1 -0
  634. package/dist/server/runtime.js +296 -0
  635. package/dist/server/runtime.js.map +1 -0
  636. package/dist/server/service.d.ts +75 -0
  637. package/dist/server/service.d.ts.map +1 -0
  638. package/dist/server/service.js +213 -0
  639. package/dist/server/service.js.map +1 -0
  640. package/dist/server/services/access.d.ts +10 -0
  641. package/dist/server/services/access.d.ts.map +1 -0
  642. package/dist/server/services/access.js +33 -0
  643. package/dist/server/services/access.js.map +1 -0
  644. package/dist/server/services/accounts.d.ts +10 -0
  645. package/dist/server/services/accounts.d.ts.map +1 -0
  646. package/dist/server/services/accounts.js +58 -0
  647. package/dist/server/services/accounts.js.map +1 -0
  648. package/dist/server/services/attachments.d.ts +12 -0
  649. package/dist/server/services/attachments.d.ts.map +1 -0
  650. package/dist/server/services/attachments.js +92 -0
  651. package/dist/server/services/attachments.js.map +1 -0
  652. package/dist/server/services/authorization.d.ts +15 -0
  653. package/dist/server/services/authorization.d.ts.map +1 -0
  654. package/dist/server/services/authorization.js +221 -0
  655. package/dist/server/services/authorization.js.map +1 -0
  656. package/dist/server/services/dependencies.d.ts +30 -0
  657. package/dist/server/services/dependencies.d.ts.map +1 -0
  658. package/dist/server/services/dependencies.js +2 -0
  659. package/dist/server/services/dependencies.js.map +1 -0
  660. package/dist/server/services/draft-content.d.ts +10 -0
  661. package/dist/server/services/draft-content.d.ts.map +1 -0
  662. package/dist/server/services/draft-content.js +81 -0
  663. package/dist/server/services/draft-content.js.map +1 -0
  664. package/dist/server/services/drafts.d.ts +12 -0
  665. package/dist/server/services/drafts.d.ts.map +1 -0
  666. package/dist/server/services/drafts.js +173 -0
  667. package/dist/server/services/drafts.js.map +1 -0
  668. package/dist/server/services/errors.d.ts +76 -0
  669. package/dist/server/services/errors.d.ts.map +1 -0
  670. package/dist/server/services/errors.js +131 -0
  671. package/dist/server/services/errors.js.map +1 -0
  672. package/dist/server/services/input.d.ts +7 -0
  673. package/dist/server/services/input.d.ts.map +1 -0
  674. package/dist/server/services/input.js +37 -0
  675. package/dist/server/services/input.js.map +1 -0
  676. package/dist/server/services/management.d.ts +17 -0
  677. package/dist/server/services/management.d.ts.map +1 -0
  678. package/dist/server/services/management.js +133 -0
  679. package/dist/server/services/management.js.map +1 -0
  680. package/dist/server/services/messages.d.ts +18 -0
  681. package/dist/server/services/messages.d.ts.map +1 -0
  682. package/dist/server/services/messages.js +77 -0
  683. package/dist/server/services/messages.js.map +1 -0
  684. package/dist/server/services/preferences.d.ts +21 -0
  685. package/dist/server/services/preferences.d.ts.map +1 -0
  686. package/dist/server/services/preferences.js +186 -0
  687. package/dist/server/services/preferences.js.map +1 -0
  688. package/dist/server/services/provider-lifecycle.d.ts +5 -0
  689. package/dist/server/services/provider-lifecycle.d.ts.map +1 -0
  690. package/dist/server/services/provider-lifecycle.js +46 -0
  691. package/dist/server/services/provider-lifecycle.js.map +1 -0
  692. package/dist/server/services/providers.d.ts +8 -0
  693. package/dist/server/services/providers.d.ts.map +1 -0
  694. package/dist/server/services/providers.js +58 -0
  695. package/dist/server/services/providers.js.map +1 -0
  696. package/dist/server/services/submissions.d.ts +14 -0
  697. package/dist/server/services/submissions.d.ts.map +1 -0
  698. package/dist/server/services/submissions.js +93 -0
  699. package/dist/server/services/submissions.js.map +1 -0
  700. package/dist/server/services/sync.d.ts +15 -0
  701. package/dist/server/services/sync.d.ts.map +1 -0
  702. package/dist/server/services/sync.js +74 -0
  703. package/dist/server/services/sync.js.map +1 -0
  704. package/dist/server/store/account-guard.d.ts +4 -0
  705. package/dist/server/store/account-guard.d.ts.map +1 -0
  706. package/dist/server/store/account-guard.js +13 -0
  707. package/dist/server/store/account-guard.js.map +1 -0
  708. package/dist/server/store/account-removals.d.ts +13 -0
  709. package/dist/server/store/account-removals.d.ts.map +1 -0
  710. package/dist/server/store/account-removals.js +170 -0
  711. package/dist/server/store/account-removals.js.map +1 -0
  712. package/dist/server/store/account-writes.d.ts +6 -0
  713. package/dist/server/store/account-writes.d.ts.map +1 -0
  714. package/dist/server/store/account-writes.js +137 -0
  715. package/dist/server/store/account-writes.js.map +1 -0
  716. package/dist/server/store/accounts.d.ts +16 -0
  717. package/dist/server/store/accounts.d.ts.map +1 -0
  718. package/dist/server/store/accounts.js +142 -0
  719. package/dist/server/store/accounts.js.map +1 -0
  720. package/dist/server/store/attachments.d.ts +13 -0
  721. package/dist/server/store/attachments.d.ts.map +1 -0
  722. package/dist/server/store/attachments.js +157 -0
  723. package/dist/server/store/attachments.js.map +1 -0
  724. package/dist/server/store/authorization.d.ts +10 -0
  725. package/dist/server/store/authorization.d.ts.map +1 -0
  726. package/dist/server/store/authorization.js +71 -0
  727. package/dist/server/store/authorization.js.map +1 -0
  728. package/dist/server/store/draft-states.d.ts +5 -0
  729. package/dist/server/store/draft-states.d.ts.map +1 -0
  730. package/dist/server/store/draft-states.js +96 -0
  731. package/dist/server/store/draft-states.js.map +1 -0
  732. package/dist/server/store/mappers.d.ts +22 -0
  733. package/dist/server/store/mappers.d.ts.map +1 -0
  734. package/dist/server/store/mappers.js +378 -0
  735. package/dist/server/store/mappers.js.map +1 -0
  736. package/dist/server/store/message-cursor.d.ts +8 -0
  737. package/dist/server/store/message-cursor.d.ts.map +1 -0
  738. package/dist/server/store/message-cursor.js +26 -0
  739. package/dist/server/store/message-cursor.js.map +1 -0
  740. package/dist/server/store/message-queries.d.ts +8 -0
  741. package/dist/server/store/message-queries.d.ts.map +1 -0
  742. package/dist/server/store/message-queries.js +116 -0
  743. package/dist/server/store/message-queries.js.map +1 -0
  744. package/dist/server/store/message-writes.d.ts +8 -0
  745. package/dist/server/store/message-writes.d.ts.map +1 -0
  746. package/dist/server/store/message-writes.js +180 -0
  747. package/dist/server/store/message-writes.js.map +1 -0
  748. package/dist/server/store/messages.d.ts +32 -0
  749. package/dist/server/store/messages.d.ts.map +1 -0
  750. package/dist/server/store/messages.js +564 -0
  751. package/dist/server/store/messages.js.map +1 -0
  752. package/dist/server/store/metadata.d.ts +22 -0
  753. package/dist/server/store/metadata.d.ts.map +1 -0
  754. package/dist/server/store/metadata.js +178 -0
  755. package/dist/server/store/metadata.js.map +1 -0
  756. package/dist/server/store/outbox.d.ts +11 -0
  757. package/dist/server/store/outbox.d.ts.map +1 -0
  758. package/dist/server/store/outbox.js +98 -0
  759. package/dist/server/store/outbox.js.map +1 -0
  760. package/dist/server/store/push.d.ts +22 -0
  761. package/dist/server/store/push.d.ts.map +1 -0
  762. package/dist/server/store/push.js +302 -0
  763. package/dist/server/store/push.js.map +1 -0
  764. package/dist/server/store/rows.d.ts +224 -0
  765. package/dist/server/store/rows.d.ts.map +1 -0
  766. package/dist/server/store/rows.js +2 -0
  767. package/dist/server/store/rows.js.map +1 -0
  768. package/dist/server/store/scheduled-drafts.d.ts +6 -0
  769. package/dist/server/store/scheduled-drafts.d.ts.map +1 -0
  770. package/dist/server/store/scheduled-drafts.js +38 -0
  771. package/dist/server/store/scheduled-drafts.js.map +1 -0
  772. package/dist/server/store/serialization.d.ts +6 -0
  773. package/dist/server/store/serialization.d.ts.map +1 -0
  774. package/dist/server/store/serialization.js +27 -0
  775. package/dist/server/store/serialization.js.map +1 -0
  776. package/dist/server/store/submissions.d.ts +23 -0
  777. package/dist/server/store/submissions.d.ts.map +1 -0
  778. package/dist/server/store/submissions.js +498 -0
  779. package/dist/server/store/submissions.js.map +1 -0
  780. package/dist/server/store/sync-writes.d.ts +5 -0
  781. package/dist/server/store/sync-writes.d.ts.map +1 -0
  782. package/dist/server/store/sync-writes.js +48 -0
  783. package/dist/server/store/sync-writes.js.map +1 -0
  784. package/dist/server/store/sync.d.ts +27 -0
  785. package/dist/server/store/sync.d.ts.map +1 -0
  786. package/dist/server/store/sync.js +508 -0
  787. package/dist/server/store/sync.js.map +1 -0
  788. package/dist/server/store/templates.d.ts +10 -0
  789. package/dist/server/store/templates.d.ts.map +1 -0
  790. package/dist/server/store/templates.js +70 -0
  791. package/dist/server/store/templates.js.map +1 -0
  792. package/dist/server/store.d.ts +128 -0
  793. package/dist/server/store.d.ts.map +1 -0
  794. package/dist/server/store.js +343 -0
  795. package/dist/server/store.js.map +1 -0
  796. package/dist/server/tokens.d.ts +13 -0
  797. package/dist/server/tokens.d.ts.map +1 -0
  798. package/dist/server/tokens.js +9 -0
  799. package/dist/server/tokens.js.map +1 -0
  800. package/dist/server/types.d.ts +6 -0
  801. package/dist/server/types.d.ts.map +1 -0
  802. package/dist/server/types.js +6 -0
  803. package/dist/server/types.js.map +1 -0
  804. package/dist/server/views.d.ts +6 -0
  805. package/dist/server/views.d.ts.map +1 -0
  806. package/dist/server/views.js +65 -0
  807. package/dist/server/views.js.map +1 -0
  808. package/dist/shared/inline-images.d.ts +12 -0
  809. package/dist/shared/inline-images.d.ts.map +1 -0
  810. package/dist/shared/inline-images.js +15 -0
  811. package/dist/shared/inline-images.js.map +1 -0
  812. package/dist/shared/mail-sync-date.d.ts +2 -0
  813. package/dist/shared/mail-sync-date.d.ts.map +1 -0
  814. package/dist/shared/mail-sync-date.js +10 -0
  815. package/dist/shared/mail-sync-date.js.map +1 -0
  816. package/dist/shared/mail.d.ts +667 -0
  817. package/dist/shared/mail.d.ts.map +1 -0
  818. package/dist/shared/mail.js +50 -0
  819. package/dist/shared/mail.js.map +1 -0
  820. package/dist/shared/realtime.d.ts +5 -0
  821. package/dist/shared/realtime.d.ts.map +1 -0
  822. package/dist/shared/realtime.js +2 -0
  823. package/dist/shared/realtime.js.map +1 -0
  824. package/package.json +123 -3
  825. package/skills/nocobase-app-plugin-mail/SKILL.md +118 -0
  826. package/skills/nocobase-app-plugin-mail/references/client-integration.md +94 -0
  827. package/skills/nocobase-app-plugin-mail/references/configuration-and-accounts.md +167 -0
  828. package/skills/nocobase-app-plugin-mail/references/http-api.md +131 -0
  829. package/skills/nocobase-app-plugin-mail/references/public-api.md +117 -0
  830. package/skills/nocobase-app-plugin-mail/references/sending-and-drafts.md +53 -0
  831. package/skills/nocobase-app-plugin-mail/references/synchronization-and-diagnostics.md +57 -0
@@ -0,0 +1,1025 @@
1
+ import { authenticationToken, } from '@nocobase/app-plugin-authentication';
2
+ import { authorizationToken, } from '@nocobase/app-plugin-authorization';
3
+ import { ApiError, apiErrorHandler, apiErrorResponse, apiErrorResponses, apiValidator, dataResponse, defineApiRoutes, describeRoute, emptyResponse, listResponse, } from '@nocobase/app-server/router';
4
+ import { getRequestLocale, getRequestTranslator } from '@nocobase/i18n/server';
5
+ import { Hono } from 'hono';
6
+ import { bodyLimit } from 'hono/body-limit';
7
+ import { z } from 'zod';
8
+ import { resolveMailOAuthCallbackUrl, resolveMailOAuthOrigin, } from '../config.js';
9
+ import { MailIdempotencyConflictError } from '../operations/send-mail.js';
10
+ import { classifyMailProviderError, isMailProviderError, MailError, toPublicError, } from '../services/errors.js';
11
+ import { mailServiceToken } from '../tokens.js';
12
+ import { AccountConversationParams, AccountMessageAttachmentParams, AccountMessageParams, AccountParams, AttachmentParams, ConnectAccountInput, ConversationMessagesQuery, DeleteMessageQuery, IdentityParams, LabelParams, ManageMessagesInput, ManagementMessagesQuery, MessagesQuery, ModifyMessageLabelsInput, MoveMessageInput, ResolveDraftConflictInput, SaveDraftInput, ManagedLogsQuery, SaveLabelInput, SaveSignatureInput, SaveTemplateInput, SendBulkInput, SendMessageInput, SignatureParams, StartAuthorizationInput, StartSyncInput, SubmissionParams, SubmissionsQuery, SyncRunParams, SyncRunsQuery, TemplateParams, UpdateAccountInput, UpdateIdentityInput, UpdateLabelInput, UpdateMessageInput, UpdateSignatureInput, UpdateTemplateInput, MailAccountSchema, MailAuthorizationSchema, MailBoundedListMeta, MailCursorListMeta, MailFolderSchema, MailIdentitySchema, MailLabelSchema, MailManagedAccountSchema, MailManagementActionResultSchema, MailMessageSchema, MailMessageSummarySchema, MailOutboundAttachmentSchema, MailPageListMeta, MailProviderSchema, MailSignatureSchema, MailSubmissionLogSchema, MailSubmissionSchema, MailSyncRunSchema, MailTemplateSchema, MailUnreadCountSchema, } from './schemas.js';
13
+ /** The namespace of every Mail route and the domain of every error Mail defines. */
14
+ const MAIL_DOMAIN = 'mail';
15
+ const MAIL_NAMESPACE = '@nocobase/app-plugin-mail';
16
+ const MAX_ATTACHMENT_UPLOAD_BYTES = 27 * 1024 * 1024;
17
+ const MAX_JSON_REQUEST_BYTES = 8 * 1024 * 1024;
18
+ const MAIL_WORKSPACE_RESOURCE = 'mail.workspace';
19
+ const MAIL_ADMIN_RESOURCE = 'mail.admin';
20
+ const MAIL_MANAGEMENT_RESOURCE = 'mail.management';
21
+ /** Answered by a route that waits for the mail Provider when the Provider throttles the request. */
22
+ const providerRateLimited = 'The mail Provider is rate limiting requests or a Provider quota ran out (`MAIL_PROVIDER_REQUEST_FAILED`). When the Provider says how long to wait, `Retry-After` and `metadata.retryAfter` give the delay in seconds.';
23
+ /** Answered by a route that waits for the mail Provider when the Provider cannot be reached or fails on its side. */
24
+ const providerUnavailable = 'The mail Provider could not be reached, timed out or failed on its side (`MAIL_PROVIDER_REQUEST_FAILED`); retrying later may succeed.';
25
+ /** The API document entry of one Mail route. */
26
+ function mailRoute(operationId, summary, responses, options = {}) {
27
+ const errors = {};
28
+ for (const [code, description] of Object.entries(options.errors ?? {})) {
29
+ const status = Number(code);
30
+ errors[code] =
31
+ description === true
32
+ ? apiErrorResponse(status)
33
+ : apiErrorResponse(status, description);
34
+ }
35
+ return describeRoute({
36
+ tags: ['Mail'],
37
+ operationId,
38
+ summary,
39
+ ...(options.description ? { description: options.description } : {}),
40
+ ...(options.requestBody ? { requestBody: options.requestBody } : {}),
41
+ responses: { ...responses, ...apiErrorResponses, ...errors },
42
+ });
43
+ }
44
+ /** A file answered as it is stored, with the attachment's own media type. */
45
+ const attachmentDownloadResponse = {
46
+ description: 'The file, with its own `Content-Type` and a `Content-Disposition: attachment` header naming it.',
47
+ content: { '*/*': { schema: { type: 'string', format: 'binary' } } },
48
+ };
49
+ const attachmentUploadBody = {
50
+ required: true,
51
+ content: {
52
+ 'multipart/form-data': {
53
+ schema: {
54
+ type: 'object',
55
+ required: ['file'],
56
+ properties: {
57
+ file: {
58
+ type: 'string',
59
+ format: 'binary',
60
+ description: 'The file to attach, at most 27 MiB.',
61
+ },
62
+ },
63
+ },
64
+ },
65
+ },
66
+ };
67
+ export const mailApiRoutes = defineApiRoutes(({ container, config, publicBasePath }) => {
68
+ const router = new Hono();
69
+ const routes = new Hono();
70
+ const authentication = container.resolve(authenticationToken);
71
+ const authorization = container.resolve(authorizationToken);
72
+ const mail = container.resolve(mailServiceToken);
73
+ // Authentication and page access come before anything reads the request, so a caller without access learns
74
+ // nothing about which accounts, messages, or runs exist.
75
+ routes.use('*', authentication.required(), authorization.middleware(), async (context, next) => {
76
+ const resource = /(?:^|\/)mail\/settings(?:\/|$)/u.test(context.req.path)
77
+ ? MAIL_ADMIN_RESOURCE
78
+ : /(?:^|\/)mail\/management(?:\/|$)/u.test(context.req.path)
79
+ ? MAIL_MANAGEMENT_RESOURCE
80
+ : MAIL_WORKSPACE_RESOURCE;
81
+ const allowed = await context.get('authz').can({
82
+ resource: { type: 'page', id: resource },
83
+ action: 'access',
84
+ });
85
+ if (!allowed) {
86
+ return apiErrorHandler(new ApiError({
87
+ status: 'PERMISSION_DENIED',
88
+ reason: 'MAIL_ACCESS_DENIED',
89
+ domain: MAIL_DOMAIN,
90
+ message: `Access to the ${resource} page is required.`,
91
+ ...localized(context, 'errors.accessDenied'),
92
+ }), context);
93
+ }
94
+ await next();
95
+ });
96
+ routes.onError((error, context) => {
97
+ const apiError = toMailApiError(error, context);
98
+ return withRetryAfter(apiErrorHandler(apiError, context), apiError);
99
+ });
100
+ const jsonBodyLimit = bodyLimit({
101
+ maxSize: MAX_JSON_REQUEST_BYTES,
102
+ onError: (context) => apiErrorHandler(new ApiError({
103
+ status: 'INVALID_ARGUMENT',
104
+ reason: 'BODY_TOO_LARGE',
105
+ domain: MAIL_DOMAIN,
106
+ message: `The request body exceeds ${MAX_JSON_REQUEST_BYTES} bytes.`,
107
+ httpStatus: 413,
108
+ ...localized(context, 'errors.invalidRequest'),
109
+ }), context),
110
+ });
111
+ routes.use('*', async (context, next) => {
112
+ if ((context.req.method === 'POST' || context.req.method === 'PATCH') &&
113
+ !/\/mail\/attachments$/u.test(context.req.path)) {
114
+ return jsonBodyLimit(context, next);
115
+ }
116
+ return next();
117
+ });
118
+ // -----------------------------------------------------------------------------------------------------------------
119
+ // Accounts, Providers, and authorization
120
+ routes.get('/providers', mailRoute('mailListProviders', 'List the configured mail Providers', {
121
+ '200': listResponse(MailProviderSchema, MailBoundedListMeta),
122
+ }), async (context) => context.json(boundedList(await mail.listProviders())));
123
+ routes.get('/accounts', mailRoute('mailListAccounts', "List the caller's mail accounts", {
124
+ '200': listResponse(MailAccountSchema, MailBoundedListMeta),
125
+ }), async (context) => context.json(boundedList(await mail.listAccounts(operationContext(context)))));
126
+ // Connecting verifies the credentials against the Provider before it stores anything, so it is a custom method on
127
+ // the collection rather than a plain create, but it does create the account and answers 201 with it.
128
+ routes.post('/accounts/connect', mailRoute('mailConnectAccount', 'Connect an account with credentials', { '201': dataResponse(MailAccountSchema) }, {
129
+ description: 'Verifies the address, user name and password against a credentials Provider (such as IMAP/SMTP) before storing anything, then creates the account and starts its first synchronization. OAuth Providers connect through `mailCreateAuthorization` instead.',
130
+ errors: {
131
+ 400: 'The named mail Provider is not configured or is disabled (`MAIL_PROVIDER_UNAVAILABLE`), the Provider refused the sign-in with these credentials (`MAIL_ACCOUNT_CREDENTIALS_INVALID`, with a field violation on `password`), or the Provider rejected the request otherwise (`MAIL_PROVIDER_REQUEST_FAILED`).',
132
+ 413: true,
133
+ 429: providerRateLimited,
134
+ 503: providerUnavailable,
135
+ },
136
+ }), apiValidator('json', ConnectAccountInput), async (context) => {
137
+ const input = context.req.valid('json');
138
+ return context.json({
139
+ data: await mail.connectAccount(operationContext(context), {
140
+ provider: { type: input.type, name: input.name },
141
+ address: input.address,
142
+ ...(input.displayName !== undefined
143
+ ? { displayName: input.displayName }
144
+ : {}),
145
+ username: input.username ?? input.address,
146
+ password: input.password,
147
+ initialSyncReceivedAfter: input.initialSyncReceivedAfter,
148
+ }),
149
+ }, 201);
150
+ });
151
+ routes.patch('/accounts/:accountId', mailRoute('mailUpdateAccount', 'Suspend or resume an account', { '200': dataResponse(MailAccountSchema) }, {
152
+ errors: {
153
+ 400: 'The account is being removed (`MAIL_ACCOUNT_REMOVING`), or must be reauthorized before it can be resumed (`MAIL_ACCOUNT_REAUTHORIZATION_REQUIRED`).',
154
+ 404: true,
155
+ 413: true,
156
+ },
157
+ }), apiValidator('param', AccountParams), apiValidator('json', UpdateAccountInput), async (context) => context.json({
158
+ data: await mail.updateAccount(operationContext(context), {
159
+ accountId: context.req.valid('param').accountId,
160
+ status: context.req.valid('json').status,
161
+ }),
162
+ }));
163
+ // Removal is durable and asynchronous: the account is marked for removal and a background task deletes its
164
+ // messages and Provider subscriptions, so the request is acknowledged with 202 and the account in its `removing`
165
+ // state. When the background task has already deleted the row there is nothing left to show, and the 202 has no
166
+ // body.
167
+ routes.delete('/accounts/:accountId', mailRoute('mailDeleteAccount', 'Remove an account', {
168
+ '202': dataResponse(MailAccountSchema, 'Removal started. The body holds the account in its `removing` state, or is empty when the background removal has already deleted it.'),
169
+ }, {
170
+ description: 'Removal is asynchronous: the account is marked `removing` and a background task deletes its messages and Provider subscriptions.',
171
+ errors: { 404: true },
172
+ }), apiValidator('param', AccountParams), async (context) => {
173
+ const account = await mail.removeAccount(operationContext(context), context.req.valid('param').accountId);
174
+ return account
175
+ ? context.json({ data: account }, 202)
176
+ : context.body(null, 202);
177
+ });
178
+ routes.post('/authorizations', mailRoute('mailCreateAuthorization', 'Start connecting an OAuth account', { '201': dataResponse(MailAuthorizationSchema) }, {
179
+ description: 'Answers the Provider authorization URL to open in a browser. The Provider redirects back to the Mail OAuth callback, which creates the account; that callback is a browser flow and is not part of this API.',
180
+ errors: {
181
+ 400: 'The named mail Provider is not configured or is disabled (`MAIL_PROVIDER_UNAVAILABLE`), or its configuration does not allow the requested `scopes` (`MAIL_PROVIDER_REQUEST_FAILED`).',
182
+ 413: true,
183
+ },
184
+ }), apiValidator('json', StartAuthorizationInput), async (context) => {
185
+ const input = context.req.valid('json');
186
+ const identity = config.get('app');
187
+ const origin = resolveMailOAuthOrigin(identity.publicOrigin, new URL(context.req.url).origin);
188
+ const configuredMail = config.get('mail');
189
+ return context.json({
190
+ data: await mail.startAuthorization(operationContext(context), {
191
+ provider: { type: input.type, name: input.name },
192
+ redirectUri: resolveMailOAuthCallbackUrl(configuredMail.oauthCallbackUrl, origin, publicBasePath).toString(),
193
+ ...(input.scopes !== undefined ? { scopes: input.scopes } : {}),
194
+ initialSyncReceivedAfter: input.initialSyncReceivedAfter,
195
+ }),
196
+ }, 201);
197
+ });
198
+ routes.get('/accounts/:accountId/identities', mailRoute('mailListIdentities', 'List the addresses an account can send from', { '200': listResponse(MailIdentitySchema, MailBoundedListMeta) }, {
199
+ errors: {
200
+ 400: 'The account is being removed (`MAIL_ACCOUNT_REMOVING`).',
201
+ 404: true,
202
+ },
203
+ }), apiValidator('param', AccountParams), async (context) => context.json(boundedList(await mail.listIdentities(operationContext(context), context.req.valid('param').accountId))));
204
+ routes.patch('/accounts/:accountId/identities/:identityId', mailRoute('mailUpdateIdentity', 'Change the display name of a sending address', { '200': dataResponse(MailIdentitySchema) }, {
205
+ errors: {
206
+ 400: 'The account is being removed (`MAIL_ACCOUNT_REMOVING`).',
207
+ 404: true,
208
+ 413: true,
209
+ },
210
+ }), apiValidator('param', IdentityParams), apiValidator('json', UpdateIdentityInput), async (context) => {
211
+ const { accountId, identityId } = context.req.valid('param');
212
+ return context.json({
213
+ data: await mail.updateIdentity(operationContext(context), {
214
+ accountId,
215
+ identityId,
216
+ displayName: context.req.valid('json').displayName,
217
+ }),
218
+ });
219
+ });
220
+ routes.get('/accounts/:accountId/signatures', mailRoute('mailListSignatures', "List an account's signatures", { '200': listResponse(MailSignatureSchema, MailBoundedListMeta) }, {
221
+ errors: {
222
+ 400: 'The account is being removed (`MAIL_ACCOUNT_REMOVING`).',
223
+ 404: true,
224
+ },
225
+ }), apiValidator('param', AccountParams), async (context) => context.json(boundedList(await mail.listSignatures(operationContext(context), context.req.valid('param').accountId))));
226
+ routes.post('/accounts/:accountId/signatures', mailRoute('mailCreateSignature', 'Create a signature', { '201': dataResponse(MailSignatureSchema) }, {
227
+ errors: {
228
+ 400: 'The account is being removed (`MAIL_ACCOUNT_REMOVING`).',
229
+ 404: true,
230
+ 413: true,
231
+ },
232
+ }), apiValidator('param', AccountParams), apiValidator('json', SaveSignatureInput), async (context) => {
233
+ const input = context.req.valid('json');
234
+ return context.json({
235
+ data: await mail.saveSignature(operationContext(context), {
236
+ accountId: context.req.valid('param').accountId,
237
+ name: input.name,
238
+ text: input.text ?? '',
239
+ html: input.html,
240
+ isDefault: input.isDefault,
241
+ }),
242
+ }, 201);
243
+ });
244
+ routes.patch('/accounts/:accountId/signatures/:signatureId', mailRoute('mailUpdateSignature', 'Update a signature', { '200': dataResponse(MailSignatureSchema) }, {
245
+ description: 'Omitted fields keep their stored values.',
246
+ errors: {
247
+ 400: 'The account is being removed (`MAIL_ACCOUNT_REMOVING`).',
248
+ 404: true,
249
+ 413: true,
250
+ },
251
+ }), apiValidator('param', SignatureParams), apiValidator('json', UpdateSignatureInput), async (context) => {
252
+ const { accountId, signatureId } = context.req.valid('param');
253
+ return context.json({
254
+ data: await mail.updateSignature(operationContext(context), {
255
+ ...context.req.valid('json'),
256
+ id: signatureId,
257
+ accountId,
258
+ }),
259
+ });
260
+ });
261
+ routes.delete('/accounts/:accountId/signatures/:signatureId', mailRoute('mailDeleteSignature', 'Delete a signature', { '204': emptyResponse() }, {
262
+ errors: {
263
+ 400: 'The account is being removed (`MAIL_ACCOUNT_REMOVING`).',
264
+ 404: true,
265
+ },
266
+ }), apiValidator('param', SignatureParams), async (context) => {
267
+ const { accountId, signatureId } = context.req.valid('param');
268
+ await mail.deleteSignature(operationContext(context), accountId, signatureId);
269
+ return context.body(null, 204);
270
+ });
271
+ routes.get('/accounts/:accountId/folders', mailRoute('mailListFolders', "List an account's folders", { '200': listResponse(MailFolderSchema, MailBoundedListMeta) }, {
272
+ errors: {
273
+ 400: 'The account is being removed (`MAIL_ACCOUNT_REMOVING`).',
274
+ 404: true,
275
+ },
276
+ }), apiValidator('param', AccountParams), async (context) => context.json(boundedList(await mail.listFolders(operationContext(context), context.req.valid('param').accountId))));
277
+ routes.post('/accounts/:accountId/sync', mailRoute('mailSyncAccount', 'Start synchronizing an account', {
278
+ '202': dataResponse(MailSyncRunSchema, 'Synchronization was queued; follow it with `mailGetSyncRun`.'),
279
+ }, {
280
+ errors: {
281
+ 400: 'The account is not active (`MAIL_ACCOUNT_INACTIVE`), an incremental synchronization was requested before the initial one completed (`MAIL_INITIAL_SYNC_REQUIRED`), or an initial synchronization has no start date (`INVALID_MAIL_REQUEST`).',
282
+ 404: true,
283
+ 413: true,
284
+ },
285
+ }), apiValidator('param', AccountParams), apiValidator('json', StartSyncInput), async (context) => {
286
+ const input = context.req.valid('json');
287
+ return context.json({
288
+ data: await mail.startSync(operationContext(context), {
289
+ accountId: context.req.valid('param').accountId,
290
+ ...(input.mode !== undefined ? { mode: input.mode } : {}),
291
+ ...(input.receivedAfter !== undefined
292
+ ? { receivedAfter: input.receivedAfter }
293
+ : {}),
294
+ }),
295
+ }, 202);
296
+ });
297
+ // -----------------------------------------------------------------------------------------------------------------
298
+ // Templates and labels
299
+ routes.get('/templates', mailRoute('mailListTemplates', "List the caller's templates", {
300
+ '200': listResponse(MailTemplateSchema, MailBoundedListMeta),
301
+ }), async (context) => context.json(boundedList(await mail.listTemplates(operationContext(context)))));
302
+ routes.post('/templates', mailRoute('mailCreateTemplate', 'Create a template', { '201': dataResponse(MailTemplateSchema) }, { errors: { 413: true } }), apiValidator('json', SaveTemplateInput), async (context) => {
303
+ const input = context.req.valid('json');
304
+ return context.json({
305
+ data: await mail.saveTemplate(operationContext(context), {
306
+ name: input.name,
307
+ subject: input.subject ?? '',
308
+ text: input.text,
309
+ html: input.html,
310
+ }),
311
+ }, 201);
312
+ });
313
+ routes.patch('/templates/:templateId', mailRoute('mailUpdateTemplate', 'Update a template', { '200': dataResponse(MailTemplateSchema) }, {
314
+ description: 'Omitted fields keep their stored values.',
315
+ errors: { 404: true, 413: true },
316
+ }), apiValidator('param', TemplateParams), apiValidator('json', UpdateTemplateInput), async (context) => context.json({
317
+ data: await mail.updateTemplate(operationContext(context), {
318
+ ...context.req.valid('json'),
319
+ id: context.req.valid('param').templateId,
320
+ }),
321
+ }));
322
+ routes.delete('/templates/:templateId', mailRoute('mailDeleteTemplate', 'Delete a template', { '204': emptyResponse() }, { errors: { 404: true } }), apiValidator('param', TemplateParams), async (context) => {
323
+ await mail.deleteTemplate(operationContext(context), context.req.valid('param').templateId);
324
+ return context.body(null, 204);
325
+ });
326
+ routes.get('/labels', mailRoute('mailListLabels', "List the caller's labels", {
327
+ '200': listResponse(MailLabelSchema, MailBoundedListMeta),
328
+ }), async (context) => context.json(boundedList(await mail.listLabels(operationContext(context)))));
329
+ routes.post('/labels', mailRoute('mailCreateLabel', 'Create a label', { '201': dataResponse(MailLabelSchema) }, {
330
+ errors: { 409: 'A label with this name already exists.', 413: true },
331
+ }), apiValidator('json', SaveLabelInput), async (context) => {
332
+ const input = context.req.valid('json');
333
+ return context.json({
334
+ data: await mail.createLabel(operationContext(context), {
335
+ name: input.name,
336
+ color: input.color,
337
+ }),
338
+ }, 201);
339
+ });
340
+ routes.patch('/labels/:labelId', mailRoute('mailUpdateLabel', 'Update a label', { '200': dataResponse(MailLabelSchema) }, {
341
+ description: 'Omitted fields keep their stored values.',
342
+ errors: {
343
+ 404: true,
344
+ 409: 'Another label already has this name.',
345
+ 413: true,
346
+ },
347
+ }), apiValidator('param', LabelParams), apiValidator('json', UpdateLabelInput), async (context) => context.json({
348
+ data: await mail.updateLabel(operationContext(context), {
349
+ ...context.req.valid('json'),
350
+ id: context.req.valid('param').labelId,
351
+ }),
352
+ }));
353
+ routes.delete('/labels/:labelId', mailRoute('mailDeleteLabel', 'Delete a label', { '204': emptyResponse() }, { errors: { 404: true } }), apiValidator('param', LabelParams), async (context) => {
354
+ await mail.deleteLabel(operationContext(context), context.req.valid('param').labelId);
355
+ return context.body(null, 204);
356
+ });
357
+ // -----------------------------------------------------------------------------------------------------------------
358
+ // Outbound attachments
359
+ routes.post('/attachments', mailRoute('mailUploadAttachment', 'Upload an attachment for a message being composed', {
360
+ '201': dataResponse(MailOutboundAttachmentSchema, 'The file was stored.'),
361
+ }, {
362
+ description: 'Uploads one file as `multipart/form-data` with a `file` part. Pass the answered `id` in `attachmentIds` when sending or saving a draft. An upload that is never used expires.',
363
+ errors: {
364
+ 400: 'The body has no readable `file` part, or the file is over 25 MiB or has no name (`INVALID_MAIL_REQUEST`), or attachment storage is not configured (`MAIL_ATTACHMENT_STORAGE_NOT_CONFIGURED`).',
365
+ 413: 'The upload exceeds 27 MiB.',
366
+ 415: 'The body is not `multipart/form-data`.',
367
+ },
368
+ requestBody: attachmentUploadBody,
369
+ }), bodyLimit({
370
+ maxSize: MAX_ATTACHMENT_UPLOAD_BYTES,
371
+ onError: (context) => apiErrorHandler(new ApiError({
372
+ status: 'INVALID_ARGUMENT',
373
+ reason: 'BODY_TOO_LARGE',
374
+ domain: MAIL_DOMAIN,
375
+ message: `The attachment upload exceeds ${MAX_ATTACHMENT_UPLOAD_BYTES} bytes.`,
376
+ httpStatus: 413,
377
+ ...localized(context, 'errors.invalidRequest'),
378
+ }), context),
379
+ }), async (context) => {
380
+ // A multipart body has no JSON schema; its type and file field are validated here.
381
+ const mediaType = (context.req.header('content-type') ?? '')
382
+ .split(';', 1)[0]
383
+ ?.trim()
384
+ .toLowerCase();
385
+ if (mediaType !== 'multipart/form-data') {
386
+ throw new ApiError({
387
+ status: 'INVALID_ARGUMENT',
388
+ reason: 'INVALID_MAIL_REQUEST',
389
+ domain: MAIL_DOMAIN,
390
+ message: 'An attachment upload must be multipart/form-data.',
391
+ httpStatus: 415,
392
+ });
393
+ }
394
+ let form;
395
+ try {
396
+ form = await context.req.formData();
397
+ }
398
+ catch (cause) {
399
+ throw invalidField('file', 'A valid multipart body is required.', {
400
+ cause,
401
+ });
402
+ }
403
+ const file = form.get('file');
404
+ if (!(file instanceof File)) {
405
+ throw invalidField('file', 'Mail attachment file is required.');
406
+ }
407
+ return context.json({
408
+ data: await mail.uploadAttachment(operationContext(context), {
409
+ fileName: file.name,
410
+ contentType: file.type || 'application/octet-stream',
411
+ size: file.size,
412
+ stream: file.stream(),
413
+ }),
414
+ }, 201);
415
+ });
416
+ routes.get('/attachments/:attachmentId', mailRoute('mailDownloadAttachment', 'Download an uploaded attachment', { '200': attachmentDownloadResponse }, {
417
+ errors: {
418
+ 400: 'Attachment storage is not configured (`MAIL_ATTACHMENT_STORAGE_NOT_CONFIGURED`).',
419
+ 404: true,
420
+ },
421
+ }), apiValidator('param', AttachmentParams), async (context) => {
422
+ const content = await mail.getUploadedAttachment(operationContext(context), context.req.valid('param').attachmentId);
423
+ return new Response(content.stream, {
424
+ headers: {
425
+ 'content-type': content.contentType,
426
+ 'content-disposition': attachmentDisposition(content.fileName),
427
+ 'x-content-type-options': 'nosniff',
428
+ 'cache-control': 'private, no-store',
429
+ },
430
+ });
431
+ });
432
+ // -----------------------------------------------------------------------------------------------------------------
433
+ // Messages. Fixed segments under /messages are registered before anything that takes an id.
434
+ routes.get('/messages', mailRoute('mailListMessages', "List the caller's messages", { '200': listResponse(MailMessageSummarySchema, MailCursorListMeta) }, {
435
+ description: 'Filters by account, folder (including the virtual folders), label, conversation, search text, unread and starred. Paged by `pageToken`.',
436
+ errors: {
437
+ 400: '`pageToken` is not a token this list answered (`INVALID_MAIL_REQUEST`).',
438
+ },
439
+ }), apiValidator('query', MessagesQuery), async (context) => {
440
+ const query = context.req.valid('query');
441
+ const page = await mail.listMessages(operationContext(context), {
442
+ accountIds: query.accountId ? [query.accountId] : undefined,
443
+ folderIds: query.folderId ? [query.folderId] : undefined,
444
+ labelIds: query.labelId ? [query.labelId] : undefined,
445
+ conversationId: query.conversationId,
446
+ query: query.q,
447
+ cursor: query.pageToken,
448
+ limit: query.pageSize,
449
+ withTotal: true,
450
+ unread: query.unread,
451
+ starred: query.starred,
452
+ });
453
+ return context.json({ data: page.items, meta: cursorMeta(page) });
454
+ });
455
+ routes.get('/messages/countUnread', mailRoute('mailCountUnreadMessages', "Count the caller's unread messages", { '200': dataResponse(MailUnreadCountSchema) }), async (context) => context.json({
456
+ data: await mail.getUnreadCount(operationContext(context)),
457
+ }));
458
+ routes.post('/messages/send', mailRoute('mailSendMessage', 'Send a message', { '200': dataResponse(MailSubmissionSchema) }, {
459
+ description: 'Hands the message to the outbox, or schedules it when `scheduledAt` is given. Repeating a request with the same `idempotencyKey` answers the same submission.',
460
+ errors: {
461
+ 400: 'The account, sending identity, signature, draft, related message or an attachment the body names does not exist (`MAIL_*_NOT_FOUND`), the account is not active (`MAIL_ACCOUNT_INACTIVE`), the draft is scheduled (`MAIL_DRAFT_SCHEDULED`), attachment storage is not configured (`MAIL_ATTACHMENT_STORAGE_NOT_CONFIGURED`), or the message is otherwise invalid, such as attachments over 25 MiB in total or a `scheduledAt` in the past (`INVALID_MAIL_REQUEST`).',
462
+ 409: 'The idempotency key was used for a different request, or the draft revision is outdated.',
463
+ 413: true,
464
+ },
465
+ }), apiValidator('json', SendMessageInput), async (context) => {
466
+ const input = context.req.valid('json');
467
+ return context.json({
468
+ data: await mail.sendMessage(operationContext(context), {
469
+ ...input,
470
+ subject: input.subject ?? '',
471
+ text: input.text ?? '',
472
+ html: input.html,
473
+ }),
474
+ });
475
+ });
476
+ routes.post('/messages/sendBulk', mailRoute('mailSendBulkMessages', 'Send one message to each recipient separately', {
477
+ '200': dataResponse(z.array(MailSubmissionSchema), 'One submission per recipient.'),
478
+ }, {
479
+ description: 'Each recipient receives an individual copy and cannot see the others.',
480
+ errors: {
481
+ 400: 'The account, sending identity, signature, draft, related message or an attachment the body names does not exist (`MAIL_*_NOT_FOUND`), the account is not active (`MAIL_ACCOUNT_INACTIVE`), the draft is scheduled (`MAIL_DRAFT_SCHEDULED`), attachment storage is not configured (`MAIL_ATTACHMENT_STORAGE_NOT_CONFIGURED`), or the request is otherwise invalid, such as an `idempotencyKey` used for a different draft or recipient count (`INVALID_MAIL_REQUEST`).',
482
+ 409: 'The idempotency key was used for a different request.',
483
+ 413: true,
484
+ },
485
+ }), apiValidator('json', SendBulkInput), async (context) => {
486
+ const input = context.req.valid('json');
487
+ return context.json({
488
+ data: await mail.sendBulk(operationContext(context), {
489
+ ...input,
490
+ subject: input.subject ?? '',
491
+ text: input.text ?? '',
492
+ html: input.html,
493
+ forwardBodyIncluded: input.forwardBodyIncluded === true,
494
+ }),
495
+ });
496
+ });
497
+ // Saving a draft creates it or replaces the draft named by `draftMessageId` or `draftKey`, so it is a custom method
498
+ // rather than a create.
499
+ routes.post('/messages/saveDraft', mailRoute('mailSaveDraft', 'Create or replace a draft', { '200': dataResponse(MailMessageSchema) }, {
500
+ description: 'Replaces the draft named by `draftMessageId` or `draftKey`, or creates one.',
501
+ errors: {
502
+ 400: 'The account, sending identity, draft, related message or an attachment the body names does not exist (`MAIL_*_NOT_FOUND`), the account is not active (`MAIL_ACCOUNT_INACTIVE`), the draft is scheduled (`MAIL_DRAFT_SCHEDULED`), or the draft is otherwise invalid, such as both a reply and a forward (`INVALID_MAIL_REQUEST`).',
503
+ 409: 'The idempotency key was used for a different request, or `draftRevision` is not newer than the stored draft.',
504
+ 413: true,
505
+ },
506
+ }), apiValidator('json', SaveDraftInput), async (context) => {
507
+ const input = context.req.valid('json');
508
+ return context.json({
509
+ data: await mail.saveDraft(operationContext(context), {
510
+ ...input,
511
+ to: input.to ?? [],
512
+ subject: input.subject ?? '',
513
+ text: input.text ?? '',
514
+ html: input.html,
515
+ }),
516
+ });
517
+ });
518
+ routes.get('/accounts/:accountId/conversations/:conversationId/messages', mailRoute('mailListConversationMessages', 'List the messages of a conversation', { '200': listResponse(MailMessageSchema, MailCursorListMeta) }, {
519
+ errors: {
520
+ 400: 'The account is being removed (`MAIL_ACCOUNT_REMOVING`), or `pageToken` is not a token this list answered (`INVALID_MAIL_REQUEST`).',
521
+ 404: true,
522
+ },
523
+ }), apiValidator('param', AccountConversationParams), apiValidator('query', ConversationMessagesQuery), async (context) => {
524
+ const { accountId, conversationId } = context.req.valid('param');
525
+ const query = context.req.valid('query');
526
+ const page = await mail.listConversationMessages(operationContext(context), accountId, conversationId, { cursor: query.pageToken, limit: query.pageSize });
527
+ return context.json({ data: page.items, meta: cursorMeta(page) });
528
+ });
529
+ routes.get('/accounts/:accountId/messages/:messageId', mailRoute('mailGetMessage', 'Get a message', { '200': dataResponse(MailMessageSchema) }, { errors: { 404: true } }), apiValidator('param', AccountMessageParams), async (context) => {
530
+ const { accountId, messageId } = context.req.valid('param');
531
+ const message = await mail.getMessage(operationContext(context), accountId, messageId);
532
+ if (!message)
533
+ throw messageNotFound(context);
534
+ return context.json({ data: message });
535
+ });
536
+ routes.patch('/accounts/:accountId/messages/:messageId', mailRoute('mailUpdateMessage', 'Mark a message read or starred, or change its note', { '200': dataResponse(MailMessageSchema) }, {
537
+ description: '`read` and `starred` are synchronized to the Provider; `note` and `todo` stay in NocoBase.',
538
+ errors: {
539
+ 400: 'The account is not active (`MAIL_ACCOUNT_INACTIVE`) or must be reconnected because the mail Provider no longer accepts its authorization (`MAIL_ACCOUNT_REAUTHORIZATION_REQUIRED`), or the mail Provider is not configured (`MAIL_PROVIDER_UNAVAILABLE`), cannot change read or starred state (`MAIL_PROVIDER_OPERATION_UNSUPPORTED`), or rejected the change (`MAIL_PROVIDER_REQUEST_FAILED`).',
540
+ 404: true,
541
+ 413: true,
542
+ 429: providerRateLimited,
543
+ 503: providerUnavailable,
544
+ },
545
+ }), apiValidator('param', AccountMessageParams), apiValidator('json', UpdateMessageInput), async (context) => {
546
+ const { accountId, messageId } = context.req.valid('param');
547
+ const input = context.req.valid('json');
548
+ return context.json({
549
+ data: await mail.updateMessage(operationContext(context), {
550
+ accountId,
551
+ messageId,
552
+ read: input.read,
553
+ starred: input.starred,
554
+ note: input.note,
555
+ todo: input.todo,
556
+ }),
557
+ });
558
+ });
559
+ routes.delete('/accounts/:accountId/messages/:messageId', mailRoute('mailDeleteMessage', 'Delete a message', { '204': emptyResponse() }, {
560
+ description: 'Moves the message to the trash, or deletes it at the Provider when `permanently` is `true`.',
561
+ errors: {
562
+ 400: 'The account is not active (`MAIL_ACCOUNT_INACTIVE`) or must be reconnected because the mail Provider no longer accepts its authorization (`MAIL_ACCOUNT_REAUTHORIZATION_REQUIRED`), the draft is scheduled (`MAIL_DRAFT_SCHEDULED`), or the mail Provider is not configured (`MAIL_PROVIDER_UNAVAILABLE`), cannot delete messages (`MAIL_PROVIDER_OPERATION_UNSUPPORTED`), or rejected the request (`MAIL_PROVIDER_REQUEST_FAILED`).',
563
+ 404: true,
564
+ 429: providerRateLimited,
565
+ 503: providerUnavailable,
566
+ },
567
+ }), apiValidator('param', AccountMessageParams), apiValidator('query', DeleteMessageQuery), async (context) => {
568
+ const { accountId, messageId } = context.req.valid('param');
569
+ await mail.deleteMessage(operationContext(context), {
570
+ accountId,
571
+ messageId,
572
+ permanently: context.req.valid('query').permanently,
573
+ });
574
+ return context.body(null, 204);
575
+ });
576
+ routes.get('/accounts/:accountId/messages/:messageId/attachments/:attachmentId', mailRoute('mailDownloadMessageAttachment', 'Download an attachment of a message', { '200': attachmentDownloadResponse }, {
577
+ errors: {
578
+ 400: 'The attachment is not available at the Provider (`MAIL_ATTACHMENT_UNAVAILABLE`), attachment storage is not configured (`MAIL_ATTACHMENT_STORAGE_NOT_CONFIGURED`), the account must be reconnected because the mail Provider no longer accepts its authorization (`MAIL_ACCOUNT_REAUTHORIZATION_REQUIRED`), or the mail Provider is not configured (`MAIL_PROVIDER_UNAVAILABLE`), cannot download attachments (`MAIL_PROVIDER_OPERATION_UNSUPPORTED`) or rejected the request (`MAIL_PROVIDER_REQUEST_FAILED`).',
579
+ 404: true,
580
+ 429: providerRateLimited,
581
+ 503: providerUnavailable,
582
+ },
583
+ }), apiValidator('param', AccountMessageAttachmentParams), async (context) => {
584
+ const { accountId, messageId, attachmentId } = context.req.valid('param');
585
+ return attachmentResponse(await mail.getAttachment(operationContext(context), accountId, messageId, attachmentId));
586
+ });
587
+ routes.post('/accounts/:accountId/messages/:messageId/modifyLabels', mailRoute('mailModifyMessageLabels', 'Add or remove labels on a message', { '200': dataResponse(MailMessageSchema) }, {
588
+ errors: {
589
+ 400: 'The account is not active (`MAIL_ACCOUNT_INACTIVE`), or a label id names no label of the caller (`MAIL_LABEL_NOT_FOUND`).',
590
+ 404: true,
591
+ 413: true,
592
+ },
593
+ }), apiValidator('param', AccountMessageParams), apiValidator('json', ModifyMessageLabelsInput), async (context) => {
594
+ const { accountId, messageId } = context.req.valid('param');
595
+ const input = context.req.valid('json');
596
+ return context.json({
597
+ data: await mail.updateMessageLabels(operationContext(context), {
598
+ accountId,
599
+ messageId,
600
+ addLabelIds: input.addLabelIds,
601
+ removeLabelIds: input.removeLabelIds,
602
+ }),
603
+ });
604
+ });
605
+ routes.post('/accounts/:accountId/messages/:messageId/move', mailRoute('mailMoveMessage', 'Move a message to another folder', { '200': dataResponse(MailMessageSchema) }, {
606
+ errors: {
607
+ 400: 'The account is not active (`MAIL_ACCOUNT_INACTIVE`) or must be reconnected because the mail Provider no longer accepts its authorization (`MAIL_ACCOUNT_REAUTHORIZATION_REQUIRED`), the message is a scheduled or local-only draft (`MAIL_DRAFT_SCHEDULED`, `MAIL_DRAFT_LOCAL_ONLY`), `providerFolderId` names no folder of the account (`MAIL_FOLDER_NOT_FOUND`), or the mail Provider is not configured (`MAIL_PROVIDER_UNAVAILABLE`), cannot move messages (`MAIL_PROVIDER_OPERATION_UNSUPPORTED`), or rejected the move (`MAIL_PROVIDER_REQUEST_FAILED`).',
608
+ 404: true,
609
+ 413: true,
610
+ 429: providerRateLimited,
611
+ 503: providerUnavailable,
612
+ },
613
+ }), apiValidator('param', AccountMessageParams), apiValidator('json', MoveMessageInput), async (context) => {
614
+ const { accountId, messageId } = context.req.valid('param');
615
+ return context.json({
616
+ data: await mail.moveMessage(operationContext(context), {
617
+ accountId,
618
+ messageId,
619
+ providerFolderId: context.req.valid('json').providerFolderId,
620
+ }),
621
+ });
622
+ });
623
+ routes.post('/accounts/:accountId/messages/:messageId/retryContent', mailRoute('mailRetryMessageContent', 'Download the body of a message again', { '200': dataResponse(MailMessageSchema) }, {
624
+ description: 'For a message whose `contentStatus` is `deferred` or `failed`.',
625
+ errors: {
626
+ 400: 'The account is not active (`MAIL_ACCOUNT_INACTIVE`) or must be reconnected because the mail Provider no longer accepts its authorization (`MAIL_ACCOUNT_REAUTHORIZATION_REQUIRED`), or the mail Provider is not configured (`MAIL_PROVIDER_UNAVAILABLE`), cannot load message content (`MAIL_PROVIDER_OPERATION_UNSUPPORTED`), or rejected the request (`MAIL_PROVIDER_REQUEST_FAILED`).',
627
+ 404: true,
628
+ 429: providerRateLimited,
629
+ 503: providerUnavailable,
630
+ },
631
+ }), apiValidator('param', AccountMessageParams), async (context) => {
632
+ const { accountId, messageId } = context.req.valid('param');
633
+ return context.json({
634
+ data: await mail.retryMessageContent(operationContext(context), accountId, messageId),
635
+ });
636
+ });
637
+ routes.post('/accounts/:accountId/messages/:messageId/resolveDraftConflict', mailRoute('mailResolveDraftConflict', 'Resolve a draft changed both here and at the Provider', { '200': dataResponse(MailMessageSchema) }, {
638
+ description: '`useRemote` takes the Provider version; `keepLocal` overwrites it with the local one.',
639
+ errors: {
640
+ 400: 'The account is not active (`MAIL_ACCOUNT_INACTIVE`), the draft is scheduled (`MAIL_DRAFT_SCHEDULED`), or the message has no draft conflict (`MAIL_DRAFT_CONFLICT_NOT_FOUND`).',
641
+ 404: true,
642
+ 413: true,
643
+ },
644
+ }), apiValidator('param', AccountMessageParams), apiValidator('json', ResolveDraftConflictInput), async (context) => {
645
+ const { accountId, messageId } = context.req.valid('param');
646
+ return context.json({
647
+ data: await mail.resolveDraftConflict(operationContext(context), {
648
+ accountId,
649
+ messageId,
650
+ action: context.req.valid('json').action,
651
+ }),
652
+ });
653
+ });
654
+ // -----------------------------------------------------------------------------------------------------------------
655
+ // Sync runs and submissions: the caller's own logs, paged by page number.
656
+ routes.get('/syncRuns', mailRoute('mailListSyncRuns', "List the caller's synchronization runs", {
657
+ '200': listResponse(MailSyncRunSchema, MailPageListMeta),
658
+ }), apiValidator('query', SyncRunsQuery), async (context) => {
659
+ const { page, pageSize } = context.req.valid('query');
660
+ const result = await mail.listSyncRunsPage(operationContext(context), (page - 1) * pageSize, pageSize);
661
+ return context.json({
662
+ data: result.items,
663
+ meta: { page, pageSize, total: result.total },
664
+ });
665
+ });
666
+ routes.get('/syncRuns/:syncRunId', mailRoute('mailGetSyncRun', 'Get a synchronization run', { '200': dataResponse(MailSyncRunSchema) }, { errors: { 404: true } }), apiValidator('param', SyncRunParams), async (context) => {
667
+ const run = await mail.getSyncRun(operationContext(context), context.req.valid('param').syncRunId);
668
+ if (!run) {
669
+ throw new ApiError({
670
+ status: 'NOT_FOUND',
671
+ reason: 'MAIL_SYNC_RUN_NOT_FOUND',
672
+ domain: MAIL_DOMAIN,
673
+ message: 'Mail sync run was not found.',
674
+ ...localized(context, 'errors.syncRunNotFound'),
675
+ });
676
+ }
677
+ return context.json({ data: run });
678
+ });
679
+ routes.post('/syncRuns/:syncRunId/retry', mailRoute('mailRetrySyncRun', 'Retry a synchronization run', { '202': dataResponse(MailSyncRunSchema, 'The run was queued again.') }, {
680
+ errors: {
681
+ 400: 'The run is not failed or cancelled (`MAIL_SYNC_RUN_STATE_INVALID`), its account is being removed or is not active (`MAIL_ACCOUNT_REMOVING`, `MAIL_ACCOUNT_INACTIVE`), or an incremental run is retried before the initial synchronization completed (`MAIL_INITIAL_SYNC_REQUIRED`).',
682
+ 404: true,
683
+ },
684
+ }), apiValidator('param', SyncRunParams), async (context) => context.json({
685
+ data: await mail.retrySyncRun(operationContext(context), context.req.valid('param').syncRunId),
686
+ }, 202));
687
+ routes.post('/syncRuns/:syncRunId/cancel', mailRoute('mailCancelSyncRun', 'Cancel a synchronization run', { '200': dataResponse(MailSyncRunSchema) }, {
688
+ errors: {
689
+ 400: 'The run is no longer pending or running (`MAIL_SYNC_RUN_STATE_INVALID`), or its account is being removed (`MAIL_ACCOUNT_REMOVING`).',
690
+ 404: true,
691
+ },
692
+ }), apiValidator('param', SyncRunParams), async (context) => context.json({
693
+ data: await mail.cancelSyncRun(operationContext(context), context.req.valid('param').syncRunId),
694
+ }));
695
+ routes.get('/submissions', mailRoute('mailListSubmissions', "List the caller's sent and scheduled messages", { '200': listResponse(MailSubmissionLogSchema, MailPageListMeta) }, {
696
+ description: 'With `groupByBatch`, a bulk send counts as one row and `meta.total` counts batches.',
697
+ }), apiValidator('query', SubmissionsQuery), async (context) => {
698
+ const { page, pageSize, bulkOnly, groupByBatch } = context.req.valid('query');
699
+ const result = await mail.listSubmissionsPage(operationContext(context), bulkOnly ?? false, (page - 1) * pageSize, groupByBatch ?? false, pageSize);
700
+ return context.json({
701
+ data: result.items,
702
+ meta: { page, pageSize, total: result.total },
703
+ });
704
+ });
705
+ routes.post('/submissions/:submissionId/retry', mailRoute('mailRetrySubmission', 'Retry a failed submission', { '200': dataResponse(MailSubmissionLogSchema) }, {
706
+ errors: {
707
+ 400: 'The submission has not failed (`MAIL_SUBMISSION_STATE_INVALID`), or its account is being removed (`MAIL_ACCOUNT_REMOVING`).',
708
+ 404: true,
709
+ },
710
+ }), apiValidator('param', SubmissionParams), async (context) => context.json({
711
+ data: await mail.retrySubmission(operationContext(context), context.req.valid('param').submissionId),
712
+ }));
713
+ routes.post('/submissions/:submissionId/cancel', mailRoute('mailCancelSubmission', 'Cancel a scheduled or pending submission', { '200': dataResponse(MailSubmissionLogSchema) }, {
714
+ errors: {
715
+ 400: 'The submission is no longer pending or failed (`MAIL_SUBMISSION_STATE_INVALID`), or its account is being removed (`MAIL_ACCOUNT_REMOVING`).',
716
+ 404: true,
717
+ },
718
+ }), apiValidator('param', SubmissionParams), async (context) => context.json({
719
+ data: await mail.cancelSubmission(operationContext(context), context.req.valid('param').submissionId),
720
+ }));
721
+ // -----------------------------------------------------------------------------------------------------------------
722
+ // Administration (`mail.admin`): every user's accounts and operation logs. The logs grow without bound, so sync
723
+ // runs and submissions are separate lists paged by page number, as the caller's own logs are.
724
+ //
725
+ // `/settings/accounts` and `/management/accounts` read the same list but are separate operations of separate
726
+ // pages: each is gated by its own page permission, and merging them would force granting one page to use the
727
+ // other.
728
+ routes.get('/settings/accounts', mailRoute('mailListAdminAccounts', "List every user's mail accounts for the Mail settings page", { '200': listResponse(MailManagedAccountSchema, MailBoundedListMeta) }, { description: 'Requires access to the `mail.admin` page.' }), async (context) => context.json(boundedList(await mail.listManagedAccounts(operationContext(context)))));
729
+ routes.get('/settings/syncRuns', mailRoute('mailListAdminSyncRuns', "List every user's synchronization runs", { '200': listResponse(MailSyncRunSchema, MailPageListMeta) }, { description: 'Requires access to the `mail.admin` page.' }), apiValidator('query', ManagedLogsQuery), async (context) => {
730
+ const { page, pageSize } = context.req.valid('query');
731
+ const result = await mail.listManagedSyncRunsPage(operationContext(context), (page - 1) * pageSize, pageSize);
732
+ return context.json({
733
+ data: result.items,
734
+ meta: { page, pageSize, total: result.total },
735
+ });
736
+ });
737
+ routes.get('/settings/submissions', mailRoute('mailListAdminSubmissions', "List every user's submissions", { '200': listResponse(MailSubmissionLogSchema, MailPageListMeta) }, { description: 'Requires access to the `mail.admin` page.' }), apiValidator('query', ManagedLogsQuery), async (context) => {
738
+ const { page, pageSize } = context.req.valid('query');
739
+ const result = await mail.listManagedSubmissionsPage(operationContext(context), (page - 1) * pageSize, pageSize);
740
+ return context.json({
741
+ data: result.items,
742
+ meta: { page, pageSize, total: result.total },
743
+ });
744
+ });
745
+ // -----------------------------------------------------------------------------------------------------------------
746
+ // Management (`mail.management`): every user's mailboxes, as an administrative table paged by page number.
747
+ routes.get('/management/accounts', mailRoute('mailListManagedAccounts', "List every user's mail accounts for mailbox management", { '200': listResponse(MailManagedAccountSchema, MailBoundedListMeta) }, { description: 'Requires access to the `mail.management` page.' }), async (context) => context.json(boundedList(await mail.listManagedAccounts(operationContext(context)))));
748
+ routes.get('/management/accounts/:accountId/folders', mailRoute('mailListManagedFolders', "List the folders of any user's account", { '200': listResponse(MailFolderSchema, MailBoundedListMeta) }, {
749
+ description: 'Requires access to the `mail.management` page.',
750
+ errors: { 404: true },
751
+ }), apiValidator('param', AccountParams), async (context) => context.json(boundedList(await mail.listManagedFolders(operationContext(context), context.req.valid('param').accountId))));
752
+ routes.get('/management/messages', mailRoute('mailListManagedMessages', "List messages across every user's accounts", { '200': listResponse(MailMessageSummarySchema, MailPageListMeta) }, { description: 'Requires access to the `mail.management` page.' }), apiValidator('query', ManagementMessagesQuery), async (context) => {
753
+ const query = context.req.valid('query');
754
+ const result = await mail.listManagedMessages(operationContext(context), {
755
+ accountIds: query.accountId ? [query.accountId] : undefined,
756
+ folderIds: query.folderId ? [query.folderId] : undefined,
757
+ query: query.q,
758
+ offset: (query.page - 1) * query.pageSize,
759
+ limit: query.pageSize,
760
+ withTotal: true,
761
+ unread: query.unread,
762
+ starred: query.starred,
763
+ });
764
+ return context.json({
765
+ data: result.items,
766
+ meta: {
767
+ page: query.page,
768
+ pageSize: query.pageSize,
769
+ total: result.total ?? result.items.length,
770
+ },
771
+ });
772
+ });
773
+ routes.post('/management/messages/batchApply', mailRoute('mailBatchApplyManagedMessages', 'Apply one action to several messages of any user', { '200': dataResponse(MailManagementActionResultSchema) }, {
774
+ description: 'Requires access to the `mail.management` page. Each message succeeds or fails on its own; the result reports both.',
775
+ errors: { 413: true },
776
+ }), apiValidator('json', ManageMessagesInput), async (context) => context.json({
777
+ data: await mail.manageMessages(operationContext(context), context.req.valid('json')),
778
+ }));
779
+ routes.get('/management/accounts/:accountId/messages/:messageId', mailRoute('mailGetManagedMessage', "Get a message of any user's account", { '200': dataResponse(MailMessageSchema) }, {
780
+ description: 'Requires access to the `mail.management` page.',
781
+ errors: { 404: true },
782
+ }), apiValidator('param', AccountMessageParams), async (context) => {
783
+ const { accountId, messageId } = context.req.valid('param');
784
+ const message = await mail.getManagedMessage(operationContext(context), accountId, messageId);
785
+ if (!message)
786
+ throw messageNotFound(context);
787
+ return context.json({ data: message });
788
+ });
789
+ routes.get('/management/accounts/:accountId/messages/:messageId/attachments/:attachmentId', mailRoute('mailDownloadManagedMessageAttachment', "Download an attachment of any user's message", { '200': attachmentDownloadResponse }, {
790
+ description: 'Requires access to the `mail.management` page.',
791
+ errors: {
792
+ 400: 'The attachment is not available at the Provider (`MAIL_ATTACHMENT_UNAVAILABLE`), attachment storage is not configured (`MAIL_ATTACHMENT_STORAGE_NOT_CONFIGURED`), the account must be reconnected because the mail Provider no longer accepts its authorization (`MAIL_ACCOUNT_REAUTHORIZATION_REQUIRED`), or the mail Provider is not configured (`MAIL_PROVIDER_UNAVAILABLE`), cannot download attachments (`MAIL_PROVIDER_OPERATION_UNSUPPORTED`) or rejected the request (`MAIL_PROVIDER_REQUEST_FAILED`).',
793
+ 404: true,
794
+ 429: providerRateLimited,
795
+ 503: providerUnavailable,
796
+ },
797
+ }), apiValidator('param', AccountMessageAttachmentParams), async (context) => {
798
+ const { accountId, messageId, attachmentId } = context.req.valid('param');
799
+ return attachmentResponse(await mail.getManagedAttachment(operationContext(context), accountId, messageId, attachmentId));
800
+ });
801
+ router.route('/mail', routes);
802
+ return router;
803
+ });
804
+ function operationContext(context) {
805
+ return {
806
+ actorId: context.get('auth').user.id,
807
+ signal: context.req.raw.signal,
808
+ };
809
+ }
810
+ /**
811
+ * The body of a bounded list: a user's accounts, an account's folders, the configured Providers, and similar lists
812
+ * whose size the user or the configuration limits. They are answered whole, with `meta.total`.
813
+ */
814
+ function boundedList(items) {
815
+ return { data: items, meta: { total: items.length } };
816
+ }
817
+ /** `meta` for a cursor-paged feed: the opaque token for the next page, absent on the last one, and the total. */
818
+ function cursorMeta(page) {
819
+ return {
820
+ ...(page.nextCursor ? { nextPageToken: page.nextCursor } : {}),
821
+ ...(page.total !== undefined ? { total: page.total } : {}),
822
+ };
823
+ }
824
+ function attachmentResponse(content) {
825
+ const headers = new Headers({
826
+ 'content-type': safeContentType(content.contentType),
827
+ 'content-disposition': attachmentDisposition(content.fileName),
828
+ 'x-content-type-options': 'nosniff',
829
+ });
830
+ if (content.size !== undefined) {
831
+ headers.set('content-length', String(content.size));
832
+ }
833
+ return new Response(content.stream, { headers });
834
+ }
835
+ /** The request's translation of a Mail message key, as the error body's `localizedMessage`. */
836
+ function localized(context, key) {
837
+ try {
838
+ const locale = getRequestLocale(context);
839
+ const message = getRequestTranslator(context, MAIL_NAMESPACE)(key);
840
+ return locale && message ? { localizedMessage: { locale, message } } : {};
841
+ }
842
+ catch {
843
+ // A router mounted without the i18n middleware answers without a translation.
844
+ return {};
845
+ }
846
+ }
847
+ function messageNotFound(context) {
848
+ return new ApiError({
849
+ status: 'NOT_FOUND',
850
+ reason: 'MAIL_MESSAGE_NOT_FOUND',
851
+ domain: MAIL_DOMAIN,
852
+ message: 'Mail message was not found.',
853
+ ...localized(context, 'errors.messageNotFound'),
854
+ });
855
+ }
856
+ function invalidField(field, description, options = {}) {
857
+ return new ApiError({
858
+ status: 'INVALID_ARGUMENT',
859
+ reason: 'INVALID_MAIL_REQUEST',
860
+ domain: MAIL_DOMAIN,
861
+ message: description,
862
+ fieldViolations: [{ field, description }],
863
+ cause: options.cause,
864
+ });
865
+ }
866
+ /**
867
+ * Translate Mail's own errors into the standard error body. Anything else is returned unchanged, so `apiErrorHandler`
868
+ * renders what the framework recognizes and the application answers the rest with an opaque 500.
869
+ */
870
+ function toMailApiError(error, context) {
871
+ if (error instanceof MailIdempotencyConflictError) {
872
+ return new ApiError({
873
+ status: 'ABORTED',
874
+ reason: 'MAIL_IDEMPOTENCY_CONFLICT',
875
+ domain: MAIL_DOMAIN,
876
+ message: error.message,
877
+ ...localized(context, 'errors.idempotencyConflict'),
878
+ cause: error,
879
+ });
880
+ }
881
+ if (error instanceof MailError) {
882
+ if (error.status === 'INVALID_ARGUMENT') {
883
+ return new ApiError({
884
+ status: 'INVALID_ARGUMENT',
885
+ reason: error.reason,
886
+ domain: MAIL_DOMAIN,
887
+ message: error.message,
888
+ ...(error.field
889
+ ? {
890
+ fieldViolations: [
891
+ { field: error.field, description: error.message },
892
+ ],
893
+ }
894
+ : {}),
895
+ ...localized(context, 'errors.invalidRequest'),
896
+ cause: error,
897
+ });
898
+ }
899
+ // A missing resource is 404 only when the URL names it; one the body or query refers to makes the request invalid.
900
+ if (error.status === 'NOT_FOUND' &&
901
+ error.field &&
902
+ !pathParameters(context)[error.field]) {
903
+ return new ApiError({
904
+ status: 'INVALID_ARGUMENT',
905
+ reason: error.reason,
906
+ domain: MAIL_DOMAIN,
907
+ message: error.message,
908
+ fieldViolations: [{ field: error.field, description: error.message }],
909
+ ...localized(context, 'errors.invalidRequest'),
910
+ cause: error,
911
+ });
912
+ }
913
+ return new ApiError({
914
+ status: error.status,
915
+ reason: error.reason,
916
+ domain: MAIL_DOMAIN,
917
+ message: error.message,
918
+ ...localized(context, error.reason === 'MAIL_MESSAGE_NOT_FOUND'
919
+ ? 'errors.messageNotFound'
920
+ : error.reason === 'MAIL_SYNC_RUN_NOT_FOUND'
921
+ ? 'errors.syncRunNotFound'
922
+ : 'errors.requestFailed'),
923
+ cause: error,
924
+ });
925
+ }
926
+ if (isMailProviderError(error))
927
+ return mailProviderApiError(error, context);
928
+ // Anything else, including a TypeError from a programming mistake, is unexpected and becomes an opaque 500.
929
+ return error;
930
+ }
931
+ /**
932
+ * The answer to a Provider failure a route met while it waited for the Provider, by the kind `classifyMailProviderError`
933
+ * assigns. `metadata` carries the public Provider error (`code`, `category`, `retryable`, and `retryAfterMs` and an
934
+ * allowlisted `reasonCode` when known), never the Provider's own message; a known delay is also `metadata.retryAfter`
935
+ * in whole seconds, which `withRetryAfter` repeats as the `Retry-After` header.
936
+ */
937
+ function mailProviderApiError(error, context) {
938
+ const publicError = toPublicError(error);
939
+ const retryAfter = error.retryAfterMs !== undefined && error.retryAfterMs > 0
940
+ ? Math.ceil(error.retryAfterMs / 1000)
941
+ : undefined;
942
+ const metadata = {
943
+ ...publicError,
944
+ ...(retryAfter === undefined ? {} : { retryAfter }),
945
+ };
946
+ switch (classifyMailProviderError(error)) {
947
+ case 'RATE_LIMITED':
948
+ return new ApiError({
949
+ status: 'RESOURCE_EXHAUSTED',
950
+ reason: 'MAIL_PROVIDER_REQUEST_FAILED',
951
+ domain: MAIL_DOMAIN,
952
+ message: 'The mail Provider is rate limiting requests.',
953
+ metadata,
954
+ ...localized(context, 'errors.providerRateLimited'),
955
+ cause: error,
956
+ });
957
+ case 'UNAVAILABLE':
958
+ return new ApiError({
959
+ status: 'UNAVAILABLE',
960
+ reason: 'MAIL_PROVIDER_REQUEST_FAILED',
961
+ domain: MAIL_DOMAIN,
962
+ message: 'The mail Provider could not be reached.',
963
+ metadata,
964
+ ...localized(context, 'errors.providerUnavailable'),
965
+ cause: error,
966
+ });
967
+ case 'REAUTHORIZATION_REQUIRED':
968
+ return new ApiError({
969
+ status: 'FAILED_PRECONDITION',
970
+ reason: 'MAIL_ACCOUNT_REAUTHORIZATION_REQUIRED',
971
+ domain: MAIL_DOMAIN,
972
+ message: 'The mail Provider no longer accepts the account authorization; reconnect the account.',
973
+ metadata,
974
+ ...localized(context, 'errors.reauthorizationRequired'),
975
+ cause: error,
976
+ });
977
+ case 'REJECTED':
978
+ return new ApiError({
979
+ status: 'FAILED_PRECONDITION',
980
+ reason: 'MAIL_PROVIDER_REQUEST_FAILED',
981
+ domain: MAIL_DOMAIN,
982
+ message: 'The mail Provider rejected the request.',
983
+ metadata,
984
+ ...localized(context, 'errors.requestFailed'),
985
+ cause: error,
986
+ });
987
+ }
988
+ }
989
+ /** Repeat a known Provider delay as the `Retry-After` header of a 429 or 503. */
990
+ function withRetryAfter(response, error) {
991
+ if (!(error instanceof ApiError) ||
992
+ (error.status !== 'RESOURCE_EXHAUSTED' && error.status !== 'UNAVAILABLE'))
993
+ return response;
994
+ const retryAfter = error.metadata?.retryAfter;
995
+ if (typeof retryAfter === 'number')
996
+ response.headers.set('Retry-After', String(retryAfter));
997
+ return response;
998
+ }
999
+ function pathParameters(context) {
1000
+ try {
1001
+ return context.req.param();
1002
+ }
1003
+ catch {
1004
+ return {};
1005
+ }
1006
+ }
1007
+ function attachmentDisposition(fileName) {
1008
+ const fallback = Array.from(fileName, (character) => {
1009
+ const code = character.codePointAt(0) ?? 0;
1010
+ return code >= 0x20 &&
1011
+ code <= 0x7e &&
1012
+ character !== '"' &&
1013
+ character !== '\\'
1014
+ ? character
1015
+ : '_';
1016
+ }).join('') || 'attachment';
1017
+ const encoded = encodeURIComponent(fileName).replace(/['()*]/gu, (character) => `%${character.codePointAt(0)?.toString(16).toUpperCase()}`);
1018
+ return `attachment; filename="${fallback}"; filename*=UTF-8''${encoded}`;
1019
+ }
1020
+ function safeContentType(value) {
1021
+ return /^[\w!#$&^_.+-]+\/[\w!#$&^_.+-]+(?:\s*;[^\r\n]*)?$/u.test(value)
1022
+ ? value
1023
+ : 'application/octet-stream';
1024
+ }
1025
+ //# sourceMappingURL=api.js.map