@cosmicdrift/kumiko-bundled-features 0.392.2 → 0.394.0

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 (380) hide show
  1. package/dist/account-security/feature.d.ts +2 -0
  2. package/dist/account-security/feature.js +3 -1
  3. package/dist/audit/event-classification.js +1 -0
  4. package/dist/auth-email-password/handlers/change-password.write.d.ts +1 -1
  5. package/dist/auth-email-password/handlers/confirm-account-unlock.write.d.ts +1 -1
  6. package/dist/auth-email-password/handlers/invite-accept-with-login.write.d.ts +1 -1
  7. package/dist/auth-email-password/handlers/invite-accept.write.d.ts +1 -1
  8. package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
  9. package/dist/auth-email-password/handlers/invite-signup-complete.write.d.ts +1 -1
  10. package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
  11. package/dist/auth-email-password/handlers/logout.write.d.ts +1 -1
  12. package/dist/auth-email-password/handlers/request-account-unlock.write.d.ts +1 -1
  13. package/dist/auth-email-password/handlers/request-email-verification.write.d.ts +1 -1
  14. package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -1
  15. package/dist/auth-email-password/handlers/reset-password.write.d.ts +1 -1
  16. package/dist/auth-email-password/handlers/signup-confirm.write.d.ts +1 -1
  17. package/dist/auth-email-password/handlers/signup-request.write.d.ts +1 -1
  18. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
  19. package/dist/auth-email-password/handlers/token-request-handler.d.ts +1 -1
  20. package/dist/auth-email-password/handlers/verify-email.write.d.ts +1 -1
  21. package/dist/auth-email-password/web/auth-client.d.ts +3 -7
  22. package/dist/auth-email-password/web/auth-client.js +3 -24
  23. package/dist/auth-email-password/web/auth-gate.d.ts +2 -4
  24. package/dist/auth-email-password/web/auth-redirect.d.ts +2 -2
  25. package/dist/auth-email-password/web/auth-redirect.js +3 -12
  26. package/dist/auth-email-password/web/index.d.ts +2 -1
  27. package/dist/auth-email-password/web/index.js +1 -0
  28. package/dist/auth-email-password/web/login-failure.d.ts +9 -0
  29. package/dist/auth-email-password/web/login-failure.js +32 -0
  30. package/dist/auth-email-password/web/login-url.d.ts +5 -0
  31. package/dist/auth-email-password/web/login-url.js +13 -0
  32. package/dist/auth-email-password/web/static-login.d.ts +42 -0
  33. package/dist/auth-email-password/web/static-login.js +122 -0
  34. package/dist/auth-mfa/handlers/disable.write.d.ts +1 -1
  35. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.d.ts +1 -1
  36. package/dist/auth-mfa/handlers/enable-confirm.write.d.ts +1 -1
  37. package/dist/auth-mfa/handlers/enable-start-preauth.write.d.ts +1 -1
  38. package/dist/auth-mfa/handlers/enable-start.write.d.ts +1 -1
  39. package/dist/auth-mfa/handlers/regenerate-recovery.write.d.ts +1 -1
  40. package/dist/auth-mfa/handlers/verify.write.d.ts +1 -1
  41. package/dist/auth-mfa/web/mfa-client.js +4 -43
  42. package/dist/billing-foundation/checkout-core.d.ts +2 -1
  43. package/dist/billing-foundation/checkout-core.js +2 -11
  44. package/dist/billing-foundation/events.d.ts +1 -0
  45. package/dist/billing-foundation/events.js +2 -0
  46. package/dist/billing-foundation/feature.js +1 -1
  47. package/dist/billing-foundation/handlers/create-checkout-session.write.js +9 -2
  48. package/dist/billing-foundation/handlers/process-payment-event.write.d.ts +1 -0
  49. package/dist/billing-foundation/handlers/process-payment-event.write.js +2 -0
  50. package/dist/billing-foundation/index.d.ts +1 -1
  51. package/dist/billing-foundation/one-off-checkout-gate.d.ts +9 -0
  52. package/dist/billing-foundation/one-off-checkout-gate.js +30 -0
  53. package/dist/billing-foundation/one-off-customer.d.ts +6 -0
  54. package/dist/billing-foundation/one-off-customer.js +12 -0
  55. package/dist/billing-foundation/plan-catalog.js +1 -0
  56. package/dist/billing-foundation/types.d.ts +25 -0
  57. package/dist/billing-foundation/web/billing-plans-panel.js +1 -0
  58. package/dist/billing-foundation/web/plan-switch-confirm-dialog.js +7 -3
  59. package/dist/billing-foundation/web/plan-switch-recurring.d.ts +16 -0
  60. package/dist/billing-foundation/web/plan-switch-recurring.js +10 -0
  61. package/dist/billing-foundation/webhook-handler.js +1 -0
  62. package/dist/cap-overview/handlers/caps-usage.query.js +3 -3
  63. package/dist/cap-overview/types.d.ts +2 -0
  64. package/dist/channel-in-app/handlers/mark-all-read.write.d.ts +1 -1
  65. package/dist/channel-in-app/handlers/mark-read.write.d.ts +1 -1
  66. package/dist/compliance-profiles/handlers/set-profile.write.d.ts +1 -1
  67. package/dist/compliance-profiles/web/compliance-profile-catalog.js +8 -1
  68. package/dist/config/handlers/reset.write.d.ts +1 -1
  69. package/dist/config/handlers/set.write.d.ts +1 -1
  70. package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -1
  71. package/dist/delivery/handlers/resubscribe-address.write.d.ts +1 -1
  72. package/dist/delivery/handlers/resubscribe-user.write.d.ts +1 -1
  73. package/dist/delivery/handlers/set-preference.write.d.ts +1 -1
  74. package/dist/delivery/handlers/unsubscribe-address.write.d.ts +1 -1
  75. package/dist/delivery/handlers/unsubscribe-user.write.d.ts +1 -1
  76. package/dist/feature-toggles/handlers/set.write.d.ts +1 -1
  77. package/dist/file-foundation/read-file-ref-content.d.ts +2 -0
  78. package/dist/file-foundation/read-file-ref-content.js +9 -3
  79. package/dist/form-draft/handlers/discard.write.d.ts +1 -1
  80. package/dist/form-draft/handlers/save.write.d.ts +1 -1
  81. package/dist/inbound-mail-foundation/account-health.d.ts +17 -0
  82. package/dist/inbound-mail-foundation/account-health.js +101 -0
  83. package/dist/inbound-mail-foundation/account-poll-schedule.d.ts +19 -0
  84. package/dist/inbound-mail-foundation/account-poll-schedule.js +44 -0
  85. package/dist/inbound-mail-foundation/account-poll.d.ts +24 -0
  86. package/dist/inbound-mail-foundation/account-poll.js +157 -0
  87. package/dist/inbound-mail-foundation/account-record.d.ts +1 -0
  88. package/dist/inbound-mail-foundation/account-record.js +1 -0
  89. package/dist/inbound-mail-foundation/account-wake.d.ts +3 -0
  90. package/dist/inbound-mail-foundation/account-wake.js +17 -0
  91. package/dist/inbound-mail-foundation/connect-callback-verify.d.ts +10 -0
  92. package/dist/inbound-mail-foundation/connect-callback-verify.js +31 -0
  93. package/dist/inbound-mail-foundation/connect-route-responses.d.ts +13 -0
  94. package/dist/inbound-mail-foundation/connect-route-responses.js +16 -0
  95. package/dist/inbound-mail-foundation/connect-routes.js +52 -33
  96. package/dist/inbound-mail-foundation/constants.d.ts +50 -2
  97. package/dist/inbound-mail-foundation/constants.js +53 -3
  98. package/dist/inbound-mail-foundation/content-refetch.d.ts +14 -0
  99. package/dist/inbound-mail-foundation/content-refetch.js +93 -0
  100. package/dist/inbound-mail-foundation/describe-error.d.ts +1 -0
  101. package/dist/inbound-mail-foundation/describe-error.js +5 -0
  102. package/dist/inbound-mail-foundation/entities.d.ts +26 -0
  103. package/dist/inbound-mail-foundation/entities.js +43 -3
  104. package/dist/inbound-mail-foundation/events.d.ts +97 -0
  105. package/dist/inbound-mail-foundation/events.js +57 -2
  106. package/dist/inbound-mail-foundation/feature.d.ts +5 -2
  107. package/dist/inbound-mail-foundation/feature.js +122 -106
  108. package/dist/inbound-mail-foundation/file-storage-failure.d.ts +8 -0
  109. package/dist/inbound-mail-foundation/file-storage-failure.js +19 -0
  110. package/dist/inbound-mail-foundation/handlers/account-address.d.ts +16 -0
  111. package/dist/inbound-mail-foundation/handlers/account-address.js +51 -0
  112. package/dist/inbound-mail-foundation/handlers/acknowledge-quarantine.write.d.ts +8 -0
  113. package/dist/inbound-mail-foundation/handlers/acknowledge-quarantine.write.js +39 -0
  114. package/dist/inbound-mail-foundation/handlers/attach-message-content.write.d.ts +60 -0
  115. package/dist/inbound-mail-foundation/handlers/attach-message-content.write.js +128 -0
  116. package/dist/inbound-mail-foundation/handlers/connect-account.write.d.ts +3 -2
  117. package/dist/inbound-mail-foundation/handlers/connect-account.write.js +97 -56
  118. package/dist/inbound-mail-foundation/handlers/disconnect-account.write.d.ts +2 -1
  119. package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +55 -42
  120. package/dist/inbound-mail-foundation/handlers/ingest-message-prepare.d.ts +26 -0
  121. package/dist/inbound-mail-foundation/handlers/ingest-message-prepare.js +131 -0
  122. package/dist/inbound-mail-foundation/handlers/ingest-message-schema.d.ts +64 -0
  123. package/dist/inbound-mail-foundation/handlers/ingest-message-schema.js +39 -0
  124. package/dist/inbound-mail-foundation/handlers/ingest-message.write.d.ts +74 -21
  125. package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +57 -134
  126. package/dist/inbound-mail-foundation/handlers/list-accounts.query.d.ts +2 -1
  127. package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +60 -26
  128. package/dist/inbound-mail-foundation/handlers/list-messages.query.d.ts +2 -1
  129. package/dist/inbound-mail-foundation/handlers/list-messages.query.js +43 -29
  130. package/dist/inbound-mail-foundation/handlers/list-quarantine.query.d.ts +3 -0
  131. package/dist/inbound-mail-foundation/handlers/list-quarantine.query.js +75 -0
  132. package/dist/inbound-mail-foundation/handlers/mark-credential-stored.write.d.ts +7 -0
  133. package/dist/inbound-mail-foundation/handlers/mark-credential-stored.write.js +38 -0
  134. package/dist/inbound-mail-foundation/handlers/message-content.d.ts +70 -0
  135. package/dist/inbound-mail-foundation/handlers/message-content.js +137 -0
  136. package/dist/inbound-mail-foundation/handlers/message-content.query.d.ts +19 -0
  137. package/dist/inbound-mail-foundation/handlers/message-content.query.js +59 -0
  138. package/dist/inbound-mail-foundation/handlers/message-detail.query.d.ts +3 -0
  139. package/dist/inbound-mail-foundation/handlers/message-detail.query.js +23 -0
  140. package/dist/inbound-mail-foundation/handlers/reactivate-account.write.d.ts +8 -0
  141. package/dist/inbound-mail-foundation/handlers/reactivate-account.write.js +84 -0
  142. package/dist/inbound-mail-foundation/handlers/retry-quarantined-message.write.d.ts +8 -0
  143. package/dist/inbound-mail-foundation/handlers/retry-quarantined-message.write.js +34 -0
  144. package/dist/inbound-mail-foundation/handlers/scope-visibility.d.ts +10 -2
  145. package/dist/inbound-mail-foundation/handlers/scope-visibility.js +27 -8
  146. package/dist/inbound-mail-foundation/handlers/set-trusted-authserv-id.write.d.ts +13 -0
  147. package/dist/inbound-mail-foundation/handlers/set-trusted-authserv-id.write.js +54 -0
  148. package/dist/inbound-mail-foundation/handlers/thread-rollup.d.ts +24 -0
  149. package/dist/inbound-mail-foundation/handlers/thread-rollup.js +98 -0
  150. package/dist/inbound-mail-foundation/handlers/trusted-authserv-id.d.ts +3 -0
  151. package/dist/inbound-mail-foundation/handlers/trusted-authserv-id.js +3 -0
  152. package/dist/inbound-mail-foundation/handlers/update-account.write.d.ts +13 -3
  153. package/dist/inbound-mail-foundation/handlers/update-account.write.js +62 -46
  154. package/dist/inbound-mail-foundation/i18n.js +72 -2
  155. package/dist/inbound-mail-foundation/inbound-oauth-flow.d.ts +43 -0
  156. package/dist/inbound-mail-foundation/inbound-oauth-flow.js +1 -0
  157. package/dist/inbound-mail-foundation/index.d.ts +19 -6
  158. package/dist/inbound-mail-foundation/index.js +17 -6
  159. package/dist/inbound-mail-foundation/ingest-failures.d.ts +77 -0
  160. package/dist/inbound-mail-foundation/ingest-failures.js +79 -0
  161. package/dist/inbound-mail-foundation/ingest-payload.d.ts +7 -0
  162. package/dist/inbound-mail-foundation/ingest-payload.js +34 -0
  163. package/dist/inbound-mail-foundation/mail-roles.d.ts +21 -0
  164. package/dist/inbound-mail-foundation/mail-roles.js +83 -0
  165. package/dist/inbound-mail-foundation/normalize-inbound-message.d.ts +17 -0
  166. package/dist/inbound-mail-foundation/normalize-inbound-message.js +90 -0
  167. package/dist/inbound-mail-foundation/oauth-access-token.d.ts +3 -1
  168. package/dist/inbound-mail-foundation/oauth-access-token.js +13 -2
  169. package/dist/inbound-mail-foundation/outbound-attachment-access.d.ts +5 -0
  170. package/dist/inbound-mail-foundation/outbound-attachment-access.js +16 -0
  171. package/dist/inbound-mail-foundation/outbound-attachments.d.ts +18 -0
  172. package/dist/inbound-mail-foundation/outbound-attachments.js +77 -0
  173. package/dist/inbound-mail-foundation/outbound-mail.d.ts +15 -1
  174. package/dist/inbound-mail-foundation/outbound-mail.js +4 -0
  175. package/dist/inbound-mail-foundation/probe.js +1 -1
  176. package/dist/inbound-mail-foundation/projection.d.ts +28 -0
  177. package/dist/inbound-mail-foundation/projection.js +25 -3
  178. package/dist/inbound-mail-foundation/provider-errors.d.ts +28 -0
  179. package/dist/inbound-mail-foundation/provider-errors.js +66 -0
  180. package/dist/inbound-mail-foundation/quarantine-entities.d.ts +61 -0
  181. package/dist/inbound-mail-foundation/quarantine-entities.js +47 -0
  182. package/dist/inbound-mail-foundation/quarantine-header-import.d.ts +9 -0
  183. package/dist/inbound-mail-foundation/quarantine-header-import.js +53 -0
  184. package/dist/inbound-mail-foundation/quarantine-retry.d.ts +13 -0
  185. package/dist/inbound-mail-foundation/quarantine-retry.js +97 -0
  186. package/dist/inbound-mail-foundation/quarantine-screens.d.ts +4 -0
  187. package/dist/inbound-mail-foundation/quarantine-screens.js +57 -0
  188. package/dist/inbound-mail-foundation/quarantine.d.ts +13 -0
  189. package/dist/inbound-mail-foundation/quarantine.js +46 -0
  190. package/dist/inbound-mail-foundation/retention-job.d.ts +2 -0
  191. package/dist/inbound-mail-foundation/retention-job.js +62 -0
  192. package/dist/inbound-mail-foundation/retention-sweep.d.ts +4 -0
  193. package/dist/inbound-mail-foundation/retention-sweep.js +35 -8
  194. package/dist/inbound-mail-foundation/screens.d.ts +14 -2
  195. package/dist/inbound-mail-foundation/screens.js +217 -79
  196. package/dist/inbound-mail-foundation/send-from-account.d.ts +11 -1
  197. package/dist/inbound-mail-foundation/send-from-account.js +19 -2
  198. package/dist/inbound-mail-foundation/sender-auth.d.ts +63 -0
  199. package/dist/inbound-mail-foundation/sender-auth.js +198 -0
  200. package/dist/inbound-mail-foundation/supervisor-ingest.d.ts +43 -0
  201. package/dist/inbound-mail-foundation/supervisor-ingest.js +195 -0
  202. package/dist/inbound-mail-foundation/sync-account-now.d.ts +22 -0
  203. package/dist/inbound-mail-foundation/sync-account-now.js +29 -0
  204. package/dist/inbound-mail-foundation/sync-cursor-store.d.ts +9 -0
  205. package/dist/inbound-mail-foundation/sync-cursor-store.js +54 -0
  206. package/dist/inbound-mail-foundation/syncable-accounts.d.ts +16 -0
  207. package/dist/inbound-mail-foundation/syncable-accounts.js +79 -0
  208. package/dist/inbound-mail-foundation/tenant-destroy-hook.js +5 -1
  209. package/dist/inbound-mail-foundation/types.d.ts +61 -62
  210. package/dist/inbound-mail-foundation/types.js +0 -54
  211. package/dist/inbound-mail-foundation/wake-consumer.d.ts +2 -0
  212. package/dist/inbound-mail-foundation/wake-consumer.js +30 -0
  213. package/dist/inbound-mail-foundation/watch-catch-up.d.ts +19 -0
  214. package/dist/inbound-mail-foundation/watch-catch-up.js +33 -0
  215. package/dist/inbound-mail-foundation/watch-lease.d.ts +15 -0
  216. package/dist/inbound-mail-foundation/watch-lease.js +81 -0
  217. package/dist/inbound-mail-foundation/watch-supervisor-types.d.ts +83 -0
  218. package/dist/inbound-mail-foundation/watch-supervisor-types.js +1 -0
  219. package/dist/inbound-mail-foundation/watch-supervisor.d.ts +1 -53
  220. package/dist/inbound-mail-foundation/watch-supervisor.js +123 -290
  221. package/dist/inbound-mail-foundation/web/client-plugin.d.ts +14 -0
  222. package/dist/inbound-mail-foundation/web/client-plugin.js +40 -0
  223. package/dist/inbound-mail-foundation/web/constants.d.ts +5 -0
  224. package/dist/inbound-mail-foundation/web/constants.js +6 -0
  225. package/dist/inbound-mail-foundation/web/index.d.ts +1 -0
  226. package/dist/inbound-mail-foundation/web/index.js +1 -0
  227. package/dist/inbound-provider-imap/connect-mailbox.d.ts +28 -5
  228. package/dist/inbound-provider-imap/connect-mailbox.js +91 -41
  229. package/dist/inbound-provider-imap/connect-my-mailbox.d.ts +13 -0
  230. package/dist/inbound-provider-imap/connect-my-mailbox.js +25 -0
  231. package/dist/inbound-provider-imap/constants.d.ts +12 -1
  232. package/dist/inbound-provider-imap/constants.js +12 -1
  233. package/dist/inbound-provider-imap/credential-document.js +2 -2
  234. package/dist/inbound-provider-imap/download-batch.d.ts +6 -0
  235. package/dist/inbound-provider-imap/download-batch.js +24 -0
  236. package/dist/inbound-provider-imap/feature.d.ts +2 -2
  237. package/dist/inbound-provider-imap/feature.js +49 -32
  238. package/dist/inbound-provider-imap/fetch-content.d.ts +4 -0
  239. package/dist/inbound-provider-imap/fetch-content.js +68 -0
  240. package/dist/inbound-provider-imap/i18n.js +18 -2
  241. package/dist/inbound-provider-imap/imap-client.d.ts +27 -23
  242. package/dist/inbound-provider-imap/imap-client.js +30 -68
  243. package/dist/inbound-provider-imap/index.d.ts +2 -1
  244. package/dist/inbound-provider-imap/index.js +2 -1
  245. package/dist/inbound-provider-imap/mime-content.d.ts +11 -0
  246. package/dist/inbound-provider-imap/mime-content.js +66 -0
  247. package/dist/inbound-provider-imap/parse-message.d.ts +35 -0
  248. package/dist/inbound-provider-imap/parse-message.js +113 -0
  249. package/dist/inbound-provider-imap/reconnect-account.d.ts +17 -0
  250. package/dist/inbound-provider-imap/reconnect-account.js +18 -0
  251. package/dist/inbound-provider-imap/reconnect-mailbox.d.ts +14 -0
  252. package/dist/inbound-provider-imap/reconnect-mailbox.js +71 -0
  253. package/dist/inbound-provider-imap/reconnect-prefill.query.d.ts +3 -0
  254. package/dist/inbound-provider-imap/reconnect-prefill.query.js +34 -0
  255. package/dist/inbound-provider-imap/screens.d.ts +6 -1
  256. package/dist/inbound-provider-imap/screens.js +174 -41
  257. package/dist/inbound-provider-imap/send-mail.d.ts +2 -2
  258. package/dist/inbound-provider-imap/send-mail.js +8 -1
  259. package/dist/inbound-provider-imap/sender-auth-headers.d.ts +3 -0
  260. package/dist/inbound-provider-imap/sender-auth-headers.js +18 -0
  261. package/dist/inbound-provider-inmemory/feature.d.ts +15 -0
  262. package/dist/inbound-provider-inmemory/feature.js +57 -2
  263. package/dist/inbound-provider-inmemory/index.d.ts +1 -1
  264. package/dist/inbound-provider-inmemory/index.js +1 -1
  265. package/dist/jobs/handlers/consumer.write.d.ts +4 -4
  266. package/dist/jobs/handlers/retry.write.d.ts +1 -1
  267. package/dist/jobs/handlers/trigger.write.d.ts +1 -1
  268. package/dist/managed-pages/handlers/set.write.d.ts +1 -1
  269. package/dist/managed-pages/web/i18n.js +1 -0
  270. package/dist/personal-access-tokens/handlers/create.write.d.ts +1 -1
  271. package/dist/personal-access-tokens/handlers/revoke.write.d.ts +1 -1
  272. package/dist/readiness/handlers/run-probes.write.d.ts +1 -1
  273. package/dist/secrets/handlers/delete.write.d.ts +1 -1
  274. package/dist/secrets/handlers/set.write.d.ts +1 -1
  275. package/dist/sessions/handlers/revoke-all-for-user.write.d.ts +1 -1
  276. package/dist/sessions/handlers/revoke-all-others.write.d.ts +1 -1
  277. package/dist/sessions/handlers/revoke.write.d.ts +1 -1
  278. package/dist/subscription-paddle/checkout-config.query.d.ts +3 -0
  279. package/dist/subscription-paddle/checkout-config.query.js +14 -0
  280. package/dist/subscription-paddle/checkout-methods.d.ts +6 -0
  281. package/dist/subscription-paddle/checkout-methods.js +85 -0
  282. package/dist/subscription-paddle/checkout-page.d.ts +19 -0
  283. package/dist/subscription-paddle/checkout-page.js +126 -0
  284. package/dist/subscription-paddle/client.d.ts +24 -0
  285. package/dist/subscription-paddle/client.js +60 -0
  286. package/dist/subscription-paddle/constants.d.ts +16 -0
  287. package/dist/subscription-paddle/constants.js +25 -0
  288. package/dist/subscription-paddle/feature.d.ts +49 -0
  289. package/dist/subscription-paddle/feature.js +192 -0
  290. package/dist/subscription-paddle/index.d.ts +2 -0
  291. package/dist/subscription-paddle/index.js +4 -0
  292. package/dist/subscription-paddle/plan-switch-methods.d.ts +6 -0
  293. package/dist/subscription-paddle/plan-switch-methods.js +114 -0
  294. package/dist/subscription-paddle/price-methods.d.ts +15 -0
  295. package/dist/subscription-paddle/price-methods.js +105 -0
  296. package/dist/subscription-paddle/runtime.d.ts +42 -0
  297. package/dist/subscription-paddle/runtime.js +109 -0
  298. package/dist/subscription-paddle/subscription-state.d.ts +34 -0
  299. package/dist/subscription-paddle/subscription-state.js +48 -0
  300. package/dist/subscription-paddle/tenant-binding.d.ts +8 -0
  301. package/dist/subscription-paddle/tenant-binding.js +26 -0
  302. package/dist/subscription-paddle/verify-webhook.d.ts +4 -0
  303. package/dist/subscription-paddle/verify-webhook.js +55 -0
  304. package/dist/subscription-paddle/webhook-events.d.ts +9 -0
  305. package/dist/subscription-paddle/webhook-events.js +124 -0
  306. package/dist/template-resolver/handlers/collection-item.query.js +2 -2
  307. package/dist/template-resolver/handlers/collection-list.query.js +4 -2
  308. package/dist/template-resolver/handlers/collection-set.write.d.ts +1 -1
  309. package/dist/template-resolver/handlers/collection-shared.d.ts +11 -0
  310. package/dist/template-resolver/handlers/collection-shared.js +16 -0
  311. package/dist/template-resolver/handlers/copy-locale.write.d.ts +1 -1
  312. package/dist/template-resolver/handlers/discard-draft.write.d.ts +2 -2
  313. package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +1 -1
  314. package/dist/template-resolver/handlers/import-bundle.write.d.ts +1 -1
  315. package/dist/template-resolver/handlers/patch-draft.write.d.ts +1 -1
  316. package/dist/template-resolver/handlers/reset-to-system.write.d.ts +1 -1
  317. package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +1 -1
  318. package/dist/template-resolver/handlers/resource-remove.write.d.ts +1 -1
  319. package/dist/template-resolver/handlers/resource-upload.write.d.ts +1 -1
  320. package/dist/template-resolver/handlers/send-test.write.d.ts +1 -1
  321. package/dist/template-resolver/handlers/set.write.d.ts +1 -1
  322. package/dist/template-resolver/handlers/toggle-status.write.d.ts +4 -4
  323. package/dist/template-resolver/handlers/upsert-system.write.d.ts +1 -1
  324. package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +1 -1
  325. package/dist/template-resolver/web/client-plugin.d.ts +4 -0
  326. package/dist/template-resolver/web/client-plugin.js +134 -52
  327. package/dist/template-resolver/web/i18n.js +1 -1
  328. package/dist/tenant/handlers/add-member.write.d.ts +1 -1
  329. package/dist/tenant/handlers/cancel-invitation.write.d.ts +1 -1
  330. package/dist/tenant/handlers/create.write.d.ts +1 -1
  331. package/dist/tenant/handlers/remove-member.write.d.ts +1 -1
  332. package/dist/tenant/handlers/toggle-enabled.write.d.ts +2 -2
  333. package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
  334. package/dist/tenant/handlers/update.write.d.ts +1 -1
  335. package/dist/tenant-handover/handlers/claim.write.d.ts +1 -1
  336. package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +1 -1
  337. package/dist/tenant-lifecycle/handlers/request-destruction.write.d.ts +1 -1
  338. package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +1 -1
  339. package/dist/user/handlers/create.write.d.ts +1 -1
  340. package/dist/user/handlers/update.write.d.ts +1 -1
  341. package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +1 -1
  342. package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.d.ts +1 -1
  343. package/dist/user-data-rights/handlers/lift-restriction.write.d.ts +1 -1
  344. package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +1 -1
  345. package/dist/user-data-rights/handlers/request-deletion.write.d.ts +1 -1
  346. package/dist/user-data-rights/handlers/request-export.write.d.ts +1 -1
  347. package/dist/user-data-rights/handlers/restrict-account.write.d.ts +1 -1
  348. package/dist/user-data-rights/handlers/run-forget-cleanup.write.d.ts +1 -1
  349. package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +2 -1
  350. package/dist/user-profile/handlers/change-email.write.d.ts +1 -1
  351. package/dist/waitlist/handlers/export-purge.write.d.ts +2 -2
  352. package/dist/waitlist/handlers/invite.write.d.ts +1 -1
  353. package/dist/waitlist/handlers/reject.write.d.ts +1 -1
  354. package/dist/waitlist/handlers/submit.write.d.ts +1 -1
  355. package/package.json +33 -17
  356. package/src/account-security/changes.json +6 -0
  357. package/src/auth-email-password/changes.json +6 -0
  358. package/src/billing-foundation/changes.json +30 -0
  359. package/src/cap-overview/changes.json +6 -0
  360. package/src/files/changes.json +18 -0
  361. package/src/inbound-mail-foundation/changes.json +155 -0
  362. package/src/inbound-provider-imap/changes.json +42 -0
  363. package/src/jobs/changes.json +18 -0
  364. package/src/subscription-paddle/changes.json +15 -0
  365. package/src/template-resolver/changes.json +12 -0
  366. package/dist/subscription-dodo/client.d.ts +0 -17
  367. package/dist/subscription-dodo/client.js +0 -31
  368. package/dist/subscription-dodo/constants.d.ts +0 -12
  369. package/dist/subscription-dodo/constants.js +0 -19
  370. package/dist/subscription-dodo/feature.d.ts +0 -37
  371. package/dist/subscription-dodo/feature.js +0 -144
  372. package/dist/subscription-dodo/index.d.ts +0 -2
  373. package/dist/subscription-dodo/index.js +0 -4
  374. package/dist/subscription-dodo/plugin-methods.d.ts +0 -23
  375. package/dist/subscription-dodo/plugin-methods.js +0 -260
  376. package/dist/subscription-dodo/runtime.d.ts +0 -27
  377. package/dist/subscription-dodo/runtime.js +0 -89
  378. package/dist/subscription-dodo/verify-webhook.d.ts +0 -37
  379. package/dist/subscription-dodo/verify-webhook.js +0 -189
  380. package/src/subscription-dodo/changes.json +0 -14
@@ -0,0 +1,8 @@
1
+ import { type WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
2
+ import * as z from "zod";
3
+ import { type InboundMailRoles } from "../mail-roles.ts";
4
+ export declare const RECONNECT_AUDIT_REASON = "reconnect";
5
+ export declare const reactivateAccountSchema: z.ZodObject<{
6
+ accountId: z.ZodUUID;
7
+ }, z.core.$strict>;
8
+ export declare function createReactivateAccountHandler({ memberRoles, adminRoles, }: InboundMailRoles): WriteHandlerDef;
@@ -0,0 +1,84 @@
1
+ // reactivate-account — the only way from disconnected back to active (it also clears auth_error
2
+ // and degraded). update-account keeps rejecting disconnected accounts so a late supervisor tick
3
+ // can never undo a disconnect; a deliberate reconnect goes through here instead, keeping the
4
+ // accountId and with it cursor, seen anchors and threads.
5
+ import { requireSecretsContext } from "@cosmicdrift/kumiko-bundled-features/secrets";
6
+ import { SYSTEM_ROLE, SYSTEM_USER_ID, } from "@cosmicdrift/kumiko-framework/engine";
7
+ import { AccessDeniedError, failNotFound, UnprocessableError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
8
+ import * as z from "zod";
9
+ import { InboundMailAccountStatuses, InboundMailConnectErrors, InboundMailFoundationHandlers, inboundCredentialSecretKey, } from "../constants.js";
10
+ import { MAIL_ACCOUNT_AGGREGATE_TYPE, MAIL_ACCOUNT_UPDATED_EVENT_QN, } from "../events.js";
11
+ import { accessRolesOf } from "../mail-roles.js";
12
+ import { loadCurrentMailAccountPayload } from "./account-state.js";
13
+ import { mayReactivateMailbox } from "./scope-visibility.js";
14
+ export const RECONNECT_AUDIT_REASON = "reconnect";
15
+ // Strict: the trusted authserv-id only changes through set-trusted-authserv-id.
16
+ export const reactivateAccountSchema = z.object({ accountId: z.uuid() }).strict();
17
+ const CREDENTIAL_PRESENCE_REASON = "the credential secret is checked on the request transaction because reconnect-mailbox writes it earlier in the same transaction, which a separate connection would not see";
18
+ // The OAuth callback re-arms a personal mailbox for its owner after the owner signed in: the signed
19
+ // state carries the owner, the dispatcher only sees the system user.
20
+ function isSystemActor(user) {
21
+ return user.id === SYSTEM_USER_ID && user.roles.includes(SYSTEM_ROLE);
22
+ }
23
+ function isAlreadyHealthy(current) {
24
+ return (current.status === InboundMailAccountStatuses.active &&
25
+ current.watchState === "idle" &&
26
+ current.lastError === null);
27
+ }
28
+ // Returns false when nothing had to change.
29
+ async function appendMailAccountReactivation(ctx, accountId, current) {
30
+ if (isAlreadyHealthy(current))
31
+ return false;
32
+ const eventPayload = {
33
+ ...current,
34
+ status: InboundMailAccountStatuses.active,
35
+ watchState: "idle",
36
+ lastError: null,
37
+ };
38
+ const headers = {
39
+ providerName: current.provider,
40
+ reason: RECONNECT_AUDIT_REASON,
41
+ };
42
+ await ctx.events.unsafeAppend({
43
+ aggregateId: accountId,
44
+ aggregateType: MAIL_ACCOUNT_AGGREGATE_TYPE,
45
+ type: MAIL_ACCOUNT_UPDATED_EVENT_QN,
46
+ payload: eventPayload,
47
+ headers,
48
+ });
49
+ return true;
50
+ }
51
+ export function createReactivateAccountHandler({ memberRoles, adminRoles, }) {
52
+ return {
53
+ name: "reactivate-account",
54
+ schema: reactivateAccountSchema,
55
+ // Members: the owner re-arms a personal mailbox; mayReactivateMailbox keeps shared ones to admins.
56
+ access: { roles: accessRolesOf(InboundMailFoundationHandlers.reactivateAccount, memberRoles) },
57
+ agent: { expose: false },
58
+ escapeHatch: { grants: ["unsafeRaw"], reason: CREDENTIAL_PRESENCE_REASON },
59
+ description: "Brings a disconnected, interrupted or sign-in-failed mailbox back to active under its existing accountId, so cursor, seen messages and threads survive; the caller replaces the credential in the same flow.",
60
+ handler: async (event, ctx) => {
61
+ const payload = reactivateAccountSchema.parse(event.payload);
62
+ const current = await loadCurrentMailAccountPayload(ctx, payload.accountId);
63
+ if (!current)
64
+ return failNotFound("mail-account", payload.accountId);
65
+ if (!isSystemActor(event.user) &&
66
+ !mayReactivateMailbox(current.ownerUserId, event.user, adminRoles)) {
67
+ return writeFailure(new AccessDeniedError({ details: { reason: "not_mailbox_owner" } }));
68
+ }
69
+ // Nothing to re-arm, so no credential is needed either.
70
+ if (isAlreadyHealthy(current)) {
71
+ return { isSuccess: true, data: { accountId: payload.accountId, changed: false } };
72
+ }
73
+ // Every other starting state: without a credential the supervisor would only fail again.
74
+ const secrets = requireSecretsContext({ secrets: ctx.services.secrets, user: ctx.user }, "reactivate-account").withRunner(ctx.db.unsafeRaw());
75
+ const hasCredential = await secrets.has(event.user.tenantId, inboundCredentialSecretKey(payload.accountId));
76
+ if (!hasCredential) {
77
+ const { reason, i18nKey } = InboundMailConnectErrors.credentialRequired;
78
+ return writeFailure(new UnprocessableError(reason, { i18nKey }));
79
+ }
80
+ const changed = await appendMailAccountReactivation(ctx, payload.accountId, current);
81
+ return { isSuccess: true, data: { accountId: payload.accountId, changed } };
82
+ },
83
+ };
84
+ }
@@ -0,0 +1,8 @@
1
+ import type { WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
2
+ import * as z from "zod";
3
+ import type { InboundMailRoles } from "../mail-roles.ts";
4
+ export declare const retryQuarantinedMessageSchema: z.ZodObject<{
5
+ accountId: z.ZodUUID;
6
+ providerMessageId: z.ZodString;
7
+ }, z.core.$strict>;
8
+ export declare function createRetryQuarantinedMessageHandler({ memberRoles, adminRoles, }: InboundMailRoles): WriteHandlerDef;
@@ -0,0 +1,34 @@
1
+ // retry-quarantined-message — marks one quarantined mail for a reload. The next successful sync of
2
+ // the mailbox fetches it again and, when it now goes through, removes the quarantine row
3
+ // (quarantine-retry.ts). The handler only flags; the supervisor owns the provider connection.
4
+ import { AccessDeniedError, failNotFound, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
5
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
6
+ import * as z from "zod";
7
+ import { quarantineTable } from "../quarantine-entities.js";
8
+ import { loadCurrentMailAccountPayload } from "./account-state.js";
9
+ import { mayManageQuarantine } from "./scope-visibility.js";
10
+ export const retryQuarantinedMessageSchema = z
11
+ .object({ accountId: z.uuid(), providerMessageId: z.string().min(1).max(500) })
12
+ .strict();
13
+ export function createRetryQuarantinedMessageHandler({ memberRoles, adminRoles, }) {
14
+ return {
15
+ name: "retry-quarantined-message",
16
+ schema: retryQuarantinedMessageSchema,
17
+ access: { roles: [...memberRoles] },
18
+ agent: { expose: false },
19
+ description: "Asks for a quarantined mail to be loaded again from the provider on the next sync of its mailbox; the owner of a personal mailbox or, for a shared one, an admin may call it, and the quarantine entry disappears once the mail went through.",
20
+ handler: async (event, ctx) => {
21
+ const payload = retryQuarantinedMessageSchema.parse(event.payload);
22
+ const account = await loadCurrentMailAccountPayload(ctx, payload.accountId);
23
+ if (!account)
24
+ return failNotFound("mail-account", payload.accountId);
25
+ if (!mayManageQuarantine(account.ownerUserId, event.user, adminRoles)) {
26
+ return writeFailure(new AccessDeniedError({ details: { reason: "not_mailbox_owner" } }));
27
+ }
28
+ const updated = await ctx.db.updateMany(quarantineTable, { retryRequestedAt: Temporal.Now.instant().toString() }, { accountId: payload.accountId, providerMessageId: payload.providerMessageId });
29
+ if (updated.length === 0)
30
+ return failNotFound("mail-quarantine", payload.providerMessageId);
31
+ return { isSuccess: true, data: { requested: true } };
32
+ },
33
+ };
34
+ }
@@ -1,4 +1,12 @@
1
- export declare function isVisibleToCaller(row: Readonly<Record<string, unknown>>, user: {
1
+ type Caller = {
2
2
  readonly id: string;
3
3
  readonly roles: readonly string[];
4
- }): boolean;
4
+ };
5
+ export declare function isVisibleToCaller(row: Readonly<Record<string, unknown>>, user: Caller): boolean;
6
+ export declare function isMailAdmin(user: {
7
+ readonly roles: readonly string[];
8
+ }, adminRoles: readonly string[]): boolean;
9
+ export declare function isAccountListableBy(row: Readonly<Record<string, unknown>>, user: Caller, adminRoles: readonly string[]): boolean;
10
+ export declare function mayReactivateMailbox(ownerUserId: unknown, user: Caller, adminRoles: readonly string[]): boolean;
11
+ export declare function mayManageQuarantine(ownerUserId: string | null, user: Caller, adminRoles: readonly string[]): boolean;
12
+ export {};
@@ -1,11 +1,30 @@
1
- // Scope visibility: shared rows (`ownerUserId=null`) are visible to any authorized caller;
2
- // personal rows are visible only to the owner, `TenantAdmin`, or `SystemAdmin` for compliance.
3
- // The same rule applies to `read_mail_accounts` and `read_inbound_messages`.
1
+ // Scope visibility: shared rows (`ownerUserId=null`) are visible to any authorized caller.
2
+ // A personal mailbox is private to its owner: its messages, bodies, attachments and quarantine are
3
+ // never shown to admins, who see only the operational metadata of the account (address, status).
4
+ function isShared(ownerUserId) {
5
+ return ownerUserId == null || ownerUserId === "";
6
+ }
4
7
  export function isVisibleToCaller(row, user) {
5
8
  const owner = row["ownerUserId"];
6
- if (owner == null || owner === "")
7
- return true;
8
- if (owner === user.id)
9
- return true;
10
- return user.roles.includes("TenantAdmin") || user.roles.includes("SystemAdmin");
9
+ return isShared(owner) || owner === user.id;
10
+ }
11
+ export function isMailAdmin(user, adminRoles) {
12
+ return adminRoles.some((role) => user.roles.includes(role));
13
+ }
14
+ // Account metadata (not content) of a personal mailbox stays visible to admins for operations.
15
+ export function isAccountListableBy(row, user, adminRoles) {
16
+ return isVisibleToCaller(row, user) || isMailAdmin(user, adminRoles);
17
+ }
18
+ // A personal mailbox is re-armed (and its sender check changed) by its owner only: a reconnect
19
+ // re-reads the owner's mail with whatever credential the caller supplies, so an admin must not do
20
+ // it. A shared mailbox belongs to the tenant: admins only.
21
+ export function mayReactivateMailbox(ownerUserId, user, adminRoles) {
22
+ if (isShared(ownerUserId))
23
+ return isMailAdmin(user, adminRoles);
24
+ return ownerUserId === user.id;
25
+ }
26
+ // Quarantine entries carry sender and subject of the mail, so those of a personal mailbox are the
27
+ // owner's alone; shared mailboxes are administered by admins.
28
+ export function mayManageQuarantine(ownerUserId, user, adminRoles) {
29
+ return isShared(ownerUserId) ? isMailAdmin(user, adminRoles) : ownerUserId === user.id;
11
30
  }
@@ -0,0 +1,13 @@
1
+ import * as z from "zod";
2
+ import type { InboundMailRoles } from "../mail-roles.ts";
3
+ export declare const setTrustedAuthservIdSchema: z.ZodObject<{
4
+ accountId: z.ZodUUID;
5
+ trustedAuthservId: z.ZodNullable<z.ZodString>;
6
+ }, z.core.$strict>;
7
+ export declare const createSetTrustedAuthservIdHandler: ({ adminRoles }: InboundMailRoles) => import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"set-trusted-authserv-id", z.ZodObject<{
8
+ accountId: z.ZodUUID;
9
+ trustedAuthservId: z.ZodNullable<z.ZodString>;
10
+ }, z.core.$strict>, {
11
+ accountId: string;
12
+ changed: boolean;
13
+ }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined, undefined>;
@@ -0,0 +1,54 @@
1
+ // set-trusted-authserv-id — the one place that changes which Authentication-Results header the
2
+ // sender check believes. It is separate from connect/update/reactivate on purpose: a payload that
3
+ // can smuggle this value in through a generic update would let any caller mark forged headers as
4
+ // trusted, and it must never be a model-callable tool.
5
+ import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
6
+ import { failNotFound, failUnprocessable } from "@cosmicdrift/kumiko-framework/errors";
7
+ import * as z from "zod";
8
+ import { InboundMailAccountStatuses } from "../constants.js";
9
+ import { MAIL_ACCOUNT_AGGREGATE_TYPE, MAIL_ACCOUNT_UPDATED_EVENT_QN, } from "../events.js";
10
+ import { normalizeTrustedAuthservId } from "../sender-auth.js";
11
+ import { loadCurrentMailAccountPayload } from "./account-state.js";
12
+ import { trustedAuthservIdInputSchema } from "./trusted-authserv-id.js";
13
+ import { auditReasonFor } from "./update-account.write.js";
14
+ export const setTrustedAuthservIdSchema = z
15
+ .object({
16
+ accountId: z.uuid(),
17
+ trustedAuthservId: trustedAuthservIdInputSchema,
18
+ })
19
+ .strict();
20
+ export const createSetTrustedAuthservIdHandler = ({ adminRoles }) => defineWriteHandler({
21
+ name: "set-trusted-authserv-id",
22
+ schema: setTrustedAuthservIdSchema,
23
+ // A tenant security policy, personal mailboxes included: a trusted authserv-id makes forged senders
24
+ // count as authenticated for the whole tenant (contact matching, auto replies), so owners never set it.
25
+ access: { roles: [...adminRoles] },
26
+ agent: { expose: false },
27
+ description: "Sets or clears the authserv-id of the mailbox's own server, which decides whose Authentication-Results header the sender check trusts; null or empty clears it, a connected mailbox only.",
28
+ handler: async (event, ctx) => {
29
+ const payload = event.payload;
30
+ const current = await loadCurrentMailAccountPayload(ctx, payload.accountId);
31
+ if (!current)
32
+ return failNotFound("mail-account", payload.accountId);
33
+ if (current.status === InboundMailAccountStatuses.disconnected) {
34
+ return failUnprocessable("account_disconnected", { accountId: payload.accountId });
35
+ }
36
+ const trustedAuthservId = normalizeTrustedAuthservId(payload.trustedAuthservId);
37
+ if (current.trustedAuthservId === trustedAuthservId) {
38
+ return { isSuccess: true, data: { accountId: payload.accountId, changed: false } };
39
+ }
40
+ const eventPayload = { ...current, trustedAuthservId };
41
+ const headers = {
42
+ providerName: current.provider,
43
+ reason: auditReasonFor(event.user, undefined),
44
+ };
45
+ await ctx.events.unsafeAppend({
46
+ aggregateId: payload.accountId,
47
+ aggregateType: MAIL_ACCOUNT_AGGREGATE_TYPE,
48
+ type: MAIL_ACCOUNT_UPDATED_EVENT_QN,
49
+ payload: eventPayload,
50
+ headers,
51
+ });
52
+ return { isSuccess: true, data: { accountId: payload.accountId, changed: true } };
53
+ },
54
+ });
@@ -0,0 +1,24 @@
1
+ import { type configuredPiiSubjectKms } from "@cosmicdrift/kumiko-framework/crypto";
2
+ import type { HandlerContext, TenantId } from "@cosmicdrift/kumiko-framework/engine";
3
+ import { type InboundMessageEventHeaders } from "../events.ts";
4
+ import type { IngestMessagePayload } from "./ingest-message-schema.ts";
5
+ /**
6
+ * Normalizes threadKey:
7
+ * 1. Use the provider thread ID when present — prefix it with the provider
8
+ * so IMAP References keys and Graph conversationIds never collide.
9
+ * 2. Otherwise use the root of the References chain (the oldest Message-ID,
10
+ * which is the thread anchor shared by all replies).
11
+ * 3. Otherwise use the own Message-ID → single-message thread; a later
12
+ * reply carries it as the References root and joins the same thread.
13
+ */
14
+ export declare function buildThreadKey(p: IngestMessagePayload, effectiveMessageIdHeader: string): string;
15
+ export type ThreadRollupInput = {
16
+ readonly tenantId: TenantId;
17
+ readonly threadAggId: string;
18
+ readonly threadKey: string;
19
+ readonly subject: string;
20
+ readonly receivedAtIso: string;
21
+ readonly headers: InboundMessageEventHeaders;
22
+ readonly piiKms: ReturnType<typeof configuredPiiSubjectKms>;
23
+ };
24
+ export declare function appendThreadRollup(ctx: HandlerContext, input: ThreadRollupInput): Promise<void>;
@@ -0,0 +1,98 @@
1
+ import { encryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
2
+ import { acquireNamespacedAdvisoryLock, countWhere } from "@cosmicdrift/kumiko-framework/db";
3
+ import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
4
+ import { MAIL_THREAD_PII_FIELDS, mailThreadEntity } from "../entities.js";
5
+ import { MAIL_THREAD_AGGREGATE_TYPE, MAIL_THREAD_UPDATED_EVENT_QN, } from "../events.js";
6
+ import { inboundMessagesProjectionTable } from "../projection.js";
7
+ const THREAD_ROLLUP_MAX_ATTEMPTS = 5;
8
+ // pg_advisory_xact_lock namespace (int4) for the step-5 thread-rollup lock
9
+ // — 'inbm' as ASCII, keeps it disjoint from the framework's other fixed
10
+ // single-int advisory-lock keys (schema bootstrap, es-ops boot).
11
+ const THREAD_ROLLUP_LOCK_NAMESPACE = 0x696e626d;
12
+ /**
13
+ * Normalizes threadKey:
14
+ * 1. Use the provider thread ID when present — prefix it with the provider
15
+ * so IMAP References keys and Graph conversationIds never collide.
16
+ * 2. Otherwise use the root of the References chain (the oldest Message-ID,
17
+ * which is the thread anchor shared by all replies).
18
+ * 3. Otherwise use the own Message-ID → single-message thread; a later
19
+ * reply carries it as the References root and joins the same thread.
20
+ */
21
+ export function buildThreadKey(p, effectiveMessageIdHeader) {
22
+ if (p.providerThreadId)
23
+ return `${p.providerName}:${p.providerThreadId}`.slice(0, 500);
24
+ const referencesRoot = p.references[0];
25
+ if (referencesRoot)
26
+ return `mid:${referencesRoot}`.slice(0, 500);
27
+ return `mid:${effectiveMessageIdHeader}`.slice(0, 500);
28
+ }
29
+ // ---------------------------------------------------------------
30
+ // 5. Thread-Rollup. New snapshot (live COUNT + max(lastMessageAt)) via
31
+ // ctx.events.tryAppend in a bounded retry loop — the apply stays a
32
+ // dumb, replay-idempotent UPSERT. lastMessageAtIso is not PII (a
33
+ // timestamp), so it stays plaintext-comparable; subject is
34
+ // encrypted once up front (constant across retries, doesn't
35
+ // depend on the thread's prior state).
36
+ //
37
+ // messageCount is a live COUNT, not previousCount+1, so two
38
+ // concurrent ingests on the same thread (Watch-Push vs.
39
+ // Poll-Reconciliation overlap, or two different messages of the
40
+ // same thread arriving at once) converge on the true count.
41
+ // acquireNamespacedAdvisoryLock below serializes this whole
42
+ // countWhere+append section per threadAggId — transaction-scoped,
43
+ // so it survives events.tryAppend's inner SAVEPOINT and releases at
44
+ // the enclosing TX's commit/rollback. Without it, a concurrent
45
+ // commit landing between countWhere and events.tryAppend's own fresh
46
+ // version-read makes the append succeed with a stale count instead
47
+ // of a VersionConflictError, so the retry loop below never sees it
48
+ // as a conflict to retry (the original gap). Unlike step 4, a
49
+ // lost version-conflict race here must NOT be treated as a no-op
50
+ // duplicate — skipping would leave this message's contribution to
51
+ // messageCount/lastMessageAt out of the thread forever. Exhausting
52
+ // all attempts throws, rolling back the whole TX (including the
53
+ // step-4 message append) for a clean re-ingest.
54
+ // ---------------------------------------------------------------
55
+ export async function appendThreadRollup(ctx, input) {
56
+ const { tenantId, threadAggId, threadKey, subject, receivedAtIso, headers, piiKms } = input;
57
+ const threadPlainPii = { tenantId, subject: subject };
58
+ const encryptedThreadFields = piiKms
59
+ ? await encryptPiiFieldValues(threadPlainPii, mailThreadEntity, MAIL_THREAD_PII_FIELDS, piiKms, {
60
+ requestId: `inbound-mail-foundation:ingest-message:thread:${threadAggId}`,
61
+ tenantId,
62
+ }, { tenantId, entityName: MAIL_THREAD_AGGREGATE_TYPE })
63
+ : threadPlainPii;
64
+ const threadRollupRunner = ctx.db.unsafeRaw();
65
+ await acquireNamespacedAdvisoryLock(threadRollupRunner, THREAD_ROLLUP_LOCK_NAMESPACE, threadAggId);
66
+ let threadAppendOk = false;
67
+ for (let attempt = 1; attempt <= THREAD_ROLLUP_MAX_ATTEMPTS && !threadAppendOk; attempt++) {
68
+ const threadEvents = await ctx.events.load(threadAggId);
69
+ const lastThreadEvent = threadEvents[threadEvents.length - 1];
70
+ // @cast-boundary engine-payload — eigene events, shape via defineEvent
71
+ const previousThread = lastThreadEvent?.payload;
72
+ const previousLastAt = previousThread?.lastMessageAtIso ?? "";
73
+ const newLastAt = receivedAtIso > previousLastAt ? receivedAtIso : previousLastAt;
74
+ const messageCount = await countWhere(threadRollupRunner, inboundMessagesProjectionTable, {
75
+ tenantId,
76
+ threadKey,
77
+ });
78
+ const threadEventPayload = {
79
+ threadKey,
80
+ subject: encryptedThreadFields.subject,
81
+ lastMessageAtIso: newLastAt,
82
+ messageCount,
83
+ };
84
+ const threadAppend = await ctx.events.tryAppend({
85
+ aggregateId: threadAggId,
86
+ aggregateType: MAIL_THREAD_AGGREGATE_TYPE,
87
+ type: MAIL_THREAD_UPDATED_EVENT_QN,
88
+ payload: threadEventPayload,
89
+ headers,
90
+ });
91
+ threadAppendOk = threadAppend.ok;
92
+ }
93
+ if (!threadAppendOk) {
94
+ throw new InternalError({
95
+ message: `ingest-message: thread-rollup append for "${threadAggId}" lost the version-conflict race ${THREAD_ROLLUP_MAX_ATTEMPTS} times in a row — aborting for a clean re-ingest instead of persisting a stale messageCount.`,
96
+ });
97
+ }
98
+ }
@@ -0,0 +1,3 @@
1
+ import * as z from "zod";
2
+ /** authserv-id of the mailbox's own server, set only through set-trusted-authserv-id; null or empty clears it. */
3
+ export declare const trustedAuthservIdInputSchema: z.ZodNullable<z.ZodString>;
@@ -0,0 +1,3 @@
1
+ import * as z from "zod";
2
+ /** authserv-id of the mailbox's own server, set only through set-trusted-authserv-id; null or empty clears it. */
3
+ export const trustedAuthservIdInputSchema = z.string().trim().max(255).nullable();
@@ -1,5 +1,6 @@
1
- import { type WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
1
+ import { type SessionUser, type WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import * as z from "zod";
3
+ import { type InboundMailRoles } from "../mail-roles.ts";
3
4
  export declare const updateAccountSchema: z.ZodObject<{
4
5
  accountId: z.ZodUUID;
5
6
  status: z.ZodOptional<z.ZodEnum<{
@@ -7,8 +8,17 @@ export declare const updateAccountSchema: z.ZodObject<{
7
8
  auth_error: "auth_error";
8
9
  degraded: "degraded";
9
10
  }>>;
11
+ lastError: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
12
+ connection_failed: "connection_failed";
13
+ encryption_unavailable: "encryption_unavailable";
14
+ file_storage_unavailable: "file_storage_unavailable";
15
+ ingest_unavailable: "ingest_unavailable";
16
+ message_quarantined: "message_quarantined";
17
+ rate_limited: "rate_limited";
18
+ }>>>;
10
19
  watchState: z.ZodOptional<z.ZodString>;
11
20
  displayName: z.ZodOptional<z.ZodString>;
12
21
  reason: z.ZodOptional<z.ZodString>;
13
- }, z.core.$strip>;
14
- export declare const updateAccountHandler: WriteHandlerDef;
22
+ }, z.core.$strict>;
23
+ export declare function auditReasonFor(user: SessionUser, requestedReason: string | undefined): string;
24
+ export declare function createUpdateAccountHandler({ adminRoles }: InboundMailRoles): WriteHandlerDef;
@@ -6,10 +6,12 @@ import { ROLES } from "@cosmicdrift/kumiko-framework/auth";
6
6
  import { SYSTEM_USER_ID, } from "@cosmicdrift/kumiko-framework/engine";
7
7
  import { failNotFound, failUnprocessable } from "@cosmicdrift/kumiko-framework/errors";
8
8
  import * as z from "zod";
9
- import { InboundMailAccountStatuses } from "../constants.js";
9
+ import { InboundMailAccountStatuses, InboundMailFoundationHandlers, InboundMailLastErrors, } from "../constants.js";
10
10
  import { MAIL_ACCOUNT_AGGREGATE_TYPE, MAIL_ACCOUNT_UPDATED_EVENT_QN, } from "../events.js";
11
+ import { accessRolesOf } from "../mail-roles.js";
11
12
  import { loadCurrentMailAccountPayload } from "./account-state.js";
12
- export const updateAccountSchema = z.object({
13
+ export const updateAccountSchema = z
14
+ .object({
13
15
  accountId: z.uuid(),
14
16
  status: z
15
17
  .enum([
@@ -18,57 +20,71 @@ export const updateAccountSchema = z.object({
18
20
  InboundMailAccountStatuses.degraded,
19
21
  ])
20
22
  .optional(),
23
+ /** Why the mailbox is degraded; null clears it. Codes only, never provider error text. */
24
+ lastError: z.enum(Object.values(InboundMailLastErrors)).nullable().optional(),
21
25
  /** Ops-Signal ("idle", "watching", "backoff:3", ...). */
22
26
  watchState: z.string().max(100).optional(),
23
- displayName: z.string().max(200).optional(),
27
+ displayName: z.string().trim().max(200).optional(),
24
28
  /** Audit reason for metadata.headers, honored for the system identity only
25
29
  * ("watch_supervisor", "oauth_refresh"); user callers get a role-derived reason. */
26
30
  reason: z.string().min(1).max(100).optional(),
27
- });
31
+ })
32
+ .strict();
28
33
  const SYSTEM_FALLBACK_REASON = "system";
29
- function auditReasonFor(user, requestedReason) {
34
+ export function auditReasonFor(user, requestedReason) {
30
35
  if (user.id === SYSTEM_USER_ID)
31
36
  return requestedReason ?? SYSTEM_FALLBACK_REASON;
32
37
  return user.roles.includes(ROLES.SystemAdmin) ? "system_admin" : "tenant_admin";
33
38
  }
34
- export const updateAccountHandler = {
35
- name: "update-account",
36
- schema: updateAccountSchema,
37
- access: { roles: ["SystemAdmin", "TenantAdmin"] },
38
- description: "Records a status, watch-state or display-name change on a connected mailbox as a new snapshot with an audit reason, rejecting any change to an already disconnected mailbox; use it to flag an auth error or to re-activate a mailbox after fixing its credentials.",
39
- handler: async (event, ctx) => {
40
- // @cast-boundary engine-payload — dispatcher-zod-validated payload
41
- const payload = event.payload;
42
- const current = await loadCurrentMailAccountPayload(ctx, payload.accountId);
43
- if (!current) {
44
- return failNotFound("mail-account", payload.accountId);
45
- }
46
- if (current.status === InboundMailAccountStatuses.disconnected) {
47
- // Disconnected accounts are final; re-enabling requires a fresh connect
48
- // (new stream, new secret slot). This prevents a delayed supervisor tick
49
- // from restoring a disconnected account to active (disconnect vs. poll
50
- // race).
51
- return failUnprocessable("account_disconnected", { accountId: payload.accountId });
52
- }
53
- // address/provider/authMethod come unchanged from the latest snapshot
54
- // (address remains ciphertext—never re-encrypt it).
55
- const eventPayload = {
56
- ...current,
57
- ...(payload.status !== undefined ? { status: payload.status } : {}),
58
- ...(payload.watchState !== undefined ? { watchState: payload.watchState } : {}),
59
- ...(payload.displayName !== undefined ? { displayName: payload.displayName } : {}),
60
- };
61
- const headers = {
62
- providerName: current.provider,
63
- reason: auditReasonFor(event.user, payload.reason),
64
- };
65
- await ctx.events.unsafeAppend({
66
- aggregateId: payload.accountId,
67
- aggregateType: MAIL_ACCOUNT_AGGREGATE_TYPE,
68
- type: MAIL_ACCOUNT_UPDATED_EVENT_QN,
69
- payload: eventPayload,
70
- headers,
71
- });
72
- return { isSuccess: true, data: { accountId: payload.accountId } };
73
- },
74
- };
39
+ export function createUpdateAccountHandler({ adminRoles }) {
40
+ return {
41
+ name: "update-account",
42
+ schema: updateAccountSchema,
43
+ access: { roles: accessRolesOf(InboundMailFoundationHandlers.updateAccount, adminRoles) },
44
+ description: "Renames a connected mailbox (display name) as a new snapshot with an audit reason, rejecting any change to an already disconnected mailbox; status, error and watch state are reserved for the system identity.",
45
+ handler: async (event, ctx) => {
46
+ // @cast-boundary engine-payload — dispatcher-zod-validated payload
47
+ const payload = event.payload;
48
+ // Status, error and watch state are machine signals of the supervisor and the OAuth refresh; an admin
49
+ // setting them could hide an auth error or fake an active mailbox.
50
+ const isSystemOnlyField = payload.status !== undefined ||
51
+ payload.lastError !== undefined ||
52
+ payload.watchState !== undefined ||
53
+ payload.reason !== undefined;
54
+ if (isSystemOnlyField && event.user.id !== SYSTEM_USER_ID) {
55
+ return failUnprocessable("field_requires_system_identity");
56
+ }
57
+ const current = await loadCurrentMailAccountPayload(ctx, payload.accountId);
58
+ if (!current) {
59
+ return failNotFound("mail-account", payload.accountId);
60
+ }
61
+ if (current.status === InboundMailAccountStatuses.disconnected) {
62
+ // Only reactivate-account may leave disconnected. This prevents a delayed
63
+ // supervisor tick from restoring a disconnected account to active
64
+ // (disconnect vs. poll race).
65
+ return failUnprocessable("account_disconnected", { accountId: payload.accountId });
66
+ }
67
+ // address/provider/authMethod come unchanged from the latest snapshot
68
+ // (address remains ciphertext—never re-encrypt it).
69
+ const eventPayload = {
70
+ ...current,
71
+ ...(payload.status !== undefined ? { status: payload.status } : {}),
72
+ ...(payload.watchState !== undefined ? { watchState: payload.watchState } : {}),
73
+ ...(payload.displayName !== undefined ? { displayName: payload.displayName } : {}),
74
+ ...(payload.lastError !== undefined ? { lastError: payload.lastError } : {}),
75
+ };
76
+ const headers = {
77
+ providerName: current.provider,
78
+ reason: auditReasonFor(event.user, payload.reason),
79
+ };
80
+ await ctx.events.unsafeAppend({
81
+ aggregateId: payload.accountId,
82
+ aggregateType: MAIL_ACCOUNT_AGGREGATE_TYPE,
83
+ type: MAIL_ACCOUNT_UPDATED_EVENT_QN,
84
+ payload: eventPayload,
85
+ headers,
86
+ });
87
+ return { isSuccess: true, data: { accountId: payload.accountId } };
88
+ },
89
+ };
90
+ }